Blender V5.0
wm_message_bus_rna.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <cstdio>
10
11#include "CLG_log.h"
12#include "MEM_guardedalloc.h"
13
14#include "DNA_ID.h"
15
16#include "BLI_ghash.h"
17#include "BLI_listbase.h"
18#include "BLI_string.h"
19
20#include "WM_message.hh"
21#include "WM_types.hh"
23
24#include "RNA_access.hh"
25#include "RNA_path.hh"
26
27/* -------------------------------------------------------------------- */
30
32{
33 size_t y = size_t(key) >> sizeof(void *);
34 return uint(y);
35}
36
37static uint wm_msg_rna_gset_hash(const void *key_p)
38{
39 const wmMsgSubscribeKey_RNA *key = static_cast<const wmMsgSubscribeKey_RNA *>(key_p);
40 const wmMsgParams_RNA *params = &key->msg.params;
41 // printf("%s\n", RNA_struct_identifier(params->ptr.type));
42 uint k = void_hash_uint(params->ptr.type);
43 k ^= void_hash_uint(params->ptr.data);
44 k ^= void_hash_uint(params->ptr.owner_id);
45 k ^= void_hash_uint(params->prop);
46 return k;
47}
48static bool wm_msg_rna_gset_cmp(const void *key_a_p, const void *key_b_p)
49{
50 const wmMsgParams_RNA *params_a = &((const wmMsgSubscribeKey_RNA *)key_a_p)->msg.params;
51 const wmMsgParams_RNA *params_b = &((const wmMsgSubscribeKey_RNA *)key_b_p)->msg.params;
52 return !((params_a->ptr.type == params_b->ptr.type) &&
53 (params_a->ptr.owner_id == params_b->ptr.owner_id) &&
54 (params_a->ptr.data == params_b->ptr.data) && (params_a->prop == params_b->prop));
55}
56
57static void *wm_msg_rna_gset_key_duplicate(const void *key_p)
58{
59 const wmMsgSubscribeKey_RNA *key_src = static_cast<const wmMsgSubscribeKey_RNA *>(key_p);
60 return MEM_new<wmMsgSubscribeKey_RNA>(__func__, *key_src);
61}
62static void wm_msg_rna_gset_key_free(void *key_p)
63{
64 wmMsgSubscribeKey_RNA *key = static_cast<wmMsgSubscribeKey_RNA *>(key_p);
65 wmMsgSubscribeValueLink *msg_lnk_next;
66 for (wmMsgSubscribeValueLink *msg_lnk =
67 static_cast<wmMsgSubscribeValueLink *>(key->head.values.first);
68 msg_lnk;
69 msg_lnk = msg_lnk_next)
70 {
71 msg_lnk_next = msg_lnk->next;
72 wm_msg_subscribe_value_free(&key->head, msg_lnk);
73 }
74 if (key->msg.params.data_path != nullptr) {
76 }
77 MEM_delete(key);
78}
79
80static void wm_msg_rna_repr(FILE *stream, const wmMsgSubscribeKey *msg_key)
81{
82 const wmMsgSubscribeKey_RNA *m = (wmMsgSubscribeKey_RNA *)msg_key;
83 const char *none = "<none>";
84 fprintf(stream,
85 "<wmMsg_RNA %p, "
86 "id='%s', "
87 "%s.%s values_len=%d\n",
88 m,
89 m->msg.head.id,
93}
94
95static void wm_msg_rna_update_by_id(wmMsgBus *mbus, ID *id_src, ID *id_dst)
96{
98 GSetIterator gs_iter;
99 BLI_gsetIterator_init(&gs_iter, gs);
100 while (BLI_gsetIterator_done(&gs_iter) == false) {
101 wmMsgSubscribeKey_RNA *key = static_cast<wmMsgSubscribeKey_RNA *>(
102 BLI_gsetIterator_getKey(&gs_iter));
103 BLI_gsetIterator_step(&gs_iter);
104 if (key->msg.params.ptr.owner_id == id_src) {
105
106 /* GSet always needs updating since the key changes. */
107 BLI_gset_remove(gs, key, nullptr);
108
109 /* Remove any non-persistent values, so a single persistent
110 * value doesn't modify behavior for the rest. */
112 msg_lnk = static_cast<wmMsgSubscribeValueLink *>(key->head.values.first),
113 *msg_lnk_next;
114 msg_lnk;
115 msg_lnk = msg_lnk_next)
116 {
117 msg_lnk_next = msg_lnk->next;
118 if (msg_lnk->params.is_persistent == false) {
119 if (msg_lnk->params.tag) {
120 mbus->messages_tag_count -= 1;
121 }
122 wm_msg_subscribe_value_free(&key->head, msg_lnk);
123 }
124 }
125
126 bool remove = true;
127
128 if (BLI_listbase_is_empty(&key->head.values)) {
129 /* Remove, no reason to keep. */
130 }
131 else if (key->msg.params.ptr.data == key->msg.params.ptr.owner_id) {
132 /* Simple, just update the ID. */
133 key->msg.params.ptr.data = id_dst;
134 key->msg.params.ptr.owner_id = id_dst;
135 remove = false;
136 }
137 else {
138 /* We need to resolve this from the new ID pointer. */
139 PointerRNA idptr = RNA_id_pointer_create(id_dst);
141 PropertyRNA *prop = nullptr;
142 if (RNA_path_resolve(&idptr, key->msg.params.data_path, &ptr, &prop) &&
143 (prop == nullptr) == (key->msg.params.prop == nullptr))
144 {
145 key->msg.params.ptr = ptr;
146 key->msg.params.prop = prop;
147 remove = false;
148 }
149 }
150
151 if (remove) {
153 msg_lnk = static_cast<wmMsgSubscribeValueLink *>(key->head.values.first),
154 *msg_lnk_next;
155 msg_lnk;
156 msg_lnk = msg_lnk_next)
157 {
158 msg_lnk_next = msg_lnk->next;
159 if (msg_lnk->params.is_persistent == false) {
160 if (msg_lnk->params.tag) {
161 mbus->messages_tag_count -= 1;
162 }
163 wm_msg_subscribe_value_free(&key->head, msg_lnk);
164 }
165 }
166 /* Failed to persist, remove the key. */
167 BLI_remlink(&mbus->messages, key);
169 }
170 else {
171 /* Note that it's not impossible this key exists, however it is very unlikely
172 * since a subscriber would need to register in the middle of an undo for eg.
173 * so assert for now. */
174 BLI_assert(!BLI_gset_haskey(gs, key));
175 BLI_gset_add(gs, key);
176 }
177 }
178 }
179}
180
181static void wm_msg_rna_remove_by_id(wmMsgBus *mbus, const ID *id)
182{
184 GSetIterator gs_iter;
185 BLI_gsetIterator_init(&gs_iter, gs);
186 while (BLI_gsetIterator_done(&gs_iter) == false) {
187 wmMsgSubscribeKey_RNA *key = static_cast<wmMsgSubscribeKey_RNA *>(
188 BLI_gsetIterator_getKey(&gs_iter));
189 BLI_gsetIterator_step(&gs_iter);
190 if (key->msg.params.ptr.owner_id == id) {
191 /* Clear here so we can decrement 'messages_tag_count'. */
193 msg_lnk = static_cast<wmMsgSubscribeValueLink *>(key->head.values.first),
194 *msg_lnk_next;
195 msg_lnk;
196 msg_lnk = msg_lnk_next)
197 {
198 msg_lnk_next = msg_lnk->next;
199 if (msg_lnk->params.tag) {
200 mbus->messages_tag_count -= 1;
201 }
202 wm_msg_subscribe_value_free(&key->head, msg_lnk);
203 }
204
205 BLI_remlink(&mbus->messages, key);
206 BLI_gset_remove(gs, key, nullptr);
208 }
209 }
210}
211
213{
214 msgtype_info->gset.hash_fn = wm_msg_rna_gset_hash;
215 msgtype_info->gset.cmp_fn = wm_msg_rna_gset_cmp;
218
219 msgtype_info->repr = wm_msg_rna_repr;
222}
223
225
226/* -------------------------------------------------------------------- */
229
231{
232 wmMsgSubscribeKey_RNA key_test;
233 key_test.msg.params = *msg_key_params;
234 return static_cast<wmMsgSubscribeKey_RNA *>(
236}
237
238void WM_msg_publish_rna_params(wmMsgBus *mbus, const wmMsgParams_RNA *msg_key_params)
239{
241
242 const char *none = "<none>";
244 "rna(id='%s', %s.%s)",
245 msg_key_params->ptr.owner_id ? ((ID *)msg_key_params->ptr.owner_id)->name : none,
246 msg_key_params->ptr.type ? RNA_struct_identifier(msg_key_params->ptr.type) : none,
247 msg_key_params->prop ? RNA_property_identifier((PropertyRNA *)msg_key_params->prop) :
248 none);
249
250 if ((key = WM_msg_lookup_rna(mbus, msg_key_params))) {
251 WM_msg_publish_with_key(mbus, &key->head);
252 }
253
254 /* Support anonymous subscribers, this may be some extra overhead
255 * but we want to be able to be more ambiguous. */
256 if (msg_key_params->ptr.owner_id || msg_key_params->ptr.data) {
257 wmMsgParams_RNA msg_key_params_anon = *msg_key_params;
258
259 /* We might want to enable this later? */
260 if (msg_key_params_anon.prop != nullptr) {
261 /* All properties for this type. */
262 msg_key_params_anon.prop = nullptr;
263 if ((key = WM_msg_lookup_rna(mbus, &msg_key_params_anon))) {
264 WM_msg_publish_with_key(mbus, &key->head);
265 }
266 msg_key_params_anon.prop = msg_key_params->prop;
267 }
268
269 msg_key_params_anon.ptr.owner_id = nullptr;
270 msg_key_params_anon.ptr.data = nullptr;
271 if ((key = WM_msg_lookup_rna(mbus, &msg_key_params_anon))) {
272 WM_msg_publish_with_key(mbus, &key->head);
273 }
274
275 /* Support subscribers to a type. */
276 if (msg_key_params->prop) {
277 msg_key_params_anon.prop = nullptr;
278 if ((key = WM_msg_lookup_rna(mbus, &msg_key_params_anon))) {
279 WM_msg_publish_with_key(mbus, &key->head);
280 }
281 }
282 }
283}
284
286{
288 params.ptr = *ptr;
289 params.prop = prop;
291}
292
294 const wmMsgParams_RNA *msg_key_params,
295 const wmMsgSubscribeValue *msg_val_params,
296 const char *id_repr)
297{
298 wmMsgSubscribeKey_RNA msg_key_test{};
299
300 /* Use when added. */
301 msg_key_test.msg.head.id = id_repr;
302 msg_key_test.msg.head.type = WM_MSG_TYPE_RNA;
303 /* For lookup. */
304 msg_key_test.msg.params = *msg_key_params;
305
306 const char *none = "<none>";
308 "rna(id='%s', %s.%s, info='%s')",
309 msg_key_params->ptr.owner_id ? ((ID *)msg_key_params->ptr.owner_id)->name : none,
310 msg_key_params->ptr.type ? RNA_struct_identifier(msg_key_params->ptr.type) : none,
311 msg_key_params->prop ? RNA_property_identifier((PropertyRNA *)msg_key_params->prop) :
312 none,
313 id_repr);
314
316 mbus, &msg_key_test.head, msg_val_params);
317
318 if (msg_val_params->is_persistent) {
319 if (msg_key->msg.params.data_path == nullptr) {
320 if (msg_key->msg.params.ptr.data != msg_key->msg.params.ptr.owner_id) {
321 /* We assume prop type can't change. */
322 const std::optional<std::string> str = RNA_path_from_ID_to_struct(
323 &msg_key->msg.params.ptr);
324 msg_key->msg.params.data_path = str ? BLI_strdupn(str->c_str(), str->size()) : nullptr;
325 }
326 }
327 }
328}
329
332 const PropertyRNA *prop,
333 const wmMsgSubscribeValue *msg_val_params,
334 const char *id_repr)
335{
337 params.ptr = *ptr;
338 params.prop = prop;
339 WM_msg_subscribe_rna_params(mbus, &params, msg_val_params, id_repr);
340}
341
343
344/* -------------------------------------------------------------------------- */
349
351 ID *id,
352 const wmMsgSubscribeValue *msg_val_params,
353 const char *id_repr)
354{
355 wmMsgParams_RNA msg_key_params = {{}};
356 msg_key_params.ptr = RNA_id_pointer_create(id);
357 WM_msg_subscribe_rna_params(mbus, &msg_key_params, msg_val_params, id_repr);
358}
359
361{
362 wmMsgParams_RNA msg_key_params = {{}};
363 msg_key_params.ptr = RNA_id_pointer_create(id);
364 WM_msg_publish_rna_params(mbus, &msg_key_params);
365}
366
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_INLINE
struct GSet GSet
Definition BLI_ghash.h:337
bool BLI_gset_haskey(const GSet *gs, const void *key) ATTR_WARN_UNUSED_RESULT
void * BLI_gset_lookup(const GSet *gs, const void *key) ATTR_WARN_UNUSED_RESULT
BLI_INLINE bool BLI_gsetIterator_done(const GSetIterator *gsi)
Definition BLI_ghash.h:463
BLI_INLINE void * BLI_gsetIterator_getKey(GSetIterator *gsi)
Definition BLI_ghash.h:455
BLI_INLINE void BLI_gsetIterator_init(GSetIterator *gsi, GSet *gs)
Definition BLI_ghash.h:447
BLI_INLINE void BLI_gsetIterator_step(GSetIterator *gsi)
Definition BLI_ghash.h:459
bool BLI_gset_add(GSet *gs, void *key)
Definition BLI_ghash.cc:966
bool BLI_gset_remove(GSet *gs, const void *key, GSetKeyFreeFP keyfreefp)
Definition BLI_ghash.cc:999
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
int BLI_listbase_count(const ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:524
char * BLI_strdupn(const char *str, size_t len) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition string.cc:30
unsigned int uint
#define CLOG_DEBUG(clg_ref,...)
Definition CLG_log.h:191
#define CLOG_TRACE(clg_ref,...)
Definition CLG_log.h:192
ID and Library types, which are fundamental for SDNA.
Read Guarded memory(de)allocation.
CLG_LogRef * WM_LOG_MSGBUS_PUB
CLG_LogRef * WM_LOG_MSGBUS_SUB
#define str(s)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
const char * RNA_struct_identifier(const StructRNA *type)
const char * RNA_property_identifier(const PropertyRNA *prop)
PointerRNA RNA_id_pointer_create(ID *id)
std::optional< std::string > RNA_path_from_ID_to_struct(const PointerRNA *ptr)
Definition rna_path.cc:1014
bool RNA_path_resolve(const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop)
Definition rna_path.cc:532
Definition DNA_ID.h:414
char name[258]
Definition DNA_ID.h:432
void * first
ID * owner_id
Definition RNA_types.hh:51
StructRNA * type
Definition RNA_types.hh:52
void * data
Definition RNA_types.hh:53
GSet * messages_gset[WM_MSG_TYPE_NUM]
const PropertyRNA * prop
wmMsgSubscribeKey head
void(* remove_by_id)(wmMsgBus *mbus, const ID *id)
void(* update_by_id)(wmMsgBus *mbus, ID *id_src, ID *id_dst)
struct wmMsgTypeInfo::@062176151244273150215163026361115361006314120344 gset
void *(* key_duplicate_fn)(const void *key)
void(* repr)(FILE *stream, const wmMsgSubscribeKey *msg_key)
unsigned int(* hash_fn)(const void *msg)
void(* key_free_fn)(void *key)
bool(* cmp_fn)(const void *a, const void *b)
wmMsgParams_RNA params
unsigned int type
const char * id
PointerRNA * ptr
Definition wm_files.cc:4238
void WM_msg_publish_with_key(wmMsgBus *mbus, wmMsgSubscribeKey *msg_key)
void wm_msg_subscribe_value_free(wmMsgSubscribeKey *msg_key, wmMsgSubscribeValueLink *msg_lnk)
wmMsgSubscribeKey * WM_msg_subscribe_with_key(wmMsgBus *mbus, const wmMsgSubscribeKey *msg_key_test, const wmMsgSubscribeValue *msg_val_params)
@ WM_MSG_TYPE_RNA
void WM_msg_subscribe_rna_params(wmMsgBus *mbus, const wmMsgParams_RNA *msg_key_params, const wmMsgSubscribeValue *msg_val_params, const char *id_repr)
wmMsgSubscribeKey_RNA * WM_msg_lookup_rna(wmMsgBus *mbus, const wmMsgParams_RNA *msg_key_params)
void WM_msg_subscribe_rna(wmMsgBus *mbus, PointerRNA *ptr, const PropertyRNA *prop, const wmMsgSubscribeValue *msg_val_params, const char *id_repr)
BLI_INLINE uint void_hash_uint(const void *key)
void WM_msg_publish_rna_params(wmMsgBus *mbus, const wmMsgParams_RNA *msg_key_params)
void WM_msgtypeinfo_init_rna(wmMsgTypeInfo *msgtype_info)
static void wm_msg_rna_repr(FILE *stream, const wmMsgSubscribeKey *msg_key)
static bool wm_msg_rna_gset_cmp(const void *key_a_p, const void *key_b_p)
void WM_msg_publish_ID(wmMsgBus *mbus, ID *id)
void WM_msg_publish_rna(wmMsgBus *mbus, PointerRNA *ptr, PropertyRNA *prop)
static void wm_msg_rna_remove_by_id(wmMsgBus *mbus, const ID *id)
static void wm_msg_rna_update_by_id(wmMsgBus *mbus, ID *id_src, ID *id_dst)
static void * wm_msg_rna_gset_key_duplicate(const void *key_p)
static void wm_msg_rna_gset_key_free(void *key_p)
static uint wm_msg_rna_gset_hash(const void *key_p)
void WM_msg_subscribe_ID(wmMsgBus *mbus, ID *id, const wmMsgSubscribeValue *msg_val_params, const char *id_repr)