Blender V4.3
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
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#include "BLI_utildefines.h"
20
21#include "WM_message.hh"
22#include "WM_types.hh"
24
25#include "RNA_access.hh"
26#include "RNA_path.hh"
27
28/* -------------------------------------------------------------------- */
33{
34 size_t y = size_t(key) >> sizeof(void *);
35 return uint(y);
36}
37
38static uint wm_msg_rna_gset_hash(const void *key_p)
39{
40 const wmMsgSubscribeKey_RNA *key = static_cast<const wmMsgSubscribeKey_RNA *>(key_p);
41 const wmMsgParams_RNA *params = &key->msg.params;
42 // printf("%s\n", RNA_struct_identifier(params->ptr.type));
43 uint k = void_hash_uint(params->ptr.type);
44 k ^= void_hash_uint(params->ptr.data);
45 k ^= void_hash_uint(params->ptr.owner_id);
46 k ^= void_hash_uint(params->prop);
47 return k;
48}
49static bool wm_msg_rna_gset_cmp(const void *key_a_p, const void *key_b_p)
50{
51 const wmMsgParams_RNA *params_a = &((const wmMsgSubscribeKey_RNA *)key_a_p)->msg.params;
52 const wmMsgParams_RNA *params_b = &((const wmMsgSubscribeKey_RNA *)key_b_p)->msg.params;
53 return !((params_a->ptr.type == params_b->ptr.type) &&
54 (params_a->ptr.owner_id == params_b->ptr.owner_id) &&
55 (params_a->ptr.data == params_b->ptr.data) && (params_a->prop == params_b->prop));
56}
57static void wm_msg_rna_gset_key_free(void *key_p)
58{
59 wmMsgSubscribeKey_RNA *key = static_cast<wmMsgSubscribeKey_RNA *>(key_p);
60 wmMsgSubscribeValueLink *msg_lnk_next;
61 for (wmMsgSubscribeValueLink *msg_lnk =
62 static_cast<wmMsgSubscribeValueLink *>(key->head.values.first);
63 msg_lnk;
64 msg_lnk = msg_lnk_next)
65 {
66 msg_lnk_next = msg_lnk->next;
67 wm_msg_subscribe_value_free(&key->head, msg_lnk);
68 }
69 if (key->msg.params.data_path != nullptr) {
71 }
72 MEM_freeN(key);
73}
74
75static void wm_msg_rna_repr(FILE *stream, const wmMsgSubscribeKey *msg_key)
76{
77 const wmMsgSubscribeKey_RNA *m = (wmMsgSubscribeKey_RNA *)msg_key;
78 const char *none = "<none>";
79 fprintf(stream,
80 "<wmMsg_RNA %p, "
81 "id='%s', "
82 "%s.%s values_len=%d\n",
83 m,
84 m->msg.head.id,
88}
89
90static void wm_msg_rna_update_by_id(wmMsgBus *mbus, ID *id_src, ID *id_dst)
91{
93 GSetIterator gs_iter;
94 BLI_gsetIterator_init(&gs_iter, gs);
95 while (BLI_gsetIterator_done(&gs_iter) == false) {
96 wmMsgSubscribeKey_RNA *key = static_cast<wmMsgSubscribeKey_RNA *>(
97 BLI_gsetIterator_getKey(&gs_iter));
98 BLI_gsetIterator_step(&gs_iter);
99 if (key->msg.params.ptr.owner_id == id_src) {
100
101 /* GSet always needs updating since the key changes. */
102 BLI_gset_remove(gs, key, nullptr);
103
104 /* Remove any non-persistent values, so a single persistent
105 * value doesn't modify behavior for the rest. */
107 msg_lnk = static_cast<wmMsgSubscribeValueLink *>(key->head.values.first),
108 *msg_lnk_next;
109 msg_lnk;
110 msg_lnk = msg_lnk_next)
111 {
112 msg_lnk_next = msg_lnk->next;
113 if (msg_lnk->params.is_persistent == false) {
114 if (msg_lnk->params.tag) {
115 mbus->messages_tag_count -= 1;
116 }
117 wm_msg_subscribe_value_free(&key->head, msg_lnk);
118 }
119 }
120
121 bool remove = true;
122
123 if (BLI_listbase_is_empty(&key->head.values)) {
124 /* Remove, no reason to keep. */
125 }
126 else if (key->msg.params.ptr.data == key->msg.params.ptr.owner_id) {
127 /* Simple, just update the ID. */
128 key->msg.params.ptr.data = id_dst;
129 key->msg.params.ptr.owner_id = id_dst;
130 remove = false;
131 }
132 else {
133 /* We need to resolve this from the new ID pointer. */
134 PointerRNA idptr = RNA_id_pointer_create(id_dst);
136 PropertyRNA *prop = nullptr;
137 if (RNA_path_resolve(&idptr, key->msg.params.data_path, &ptr, &prop) &&
138 (prop == nullptr) == (key->msg.params.prop == nullptr))
139 {
140 key->msg.params.ptr = ptr;
141 key->msg.params.prop = prop;
142 remove = false;
143 }
144 }
145
146 if (remove) {
148 msg_lnk = static_cast<wmMsgSubscribeValueLink *>(key->head.values.first),
149 *msg_lnk_next;
150 msg_lnk;
151 msg_lnk = msg_lnk_next)
152 {
153 msg_lnk_next = msg_lnk->next;
154 if (msg_lnk->params.is_persistent == false) {
155 if (msg_lnk->params.tag) {
156 mbus->messages_tag_count -= 1;
157 }
158 wm_msg_subscribe_value_free(&key->head, msg_lnk);
159 }
160 }
161 /* Failed to persist, remove the key. */
162 BLI_remlink(&mbus->messages, key);
164 }
165 else {
166 /* Note that it's not impossible this key exists, however it is very unlikely
167 * since a subscriber would need to register in the middle of an undo for eg.
168 * so assert for now. */
169 BLI_assert(!BLI_gset_haskey(gs, key));
170 BLI_gset_add(gs, key);
171 }
172 }
173 }
174}
175
176static void wm_msg_rna_remove_by_id(wmMsgBus *mbus, const ID *id)
177{
179 GSetIterator gs_iter;
180 BLI_gsetIterator_init(&gs_iter, gs);
181 while (BLI_gsetIterator_done(&gs_iter) == false) {
182 wmMsgSubscribeKey_RNA *key = static_cast<wmMsgSubscribeKey_RNA *>(
183 BLI_gsetIterator_getKey(&gs_iter));
184 BLI_gsetIterator_step(&gs_iter);
185 if (key->msg.params.ptr.owner_id == id) {
186 /* Clear here so we can decrement 'messages_tag_count'. */
188 msg_lnk = static_cast<wmMsgSubscribeValueLink *>(key->head.values.first),
189 *msg_lnk_next;
190 msg_lnk;
191 msg_lnk = msg_lnk_next)
192 {
193 msg_lnk_next = msg_lnk->next;
194 if (msg_lnk->params.tag) {
195 mbus->messages_tag_count -= 1;
196 }
197 wm_msg_subscribe_value_free(&key->head, msg_lnk);
198 }
199
200 BLI_remlink(&mbus->messages, key);
201 BLI_gset_remove(gs, key, nullptr);
203 }
204 }
205}
206
208{
209 msgtype_info->gset.hash_fn = wm_msg_rna_gset_hash;
210 msgtype_info->gset.cmp_fn = wm_msg_rna_gset_cmp;
212
213 msgtype_info->repr = wm_msg_rna_repr;
216
217 msgtype_info->msg_key_size = sizeof(wmMsgSubscribeKey_RNA);
218}
219
222/* -------------------------------------------------------------------- */
227{
228 wmMsgSubscribeKey_RNA key_test;
229 key_test.msg.params = *msg_key_params;
230 return static_cast<wmMsgSubscribeKey_RNA *>(
232}
233
234void WM_msg_publish_rna_params(wmMsgBus *mbus, const wmMsgParams_RNA *msg_key_params)
235{
237
238 const char *none = "<none>";
240 2,
241 "rna(id='%s', %s.%s)",
242 msg_key_params->ptr.owner_id ? ((ID *)msg_key_params->ptr.owner_id)->name : none,
243 msg_key_params->ptr.type ? RNA_struct_identifier(msg_key_params->ptr.type) : none,
244 msg_key_params->prop ? RNA_property_identifier((PropertyRNA *)msg_key_params->prop) :
245 none);
246
247 if ((key = WM_msg_lookup_rna(mbus, msg_key_params))) {
248 WM_msg_publish_with_key(mbus, &key->head);
249 }
250
251 /* Support anonymous subscribers, this may be some extra overhead
252 * but we want to be able to be more ambiguous. */
253 if (msg_key_params->ptr.owner_id || msg_key_params->ptr.data) {
254 wmMsgParams_RNA msg_key_params_anon = *msg_key_params;
255
256 /* We might want to enable this later? */
257 if (msg_key_params_anon.prop != nullptr) {
258 /* All properties for this type. */
259 msg_key_params_anon.prop = nullptr;
260 if ((key = WM_msg_lookup_rna(mbus, &msg_key_params_anon))) {
261 WM_msg_publish_with_key(mbus, &key->head);
262 }
263 msg_key_params_anon.prop = msg_key_params->prop;
264 }
265
266 msg_key_params_anon.ptr.owner_id = nullptr;
267 msg_key_params_anon.ptr.data = nullptr;
268 if ((key = WM_msg_lookup_rna(mbus, &msg_key_params_anon))) {
269 WM_msg_publish_with_key(mbus, &key->head);
270 }
271
272 /* Support subscribers to a type. */
273 if (msg_key_params->prop) {
274 msg_key_params_anon.prop = nullptr;
275 if ((key = WM_msg_lookup_rna(mbus, &msg_key_params_anon))) {
276 WM_msg_publish_with_key(mbus, &key->head);
277 }
278 }
279 }
280}
281
283{
285 params.ptr = *ptr;
286 params.prop = prop;
288}
289
291 const wmMsgParams_RNA *msg_key_params,
292 const wmMsgSubscribeValue *msg_val_params,
293 const char *id_repr)
294{
295 wmMsgSubscribeKey_RNA msg_key_test = {{nullptr}};
296
297 /* Use when added. */
298 msg_key_test.msg.head.id = id_repr;
299 msg_key_test.msg.head.type = WM_MSG_TYPE_RNA;
300 /* For lookup. */
301 msg_key_test.msg.params = *msg_key_params;
302
303 const char *none = "<none>";
305 3,
306 "rna(id='%s', %s.%s, info='%s')",
307 msg_key_params->ptr.owner_id ? ((ID *)msg_key_params->ptr.owner_id)->name : none,
308 msg_key_params->ptr.type ? RNA_struct_identifier(msg_key_params->ptr.type) : none,
309 msg_key_params->prop ? RNA_property_identifier((PropertyRNA *)msg_key_params->prop) :
310 none,
311 id_repr);
312
314 mbus, &msg_key_test.head, msg_val_params);
315
316 if (msg_val_params->is_persistent) {
317 if (msg_key->msg.params.data_path == nullptr) {
318 if (msg_key->msg.params.ptr.data != msg_key->msg.params.ptr.owner_id) {
319 /* We assume prop type can't change. */
320 const std::optional<std::string> str = RNA_path_from_ID_to_struct(
321 &msg_key->msg.params.ptr);
322 msg_key->msg.params.data_path = str ? BLI_strdupn(str->c_str(), str->size()) : nullptr;
323 }
324 }
325 }
326}
327
330 const PropertyRNA *prop,
331 const wmMsgSubscribeValue *msg_val_params,
332 const char *id_repr)
333{
335 params.ptr = *ptr;
336 params.prop = prop;
337 WM_msg_subscribe_rna_params(mbus, &params, msg_val_params, id_repr);
338}
339
342/* -------------------------------------------------------------------------- */
349 ID *id,
350 const wmMsgSubscribeValue *msg_val_params,
351 const char *id_repr)
352{
353 wmMsgParams_RNA msg_key_params = {{nullptr}};
354 msg_key_params.ptr = RNA_id_pointer_create(id);
355 WM_msg_subscribe_rna_params(mbus, &msg_key_params, msg_val_params, id_repr);
356}
357
359{
360 wmMsgParams_RNA msg_key_params = {{nullptr}};
361 msg_key_params.ptr = RNA_id_pointer_create(id);
362 WM_msg_publish_rna_params(mbus, &msg_key_params);
363}
364
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_INLINE
struct GSet GSet
Definition BLI_ghash.h:341
bool BLI_gset_haskey(const GSet *gs, const void *key) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.c:1004
void * BLI_gset_lookup(const GSet *gs, const void *key) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.c:1058
BLI_INLINE bool BLI_gsetIterator_done(const GSetIterator *gsi)
Definition BLI_ghash.h:467
BLI_INLINE void * BLI_gsetIterator_getKey(GSetIterator *gsi)
Definition BLI_ghash.h:459
BLI_INLINE void BLI_gsetIterator_init(GSetIterator *gsi, GSet *gs)
Definition BLI_ghash.h:451
BLI_INLINE void BLI_gsetIterator_step(GSetIterator *gsi)
Definition BLI_ghash.h:463
bool BLI_gset_add(GSet *gs, void *key)
Definition BLI_ghash.c:966
bool BLI_gset_remove(GSet *gs, const void *key, GSetKeyFreeFP keyfreefp)
Definition BLI_ghash.c:999
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:130
int BLI_listbase_count(const struct ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
char * BLI_strdupn(const char *str, size_t len) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition string.c:29
unsigned int uint
#define CLOG_INFO(clg_ref, level,...)
Definition CLG_log.h:179
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:105
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:1007
bool RNA_path_resolve(const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop)
Definition rna_path.cc:525
Definition DNA_ID.h:413
char name[66]
Definition DNA_ID.h:425
void * first
ID * owner_id
Definition RNA_types.hh:40
StructRNA * type
Definition RNA_types.hh:41
void * data
Definition RNA_types.hh:42
PropertyRNA * next
GSet * messages_gset[WM_MSG_TYPE_NUM]
const PropertyRNA * prop
wmMsgSubscribeKey head
struct wmMsgTypeInfo::@1392 gset
void(* remove_by_id)(wmMsgBus *mbus, const ID *id)
void(* update_by_id)(wmMsgBus *mbus, ID *id_src, ID *id_dst)
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:4126
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_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)