forked from bartvdbraak/blender
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:
parent
5b6cfa705c
commit
7a8ac1b09b
@ -39,6 +39,7 @@ from _bpy import (
|
||||
app,
|
||||
context,
|
||||
data,
|
||||
msgbus,
|
||||
props,
|
||||
types,
|
||||
)
|
||||
|
@ -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);
|
||||
|
@ -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 *);
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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 ************* */
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -44,6 +44,7 @@
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_types.h"
|
||||
#include "WM_message.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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)) {
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
400
source/blender/python/intern/bpy_msgbus.c
Normal file
400
source/blender/python/intern/bpy_msgbus.c
Normal 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;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
30
source/blender/python/intern/bpy_msgbus.h
Normal file
30
source/blender/python/intern/bpy_msgbus.h
Normal 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__ */
|
@ -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)
|
||||
|
30
source/blender/windowmanager/WM_message.h
Normal file
30
source/blender/windowmanager/WM_message.h
Normal 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__ */
|
@ -109,6 +109,7 @@ extern "C" {
|
||||
struct bContext;
|
||||
struct wmEvent;
|
||||
struct wmWindowManager;
|
||||
struct wmMsgBus;
|
||||
struct wmOperator;
|
||||
struct ImBuf;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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. */
|
||||
|
@ -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 */
|
||||
|
||||
|
||||
|
@ -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__);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@ -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. */
|
||||
|
245
source/blender/windowmanager/message_bus/intern/wm_message_bus.c
Normal file
245
source/blender/windowmanager/message_bus/intern/wm_message_bus.c
Normal 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);
|
||||
}
|
||||
|
||||
/** \} */
|
@ -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__ */
|
@ -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);
|
||||
}
|
||||
|
||||
/** \} */
|
@ -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);
|
||||
}
|
257
source/blender/windowmanager/message_bus/wm_message_bus.h
Normal file
257
source/blender/windowmanager/message_bus/wm_message_bus.h
Normal 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__ */
|
Loading…
Reference in New Issue
Block a user