Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2017-11-13 11:43:34 +0300
committerCampbell Barton <ideasman42@gmail.com>2017-12-04 12:42:34 +0300
commit7a8ac1b09b1cf321f259b8eb9b832424d2c7bf5b (patch)
tree5f9a861e234799149fc99f25e5df7bd96fb46cae
parent5b6cfa705cb180d042f6f27e45331c12972be7ae (diff)
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
-rw-r--r--release/scripts/modules/bpy/__init__.py1
-rw-r--r--source/blender/blenkernel/BKE_context.h1
-rw-r--r--source/blender/blenkernel/BKE_screen.h7
-rw-r--r--source/blender/blenkernel/intern/context.c5
-rw-r--r--source/blender/blenloader/intern/readfile.c2
-rw-r--r--source/blender/editors/include/ED_screen.h15
-rw-r--r--source/blender/editors/include/UI_interface.h3
-rw-r--r--source/blender/editors/interface/interface.c38
-rw-r--r--source/blender/editors/screen/area.c81
-rw-r--r--source/blender/editors/screen/screen_edit.c4
-rw-r--r--source/blender/editors/space_buttons/space_buttons.c1
-rw-r--r--source/blender/editors/space_file/space_file.c29
-rw-r--r--source/blender/editors/space_time/space_time.c45
-rw-r--r--source/blender/editors/space_view3d/space_view3d.c71
-rw-r--r--source/blender/editors/space_view3d/view3d_draw.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_manipulator_camera.c51
-rw-r--r--source/blender/editors/transform/transform_manipulator.c62
-rw-r--r--source/blender/makesdna/DNA_windowmanager_types.h4
-rw-r--r--source/blender/makesrna/intern/rna_access.c9
-rw-r--r--source/blender/python/intern/CMakeLists.txt2
-rw-r--r--source/blender/python/intern/bpy.c2
-rw-r--r--source/blender/python/intern/bpy_msgbus.c400
-rw-r--r--source/blender/python/intern/bpy_msgbus.h30
-rw-r--r--source/blender/windowmanager/CMakeLists.txt5
-rw-r--r--source/blender/windowmanager/WM_message.h30
-rw-r--r--source/blender/windowmanager/WM_types.h1
-rw-r--r--source/blender/windowmanager/intern/wm.c11
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c35
-rw-r--r--source/blender/windowmanager/intern/wm_files.c5
-rw-r--r--source/blender/windowmanager/intern/wm_init_exit.c3
-rw-r--r--source/blender/windowmanager/manipulators/WM_manipulator_api.h9
-rw-r--r--source/blender/windowmanager/manipulators/WM_manipulator_types.h5
-rw-r--r--source/blender/windowmanager/manipulators/intern/wm_manipulator_map.c20
-rw-r--r--source/blender/windowmanager/manipulators/intern/wm_manipulator_target_props.c51
-rw-r--r--source/blender/windowmanager/manipulators/wm_manipulator_fn.h2
-rw-r--r--source/blender/windowmanager/message_bus/intern/wm_message_bus.c245
-rw-r--r--source/blender/windowmanager/message_bus/intern/wm_message_bus_intern.h41
-rw-r--r--source/blender/windowmanager/message_bus/intern/wm_message_bus_rna.c316
-rw-r--r--source/blender/windowmanager/message_bus/intern/wm_message_bus_static.c136
-rw-r--r--source/blender/windowmanager/message_bus/wm_message_bus.h257
40 files changed, 2034 insertions, 3 deletions
diff --git a/release/scripts/modules/bpy/__init__.py b/release/scripts/modules/bpy/__init__.py
index 6312c25065f..a80135a59e1 100644
--- a/release/scripts/modules/bpy/__init__.py
+++ b/release/scripts/modules/bpy/__init__.py
@@ -39,6 +39,7 @@ from _bpy import (
app,
context,
data,
+ msgbus,
props,
types,
)
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 <Python.h>
+
+#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 <string.h>
+
+#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 <stdio.h>
+#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 = "<none>";
+ fprintf(stream,
+ "<wmMsg_RNA %p, "
+ "id='%s', "
+ "%s.%s values_len=%d\n",
+ m, m->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 <stdio.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 "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,
+ "<wmMsg_Static %p, "
+ "id='%s', "
+ "values_len=%d\n",
+ m, m->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__ */