From 7a8ac1b09b1cf321f259b8eb9b832424d2c7bf5b Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 13 Nov 2017 19:43:34 +1100 Subject: WM: message bus replacement for property notifiers Use dynamically generated message publish/subscribe so buttons and manipulators update properly. This resolves common glitches where manipulators weren't updating as well as the UI when add-ons exposed properties which hard coded listeners weren't checking for. Python can also publish/scribe changes via `bpy.msgbus`. See D2917 --- source/blender/blenkernel/BKE_context.h | 1 + source/blender/blenkernel/BKE_screen.h | 7 + source/blender/blenkernel/intern/context.c | 5 + source/blender/blenloader/intern/readfile.c | 2 + source/blender/editors/include/ED_screen.h | 15 + source/blender/editors/include/UI_interface.h | 3 + source/blender/editors/interface/interface.c | 38 ++ source/blender/editors/screen/area.c | 81 +++++ source/blender/editors/screen/screen_edit.c | 4 + .../blender/editors/space_buttons/space_buttons.c | 1 + source/blender/editors/space_file/space_file.c | 29 ++ source/blender/editors/space_time/space_time.c | 45 +++ source/blender/editors/space_view3d/space_view3d.c | 71 ++++ source/blender/editors/space_view3d/view3d_draw.c | 2 + .../space_view3d/view3d_manipulator_camera.c | 51 +++ .../editors/transform/transform_manipulator.c | 62 ++++ source/blender/makesdna/DNA_windowmanager_types.h | 4 + source/blender/makesrna/intern/rna_access.c | 9 + source/blender/python/intern/CMakeLists.txt | 2 + source/blender/python/intern/bpy.c | 2 + source/blender/python/intern/bpy_msgbus.c | 400 +++++++++++++++++++++ source/blender/python/intern/bpy_msgbus.h | 30 ++ source/blender/windowmanager/CMakeLists.txt | 5 + source/blender/windowmanager/WM_message.h | 30 ++ source/blender/windowmanager/WM_types.h | 1 + source/blender/windowmanager/intern/wm.c | 11 +- .../blender/windowmanager/intern/wm_event_system.c | 35 +- source/blender/windowmanager/intern/wm_files.c | 5 + source/blender/windowmanager/intern/wm_init_exit.c | 3 + .../manipulators/WM_manipulator_api.h | 9 + .../manipulators/WM_manipulator_types.h | 5 + .../manipulators/intern/wm_manipulator_map.c | 20 ++ .../intern/wm_manipulator_target_props.c | 51 +++ .../windowmanager/manipulators/wm_manipulator_fn.h | 2 + .../message_bus/intern/wm_message_bus.c | 245 +++++++++++++ .../message_bus/intern/wm_message_bus_intern.h | 41 +++ .../message_bus/intern/wm_message_bus_rna.c | 316 ++++++++++++++++ .../message_bus/intern/wm_message_bus_static.c | 136 +++++++ .../windowmanager/message_bus/wm_message_bus.h | 257 +++++++++++++ 39 files changed, 2033 insertions(+), 3 deletions(-) create mode 100644 source/blender/python/intern/bpy_msgbus.c create mode 100644 source/blender/python/intern/bpy_msgbus.h create mode 100644 source/blender/windowmanager/WM_message.h create mode 100644 source/blender/windowmanager/message_bus/intern/wm_message_bus.c create mode 100644 source/blender/windowmanager/message_bus/intern/wm_message_bus_intern.h create mode 100644 source/blender/windowmanager/message_bus/intern/wm_message_bus_rna.c create mode 100644 source/blender/windowmanager/message_bus/intern/wm_message_bus_static.c create mode 100644 source/blender/windowmanager/message_bus/wm_message_bus.h (limited to 'source/blender') diff --git a/source/blender/blenkernel/BKE_context.h b/source/blender/blenkernel/BKE_context.h index 5628c650d34..75e658613cb 100644 --- a/source/blender/blenkernel/BKE_context.h +++ b/source/blender/blenkernel/BKE_context.h @@ -156,6 +156,7 @@ struct ARegion *CTX_wm_region(const bContext *C); void *CTX_wm_region_data(const bContext *C); struct ARegion *CTX_wm_menu(const bContext *C); struct wmManipulatorGroup *CTX_wm_manipulator_group(const bContext *C); +struct wmMsgBus *CTX_wm_message_bus(const bContext *C); struct ReportList *CTX_wm_reports(const bContext *C); struct View3D *CTX_wm_view3d(const bContext *C); diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h index 8ba103b915e..6669f3103da 100644 --- a/source/blender/blenkernel/BKE_screen.h +++ b/source/blender/blenkernel/BKE_screen.h @@ -56,6 +56,7 @@ struct wmWindow; struct wmWindowManager; struct WorkSpace; struct GPUFXSettings; +struct wmMsgBus; #include "BLI_compiler_attrs.h" @@ -139,6 +140,12 @@ typedef struct ARegionType { /* contextual changes should be handled here */ void (*listener)(struct bScreen *, struct ScrArea *, struct ARegion *, struct wmNotifier *, const struct Scene *scene); + /* Optional callback to generate subscriptions. */ + void (*message_subscribe)( + const struct bContext *C, + struct WorkSpace *workspace, struct Scene *scene, + struct bScreen *sc, struct ScrArea *sa, struct ARegion *ar, + struct wmMsgBus *mbus); void (*free)(struct ARegion *); diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c index 855216d089b..7b7a7c8b7af 100644 --- a/source/blender/blenkernel/intern/context.c +++ b/source/blender/blenkernel/intern/context.c @@ -679,6 +679,11 @@ struct wmManipulatorGroup *CTX_wm_manipulator_group(const bContext *C) return C->wm.manipulator_group; } +struct wmMsgBus *CTX_wm_message_bus(const bContext *C) +{ + return C->wm.manager ? C->wm.manager->message_bus : NULL; +} + struct ReportList *CTX_wm_reports(const bContext *C) { if (C->wm.manager) diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index e6d2be02977..9e09d2e0bd1 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -6531,6 +6531,8 @@ static void direct_link_windowmanager(FileData *fd, wmWindowManager *wm) wm->addonconf = NULL; wm->userconf = NULL; + wm->message_bus = NULL; + BLI_listbase_clear(&wm->jobs); BLI_listbase_clear(&wm->drags); diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index 23e65011387..89a6828a59d 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -55,6 +55,9 @@ struct ARegion; struct uiBlock; struct rcti; struct Main; +struct wmMsgBus; +struct wmMsgSubscribeKey; +struct wmMsgSubscribeValue; /* regions */ void ED_region_do_listen( @@ -86,6 +89,18 @@ void ED_region_grid_draw(struct ARegion *ar, float zoomx, float zoomy); float ED_region_blend_factor(struct ARegion *ar); void ED_region_visible_rect(struct ARegion *ar, struct rcti *rect); +/* message_bus callbacks */ +void ED_region_do_msg_notify_tag_redraw( + struct bContext *C, struct wmMsgSubscribeKey *msg_key, struct wmMsgSubscribeValue *msg_val); +void ED_area_do_msg_notify_tag_refresh( + struct bContext *C, struct wmMsgSubscribeKey *msg_key, struct wmMsgSubscribeValue *msg_val); + +/* message bus */ +void ED_region_message_subscribe( + struct bContext *C, + struct WorkSpace *workspace, struct Scene *scene, + struct bScreen *screen, struct ScrArea *sa, struct ARegion *ar, + struct wmMsgBus *mbus); /* spaces */ void ED_spacetypes_keymap(struct wmKeyConfig *keyconf); diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index e2a6ca65b51..eba31a21094 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -73,6 +73,7 @@ struct bNodeSocket; struct wmDropBox; struct wmDrag; struct wmEvent; +struct wmMsgBus; typedef struct uiBut uiBut; typedef struct uiBlock uiBlock; @@ -875,6 +876,8 @@ uiLayout *UI_block_layout(uiBlock *block, int dir, int type, int x, int y, int s void UI_block_layout_set_current(uiBlock *block, uiLayout *layout); void UI_block_layout_resolve(uiBlock *block, int *x, int *y); +void UI_region_message_subscribe(struct ARegion *ar, struct wmMsgBus *mbus); + uiBlock *uiLayoutGetBlock(uiLayout *layout); void uiLayoutSetFunc(uiLayout *layout, uiMenuHandleFunc handlefunc, void *argv); diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index fbdd48d42a7..9e6d9f23442 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -41,6 +41,7 @@ #include "DNA_scene_types.h" #include "DNA_screen_types.h" #include "DNA_userdef_types.h" +#include "DNA_workspace_types.h" #include "BLI_math.h" #include "BLI_listbase.h" @@ -69,11 +70,14 @@ #include "WM_api.h" #include "WM_types.h" #include "wm_subwindow.h" +#include "WM_message.h" #include "RNA_access.h" #include "BPY_extern.h" +#include "ED_screen.h" + #include "IMB_colormanagement.h" #include "interface_intern.h" @@ -1453,6 +1457,40 @@ void UI_block_draw(const bContext *C, uiBlock *block) ui_draw_links(block); } +static void ui_block_message_subscribe(ARegion *ar, struct wmMsgBus *mbus, uiBlock *block) +{ + uiBut *but_prev = NULL; + /* possibly we should keep the region this block is contained in? */ + for (uiBut *but = block->buttons.first; but; but = but->next) { + if (but->rnapoin.type && but->rnaprop) { + /* quick check to avoid adding buttons representing a vector, multiple times. */ + if ((but_prev && + (but_prev->rnaprop == but->rnaprop) && + (but_prev->rnapoin.type == but->rnapoin.type) && + (but_prev->rnapoin.data == but->rnapoin.data) && + (but_prev->rnapoin.id.data == but->rnapoin.id.data)) == false) + { + /* TODO: could make this into utility function. */ + WM_msg_subscribe_rna( + mbus, &but->rnapoin, but->rnaprop, + &(const wmMsgSubscribeValue){ + .owner = ar, + .user_data = ar, + .notify = ED_region_do_msg_notify_tag_redraw, + }, __func__); + but_prev = but; + } + } + } +} + +void UI_region_message_subscribe(ARegion *ar, struct wmMsgBus *mbus) +{ + for (uiBlock *block = ar->uiblocks.first; block; block = block->next) { + ui_block_message_subscribe(ar, mbus, block); + } +} + /* ************* EVENTS ************* */ /** diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 315b02cde93..08d92dfc9ff 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -51,6 +51,7 @@ #include "WM_api.h" #include "WM_types.h" +#include "WM_message.h" #include "wm_subwindow.h" #include "ED_screen.h" @@ -511,6 +512,33 @@ void ED_region_set(const bContext *C, ARegion *ar) ED_region_pixelspace(ar); } +/* Follow wmMsgNotifyFn spec */ +void ED_region_do_msg_notify_tag_redraw( + bContext *UNUSED(C), wmMsgSubscribeKey *UNUSED(msg_key), wmMsgSubscribeValue *msg_val) +{ + ARegion *ar = msg_val->owner; + ED_region_tag_redraw(ar); + + /* This avoids _many_ situations where header/properties control display settings. + * the common case is space properties in the header */ + if (ELEM(ar->regiontype, RGN_TYPE_HEADER, RGN_TYPE_UI)) { + while (ar && ar->prev) { + ar = ar->prev; + } + for (; ar; ar = ar->next) { + if (ELEM(ar->regiontype, RGN_TYPE_WINDOW, RGN_TYPE_CHANNELS)) { + ED_region_tag_redraw(ar); + } + } + } +} +/* Follow wmMsgNotifyFn spec */ +void ED_area_do_msg_notify_tag_refresh( + bContext *UNUSED(C), wmMsgSubscribeKey *UNUSED(msg_key), wmMsgSubscribeValue *msg_val) +{ + ScrArea *sa = msg_val->user_data; + ED_area_tag_refresh(sa); +} /* only exported for WM */ void ED_region_do_draw(bContext *C, ARegion *ar) @@ -589,6 +617,37 @@ void ED_region_do_draw(bContext *C, ARegion *ar) region_draw_emboss(ar, &ar->winrct); } } + + /* We may want to detach message-subscriptions from drawing. */ + { + WorkSpace *workspace = CTX_wm_workspace(C); + wmWindowManager *wm = CTX_wm_manager(C); + bScreen *screen = WM_window_get_active_screen(win); + Scene *scene = CTX_data_scene(C); + struct wmMsgBus *mbus = wm->message_bus; + WM_msgbus_clear_by_owner(mbus, ar); + + /* Cheat, always subscribe to this space type properties. + * + * This covers most cases and avoids copy-paste similar code for each space type. + */ + if (ELEM(ar->regiontype, RGN_TYPE_WINDOW, RGN_TYPE_CHANNELS, RGN_TYPE_UI, RGN_TYPE_TOOLS)) { + SpaceLink *sl = sa->spacedata.first; + + PointerRNA ptr; + RNA_pointer_create(&screen->id, &RNA_Space, sl, &ptr); + + wmMsgSubscribeValue msg_sub_value_region_tag_redraw = { + .owner = ar, + .user_data = ar, + .notify = ED_region_do_msg_notify_tag_redraw, + }; + /* All properties for this space type. */ + WM_msg_subscribe_rna(mbus, &ptr, NULL, &msg_sub_value_region_tag_redraw, __func__); + } + + ED_region_message_subscribe(C, workspace, scene, screen, sa, ar, mbus); + } } /* ********************************** @@ -2595,3 +2654,25 @@ void ED_region_cache_draw_cached_segments(const ARegion *ar, const int num_segme immUnbindProgram(); } } + +/** + * Generate subscriptions for this region. + */ +void ED_region_message_subscribe( + bContext *C, + struct WorkSpace *workspace, struct Scene *scene, + struct bScreen *screen, struct ScrArea *sa, struct ARegion *ar, + struct wmMsgBus *mbus) +{ + if (ar->manipulator_map != NULL) { + WM_manipulatormap_message_subscribe(C, ar->manipulator_map, ar, mbus); + } + + if (BLI_listbase_is_empty(&ar->uiblocks)) { + UI_region_message_subscribe(ar, mbus); + } + + if (ar->type->message_subscribe != NULL) { + ar->type->message_subscribe(C, workspace, scene, screen, sa, ar, mbus); + } +} diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index b702edf8864..d3f99dc8152 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -66,6 +66,8 @@ #include "UI_interface.h" +#include "WM_message.h" + /* XXX actually should be not here... solve later */ #include "wm_subwindow.h" @@ -1004,6 +1006,8 @@ void ED_region_exit(bContext *C, ARegion *ar) ar->regiontimer = NULL; } + WM_msgbus_clear_by_owner(wm->message_bus, ar); + CTX_wm_region_set(C, prevar); } diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c index 74b20360a53..d2f407bfa8c 100644 --- a/source/blender/editors/space_buttons/space_buttons.c +++ b/source/blender/editors/space_buttons/space_buttons.c @@ -44,6 +44,7 @@ #include "WM_api.h" #include "WM_types.h" +#include "WM_message.h" #include "RNA_access.h" diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index 780c2ec5a47..3f26604c23a 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -49,6 +49,7 @@ #include "WM_api.h" #include "WM_types.h" +#include "WM_message.h" #include "ED_space_api.h" #include "ED_screen.h" @@ -354,6 +355,33 @@ static void file_main_region_listener( } } +static void file_main_region_message_subscribe( + const struct bContext *UNUSED(C), + struct WorkSpace *UNUSED(workspace), struct Scene *UNUSED(scene), + struct bScreen *screen, struct ScrArea *sa, struct ARegion *ar, + struct wmMsgBus *mbus) +{ + SpaceFile *sfile = sa->spacedata.first; + FileSelectParams *params = ED_fileselect_get_params(sfile); + /* This is a bit odd that a region owns the subscriber for an area, + * keep for now since all subscribers for WM are regions. + * May be worth re-visiting later. */ + wmMsgSubscribeValue msg_sub_value_area_tag_refresh = { + .owner = ar, + .user_data = sa, + .notify = ED_area_do_msg_notify_tag_refresh, + }; + + /* FileSelectParams */ + { + PointerRNA ptr; + RNA_pointer_create(&screen->id, &RNA_FileSelectParams, params, &ptr); + + /* All properties for this space type. */ + WM_msg_subscribe_rna(mbus, &ptr, NULL, &msg_sub_value_area_tag_refresh, __func__); + } +} + static void file_main_region_draw(const bContext *C, ARegion *ar) { /* draw entirely, view changes should be handled here */ @@ -731,6 +759,7 @@ void ED_spacetype_file(void) art->init = file_main_region_init; art->draw = file_main_region_draw; art->listener = file_main_region_listener; + art->message_subscribe = file_main_region_message_subscribe; art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D; BLI_addhead(&st->regiontypes, art); diff --git a/source/blender/editors/space_time/space_time.c b/source/blender/editors/space_time/space_time.c index 99ac0d9024c..283dbf3b4e2 100644 --- a/source/blender/editors/space_time/space_time.c +++ b/source/blender/editors/space_time/space_time.c @@ -58,6 +58,9 @@ #include "WM_api.h" #include "WM_types.h" +#include "WM_message.h" + +#include "RNA_access.h" #include "BIF_gl.h" @@ -649,6 +652,47 @@ static void time_main_region_listener( } } +static void time_main_region_message_subscribe( + const struct bContext *UNUSED(C), + struct WorkSpace *UNUSED(workspace), struct Scene *scene, + struct bScreen *screen, struct ScrArea *sa, struct ARegion *ar, + struct wmMsgBus *mbus) +{ + PointerRNA ptr; + RNA_pointer_create(&screen->id, &RNA_SpaceTimeline, sa->spacedata.first, &ptr); + + wmMsgSubscribeValue msg_sub_value_region_tag_redraw = { + .owner = ar, + .user_data = ar, + .notify = ED_region_do_msg_notify_tag_redraw, + }; + + /* Timeline depends on scene properties. */ + { + bool use_preview = (scene->r.flag & SCER_PRV_RANGE); + extern PropertyRNA rna_Scene_frame_start; + extern PropertyRNA rna_Scene_frame_end; + extern PropertyRNA rna_Scene_frame_preview_start; + extern PropertyRNA rna_Scene_frame_preview_end; + extern PropertyRNA rna_Scene_use_preview_range; + extern PropertyRNA rna_Scene_frame_current; + const PropertyRNA *props[] = { + use_preview ? &rna_Scene_frame_preview_start : &rna_Scene_frame_start, + use_preview ? &rna_Scene_frame_preview_end : &rna_Scene_frame_end, + &rna_Scene_use_preview_range, + &rna_Scene_frame_current, + }; + + PointerRNA idptr; + RNA_id_pointer_create(&scene->id, &idptr); + + for (int i = 0; i < ARRAY_SIZE(props); i++) { + WM_msg_subscribe_rna(mbus, &idptr, props[i], &msg_sub_value_region_tag_redraw, __func__); + } + } +} + + /* ************************ header time area region *********************** */ /* add handlers, stuff you only do once or on area/region changes */ @@ -797,6 +841,7 @@ void ED_spacetype_time(void) art->init = time_main_region_init; art->draw = time_main_region_draw; art->listener = time_main_region_listener; + art->message_subscribe = time_main_region_message_subscribe; art->keymap = time_keymap; art->lock = 1; /* Due to pointcache, see T4960. */ BLI_addhead(&st->regiontypes, art); diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 12d0ed4501b..728547e7a40 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -67,6 +67,7 @@ #include "WM_api.h" #include "WM_types.h" +#include "WM_message.h" #include "RE_engine.h" #include "RE_pipeline.h" @@ -1056,6 +1057,75 @@ static void view3d_main_region_listener( } } +static void view3d_main_region_message_subscribe( + const struct bContext *UNUSED(C), + struct WorkSpace *workspace, struct Scene *scene, + struct bScreen *UNUSED(screen), struct ScrArea *UNUSED(sa), struct ARegion *ar, + struct wmMsgBus *mbus) +{ + /* Developer note: there are many properties that impact 3D view drawing, + * so instead of subscribing to individual properties, just subscribe to types + * accepting some redundant redraws. + * + * For other space types we might try avoid this, keep the 3D view as an exceptional case! */ + ViewRender *view_render = BKE_viewrender_get(scene, workspace); + wmMsgParams_RNA msg_key_params = {0}; + + /* Only subscribe to types. */ + StructRNA *type_array[] = { + /* These object have properties that impact drawing. */ + &RNA_AreaLamp, + &RNA_Camera, + &RNA_Lamp, + &RNA_Speaker, + &RNA_SunLamp, + + /* General types the 3D view depends on. */ + &RNA_Object, + &RNA_UnitSettings, /* grid-floor */ + + &RNA_ViewRenderSettings, + &RNA_World, + }; + + wmMsgSubscribeValue msg_sub_value_region_tag_redraw = { + .owner = ar, + .user_data = ar, + .notify = ED_region_do_msg_notify_tag_redraw, + }; + + for (int i = 0; i < ARRAY_SIZE(type_array); i++) { + msg_key_params.ptr.type = type_array[i]; + WM_msg_subscribe_rna_params( + mbus, + &msg_key_params, + &msg_sub_value_region_tag_redraw, + __func__); + } + + /* Subscribe to a handful of other properties. */ + RegionView3D *rv3d = ar->regiondata; + + WM_msg_subscribe_rna_anon_prop(mbus, RenderSettings, resolution_x, &msg_sub_value_region_tag_redraw); + WM_msg_subscribe_rna_anon_prop(mbus, RenderSettings, resolution_y, &msg_sub_value_region_tag_redraw); + WM_msg_subscribe_rna_anon_prop(mbus, RenderSettings, pixel_aspect_x, &msg_sub_value_region_tag_redraw); + WM_msg_subscribe_rna_anon_prop(mbus, RenderSettings, pixel_aspect_y, &msg_sub_value_region_tag_redraw); + if (rv3d->persp == RV3D_CAMOB) { + WM_msg_subscribe_rna_anon_prop(mbus, RenderSettings, use_border, &msg_sub_value_region_tag_redraw); + } + + /* Each engine could be responsible for its own engine data types. + * For now this is simplest. */ + if (STREQ(view_render->engine_id, RE_engine_id_BLENDER_EEVEE)) { + extern StructRNA RNA_ViewLayerEngineSettingsEevee; + WM_msg_subscribe_rna_anon_type(mbus, ViewLayerEngineSettingsEevee, &msg_sub_value_region_tag_redraw); + } + else if (STREQ(view_render->engine_id, RE_engine_id_BLENDER_CLAY)) { + extern StructRNA RNA_ViewLayerEngineSettingsClay; + WM_msg_subscribe_rna_anon_type(mbus, ViewLayerEngineSettingsClay, &msg_sub_value_region_tag_redraw); + } +} + /* concept is to retrieve cursor type context-less */ static void view3d_main_region_cursor(wmWindow *win, ScrArea *UNUSED(sa), ARegion *UNUSED(ar)) { @@ -1418,6 +1488,7 @@ void ED_spacetype_view3d(void) art->free = view3d_main_region_free; art->duplicate = view3d_main_region_duplicate; art->listener = view3d_main_region_listener; + art->message_subscribe = view3d_main_region_message_subscribe; art->cursor = view3d_main_region_cursor; art->lock = 1; /* can become flag, see BKE_spacedata_draw_locks */ BLI_addhead(&st->regiontypes, art); diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index c341a4cde1c..6ea2ff10af2 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -90,6 +90,8 @@ #include "WM_api.h" #include "WM_types.h" +#include "RNA_access.h" + #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" diff --git a/source/blender/editors/space_view3d/view3d_manipulator_camera.c b/source/blender/editors/space_view3d/view3d_manipulator_camera.c index a4d408eedc6..d020571930a 100644 --- a/source/blender/editors/space_view3d/view3d_manipulator_camera.c +++ b/source/blender/editors/space_view3d/view3d_manipulator_camera.c @@ -45,6 +45,7 @@ #include "WM_api.h" #include "WM_types.h" +#include "WM_message.h" #include "view3d_intern.h" /* own include */ @@ -217,6 +218,55 @@ static void WIDGETGROUP_camera_refresh(const bContext *C, wmManipulatorGroup *mg } +static void WIDGETGROUP_camera_message_subscribe( + const bContext *C, wmManipulatorGroup *mgroup, struct wmMsgBus *mbus) +{ + ARegion *ar = CTX_wm_region(C); + Object *ob = CTX_data_active_object(C); + Camera *ca = ob->data; + + wmMsgSubscribeValue msg_sub_value_mpr_tag_refresh = { + .owner = ar, + .user_data = mgroup->parent_mmap, + .notify = WM_manipulator_do_msg_notify_tag_refresh, + }; + + { + extern PropertyRNA rna_Camera_dof_distance; + extern PropertyRNA rna_Camera_draw_size; + extern PropertyRNA rna_Camera_ortho_scale; + extern PropertyRNA rna_Camera_sensor_fit; + extern PropertyRNA rna_Camera_sensor_width; + extern PropertyRNA rna_Camera_shift_x; + extern PropertyRNA rna_Camera_shift_y; + extern PropertyRNA rna_Camera_type; + const PropertyRNA *props[] = { + &rna_Camera_dof_distance, + &rna_Camera_draw_size, + &rna_Camera_ortho_scale, + &rna_Camera_sensor_fit, + &rna_Camera_sensor_width, + &rna_Camera_shift_x, + &rna_Camera_shift_y, + &rna_Camera_type, + }; + + PointerRNA idptr; + RNA_id_pointer_create(&ca->id, &idptr); + + for (int i = 0; i < ARRAY_SIZE(props); i++) { + WM_msg_subscribe_rna(mbus, &idptr, props[i], &msg_sub_value_mpr_tag_refresh, __func__); + } + } + + /* Subscribe to render settings */ + { + WM_msg_subscribe_rna_anon_prop(mbus, RenderSettings, resolution_x, &msg_sub_value_mpr_tag_refresh); + WM_msg_subscribe_rna_anon_prop(mbus, RenderSettings, resolution_y, &msg_sub_value_mpr_tag_refresh); + WM_msg_subscribe_rna_anon_prop(mbus, RenderSettings, pixel_aspect_x, &msg_sub_value_mpr_tag_refresh); + WM_msg_subscribe_rna_anon_prop(mbus, RenderSettings, pixel_aspect_y, &msg_sub_value_mpr_tag_refresh); + } +} void VIEW3D_WGT_camera(wmManipulatorGroupType *wgt) { @@ -230,6 +280,7 @@ void VIEW3D_WGT_camera(wmManipulatorGroupType *wgt) wgt->poll = WIDGETGROUP_camera_poll; wgt->setup = WIDGETGROUP_camera_setup; wgt->refresh = WIDGETGROUP_camera_refresh; + wgt->message_subscribe = WIDGETGROUP_camera_message_subscribe; } /** \} */ diff --git a/source/blender/editors/transform/transform_manipulator.c b/source/blender/editors/transform/transform_manipulator.c index 3d9ab560bc7..0e0c2f3ae25 100644 --- a/source/blender/editors/transform/transform_manipulator.c +++ b/source/blender/editors/transform/transform_manipulator.c @@ -65,6 +65,7 @@ #include "WM_api.h" #include "WM_types.h" +#include "WM_message.h" #include "ED_armature.h" #include "ED_curve.h" @@ -1115,6 +1116,47 @@ static void manipulator_line_range(const View3D *v3d, const short axis_type, flo *r_len -= *r_start; } +static void manipulator_xform_message_subscribe( + wmManipulatorGroup *mgroup, struct wmMsgBus *mbus, + bScreen *screen, ScrArea *sa, ARegion *ar, const void *type_fn) +{ + /* Subscribe to view properties */ + wmMsgSubscribeValue msg_sub_value_mpr_tag_refresh = { + .owner = ar, + .user_data = mgroup->parent_mmap, + .notify = WM_manipulator_do_msg_notify_tag_refresh, + }; + + PointerRNA space_ptr; + RNA_pointer_create(&screen->id, &RNA_SpaceView3D, sa->spacedata.first, &space_ptr); + + { + extern PropertyRNA rna_SpaceView3D_transform_orientation; + const PropertyRNA *props[] = { + &rna_SpaceView3D_transform_orientation, + }; + for (int i = 0; i < ARRAY_SIZE(props); i++) { + WM_msg_subscribe_rna(mbus, &space_ptr, props[i], &msg_sub_value_mpr_tag_refresh, __func__); + } + } + + if (type_fn == TRANSFORM_WGT_manipulator) { + extern PropertyRNA rna_SpaceView3D_pivot_point; + const PropertyRNA *props[] = { + &rna_SpaceView3D_pivot_point + }; + for (int i = 0; i < ARRAY_SIZE(props); i++) { + WM_msg_subscribe_rna(mbus, &space_ptr, props[i], &msg_sub_value_mpr_tag_refresh, __func__); + } + } + else if (type_fn == VIEW3D_WGT_xform_cage) { + /* pass */ + } + else { + BLI_assert(0); + } +} + /** \} */ @@ -1383,6 +1425,15 @@ static void WIDGETGROUP_manipulator_refresh(const bContext *C, wmManipulatorGrou MAN_ITER_AXES_END; } +static void WIDGETGROUP_manipulator_message_subscribe( + const bContext *C, wmManipulatorGroup *mgroup, struct wmMsgBus *mbus) +{ + bScreen *screen = CTX_wm_screen(C); + ScrArea *sa = CTX_wm_area(C); + ARegion *ar = CTX_wm_region(C); + manipulator_xform_message_subscribe(mgroup, mbus, screen, sa, ar, TRANSFORM_WGT_manipulator); +} + static void WIDGETGROUP_manipulator_draw_prepare(const bContext *C, wmManipulatorGroup *mgroup) { ManipulatorGroup *man = mgroup->customdata; @@ -1464,6 +1515,7 @@ void TRANSFORM_WGT_manipulator(wmManipulatorGroupType *wgt) wgt->poll = WIDGETGROUP_manipulator_poll; wgt->setup = WIDGETGROUP_manipulator_setup; wgt->refresh = WIDGETGROUP_manipulator_refresh; + wgt->message_subscribe = WIDGETGROUP_manipulator_message_subscribe; wgt->draw_prepare = WIDGETGROUP_manipulator_draw_prepare; } @@ -1585,6 +1637,15 @@ static void WIDGETGROUP_xform_cage_refresh(const bContext *C, wmManipulatorGroup } } +static void WIDGETGROUP_xform_cage_message_subscribe( + const bContext *C, wmManipulatorGroup *mgroup, struct wmMsgBus *mbus) +{ + bScreen *screen = CTX_wm_screen(C); + ScrArea *sa = CTX_wm_area(C); + ARegion *ar = CTX_wm_region(C); + manipulator_xform_message_subscribe(mgroup, mbus, screen, sa, ar, VIEW3D_WGT_xform_cage); +} + static void WIDGETGROUP_xform_cage_draw_prepare(const bContext *C, wmManipulatorGroup *mgroup) { struct XFormCageWidgetGroup *xmgroup = mgroup->customdata; @@ -1613,6 +1674,7 @@ void VIEW3D_WGT_xform_cage(wmManipulatorGroupType *wgt) wgt->poll = WIDGETGROUP_xform_cage_poll; wgt->setup = WIDGETGROUP_xform_cage_setup; wgt->refresh = WIDGETGROUP_xform_cage_refresh; + wgt->message_subscribe = WIDGETGROUP_xform_cage_message_subscribe; wgt->draw_prepare = WIDGETGROUP_xform_cage_draw_prepare; } diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h index 0c49d9c90e5..fd2a422b4c8 100644 --- a/source/blender/makesdna/DNA_windowmanager_types.h +++ b/source/blender/makesdna/DNA_windowmanager_types.h @@ -41,6 +41,7 @@ struct wmWindowManager; struct wmWindow; +struct wmMsgBus; struct wmEvent; struct wmGesture; struct wmOperatorType; @@ -156,6 +157,9 @@ typedef struct wmWindowManager { char is_interface_locked; /* indicates whether interface is locked for user interaction */ char par[7]; + + struct wmMsgBus *message_bus; + } wmWindowManager; /* wmWindowManager.initialized */ diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 44e2d4b3144..880b1ef9c08 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -62,6 +62,7 @@ #include "RNA_enum_types.h" #include "WM_api.h" +#include "WM_message.h" /* flush updates */ #include "DNA_object_types.h" @@ -1976,8 +1977,16 @@ static void rna_property_update(bContext *C, Main *bmain, Scene *scene, PointerR else prop->update(bmain, scene, ptr); } +#if 0 if (prop->noteflag) WM_main_add_notifier(prop->noteflag, ptr->id.data); +#else + { + struct wmMsgBus *mbus = CTX_wm_message_bus(C); + /* we could add NULL check, for now don't */ + WM_msg_publish_rna(mbus, ptr, prop); + } +#endif } if (!is_rna || (prop->flag & PROP_IDPROPERTY)) { diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt index 36aa310fe10..41018468695 100644 --- a/source/blender/python/intern/CMakeLists.txt +++ b/source/blender/python/intern/CMakeLists.txt @@ -65,6 +65,7 @@ set(SRC bpy_library_load.c bpy_library_write.c bpy_manipulator_wrap.c + bpy_msgbus.c bpy_operator.c bpy_operator_wrap.c bpy_path.c @@ -102,6 +103,7 @@ set(SRC bpy_intern_string.h bpy_library.h bpy_manipulator_wrap.h + bpy_msgbus.h bpy_operator.h bpy_operator_wrap.h bpy_path.h diff --git a/source/blender/python/intern/bpy.c b/source/blender/python/intern/bpy.c index edb4b3ec0ec..0b791b6acaa 100644 --- a/source/blender/python/intern/bpy.c +++ b/source/blender/python/intern/bpy.c @@ -58,6 +58,7 @@ /* external util modules */ #include "../generic/idprop_py_api.h" +#include "bpy_msgbus.h" #ifdef WITH_FREESTYLE # include "BPy_Freestyle.h" @@ -350,6 +351,7 @@ void BPy_init_modules(void) PyModule_AddObject(mod, "app", BPY_app_struct()); PyModule_AddObject(mod, "_utils_units", BPY_utils_units()); PyModule_AddObject(mod, "_utils_previews", BPY_utils_previews_module()); + PyModule_AddObject(mod, "msgbus", BPY_msgbus_module()); /* bpy context */ RNA_pointer_create(NULL, &RNA_Context, (void *)BPy_GetContext(), &ctx_ptr); diff --git a/source/blender/python/intern/bpy_msgbus.c b/source/blender/python/intern/bpy_msgbus.c new file mode 100644 index 00000000000..945d2a9b6cc --- /dev/null +++ b/source/blender/python/intern/bpy_msgbus.c @@ -0,0 +1,400 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/python/intern/bpy_msgbus.c + * \ingroup pythonintern + * This file defines '_bpy_msgbus' module, exposed as 'bpy.msgbus'. + */ + +#include + +#include "../generic/python_utildefines.h" +#include "../generic/py_capi_utils.h" +#include "../mathutils/mathutils.h" + +#include "BLI_utildefines.h" + +#include "BKE_context.h" + +#include "WM_api.h" +#include "WM_types.h" +#include "WM_message.h" + +#include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "bpy_capi_utils.h" +#include "bpy_rna.h" +#include "bpy_intern_string.h" +#include "bpy_manipulator_wrap.h" /* own include */ + + +#include "bpy_msgbus.h" /* own include */ + + +/* -------------------------------------------------------------------- */ +/** \name Internal Utils + * \{ */ + +#define BPY_MSGBUS_RNA_MSGKEY_DOC \ +" :arg key: Represents the type of data being subscribed to\n" \ +"\n" \ +" Arguments include\n" \ +" - :class:`bpy.types.Property` instance.\n" \ +" - :class:`bpy.types.Struct` type.\n" \ +" - (:class:`bpy.types.Struct`, str) type and property name.\n" \ +" :type key: Muliple\n" + +/** + * There are multiple ways we can get RNA from Python, + * it's also possible to register a type instead of an instance. + * + * This function handles converting Python to RNA subscription information. + * + * \param py_sub: See #BPY_MSGBUS_RNA_MSGKEY_DOC for description. + * \param msg_key_params: Message key with all members zeroed out. + * \return -1 on failure, 0 on success. + */ +static int py_msgbus_rna_key_from_py( + PyObject *py_sub, + wmMsgParams_RNA *msg_key_params, + const char *error_prefix) +{ + + /* Allow common case, object rotation, location - etc. */ + if (BaseMathObject_CheckExact(py_sub)) { + BaseMathObject *py_sub_math = (BaseMathObject *)py_sub; + if (py_sub_math->cb_user == NULL) { + PyErr_Format( + PyExc_TypeError, + "%s: math argument has no owner", + error_prefix); + return -1; + } + py_sub = py_sub_math->cb_user; + /* Common case will use BPy_PropertyRNA_Check below. */ + } + + if (BPy_PropertyRNA_Check(py_sub)) { + BPy_PropertyRNA *data_prop = (BPy_PropertyRNA *)py_sub; + PYRNA_PROP_CHECK_INT(data_prop); + msg_key_params->ptr = data_prop->ptr; + msg_key_params->prop = data_prop->prop; + } + else if (BPy_StructRNA_Check(py_sub)) { + /* note, this isn't typically used since we don't edit structs directly. */ + BPy_StructRNA *data_srna = (BPy_StructRNA *)py_sub; + PYRNA_STRUCT_CHECK_INT(data_srna); + msg_key_params->ptr = data_srna->ptr; + } + /* TODO - property / type, not instance. */ + else if (PyType_Check(py_sub)) { + StructRNA *data_type = pyrna_struct_as_srna(py_sub, false, error_prefix); + if (data_type == NULL) { + return -1; + } + msg_key_params->ptr.type = data_type; + } + else if (PyTuple_CheckExact(py_sub)) { + if (PyTuple_GET_SIZE(py_sub) == 2) { + PyObject *data_type_py = PyTuple_GET_ITEM(py_sub, 0); + PyObject *data_prop_py = PyTuple_GET_ITEM(py_sub, 1); + StructRNA *data_type = pyrna_struct_as_srna(data_type_py, false, error_prefix); + if (data_type == NULL) { + return -1; + } + if (!PyUnicode_CheckExact(data_prop_py)) { + PyErr_Format( + PyExc_TypeError, + "%s: expected property to be a string", + error_prefix); + return -1; + } + PointerRNA data_type_ptr = { .type = data_type, }; + const char *data_prop_str = _PyUnicode_AsString(data_prop_py); + PropertyRNA *data_prop = RNA_struct_find_property(&data_type_ptr, data_prop_str); + + if (data_prop == NULL) { + PyErr_Format( + PyExc_TypeError, + "%s: struct %.200s does not contain property %.200s", + error_prefix, + RNA_struct_identifier(data_type), + data_prop_str); + return -1; + } + + msg_key_params->ptr.type = data_type; + msg_key_params->prop = data_prop; + } + else { + PyErr_Format( + PyExc_ValueError, + "%s: Expected a pair (type, property_id)", + error_prefix); + return -1; + } + } + return 0; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Internal Callbacks + * \{ */ + +#define BPY_MSGBUS_USER_DATA_LEN 2 + +/* Follow wmMsgNotifyFn spec */ +static void bpy_msgbus_notify( + bContext *C, wmMsgSubscribeKey *UNUSED(msg_key), wmMsgSubscribeValue *msg_val) +{ + PyGILState_STATE gilstate; + bpy_context_set(C, &gilstate); + + PyObject *user_data = msg_val->user_data; + BLI_assert(PyTuple_GET_SIZE(user_data) == BPY_MSGBUS_USER_DATA_LEN); + + PyObject *callback_args = PyTuple_GET_ITEM(user_data, 0); + PyObject *callback_notify = PyTuple_GET_ITEM(user_data, 1); + + const bool is_write_ok = pyrna_write_check(); + if (!is_write_ok) { + pyrna_write_set(true); + } + + PyObject *ret = PyObject_CallObject(callback_notify, callback_args); + + if (ret == NULL) { + PyC_Err_PrintWithFunc(callback_notify); + } + else { + if (ret != Py_None) { + PyErr_SetString(PyExc_ValueError, "the return value must be None"); + PyC_Err_PrintWithFunc(callback_notify); + } + Py_DECREF(ret); + } + + bpy_context_clear(C, &gilstate); + + if (!is_write_ok) { + pyrna_write_set(false); + } +} + +/* Follow wmMsgSubscribeValueFreeDataFn spec */ +static void bpy_msgbus_subscribe_value_free_data( + struct wmMsgSubscribeKey *UNUSED(msg_key), struct wmMsgSubscribeValue *msg_val) +{ + PyGILState_STATE gilstate = PyGILState_Ensure(); + Py_DECREF(msg_val->owner); + Py_DECREF(msg_val->user_data); + PyGILState_Release(gilstate); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Public Message Bus API + * \{ */ + +PyDoc_STRVAR(bpy_msgbus_subscribe_rna_doc, +".. function:: subscribe_rna(data, owner, args, notify)\n" +"\n" +BPY_MSGBUS_RNA_MSGKEY_DOC +" :arg owner: Handle for this subscription (compared by identity).\n" +" :type owner: Any type.\n" +"\n" +" Returns a new vector int property definition.\n" +); +static PyObject *bpy_msgbus_subscribe_rna(PyObject *UNUSED(self), PyObject *args, PyObject *kw) +{ + const char *error_prefix = "subscribe_rna"; + PyObject *py_sub = NULL; + PyObject *py_owner = NULL; + PyObject *callback_args = NULL; + PyObject *callback_notify = NULL; + + enum { + IS_PERSISTENT = (1 << 0), + }; + PyObject *py_options = NULL; + EnumPropertyItem py_options_enum[] = { + {IS_PERSISTENT, "PERSISTENT", 0, ""}, + {0, NULL, 0, NULL, NULL} + }; + int options = 0; + + static const char *_keywords[] = { + "key", + "owner", + "args", + "notify", + "options", + NULL, + }; + static _PyArg_Parser _parser = {"$OOO!OO!:subscribe_rna", _keywords, 0}; + if (!_PyArg_ParseTupleAndKeywordsFast( + args, kw, &_parser, + &py_sub, &py_owner, + &PyTuple_Type, &callback_args, + &callback_notify, + &PySet_Type, &py_options)) + { + return NULL; + } + + if (py_options && + (pyrna_set_to_enum_bitfield(py_options_enum, py_options, &options, error_prefix)) == -1) + { + return NULL; + } + + /* Note: we may want to have a way to pass this in. */ + bContext *C = (bContext *)BPy_GetContext(); + struct wmMsgBus *mbus = CTX_wm_message_bus(C); + wmMsgParams_RNA msg_key_params = {0}; + + wmMsgSubscribeValue msg_val_params = {0}; + + if (py_msgbus_rna_key_from_py(py_sub, &msg_key_params, error_prefix) == -1) { + return NULL; + } + + if (!PyFunction_Check(callback_notify)) { + PyErr_Format( + PyExc_TypeError, + "notify expects a function, found %.200s", + Py_TYPE(callback_notify)->tp_name); + return NULL; + } + + if (options != 0) { + if (options & IS_PERSISTENT) { + msg_val_params.is_persistent = true; + } + } + + /* owner can be anything. */ + { + msg_val_params.owner = py_owner; + Py_INCREF(py_owner); + } + + { + PyObject *user_data = PyTuple_New(2); + PyTuple_SET_ITEMS( + user_data, + Py_INCREF_RET(callback_args), + Py_INCREF_RET(callback_notify)); + msg_val_params.user_data = user_data; + } + + msg_val_params.notify = bpy_msgbus_notify; + msg_val_params.free_data = bpy_msgbus_subscribe_value_free_data; + + WM_msg_subscribe_rna_params(mbus, &msg_key_params, &msg_val_params, __func__); + + WM_msg_dump(mbus, __func__); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(bpy_msgbus_publish_rna_doc, +".. function:: publish_rna(data, owner, args, notify)\n" +"\n" +BPY_MSGBUS_RNA_MSGKEY_DOC +"\n" +" Notify subscribers of changes to this property\n" +" (this typically doesn't need to be called explicitly since changes will automatically publish updates).\n" +" In some cases it may be useful to publish changes explicitly using more general keys.\n" +); +static PyObject *bpy_msgbus_publish_rna(PyObject *UNUSED(self), PyObject *args, PyObject *kw) +{ + const char *error_prefix = "publish_rna"; + PyObject *py_sub = NULL; + + static const char *_keywords[] = { + "key", + NULL, + }; + static _PyArg_Parser _parser = {"$O:publish_rna", _keywords, 0}; + if (!_PyArg_ParseTupleAndKeywordsFast( + args, kw, &_parser, + &py_sub)) + { + return NULL; + } + + /* Note: we may want to have a way to pass this in. */ + bContext *C = (bContext *)BPy_GetContext(); + struct wmMsgBus *mbus = CTX_wm_message_bus(C); + wmMsgParams_RNA msg_key_params = {0}; + + if (py_msgbus_rna_key_from_py(py_sub, &msg_key_params, error_prefix) == -1) { + return NULL; + } + + WM_msg_publish_rna_params(mbus, &msg_key_params); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(bpy_msgbus_clear_by_owner_doc, +".. function:: clear_by_owner(owner)\n" +"\n" +" Clear all subscribers using this owner.\n" +); +static PyObject *bpy_msgbus_clear_by_owner(PyObject *UNUSED(self), PyObject *py_owner) +{ + bContext *C = (bContext *)BPy_GetContext(); + struct wmMsgBus *mbus = CTX_wm_message_bus(C); + WM_msgbus_clear_by_owner(mbus, py_owner); + Py_RETURN_NONE; +} + +static struct PyMethodDef BPy_msgbus_methods[] = { + {"subscribe_rna", (PyCFunction)bpy_msgbus_subscribe_rna, METH_VARARGS | METH_KEYWORDS, bpy_msgbus_subscribe_rna_doc}, + {"publish_rna", (PyCFunction)bpy_msgbus_publish_rna, METH_VARARGS | METH_KEYWORDS, bpy_msgbus_publish_rna_doc}, + {"clear_by_owner", (PyCFunction)bpy_msgbus_clear_by_owner, METH_O, bpy_msgbus_clear_by_owner_doc}, + {NULL, NULL, 0, NULL} +}; + +static struct PyModuleDef _bpy_msgbus_def = { + PyModuleDef_HEAD_INIT, + .m_name = "msgbus", + .m_methods = BPy_msgbus_methods, +}; + + +PyObject *BPY_msgbus_module(void) +{ + PyObject *submodule; + + submodule = PyModule_Create(&_bpy_msgbus_def); + + return submodule; +} + +/** \} */ + diff --git a/source/blender/python/intern/bpy_msgbus.h b/source/blender/python/intern/bpy_msgbus.h new file mode 100644 index 00000000000..97b20e9b926 --- /dev/null +++ b/source/blender/python/intern/bpy_msgbus.h @@ -0,0 +1,30 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/python/intern/bpy_msgbus.h + * \ingroup pythonintern + */ + +#ifndef __BPY_MSGBUS_H__ +#define __BPY_MSGBUS_H__ + +PyObject *BPY_msgbus_module(void); + +#endif /* __BPY_MSGBUS_H__ */ diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt index 95a0371ba23..b5784fe543c 100644 --- a/source/blender/windowmanager/CMakeLists.txt +++ b/source/blender/windowmanager/CMakeLists.txt @@ -79,6 +79,9 @@ set(SRC manipulators/intern/wm_manipulator_map.c manipulators/intern/wm_manipulator_target_props.c manipulators/intern/wm_manipulator_type.c + message_bus/intern/wm_message_bus.c + message_bus/intern/wm_message_bus_rna.c + message_bus/intern/wm_message_bus_static.c WM_api.h WM_keymap.h @@ -96,6 +99,8 @@ set(SRC manipulators/wm_manipulator_fn.h manipulators/wm_manipulator_wmapi.h manipulators/intern/wm_manipulator_intern.h + message_bus/intern/wm_message_bus_intern.h + message_bus/wm_message_bus.h ) if(WITH_AUDASPACE) diff --git a/source/blender/windowmanager/WM_message.h b/source/blender/windowmanager/WM_message.h new file mode 100644 index 00000000000..48197ae99cd --- /dev/null +++ b/source/blender/windowmanager/WM_message.h @@ -0,0 +1,30 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/windowmanager/WM_message.h + * \ingroup wm + */ + +#ifndef __WM_MESSAGE_H__ +#define __WM_MESSAGE_H__ + +#include "message_bus/wm_message_bus.h" + +#endif /* __WM_MESSAGE_H__ */ diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 1717a94678b..9edae85cc3c 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -109,6 +109,7 @@ extern "C" { struct bContext; struct wmEvent; struct wmWindowManager; +struct wmMsgBus; struct wmOperator; struct ImBuf; diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index b66bddfa6bf..623a7af5165 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -56,6 +56,7 @@ #include "WM_api.h" #include "WM_types.h" +#include "WM_message.h" #include "wm_window.h" #include "wm_event_system.h" #include "wm_draw.h" @@ -396,6 +397,10 @@ void WM_check(bContext *C) wm_window_ghostwindows_ensure(wm); } + if (wm->message_bus == NULL) { + wm->message_bus = WM_msgbus_create(); + } + /* case: fileread */ /* note: this runs in bg mode to set the screen context cb */ if ((wm->initialized & WM_WINDOW_IS_INITIALIZED) == 0) { @@ -475,7 +480,11 @@ void wm_close_and_free(bContext *C, wmWindowManager *wm) } BLI_freelistN(&wm->queue); - + + if (wm->message_bus != NULL) { + WM_msgbus_destroy(wm->message_bus); + } + BLI_freelistN(&wm->paintcursors); WM_drag_free_list(&wm->drags); diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index aa8180a7df6..b1ec1c009df 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -39,7 +39,6 @@ #include "DNA_screen_types.h" #include "DNA_scene_types.h" #include "DNA_windowmanager_types.h" -#include "DNA_workspace_types.h" #include "DNA_userdef_types.h" #include "MEM_guardedalloc.h" @@ -77,6 +76,7 @@ #include "WM_api.h" #include "WM_types.h" +#include "WM_message.h" #include "wm.h" #include "wm_window.h" #include "wm_event_system.h" @@ -242,6 +242,14 @@ void WM_main_remove_notifier_reference(const void *reference) wm_notifier_clear(note); } } + + /* Remap instead. */ +#if 0 + if (wm->message_bus) { + WM_msg_id_remove(wm->message_bus, reference); + } +#endif + } } @@ -261,6 +269,17 @@ void WM_main_remap_editor_id_reference(ID *old_id, ID *new_id) } } } + + wmWindowManager *wm = bmain->wm.first; + if (wm && wm->message_bus) { + struct wmMsgBus *mbus = wm->message_bus; + if (new_id != NULL) { + WM_msg_id_update(mbus, old_id, new_id); + } + else { + WM_msg_id_remove(mbus, old_id); + } + } } static void wm_notifier_clear(wmNotifier *note) @@ -328,7 +347,9 @@ void wm_event_do_notifiers(bContext *C) if (wm == NULL) return; - + + /* disable? - keep for now since its used for window level notifiers. */ +#if 1 /* cache & catch WM level notifiers, such as frame change, scene/screen set */ for (win = wm->windows.first; win; win = win->next) { Scene *scene = WM_window_get_active_scene(win); @@ -452,6 +473,16 @@ void wm_event_do_notifiers(bContext *C) MEM_freeN(note); } +#endif /* if 1 (postpone disabling for in favor of message-bus), eventually. */ + + /* Handle message bus. */ + { + for (win = wm->windows.first; win; win = win->next) { + CTX_wm_window_set(C, win); + WM_msgbus_handle(wm->message_bus, C); + } + CTX_wm_window_set(C, NULL); + } wm_event_do_refresh_wm_and_depsgraph(C); } diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index c3ae4af1964..7c8059fcda9 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -123,6 +123,7 @@ #include "WM_api.h" #include "WM_types.h" +#include "WM_message.h" #include "wm.h" #include "wm_files.h" #include "wm_window.h" @@ -504,7 +505,11 @@ static void wm_file_read_post(bContext *C, const bool is_startup_file, const boo BLI_callback_exec(CTX_data_main(C), NULL, BLI_CB_EVT_VERSION_UPDATE); BLI_callback_exec(CTX_data_main(C), NULL, BLI_CB_EVT_LOAD_POST); +#if 1 WM_event_add_notifier(C, NC_WM | ND_FILEREAD, NULL); +#else + WM_msg_publish_static(CTX_wm_message_bus(C), WM_MSG_STATICTYPE_FILE_READ); +#endif /* report any errors. * currently disabled if addons aren't yet loaded */ diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 4975e818051..55ed8b2a091 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -93,6 +93,7 @@ #include "WM_api.h" #include "WM_types.h" +#include "WM_message.h" #include "wm_cursors.h" #include "wm_event_system.h" @@ -192,6 +193,8 @@ void WM_init(bContext *C, int argc, const char **argv) * but keep before file reading, since that may report errors */ wm_init_reports(C); + WM_msgbus_types_init(); + /* get the default database, plus a wm */ wm_homefile_read(C, NULL, G.factory_startup, false, true, NULL, NULL); diff --git a/source/blender/windowmanager/manipulators/WM_manipulator_api.h b/source/blender/windowmanager/manipulators/WM_manipulator_api.h index a3875c50348..9214bccb6a0 100644 --- a/source/blender/windowmanager/manipulators/WM_manipulator_api.h +++ b/source/blender/windowmanager/manipulators/WM_manipulator_api.h @@ -51,6 +51,8 @@ struct wmManipulatorGroupType; struct wmManipulatorMap; struct wmManipulatorMapType; struct wmManipulatorMapType_Params; +struct wmMsgSubscribeKey; +struct wmMsgSubscribeValue; #include "wm_manipulator_fn.h" @@ -216,6 +218,11 @@ const struct wmManipulatorPropertyType *WM_manipulatortype_target_property_find( void WM_manipulatortype_target_property_def( struct wmManipulatorType *wt, const char *idname, int data_type, int array_length); +/* utilities */ +void WM_manipulator_do_msg_notify_tag_refresh( + struct bContext *C, struct wmMsgSubscribeKey *msg_key, struct wmMsgSubscribeValue *msg_val); +void WM_manipulator_target_property_subscribe_all( + struct wmManipulator *mpr, struct wmMsgBus *mbus, struct ARegion *ar); /* -------------------------------------------------------------------- */ /* wmManipulatorGroup */ @@ -245,6 +252,8 @@ void WM_manipulatormap_draw( void WM_manipulatormap_add_handlers(struct ARegion *ar, struct wmManipulatorMap *mmap); bool WM_manipulatormap_select_all(struct bContext *C, struct wmManipulatorMap *mmap, const int action); bool WM_manipulatormap_cursor_set(const struct wmManipulatorMap *mmap, struct wmWindow *win); +void WM_manipulatormap_message_subscribe( + struct bContext *C, struct wmManipulatorMap *mmap, struct ARegion *ar, struct wmMsgBus *mbus); bool WM_manipulatormap_is_any_selected(const struct wmManipulatorMap *mmap); bool WM_manipulatormap_minmax( const struct wmManipulatorMap *mmap, bool use_hidden, bool use_select, diff --git a/source/blender/windowmanager/manipulators/WM_manipulator_types.h b/source/blender/windowmanager/manipulators/WM_manipulator_types.h index d4477b8e508..5fa89b8d35f 100644 --- a/source/blender/windowmanager/manipulators/WM_manipulator_types.h +++ b/source/blender/windowmanager/manipulators/WM_manipulator_types.h @@ -346,6 +346,11 @@ typedef struct wmManipulatorGroupType { * will fall back to default tweak keymap when left NULL. */ wmManipulatorGroupFnSetupKeymap setup_keymap; + /* Optionally subscribe to wmMsgBus events, + * these are calculated automatically from RNA properties, + * only needed if manipulators depend indirectly on properties. */ + wmManipulatorGroupFnMsgBusSubscribe message_subscribe; + /* keymap created with callback from above */ struct wmKeyMap *keymap; /* Only for convenient removal. */ diff --git a/source/blender/windowmanager/manipulators/intern/wm_manipulator_map.c b/source/blender/windowmanager/manipulators/intern/wm_manipulator_map.c index a174d7720e3..5d9810272cc 100644 --- a/source/blender/windowmanager/manipulators/intern/wm_manipulator_map.c +++ b/source/blender/windowmanager/manipulators/intern/wm_manipulator_map.c @@ -305,6 +305,7 @@ static bool manipulator_prepare_drawing( /* skip */ } else { + /* Ensure we get RNA updates */ if (do_draw & WM_MANIPULATOR_IS_VISIBLE_UPDATE) { /* hover manipulators need updating, even if we don't draw them */ wm_manipulator_update(mpr, C, (mmap->update_flag[drawstep] & MANIPULATORMAP_IS_PREPARE_DRAW) != 0); @@ -953,6 +954,25 @@ ListBase *wm_manipulatormap_groups_get(wmManipulatorMap *mmap) return &mmap->groups; } +void WM_manipulatormap_message_subscribe( + bContext *C, wmManipulatorMap *mmap, ARegion *ar, struct wmMsgBus *mbus) +{ + for (wmManipulatorGroup *mgroup = mmap->groups.first; mgroup; mgroup = mgroup->next) { + if (!wm_manipulatorgroup_is_visible(mgroup, C)) { + continue; + } + for (wmManipulator *mpr = mgroup->manipulators.first; mpr; mpr = mpr->next) { + if (mpr->flag & WM_MANIPULATOR_HIDDEN) { + continue; + } + WM_manipulator_target_property_subscribe_all(mpr, mbus, ar); + } + if (mgroup->type->message_subscribe != NULL) { + mgroup->type->message_subscribe(C, mgroup, mbus); + } + } +} + /** \} */ /* wmManipulatorMap */ diff --git a/source/blender/windowmanager/manipulators/intern/wm_manipulator_target_props.c b/source/blender/windowmanager/manipulators/intern/wm_manipulator_target_props.c index 836376f1c54..137e8f5639d 100644 --- a/source/blender/windowmanager/manipulators/intern/wm_manipulator_target_props.c +++ b/source/blender/windowmanager/manipulators/intern/wm_manipulator_target_props.c @@ -35,6 +35,7 @@ #include "WM_api.h" #include "WM_types.h" +#include "WM_message.h" #include "wm.h" @@ -311,3 +312,53 @@ void WM_manipulatortype_target_property_def( } /** \} */ + +/* -------------------------------------------------------------------- */ + +/** \name Property Utilities + * \{ */ + +void WM_manipulator_do_msg_notify_tag_refresh( + bContext *UNUSED(C), wmMsgSubscribeKey *UNUSED(msg_key), wmMsgSubscribeValue *msg_val) +{ + ARegion *ar = msg_val->owner; + wmManipulatorMap *mmap = msg_val->user_data; + + ED_region_tag_redraw(ar); + WM_manipulatormap_tag_refresh(mmap); +} + +/** + * Runs on the "prepare draw" pass, + * drawing the region clears. + */ +void WM_manipulator_target_property_subscribe_all( + wmManipulator *mpr, struct wmMsgBus *mbus, ARegion *ar) +{ + if (mpr->type->target_property_defs_len) { + wmManipulatorProperty *mpr_prop_array = WM_manipulator_target_property_array(mpr); + for (int i = 0; i < mpr->type->target_property_defs_len; i++) { + wmManipulatorProperty *mpr_prop = &mpr_prop_array[i]; + if (WM_manipulator_target_property_is_valid(mpr_prop)) { + if (mpr_prop->prop) { + WM_msg_subscribe_rna( + mbus, &mpr_prop->ptr, mpr_prop->prop, + &(const wmMsgSubscribeValue){ + .owner = ar, + .user_data = ar, + .notify = ED_region_do_msg_notify_tag_redraw, + }, __func__); + WM_msg_subscribe_rna( + mbus, &mpr_prop->ptr, mpr_prop->prop, + &(const wmMsgSubscribeValue){ + .owner = ar, + .user_data = mpr->parent_mgroup->parent_mmap, + .notify = WM_manipulator_do_msg_notify_tag_refresh, + }, __func__); + } + } + } + } +} + +/** \} */ diff --git a/source/blender/windowmanager/manipulators/wm_manipulator_fn.h b/source/blender/windowmanager/manipulators/wm_manipulator_fn.h index c54024529c3..7e163f8a785 100644 --- a/source/blender/windowmanager/manipulators/wm_manipulator_fn.h +++ b/source/blender/windowmanager/manipulators/wm_manipulator_fn.h @@ -42,6 +42,8 @@ typedef void (*wmManipulatorGroupFnDrawPrepare)( typedef struct wmKeyMap *(*wmManipulatorGroupFnSetupKeymap)( const struct wmManipulatorGroupType *, struct wmKeyConfig *) ATTR_WARN_UNUSED_RESULT; +typedef void (*wmManipulatorGroupFnMsgBusSubscribe)( + const struct bContext *, struct wmManipulatorGroup *, struct wmMsgBus *); /* wmManipulator */ /* See: wmManipulatorType for docs on each type. */ diff --git a/source/blender/windowmanager/message_bus/intern/wm_message_bus.c b/source/blender/windowmanager/message_bus/intern/wm_message_bus.c new file mode 100644 index 00000000000..0e7486fa293 --- /dev/null +++ b/source/blender/windowmanager/message_bus/intern/wm_message_bus.c @@ -0,0 +1,245 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/windowmanager/message_bus/intern/wm_message_bus.c + * \ingroup wm + */ + +#include + +#include "BLI_utildefines.h" +#include "BLI_listbase.h" + +#include "BLI_ghash.h" + +#include "WM_types.h" + +#include "MEM_guardedalloc.h" + +#include "message_bus/wm_message_bus.h" +#include "message_bus/intern/wm_message_bus_intern.h" + +/* -------------------------------------------------------------------------- */ +/** \name Public API + * \{ */ + +static wmMsgTypeInfo wm_msg_types[WM_MSG_TYPE_NUM] = {NULL}; + +typedef void (*wmMsgTypeInitFn)(wmMsgTypeInfo *); + +static wmMsgTypeInitFn wm_msg_init_fn[WM_MSG_TYPE_NUM] = { + WM_msgtypeinfo_init_rna, + WM_msgtypeinfo_init_static, +}; + +void WM_msgbus_types_init(void) +{ + for (uint i = 0; i < WM_MSG_TYPE_NUM; i++) { + wm_msg_init_fn[i](&wm_msg_types[i]); + } +} + +struct wmMsgBus *WM_msgbus_create(void) +{ + struct wmMsgBus *mbus = MEM_callocN(sizeof(*mbus), __func__); + const uint gset_reserve = 512; + for (uint i = 0; i < WM_MSG_TYPE_NUM; i++) { + wmMsgTypeInfo *info = &wm_msg_types[i]; + mbus->messages_gset[i] = BLI_gset_new_ex(info->gset.hash_fn, info->gset.cmp_fn, __func__, gset_reserve); + } + return mbus; +} + +void WM_msgbus_destroy(struct wmMsgBus *mbus) +{ + for (uint i = 0; i < WM_MSG_TYPE_NUM; i++) { + wmMsgTypeInfo *info = &wm_msg_types[i]; + BLI_gset_free(mbus->messages_gset[i], info->gset.key_free_fn); + } + MEM_freeN(mbus); +} + +void WM_msgbus_clear_by_owner(struct wmMsgBus *mbus, void *owner) +{ + wmMsgSubscribeKey *msg_key, *msg_key_next; + for (msg_key = mbus->messages.first; msg_key; msg_key = msg_key_next) { + msg_key_next = msg_key->next; + + wmMsgSubscribeValueLink *msg_lnk_next; + for (wmMsgSubscribeValueLink *msg_lnk = msg_key->values.first; msg_lnk; msg_lnk = msg_lnk_next) { + msg_lnk_next = msg_lnk->next; + if (msg_lnk->params.owner == owner) { + if (msg_lnk->params.free_data) { + msg_lnk->params.free_data(msg_key, &msg_lnk->params); + } + BLI_remlink(&msg_key->values, msg_lnk); + MEM_freeN(msg_lnk); + } + } + + if (BLI_listbase_is_empty(&msg_key->values)) { + wmMsgTypeInfo *info = &wm_msg_types[msg_key->msg->type]; + BLI_remlink(&mbus->messages, msg_key); + bool ok = BLI_gset_remove(mbus->messages_gset[msg_key->msg->type], msg_key, info->gset.key_free_fn); + BLI_assert(ok); + UNUSED_VARS_NDEBUG(ok); + } + } +} + +void WM_msg_dump(struct wmMsgBus *mbus, const char *info_str) +{ + printf(">>>> %s\n", info_str); + for (wmMsgSubscribeKey *key = mbus->messages.first; key; key = key->next) { + const wmMsgTypeInfo *info = &wm_msg_types[key->msg->type]; + info->repr(stdout, key); + } + printf("<<<< %s\n", info_str); +} + +void WM_msgbus_handle(struct wmMsgBus *mbus, struct bContext *C) +{ + if (mbus->messages_tag_count == 0) { + // printf("msgbus: skipping\n"); + return; + } + + if (false) { + WM_msg_dump(mbus, __func__); + } + + // uint a = 0, b = 0; + for (wmMsgSubscribeKey *key = mbus->messages.first; key; key = key->next) { + for (wmMsgSubscribeValueLink *msg_lnk = key->values.first; msg_lnk; msg_lnk = msg_lnk->next) { + if (msg_lnk->params.tag) { + msg_lnk->params.notify(C, key, &msg_lnk->params); + msg_lnk->params.tag = false; + mbus->messages_tag_count -= 1; + } + // b++; + } + // a++; + } + BLI_assert(mbus->messages_tag_count == 0); + mbus->messages_tag_count = 0; + // printf("msgbus: keys=%u values=%u\n", a, b); +} + +/** + * \param msg_key_test: Needs following #wmMsgSubscribeKey fields filled in: + * - msg.params + * - msg.head.type + * - msg.head.id + * .. other values should be zeroed. + * + * \return The key for this subscription. + * note that this is only needed in rare cases when the key needs further manipulation. + */ +wmMsgSubscribeKey *WM_msg_subscribe_with_key( + struct wmMsgBus *mbus, + const wmMsgSubscribeKey *msg_key_test, + const wmMsgSubscribeValue *msg_val_params) +{ + const uint type = msg_key_test->msg->type; + const wmMsgTypeInfo *info = &wm_msg_types[type]; + wmMsgSubscribeKey *key; + + BLI_assert(msg_key_test->msg->id != NULL); + + void **r_key; + if (!BLI_gset_ensure_p_ex(mbus->messages_gset[type], msg_key_test, &r_key)) { + key = *r_key = MEM_mallocN(info->msg_key_size, __func__); + memcpy(key, msg_key_test, info->msg_key_size); + BLI_addtail(&mbus->messages, key); + } + else { + key = *r_key; + for (wmMsgSubscribeValueLink *msg_lnk = key->values.first; msg_lnk; msg_lnk = msg_lnk->next) { + if ((msg_lnk->params.notify == msg_val_params->notify) && + (msg_lnk->params.owner == msg_val_params->owner) && + (msg_lnk->params.user_data == msg_val_params->user_data)) + { + return key; + } + } + } + + wmMsgSubscribeValueLink *msg_lnk = MEM_mallocN(sizeof(wmMsgSubscribeValueLink), __func__); + msg_lnk->params = *msg_val_params; + BLI_addtail(&key->values, msg_lnk); + return key; +} + +void WM_msg_publish_with_key(struct wmMsgBus *mbus, wmMsgSubscribeKey *msg_key) +{ + for (wmMsgSubscribeValueLink *msg_lnk = msg_key->values.first; msg_lnk; msg_lnk = msg_lnk->next) { + if (false) { /* make an option? */ + msg_lnk->params.notify(NULL, msg_key, &msg_lnk->params); + } + else { + if (msg_lnk->params.tag == false) { + msg_lnk->params.tag = true; + mbus->messages_tag_count += 1; + } + } + } +} + +void WM_msg_id_update( + struct wmMsgBus *mbus, + struct ID *id_src, struct ID *id_dst) +{ + for (uint i = 0; i < WM_MSG_TYPE_NUM; i++) { + wmMsgTypeInfo *info = &wm_msg_types[i]; + if (info->update_by_id != NULL) { + info->update_by_id(mbus, id_src, id_dst); + } + } +} + +void WM_msg_id_remove(struct wmMsgBus *mbus, const struct ID *id) +{ + for (uint i = 0; i < WM_MSG_TYPE_NUM; i++) { + wmMsgTypeInfo *info = &wm_msg_types[i]; + if (info->remove_by_id != NULL) { + info->remove_by_id(mbus, id); + } + } +} + +/** \} */ + +/* -------------------------------------------------------------------------- */ +/** \name Internal API + * + * \note While we could have a separate type for ID's, use RNA since there is enough overlap. + * \{ */ + +void wm_msg_subscribe_value_free( + wmMsgSubscribeKey *msg_key, wmMsgSubscribeValueLink *msg_lnk) +{ + if (msg_lnk->params.free_data) { + msg_lnk->params.free_data(msg_key, &msg_lnk->params); + } + BLI_remlink(&msg_key->values, msg_lnk); + MEM_freeN(msg_lnk); +} + +/** \} */ diff --git a/source/blender/windowmanager/message_bus/intern/wm_message_bus_intern.h b/source/blender/windowmanager/message_bus/intern/wm_message_bus_intern.h new file mode 100644 index 00000000000..8d7b9d4ced2 --- /dev/null +++ b/source/blender/windowmanager/message_bus/intern/wm_message_bus_intern.h @@ -0,0 +1,41 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/windowmanager/message_bus/intern/wm_message_bus_intern.h + * \ingroup wm + */ + +#ifndef __WM_MESSAGE_BUS_INTERN_H__ +#define __WM_MESSAGE_BUS_INTERN_H__ + +/* wm_message_bus.h must be included first */ + +struct wmMsgBus { + struct GSet *messages_gset[WM_MSG_TYPE_NUM]; + /** Messages in order of being added. */ + ListBase messages; + /** Avoid checking messages when no tags exist. */ + uint messages_tag_count; +}; + +void wm_msg_subscribe_value_free( + struct wmMsgSubscribeKey *msg_key, struct wmMsgSubscribeValueLink *msg_lnk); + +#endif /* __WM_MESSAGE_BUS_INTERN_H__ */ diff --git a/source/blender/windowmanager/message_bus/intern/wm_message_bus_rna.c b/source/blender/windowmanager/message_bus/intern/wm_message_bus_rna.c new file mode 100644 index 00000000000..f9d8d968b84 --- /dev/null +++ b/source/blender/windowmanager/message_bus/intern/wm_message_bus_rna.c @@ -0,0 +1,316 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/windowmanager/intern/wm_message_bus_rna.c + * \ingroup wm + */ + +#include +#include "DNA_ID.h" + +#include "BLI_utildefines.h" +#include "BLI_ghash.h" +#include "BLI_listbase.h" + +#include "WM_types.h" +#include "WM_message.h" +#include "message_bus/intern/wm_message_bus_intern.h" + +#include "RNA_access.h" + +#include "MEM_guardedalloc.h" + +/* -------------------------------------------------------------------------- */ + +BLI_INLINE uint void_hash_uint(const void *key) +{ + size_t y = (size_t)key >> (sizeof(void *)); + return (unsigned int)y; +} + +static uint wm_msg_rna_gset_hash(const void *key_p) +{ + const wmMsgSubscribeKey_RNA *key = key_p; + const wmMsgParams_RNA *params = &key->msg.params; +// printf("%s\n", RNA_struct_identifier(params->ptr.type)); + uint k = void_hash_uint(params->ptr.type); + k ^= void_hash_uint(params->ptr.data); + k ^= void_hash_uint(params->ptr.id.data); + k ^= void_hash_uint(params->prop); + return k; +} +static bool wm_msg_rna_gset_cmp(const void *key_a_p, const void *key_b_p) +{ + const wmMsgParams_RNA *params_a = &((const wmMsgSubscribeKey_RNA *)key_a_p)->msg.params; + const wmMsgParams_RNA *params_b = &((const wmMsgSubscribeKey_RNA *)key_b_p)->msg.params; + return !( + (params_a->ptr.type == + params_b->ptr.type) && + (params_a->ptr.id.data == + params_b->ptr.id.data) && + (params_a->ptr.data == + params_b->ptr.data) && + (params_a->prop == + params_b->prop) + ); +} +static void wm_msg_rna_gset_key_free(void *key_p) +{ + wmMsgSubscribeKey_RNA *key = key_p; + wmMsgSubscribeValueLink *msg_lnk_next; + for (wmMsgSubscribeValueLink *msg_lnk = key->head.values.first; msg_lnk; msg_lnk = msg_lnk_next) { + msg_lnk_next = msg_lnk->next; + wm_msg_subscribe_value_free(&key->head, msg_lnk); + } + if (key->msg.params.data_path != NULL) { + MEM_freeN(key->msg.params.data_path); + } + MEM_freeN(key); +} + +static void wm_msg_rna_repr(FILE *stream, const wmMsgSubscribeKey *msg_key) +{ + const wmMsgSubscribeKey_RNA *m = (wmMsgSubscribeKey_RNA *)msg_key; + const char *none = ""; + fprintf(stream, + "msg.head.id, + m->msg.params.ptr.type ? RNA_struct_identifier(m->msg.params.ptr.type) : none, + m->msg.params.prop ? RNA_property_identifier((PropertyRNA *)m->msg.params.prop) : none, + BLI_listbase_count(&m->head.values)); +} + +static void wm_msg_rna_update_by_id( + struct wmMsgBus *mbus, + ID *id_src, ID *id_dst) +{ + GSet *gs = mbus->messages_gset[WM_MSG_TYPE_RNA]; + GSetIterator gs_iter; + BLI_gsetIterator_init(&gs_iter, gs); + while (BLI_gsetIterator_done(&gs_iter) == false) { + wmMsgSubscribeKey_RNA *key = BLI_gsetIterator_getKey(&gs_iter); + BLI_gsetIterator_step(&gs_iter); + if (key->msg.params.ptr.id.data == id_src) { + + /* GSet always needs updating since the key changes. */ + BLI_gset_remove(gs, key, NULL); + + /* Remove any non-persistent values, so a single persistent + * value doesn't modify behavior for the rest. */ + wmMsgSubscribeValueLink *msg_lnk_next; + for (wmMsgSubscribeValueLink *msg_lnk = key->head.values.first; msg_lnk; msg_lnk = msg_lnk_next) { + msg_lnk_next = msg_lnk->next; + if (msg_lnk->params.is_persistent == false) { + wm_msg_subscribe_value_free(&key->head, msg_lnk); + } + } + + bool remove = true; + + if (BLI_listbase_is_empty(&key->head.values)) { + /* Remove, no reason to keep. */ + } + else if (key->msg.params.ptr.data == key->msg.params.ptr.id.data) { + /* Simple, just update the ID. */ + key->msg.params.ptr.data = id_dst; + key->msg.params.ptr.id.data = id_dst; + remove = false; + } + else { + /* we need to resolve this from the */ + PointerRNA idptr; + RNA_id_pointer_create(id_dst, &idptr); + PointerRNA ptr; + PropertyRNA *prop; + if (!RNA_path_resolve(&idptr, key->msg.params.data_path, &ptr, &prop)) { + key->msg.params.ptr = ptr; + key->msg.params.prop = prop; + remove = false; + } + } + + printf("AAA ~ %d\n", remove); + if (remove) { + /* Failed to persist, remove the key. */ + BLI_remlink(&mbus->messages, key); + wm_msg_rna_gset_key_free(key); + } + else { + /* note that it's not impossible this key exists, however it is very unlikely + * since a subscriber would need to register in the middle of an undo for eg. so assert for now. */ + BLI_assert(!BLI_gset_haskey(gs, key)); + BLI_gset_add(gs, key); + } + } + } +} + +static void wm_msg_rna_remove_by_id(struct wmMsgBus *mbus, const ID *id) +{ + GSet *gs = mbus->messages_gset[WM_MSG_TYPE_RNA]; + GSetIterator gs_iter; + BLI_gsetIterator_init(&gs_iter, gs); + while (BLI_gsetIterator_done(&gs_iter) == false) { + wmMsgSubscribeKey_RNA *key = BLI_gsetIterator_getKey(&gs_iter); + BLI_gsetIterator_step(&gs_iter); + if (key->msg.params.ptr.id.data == id) { + BLI_remlink(&mbus->messages, key); + BLI_gset_remove(gs, key, NULL); + wm_msg_rna_gset_key_free(key); + } + } +} + +void WM_msgtypeinfo_init_rna(wmMsgTypeInfo *msgtype_info) +{ + msgtype_info->gset.hash_fn = wm_msg_rna_gset_hash; + msgtype_info->gset.cmp_fn = wm_msg_rna_gset_cmp; + msgtype_info->gset.key_free_fn = wm_msg_rna_gset_key_free; + + msgtype_info->repr = wm_msg_rna_repr; + msgtype_info->update_by_id = wm_msg_rna_update_by_id; + msgtype_info->remove_by_id = wm_msg_rna_remove_by_id; + + msgtype_info->msg_key_size = sizeof(wmMsgSubscribeKey_RNA); +} + +/* -------------------------------------------------------------------------- */ + + +wmMsgSubscribeKey_RNA *WM_msg_lookup_rna(struct wmMsgBus *mbus, const wmMsgParams_RNA *msg_key_params) +{ + wmMsgSubscribeKey_RNA key_test; + key_test.msg.params = *msg_key_params; + return BLI_gset_lookup(mbus->messages_gset[WM_MSG_TYPE_RNA], &key_test); +} + +void WM_msg_publish_rna_params(struct wmMsgBus *mbus, const wmMsgParams_RNA *msg_key_params) +{ + wmMsgSubscribeKey_RNA *key; + + if ((key = WM_msg_lookup_rna(mbus, msg_key_params))) { + WM_msg_publish_with_key(mbus, &key->head); + } + + /* Support anonymous subscribers, this may be some extra overhead + * but we want to be able to be more ambiguous. */ + if (msg_key_params->ptr.id.data || msg_key_params->ptr.data) { + wmMsgParams_RNA msg_key_params_anon = *msg_key_params; + + /* We might want to enable this later? */ + if (msg_key_params_anon.prop != NULL) { + /* All properties for this type. */ + msg_key_params_anon.prop = NULL; + if ((key = WM_msg_lookup_rna(mbus, &msg_key_params_anon))) { + WM_msg_publish_with_key(mbus, &key->head); + } + msg_key_params_anon.prop = msg_key_params->prop; + } + + msg_key_params_anon.ptr.id.data = NULL; + msg_key_params_anon.ptr.data = NULL; + if ((key = WM_msg_lookup_rna(mbus, &msg_key_params_anon))) { + WM_msg_publish_with_key(mbus, &key->head); + } + + /* Support subscribers to a type. */ + if (msg_key_params->prop) { + msg_key_params_anon.prop = NULL; + if ((key = WM_msg_lookup_rna(mbus, &msg_key_params_anon))) { + WM_msg_publish_with_key(mbus, &key->head); + } + } + } +} + +void WM_msg_publish_rna(struct wmMsgBus *mbus, PointerRNA *ptr, PropertyRNA *prop) +{ + WM_msg_publish_rna_params(mbus, &(wmMsgParams_RNA){ .ptr = *ptr, .prop = prop, }); +} + +void WM_msg_subscribe_rna_params( + struct wmMsgBus *mbus, + const wmMsgParams_RNA *msg_key_params, + const wmMsgSubscribeValue *msg_val_params, + const char *id_repr) +{ + wmMsgSubscribeKey_RNA msg_key_test = {{NULL}}; + + /* use when added */ + msg_key_test.msg.head.id = id_repr; + msg_key_test.msg.head.type = WM_MSG_TYPE_RNA; + /* for lookup */ + msg_key_test.msg.params = *msg_key_params; + + wmMsgSubscribeKey_RNA *msg_key = (wmMsgSubscribeKey_RNA *)WM_msg_subscribe_with_key( + mbus, &msg_key_test.head, msg_val_params); + + if (msg_val_params->is_persistent) { + if (msg_key->msg.params.data_path == NULL) { + if (msg_key->msg.params.ptr.data != msg_key->msg.params.ptr.id.data) { + /* We assume prop type can't change. */ + msg_key->msg.params.data_path = RNA_path_from_ID_to_struct(&msg_key->msg.params.ptr); + } + } + } +} + +void WM_msg_subscribe_rna( + struct wmMsgBus *mbus, + PointerRNA *ptr, const PropertyRNA *prop, + const wmMsgSubscribeValue *msg_val_params, + const char *id_repr) +{ + WM_msg_subscribe_rna_params( + mbus, + &(const wmMsgParams_RNA){ + .ptr = *ptr, + .prop = prop, + }, + msg_val_params, id_repr); +} + +/** \} */ + +/* -------------------------------------------------------------------------- */ +/** \name ID variants of RNA API + * + * \note While we could have a separate type for ID's, use RNA since there is enough overlap. + * \{ */ + +void WM_msg_subscribe_ID( + struct wmMsgBus *mbus, ID *id, const wmMsgSubscribeValue *msg_val_params, + const char *id_repr) +{ + wmMsgParams_RNA msg_key_params = {NULL}; + RNA_id_pointer_create(id, &msg_key_params.ptr); + WM_msg_subscribe_rna_params(mbus, &msg_key_params, msg_val_params, id_repr); +} + +void WM_msg_publish_ID(struct wmMsgBus *mbus, ID *id) +{ + wmMsgParams_RNA msg_key_params = {NULL}; + RNA_id_pointer_create(id, &msg_key_params.ptr); + WM_msg_publish_rna_params(mbus, &msg_key_params); +} + +/** \} */ diff --git a/source/blender/windowmanager/message_bus/intern/wm_message_bus_static.c b/source/blender/windowmanager/message_bus/intern/wm_message_bus_static.c new file mode 100644 index 00000000000..59bebad7f7b --- /dev/null +++ b/source/blender/windowmanager/message_bus/intern/wm_message_bus_static.c @@ -0,0 +1,136 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/windowmanager/intern/wm_message_bus_static.c + * \ingroup wm + */ + +#include + +#include "BLI_utildefines.h" +#include "BLI_ghash.h" +#include "BLI_listbase.h" + +#include "WM_types.h" +#include "WM_message.h" +#include "message_bus/intern/wm_message_bus_intern.h" + +#include "MEM_guardedalloc.h" + +/* -------------------------------------------------------------------------- */ + +static uint wm_msg_static_gset_hash(const void *key_p) +{ + const wmMsgSubscribeKey_Static *key = key_p; + const wmMsgParams_Static *params = &key->msg.params; + uint k = params->event; + return k; +} +static bool wm_msg_static_gset_cmp(const void *key_a_p, const void *key_b_p) +{ + const wmMsgParams_Static *params_a = &((const wmMsgSubscribeKey_Static *)key_a_p)->msg.params; + const wmMsgParams_Static *params_b = &((const wmMsgSubscribeKey_Static *)key_b_p)->msg.params; + return !( + (params_a->event == + params_b->event) + ); +} +static void wm_msg_static_gset_key_free(void *key_p) +{ + wmMsgSubscribeKey *key = key_p; + wmMsgSubscribeValueLink *msg_lnk_next; + for (wmMsgSubscribeValueLink *msg_lnk = key->values.first; msg_lnk; msg_lnk = msg_lnk_next) { + msg_lnk_next = msg_lnk->next; + BLI_remlink(&key->values, msg_lnk); + MEM_freeN(msg_lnk); + } + MEM_freeN(key); +} + +static void wm_msg_static_repr(FILE *stream, const wmMsgSubscribeKey *msg_key) +{ + const wmMsgSubscribeKey_Static *m = (wmMsgSubscribeKey_Static *)msg_key; + fprintf(stream, + "msg.head.id, + BLI_listbase_count(&m->head.values)); +} + + +void WM_msgtypeinfo_init_static(wmMsgTypeInfo *msgtype_info) +{ + msgtype_info->gset.hash_fn = wm_msg_static_gset_hash; + msgtype_info->gset.cmp_fn = wm_msg_static_gset_cmp; + msgtype_info->gset.key_free_fn = wm_msg_static_gset_key_free; + msgtype_info->repr = wm_msg_static_repr; + + msgtype_info->msg_key_size = sizeof(wmMsgSubscribeKey_Static); +} + +/* -------------------------------------------------------------------------- */ + + +wmMsgSubscribeKey_Static *WM_msg_lookup_static(struct wmMsgBus *mbus, const wmMsgParams_Static *msg_key_params) +{ + wmMsgSubscribeKey_Static key_test; + key_test.msg.params = *msg_key_params; + return BLI_gset_lookup(mbus->messages_gset[WM_MSG_TYPE_STATIC], &key_test); +} + +void WM_msg_publish_static_params(struct wmMsgBus *mbus, const wmMsgParams_Static *msg_key_params) +{ + wmMsgSubscribeKey_Static *key = WM_msg_lookup_static(mbus, msg_key_params); + if (key) { + WM_msg_publish_with_key(mbus, &key->head); + } +} + +void WM_msg_publish_static(struct wmMsgBus *mbus, int event) +{ + WM_msg_publish_static_params(mbus, &(wmMsgParams_Static){ .event = event, }); +} + +void WM_msg_subscribe_static_params( + struct wmMsgBus *mbus, + const wmMsgParams_Static *msg_key_params, + const wmMsgSubscribeValue *msg_val_params, + const char *id_repr) +{ + wmMsgSubscribeKey_Static msg_key_test = {{NULL}}; + + /* use when added */ + msg_key_test.msg.head.id = id_repr; + msg_key_test.msg.head.type = WM_MSG_TYPE_STATIC; + /* for lookup */ + msg_key_test.msg.params = *msg_key_params; + + WM_msg_subscribe_with_key(mbus, &msg_key_test.head, msg_val_params); +} + +void WM_msg_subscribe_static( + struct wmMsgBus *mbus, + int event, + const wmMsgSubscribeValue *msg_val_params, + const char *id_repr) +{ + WM_msg_subscribe_static_params(mbus, &(const wmMsgParams_Static){ .event = event, }, msg_val_params, id_repr); +} diff --git a/source/blender/windowmanager/message_bus/wm_message_bus.h b/source/blender/windowmanager/message_bus/wm_message_bus.h new file mode 100644 index 00000000000..fd158e2cd7f --- /dev/null +++ b/source/blender/windowmanager/message_bus/wm_message_bus.h @@ -0,0 +1,257 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/windowmanager/wm_message_bus.h + * \ingroup wm + */ + +#ifndef __WM_MESSAGE_BUS_H__ +#define __WM_MESSAGE_BUS_H__ + +struct GSet; +struct ID; +struct bContext; +struct wmMsg; + +/* opaque (don't expose outside wm_message_bus.c) */ +struct wmMsgBus; +struct wmMsgSubscribeKey; +struct wmMsgSubscribeValue; +struct wmMsgSubscribeValueLink; + +typedef void (*wmMsgNotifyFn)( + struct bContext *C, struct wmMsgSubscribeKey *msg_key, struct wmMsgSubscribeValue *msg_val); +typedef void (*wmMsgSubscribeValueFreeDataFn)( + struct wmMsgSubscribeKey *msg_key, struct wmMsgSubscribeValue *msg_val); + +/* Exactly what arguments here is not obvious. */ +typedef void (*wmMsgSubscribeValueUpdateIdFn)( + struct bContext *C, + struct wmMsgBus *mbus, + struct ID *id_src, struct ID *id_dst, + struct wmMsgSubscribeValue *msg_val); +enum { + WM_MSG_TYPE_RNA = 0, + WM_MSG_TYPE_STATIC = 1, +}; +#define WM_MSG_TYPE_NUM 2 + +typedef struct wmMsgTypeInfo { + struct { + unsigned int (*hash_fn)(const void *msg); + bool (*cmp_fn)(const void *a, const void *b); + void (*key_free_fn)(void *key); + } gset; + + void (*update_by_id)(struct wmMsgBus *mbus, struct ID *id_src, struct ID *id_dst); + void (*remove_by_id)(struct wmMsgBus *mbus, const struct ID *id); + void (*repr)(FILE *stream, const struct wmMsgSubscribeKey *msg_key); + + /* sizeof(wmMsgSubscribeKey_*) */ + uint msg_key_size; +} wmMsgTypeInfo; + +typedef struct wmMsg { + unsigned int type; +// #ifdef DEBUG + /* For debugging: '__func__:__LINE__'. */ + const char *id; +// #endif +} wmMsg; + +typedef struct wmMsgSubscribeKey { + /** Linked list for predicable ordering, otherwise we would depend on ghash bucketing. */ + struct wmMsgSubscribeKey *next, *prev; + ListBase values; + + /* over-alloc, eg: wmMsgSubscribeKey_RNA */ + wmMsg msg[0]; +} wmMsgSubscribeKey; + +/** One of many in #wmMsgSubscribeKey.values */ +typedef struct wmMsgSubscribeValue { + struct wmMsgSubscribe *next, *prev; + + /** Handle, used to iterate and clear. */ + void *owner; + /** User data, can be whatever we like, free using the 'free_data' callback if it's owned. */ + void *user_data; + + /** Callbacks */ + wmMsgNotifyFn notify; + wmMsgSubscribeValueUpdateIdFn update_id; + wmMsgSubscribeValueFreeDataFn free_data; + + /** Keep this subscriber if possible. */ + uint is_persistent : 1; + /* tag to run when handling events, + * we may want option for immediate execution. */ + uint tag : 1; +} wmMsgSubscribeValue; + +/** One of many in #wmMsgSubscribeKey.values */ +typedef struct wmMsgSubscribeValueLink { + struct wmMsgSubscribeValueLink *next, *prev; + wmMsgSubscribeValue params; +} wmMsgSubscribeValueLink; + +void WM_msgbus_types_init(void); + +struct wmMsgBus *WM_msgbus_create(void); +void WM_msgbus_destroy(struct wmMsgBus *mbus); + +void WM_msgbus_clear_by_owner(struct wmMsgBus *mbus, void *owner); + +void WM_msg_dump(struct wmMsgBus *mbus, const char *info); +void WM_msgbus_handle(struct wmMsgBus *mbus, struct bContext *C); + +void WM_msg_publish_with_key(struct wmMsgBus *mbus, wmMsgSubscribeKey *msg_key); +wmMsgSubscribeKey *WM_msg_subscribe_with_key( + struct wmMsgBus *mbus, + const wmMsgSubscribeKey *msg_key_test, + const wmMsgSubscribeValue *msg_val_params); + +void WM_msg_id_update( + struct wmMsgBus *mbus, + struct ID *id_src, struct ID *id_dst); +void WM_msg_id_remove(struct wmMsgBus *mbus, const struct ID *id); + +/* -------------------------------------------------------------------------- */ +/* wm_message_bus_static.c */ + +enum { + /* generic window redraw */ + WM_MSG_STATICTYPE_WINDOW_DRAW = 0, + WM_MSG_STATICTYPE_SCREEN_EDIT = 1, + WM_MSG_STATICTYPE_FILE_READ = 2, +}; + +typedef struct wmMsgParams_Static { + int event; +} wmMsgParams_Static; + +typedef struct wmMsg_Static { + wmMsg head; /* keep first */ + wmMsgParams_Static params; +} wmMsg_Static; + +typedef struct wmMsgSubscribeKey_Static { + wmMsgSubscribeKey head; + wmMsg_Static msg; +} wmMsgSubscribeKey_Static; + +void WM_msgtypeinfo_init_static(wmMsgTypeInfo *msg_type); + +wmMsgSubscribeKey_Static *WM_msg_lookup_static( + struct wmMsgBus *mbus, const wmMsgParams_Static *msg_key_params); +void WM_msg_publish_static_params( + struct wmMsgBus *mbus, + const wmMsgParams_Static *msg_key_params); +void WM_msg_publish_static( + struct wmMsgBus *mbus, + /* wmMsgParams_Static (expanded) */ + int event); +void WM_msg_subscribe_static_params( + struct wmMsgBus *mbus, + const wmMsgParams_Static *msg_key_params, + const wmMsgSubscribeValue *msg_val_params, + const char *id_repr); +void WM_msg_subscribe_static( + struct wmMsgBus *mbus, + int event, + const wmMsgSubscribeValue *msg_val_params, + const char *id_repr); + +/* -------------------------------------------------------------------------- */ +/* wm_message_bus_rna.c */ + +typedef struct wmMsgParams_RNA { + /** when #PointerRNA.data & id.data are NULL. match against all. */ + PointerRNA ptr; + /** when NULL, match against any property. */ + const PropertyRNA *prop; + + /** + * Optional RNA data path for persistent RNA properties, ignore if NULL. + * otherwise it's allocated. + */ + char *data_path; +} wmMsgParams_RNA; + +typedef struct wmMsg_RNA { + wmMsg head; /* keep first */ + wmMsgParams_RNA params; +} wmMsg_RNA; + +typedef struct wmMsgSubscribeKey_RNA { + wmMsgSubscribeKey head; + wmMsg_RNA msg; +} wmMsgSubscribeKey_RNA; + +void WM_msgtypeinfo_init_rna(wmMsgTypeInfo *msg_type); + +wmMsgSubscribeKey_RNA *WM_msg_lookup_rna( + struct wmMsgBus *mbus, const wmMsgParams_RNA *msg_key_params); +void WM_msg_publish_rna_params( + struct wmMsgBus *mbus, const wmMsgParams_RNA *msg_key_params); +void WM_msg_publish_rna( + struct wmMsgBus *mbus, + /* wmMsgParams_RNA (expanded) */ + PointerRNA *ptr, PropertyRNA *prop); +void WM_msg_subscribe_rna_params( + struct wmMsgBus *mbus, + const wmMsgParams_RNA *msg_key_params, + const wmMsgSubscribeValue *msg_val_params, + const char *id_repr); +void WM_msg_subscribe_rna( + struct wmMsgBus *mbus, + PointerRNA *ptr, const PropertyRNA *prop, + const wmMsgSubscribeValue *msg_val_params, + const char *id_repr); + +/* ID variants */ +void WM_msg_subscribe_ID( + struct wmMsgBus *mbus, struct ID *id, const wmMsgSubscribeValue *msg_val_params, + const char *id_repr); +void WM_msg_publish_ID( + struct wmMsgBus *mbus, struct ID *id); + +/* Anonymous variants (for convenience) */ +#define WM_msg_subscribe_rna_anon_type(mbus, type_, value) { \ + WM_msg_subscribe_rna_params( \ + mbus, \ + &(const wmMsgParams_RNA){ \ + .ptr = (PointerRNA){.type = &RNA_##type_}, \ + .prop = NULL, \ + }, \ + value, __func__); \ +} ((void)0) +#define WM_msg_subscribe_rna_anon_prop(mbus, type_, prop_, value) { \ + extern PropertyRNA rna_##type_##_##prop_; \ + WM_msg_subscribe_rna_params( \ + mbus, \ + &(const wmMsgParams_RNA){ \ + .ptr = (PointerRNA){.type = &RNA_##type_}, \ + .prop = &rna_##type_##_##prop_, \ + }, \ + value, __func__); \ +} ((void)0) + +#endif /* __WM_MESSAGE_BUS_H__ */ -- cgit v1.2.3