227a21726f4590c070a68f6109ca6281c3708596
[blender.git] / source / blender / windowmanager / message_bus / intern / wm_message_bus_rna.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16
17 /** \file
18  * \ingroup wm
19  */
20
21 #include <stdio.h>
22
23 #include "CLG_log.h"
24 #include "MEM_guardedalloc.h"
25
26 #include "DNA_ID.h"
27
28 #include "BLI_utildefines.h"
29 #include "BLI_ghash.h"
30 #include "BLI_listbase.h"
31
32 #include "WM_types.h"
33 #include "WM_message.h"
34 #include "message_bus/intern/wm_message_bus_intern.h"
35
36 #include "RNA_access.h"
37
38 /* -------------------------------------------------------------------------- */
39
40 BLI_INLINE uint void_hash_uint(const void *key)
41 {
42   size_t y = (size_t)key >> (sizeof(void *));
43   return (unsigned int)y;
44 }
45
46 static uint wm_msg_rna_gset_hash(const void *key_p)
47 {
48   const wmMsgSubscribeKey_RNA *key = key_p;
49   const wmMsgParams_RNA *params = &key->msg.params;
50   //  printf("%s\n", RNA_struct_identifier(params->ptr.type));
51   uint k = void_hash_uint(params->ptr.type);
52   k ^= void_hash_uint(params->ptr.data);
53   k ^= void_hash_uint(params->ptr.id.data);
54   k ^= void_hash_uint(params->prop);
55   return k;
56 }
57 static bool wm_msg_rna_gset_cmp(const void *key_a_p, const void *key_b_p)
58 {
59   const wmMsgParams_RNA *params_a = &((const wmMsgSubscribeKey_RNA *)key_a_p)->msg.params;
60   const wmMsgParams_RNA *params_b = &((const wmMsgSubscribeKey_RNA *)key_b_p)->msg.params;
61   return !((params_a->ptr.type == params_b->ptr.type) &&
62            (params_a->ptr.id.data == params_b->ptr.id.data) &&
63            (params_a->ptr.data == params_b->ptr.data) && (params_a->prop == params_b->prop));
64 }
65 static void wm_msg_rna_gset_key_free(void *key_p)
66 {
67   wmMsgSubscribeKey_RNA *key = key_p;
68   wmMsgSubscribeValueLink *msg_lnk_next;
69   for (wmMsgSubscribeValueLink *msg_lnk = key->head.values.first; msg_lnk;
70        msg_lnk = msg_lnk_next) {
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 != NULL) {
75     MEM_freeN(key->msg.params.data_path);
76   }
77   MEM_freeN(key);
78 }
79
80 static 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,
90           m->msg.params.ptr.type ? RNA_struct_identifier(m->msg.params.ptr.type) : none,
91           m->msg.params.prop ? RNA_property_identifier((PropertyRNA *)m->msg.params.prop) : none,
92           BLI_listbase_count(&m->head.values));
93 }
94
95 static void wm_msg_rna_update_by_id(struct wmMsgBus *mbus, ID *id_src, ID *id_dst)
96 {
97   GSet *gs = mbus->messages_gset[WM_MSG_TYPE_RNA];
98   GSetIterator gs_iter;
99   BLI_gsetIterator_init(&gs_iter, gs);
100   while (BLI_gsetIterator_done(&gs_iter) == false) {
101     wmMsgSubscribeKey_RNA *key = BLI_gsetIterator_getKey(&gs_iter);
102     BLI_gsetIterator_step(&gs_iter);
103     if (key->msg.params.ptr.id.data == id_src) {
104
105       /* GSet always needs updating since the key changes. */
106       BLI_gset_remove(gs, key, NULL);
107
108       /* Remove any non-persistent values, so a single persistent
109        * value doesn't modify behavior for the rest. */
110       for (wmMsgSubscribeValueLink *msg_lnk = key->head.values.first, *msg_lnk_next; msg_lnk;
111            msg_lnk = msg_lnk_next) {
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.id.data) {
127         /* Simple, just update the ID. */
128         key->msg.params.ptr.data = id_dst;
129         key->msg.params.ptr.id.data = id_dst;
130         remove = false;
131       }
132       else {
133         /* We need to resolve this from the new ID pointer. */
134         PointerRNA idptr;
135         RNA_id_pointer_create(id_dst, &idptr);
136         PointerRNA ptr;
137         PropertyRNA *prop = NULL;
138         if (RNA_path_resolve(&idptr, key->msg.params.data_path, &ptr, &prop) &&
139             (prop == NULL) == (key->msg.params.prop == NULL)) {
140           key->msg.params.ptr = ptr;
141           key->msg.params.prop = prop;
142           remove = false;
143         }
144       }
145
146       if (remove) {
147         for (wmMsgSubscribeValueLink *msg_lnk = key->head.values.first, *msg_lnk_next; msg_lnk;
148              msg_lnk = msg_lnk_next) {
149           msg_lnk_next = msg_lnk->next;
150           if (msg_lnk->params.is_persistent == false) {
151             if (msg_lnk->params.tag) {
152               mbus->messages_tag_count -= 1;
153             }
154             wm_msg_subscribe_value_free(&key->head, msg_lnk);
155           }
156         }
157         /* Failed to persist, remove the key. */
158         BLI_remlink(&mbus->messages, key);
159         wm_msg_rna_gset_key_free(key);
160       }
161       else {
162         /* note that it's not impossible this key exists, however it is very unlikely
163          * since a subscriber would need to register in the middle of an undo for eg. so assert for now. */
164         BLI_assert(!BLI_gset_haskey(gs, key));
165         BLI_gset_add(gs, key);
166       }
167     }
168   }
169 }
170
171 static void wm_msg_rna_remove_by_id(struct wmMsgBus *mbus, const ID *id)
172 {
173   GSet *gs = mbus->messages_gset[WM_MSG_TYPE_RNA];
174   GSetIterator gs_iter;
175   BLI_gsetIterator_init(&gs_iter, gs);
176   while (BLI_gsetIterator_done(&gs_iter) == false) {
177     wmMsgSubscribeKey_RNA *key = BLI_gsetIterator_getKey(&gs_iter);
178     BLI_gsetIterator_step(&gs_iter);
179     if (key->msg.params.ptr.id.data == id) {
180       /* Clear here so we can decrement 'messages_tag_count'. */
181       for (wmMsgSubscribeValueLink *msg_lnk = key->head.values.first, *msg_lnk_next; msg_lnk;
182            msg_lnk = msg_lnk_next) {
183         msg_lnk_next = msg_lnk->next;
184         if (msg_lnk->params.tag) {
185           mbus->messages_tag_count -= 1;
186         }
187         wm_msg_subscribe_value_free(&key->head, msg_lnk);
188       }
189
190       BLI_remlink(&mbus->messages, key);
191       BLI_gset_remove(gs, key, NULL);
192       wm_msg_rna_gset_key_free(key);
193     }
194   }
195 }
196
197 void WM_msgtypeinfo_init_rna(wmMsgTypeInfo *msgtype_info)
198 {
199   msgtype_info->gset.hash_fn = wm_msg_rna_gset_hash;
200   msgtype_info->gset.cmp_fn = wm_msg_rna_gset_cmp;
201   msgtype_info->gset.key_free_fn = wm_msg_rna_gset_key_free;
202
203   msgtype_info->repr = wm_msg_rna_repr;
204   msgtype_info->update_by_id = wm_msg_rna_update_by_id;
205   msgtype_info->remove_by_id = wm_msg_rna_remove_by_id;
206
207   msgtype_info->msg_key_size = sizeof(wmMsgSubscribeKey_RNA);
208 }
209
210 /* -------------------------------------------------------------------------- */
211
212 wmMsgSubscribeKey_RNA *WM_msg_lookup_rna(struct wmMsgBus *mbus,
213                                          const wmMsgParams_RNA *msg_key_params)
214 {
215   wmMsgSubscribeKey_RNA key_test;
216   key_test.msg.params = *msg_key_params;
217   return BLI_gset_lookup(mbus->messages_gset[WM_MSG_TYPE_RNA], &key_test);
218 }
219
220 void WM_msg_publish_rna_params(struct wmMsgBus *mbus, const wmMsgParams_RNA *msg_key_params)
221 {
222   wmMsgSubscribeKey_RNA *key;
223
224   const char *none = "<none>";
225   CLOG_INFO(WM_LOG_MSGBUS_PUB,
226             2,
227             "rna(id='%s', %s.%s)",
228             msg_key_params->ptr.id.data ? ((ID *)msg_key_params->ptr.id.data)->name : none,
229             msg_key_params->ptr.type ? RNA_struct_identifier(msg_key_params->ptr.type) : none,
230             msg_key_params->prop ? RNA_property_identifier((PropertyRNA *)msg_key_params->prop) :
231                                    none);
232
233   if ((key = WM_msg_lookup_rna(mbus, msg_key_params))) {
234     WM_msg_publish_with_key(mbus, &key->head);
235   }
236
237   /* Support anonymous subscribers, this may be some extra overhead
238    * but we want to be able to be more ambiguous. */
239   if (msg_key_params->ptr.id.data || msg_key_params->ptr.data) {
240     wmMsgParams_RNA msg_key_params_anon = *msg_key_params;
241
242     /* We might want to enable this later? */
243     if (msg_key_params_anon.prop != NULL) {
244       /* All properties for this type. */
245       msg_key_params_anon.prop = NULL;
246       if ((key = WM_msg_lookup_rna(mbus, &msg_key_params_anon))) {
247         WM_msg_publish_with_key(mbus, &key->head);
248       }
249       msg_key_params_anon.prop = msg_key_params->prop;
250     }
251
252     msg_key_params_anon.ptr.id.data = NULL;
253     msg_key_params_anon.ptr.data = NULL;
254     if ((key = WM_msg_lookup_rna(mbus, &msg_key_params_anon))) {
255       WM_msg_publish_with_key(mbus, &key->head);
256     }
257
258     /* Support subscribers to a type. */
259     if (msg_key_params->prop) {
260       msg_key_params_anon.prop = NULL;
261       if ((key = WM_msg_lookup_rna(mbus, &msg_key_params_anon))) {
262         WM_msg_publish_with_key(mbus, &key->head);
263       }
264     }
265   }
266 }
267
268 void WM_msg_publish_rna(struct wmMsgBus *mbus, PointerRNA *ptr, PropertyRNA *prop)
269 {
270   WM_msg_publish_rna_params(mbus,
271                             &(wmMsgParams_RNA){
272                                 .ptr = *ptr,
273                                 .prop = prop,
274                             });
275 }
276
277 void WM_msg_subscribe_rna_params(struct wmMsgBus *mbus,
278                                  const wmMsgParams_RNA *msg_key_params,
279                                  const wmMsgSubscribeValue *msg_val_params,
280                                  const char *id_repr)
281 {
282   wmMsgSubscribeKey_RNA msg_key_test = {{NULL}};
283
284   /* use when added */
285   msg_key_test.msg.head.id = id_repr;
286   msg_key_test.msg.head.type = WM_MSG_TYPE_RNA;
287   /* for lookup */
288   msg_key_test.msg.params = *msg_key_params;
289
290   const char *none = "<none>";
291   CLOG_INFO(WM_LOG_MSGBUS_SUB,
292             3,
293             "rna(id='%s', %s.%s, info='%s')",
294             msg_key_params->ptr.id.data ? ((ID *)msg_key_params->ptr.id.data)->name : none,
295             msg_key_params->ptr.type ? RNA_struct_identifier(msg_key_params->ptr.type) : none,
296             msg_key_params->prop ? RNA_property_identifier((PropertyRNA *)msg_key_params->prop) :
297                                    none,
298             id_repr);
299
300   wmMsgSubscribeKey_RNA *msg_key = (wmMsgSubscribeKey_RNA *)WM_msg_subscribe_with_key(
301       mbus, &msg_key_test.head, msg_val_params);
302
303   if (msg_val_params->is_persistent) {
304     if (msg_key->msg.params.data_path == NULL) {
305       if (msg_key->msg.params.ptr.data != msg_key->msg.params.ptr.id.data) {
306         /* We assume prop type can't change. */
307         msg_key->msg.params.data_path = RNA_path_from_ID_to_struct(&msg_key->msg.params.ptr);
308       }
309     }
310   }
311 }
312
313 void WM_msg_subscribe_rna(struct wmMsgBus *mbus,
314                           PointerRNA *ptr,
315                           const PropertyRNA *prop,
316                           const wmMsgSubscribeValue *msg_val_params,
317                           const char *id_repr)
318 {
319   WM_msg_subscribe_rna_params(mbus,
320                               &(const wmMsgParams_RNA){
321                                   .ptr = *ptr,
322                                   .prop = prop,
323                               },
324                               msg_val_params,
325                               id_repr);
326 }
327
328 /** \} */
329
330 /* -------------------------------------------------------------------------- */
331 /** \name ID variants of RNA API
332  *
333  * \note While we could have a separate type for ID's, use RNA since there is enough overlap.
334  * \{ */
335
336 void WM_msg_subscribe_ID(struct wmMsgBus *mbus,
337                          ID *id,
338                          const wmMsgSubscribeValue *msg_val_params,
339                          const char *id_repr)
340 {
341   wmMsgParams_RNA msg_key_params = {{{NULL}}};
342   RNA_id_pointer_create(id, &msg_key_params.ptr);
343   WM_msg_subscribe_rna_params(mbus, &msg_key_params, msg_val_params, id_repr);
344 }
345
346 void WM_msg_publish_ID(struct wmMsgBus *mbus, ID *id)
347 {
348   wmMsgParams_RNA msg_key_params = {{{NULL}}};
349   RNA_id_pointer_create(id, &msg_key_params.ptr);
350   WM_msg_publish_rna_params(mbus, &msg_key_params);
351 }
352
353 /** \} */