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
This commit is contained in:
Campbell Barton 2017-11-13 19:43:34 +11:00
parent 5b6cfa705c
commit 7a8ac1b09b
40 changed files with 2034 additions and 3 deletions

View File

@ -39,6 +39,7 @@ from _bpy import (
app,
context,
data,
msgbus,
props,
types,
)

View File

@ -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);

View File

@ -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 *);

View File

@ -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)

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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 ************* */
/**

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -44,6 +44,7 @@
#include "WM_api.h"
#include "WM_types.h"
#include "WM_message.h"
#include "RNA_access.h"

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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"

View File

@ -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;
}
/** \} */

View File

@ -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;
}

View File

@ -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 */

View File

@ -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)) {

View File

@ -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

View File

@ -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);

View File

@ -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;
}
/** \} */

View File

@ -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__ */

View File

@ -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)

View File

@ -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__ */

View File

@ -109,6 +109,7 @@ extern "C" {
struct bContext;
struct wmEvent;
struct wmWindowManager;
struct wmMsgBus;
struct wmOperator;
struct ImBuf;

View File

@ -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);

View File

@ -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);
}

View File

@ -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 */

View File

@ -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);

View File

@ -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,

View File

@ -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. */

View File

@ -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 */

View File

@ -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__);
}
}
}
}
}
/** \} */

View File

@ -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. */

View File

@ -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);
}
/** \} */

View File

@ -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__ */

View File

@ -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);
}
/** \} */

View File

@ -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);
}

View File

@ -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__ */