Blender  V2.93
bpy_msgbus.c
Go to the documentation of this file.
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 
22 #include <Python.h>
23 
24 #include "../generic/py_capi_utils.h"
25 #include "../generic/python_utildefines.h"
26 #include "../mathutils/mathutils.h"
27 
28 #include "BLI_utildefines.h"
29 
30 #include "BKE_context.h"
31 
32 #include "WM_api.h"
33 #include "WM_message.h"
34 #include "WM_types.h"
35 
36 #include "RNA_access.h"
37 #include "RNA_define.h"
38 #include "RNA_enum_types.h"
39 
40 #include "bpy_capi_utils.h"
41 #include "bpy_gizmo_wrap.h" /* own include */
42 #include "bpy_intern_string.h"
43 #include "bpy_rna.h"
44 
45 #include "bpy_msgbus.h" /* own include */
46 
47 /* -------------------------------------------------------------------- */
51 #define BPY_MSGBUS_RNA_MSGKEY_DOC \
52  " :arg key: Represents the type of data being subscribed to\n" \
53  "\n" \
54  " Arguments include\n" \
55  " - :class:`bpy.types.Property` instance.\n" \
56  " - :class:`bpy.types.Struct` type.\n" \
57  " - (:class:`bpy.types.Struct`, str) type and property name.\n" \
58  " :type key: Muliple\n"
59 
70 static int py_msgbus_rna_key_from_py(PyObject *py_sub,
71  wmMsgParams_RNA *msg_key_params,
72  const char *error_prefix)
73 {
74 
75  /* Allow common case, object rotation, location - etc. */
76  if (BaseMathObject_CheckExact(py_sub)) {
77  BaseMathObject *py_sub_math = (BaseMathObject *)py_sub;
78  if (py_sub_math->cb_user == NULL) {
79  PyErr_Format(PyExc_TypeError, "%s: math argument has no owner", error_prefix);
80  return -1;
81  }
82  py_sub = py_sub_math->cb_user;
83  /* Common case will use BPy_PropertyRNA_Check below. */
84  }
85 
86  if (BPy_PropertyRNA_Check(py_sub)) {
87  BPy_PropertyRNA *data_prop = (BPy_PropertyRNA *)py_sub;
88  PYRNA_PROP_CHECK_INT(data_prop);
89  msg_key_params->ptr = data_prop->ptr;
90  msg_key_params->prop = data_prop->prop;
91  }
92  else if (BPy_StructRNA_Check(py_sub)) {
93  /* note, this isn't typically used since we don't edit structs directly. */
94  BPy_StructRNA *data_srna = (BPy_StructRNA *)py_sub;
95  PYRNA_STRUCT_CHECK_INT(data_srna);
96  msg_key_params->ptr = data_srna->ptr;
97  }
98  /* TODO - property / type, not instance. */
99  else if (PyType_Check(py_sub)) {
100  StructRNA *data_type = pyrna_struct_as_srna(py_sub, false, error_prefix);
101  if (data_type == NULL) {
102  return -1;
103  }
104  msg_key_params->ptr.type = data_type;
105  }
106  else if (PyTuple_CheckExact(py_sub)) {
107  if (PyTuple_GET_SIZE(py_sub) == 2) {
108  PyObject *data_type_py = PyTuple_GET_ITEM(py_sub, 0);
109  PyObject *data_prop_py = PyTuple_GET_ITEM(py_sub, 1);
110  StructRNA *data_type = pyrna_struct_as_srna(data_type_py, false, error_prefix);
111  if (data_type == NULL) {
112  return -1;
113  }
114  if (!PyUnicode_CheckExact(data_prop_py)) {
115  PyErr_Format(PyExc_TypeError, "%s: expected property to be a string", error_prefix);
116  return -1;
117  }
118  PointerRNA data_type_ptr = {
119  .type = data_type,
120  };
121  const char *data_prop_str = PyUnicode_AsUTF8(data_prop_py);
122  PropertyRNA *data_prop = RNA_struct_find_property(&data_type_ptr, data_prop_str);
123 
124  if (data_prop == NULL) {
125  PyErr_Format(PyExc_TypeError,
126  "%s: struct %.200s does not contain property %.200s",
127  error_prefix,
128  RNA_struct_identifier(data_type),
129  data_prop_str);
130  return -1;
131  }
132 
133  msg_key_params->ptr.type = data_type;
134  msg_key_params->prop = data_prop;
135  }
136  else {
137  PyErr_Format(PyExc_ValueError, "%s: Expected a pair (type, property_id)", error_prefix);
138  return -1;
139  }
140  }
141  return 0;
142 }
143 
146 /* -------------------------------------------------------------------- */
150 #define BPY_MSGBUS_USER_DATA_LEN 2
151 
152 /* Follow wmMsgNotifyFn spec */
154  wmMsgSubscribeKey *UNUSED(msg_key),
155  wmMsgSubscribeValue *msg_val)
156 {
157  PyGILState_STATE gilstate;
158  bpy_context_set(C, &gilstate);
159 
160  PyObject *user_data = msg_val->user_data;
161  BLI_assert(PyTuple_GET_SIZE(user_data) == BPY_MSGBUS_USER_DATA_LEN);
162 
163  PyObject *callback_args = PyTuple_GET_ITEM(user_data, 0);
164  PyObject *callback_notify = PyTuple_GET_ITEM(user_data, 1);
165 
166  const bool is_write_ok = pyrna_write_check();
167  if (!is_write_ok) {
168  pyrna_write_set(true);
169  }
170 
171  PyObject *ret = PyObject_CallObject(callback_notify, callback_args);
172 
173  if (ret == NULL) {
174  PyC_Err_PrintWithFunc(callback_notify);
175  }
176  else {
177  if (ret != Py_None) {
178  PyErr_SetString(PyExc_ValueError, "the return value must be None");
179  PyC_Err_PrintWithFunc(callback_notify);
180  }
181  Py_DECREF(ret);
182  }
183 
184  bpy_context_clear(C, &gilstate);
185 
186  if (!is_write_ok) {
187  pyrna_write_set(false);
188  }
189 }
190 
191 /* Follow wmMsgSubscribeValueFreeDataFn spec */
193  struct wmMsgSubscribeValue *msg_val)
194 {
195  const PyGILState_STATE gilstate = PyGILState_Ensure();
196  Py_DECREF(msg_val->owner);
197  Py_DECREF(msg_val->user_data);
198  PyGILState_Release(gilstate);
199 }
200 
203 /* -------------------------------------------------------------------- */
208  bpy_msgbus_subscribe_rna_doc,
209  ".. function:: subscribe_rna(key, owner, args, notify, options=set())\n"
211  " :arg owner: Handle for this subscription (compared by identity).\n"
212  " :type owner: Any type.\n"
213  " :arg options: Change the behavior of the subscriber.\n"
214  "\n"
215  " - ``PERSISTENT`` when set, the subscriber will be kept when remapping ID data.\n"
216  "\n"
217  " :type options: set of str.\n");
218 static PyObject *bpy_msgbus_subscribe_rna(PyObject *UNUSED(self), PyObject *args, PyObject *kw)
219 {
220  const char *error_prefix = "subscribe_rna";
221  PyObject *py_sub = NULL;
222  PyObject *py_owner = NULL;
223  PyObject *callback_args = NULL;
224  PyObject *callback_notify = NULL;
225 
226  enum {
227  IS_PERSISTENT = (1 << 0),
228  };
229  PyObject *py_options = NULL;
230  EnumPropertyItem py_options_enum[] = {
231  {IS_PERSISTENT, "PERSISTENT", 0, ""},
232  {0, NULL, 0, NULL, NULL},
233  };
234  int options = 0;
235 
236  if (PyTuple_GET_SIZE(args) != 0) {
237  PyErr_Format(PyExc_TypeError, "%s: only keyword arguments are supported", error_prefix);
238  return NULL;
239  }
240  static const char *_keywords[] = {
241  "key",
242  "owner",
243  "args",
244  "notify",
245  "options",
246  NULL,
247  };
248  static _PyArg_Parser _parser = {"OOO!O|O!:subscribe_rna", _keywords, 0};
249  if (!_PyArg_ParseTupleAndKeywordsFast(args,
250  kw,
251  &_parser,
252  &py_sub,
253  &py_owner,
254  &PyTuple_Type,
255  &callback_args,
256  &callback_notify,
257  &PySet_Type,
258  &py_options)) {
259  return NULL;
260  }
261 
262  if (py_options &&
263  (pyrna_set_to_enum_bitfield(py_options_enum, py_options, &options, error_prefix)) == -1) {
264  return NULL;
265  }
266 
267  /* Note: we may want to have a way to pass this in. */
269  struct wmMsgBus *mbus = CTX_wm_message_bus(C);
270  wmMsgParams_RNA msg_key_params = {{0}};
271 
272  wmMsgSubscribeValue msg_val_params = {0};
273 
274  if (py_msgbus_rna_key_from_py(py_sub, &msg_key_params, error_prefix) == -1) {
275  return NULL;
276  }
277 
278  if (!PyFunction_Check(callback_notify)) {
279  PyErr_Format(PyExc_TypeError,
280  "notify expects a function, found %.200s",
281  Py_TYPE(callback_notify)->tp_name);
282  return NULL;
283  }
284 
285  if (options != 0) {
286  if (options & IS_PERSISTENT) {
287  msg_val_params.is_persistent = true;
288  }
289  }
290 
291  /* owner can be anything. */
292  {
293  msg_val_params.owner = py_owner;
294  Py_INCREF(py_owner);
295  }
296 
297  {
298  PyObject *user_data = PyTuple_New(2);
299  PyTuple_SET_ITEMS(user_data, Py_INCREF_RET(callback_args), Py_INCREF_RET(callback_notify));
300  msg_val_params.user_data = user_data;
301  }
302 
303  msg_val_params.notify = bpy_msgbus_notify;
305 
306  WM_msg_subscribe_rna_params(mbus, &msg_key_params, &msg_val_params, __func__);
307 
308  if (0) { /* For debugging. */
309  WM_msg_dump(mbus, __func__);
310  }
311 
312  Py_RETURN_NONE;
313 }
314 
316  bpy_msgbus_publish_rna_doc,
317  ".. function:: publish_rna(key)\n"
319  "\n"
320  " Notify subscribers of changes to this property\n"
321  " (this typically doesn't need to be called explicitly since changes will automatically "
322  "publish updates).\n"
323  " In some cases it may be useful to publish changes explicitly using more general keys.\n");
324 static PyObject *bpy_msgbus_publish_rna(PyObject *UNUSED(self), PyObject *args, PyObject *kw)
325 {
326  const char *error_prefix = "publish_rna";
327  PyObject *py_sub = NULL;
328 
329  if (PyTuple_GET_SIZE(args) != 0) {
330  PyErr_Format(PyExc_TypeError, "%s: only keyword arguments are supported", error_prefix);
331  return NULL;
332  }
333  static const char *_keywords[] = {
334  "key",
335  NULL,
336  };
337  static _PyArg_Parser _parser = {"O:publish_rna", _keywords, 0};
338  if (!_PyArg_ParseTupleAndKeywordsFast(args, kw, &_parser, &py_sub)) {
339  return NULL;
340  }
341 
342  /* Note: we may want to have a way to pass this in. */
344  struct wmMsgBus *mbus = CTX_wm_message_bus(C);
345  wmMsgParams_RNA msg_key_params = {{0}};
346 
347  if (py_msgbus_rna_key_from_py(py_sub, &msg_key_params, error_prefix) == -1) {
348  return NULL;
349  }
350 
351  WM_msg_publish_rna_params(mbus, &msg_key_params);
352 
353  Py_RETURN_NONE;
354 }
355 
356 PyDoc_STRVAR(bpy_msgbus_clear_by_owner_doc,
357  ".. function:: clear_by_owner(owner)\n"
358  "\n"
359  " Clear all subscribers using this owner.\n");
360 static PyObject *bpy_msgbus_clear_by_owner(PyObject *UNUSED(self), PyObject *py_owner)
361 {
363  struct wmMsgBus *mbus = CTX_wm_message_bus(C);
364  WM_msgbus_clear_by_owner(mbus, py_owner);
365  Py_RETURN_NONE;
366 }
367 
368 static struct PyMethodDef BPy_msgbus_methods[] = {
369  {"subscribe_rna",
370  (PyCFunction)bpy_msgbus_subscribe_rna,
371  METH_VARARGS | METH_KEYWORDS,
372  bpy_msgbus_subscribe_rna_doc},
373  {"publish_rna",
374  (PyCFunction)bpy_msgbus_publish_rna,
375  METH_VARARGS | METH_KEYWORDS,
376  bpy_msgbus_publish_rna_doc},
377  {"clear_by_owner",
378  (PyCFunction)bpy_msgbus_clear_by_owner,
379  METH_O,
380  bpy_msgbus_clear_by_owner_doc},
381  {NULL, NULL, 0, NULL},
382 };
383 
384 static struct PyModuleDef _bpy_msgbus_def = {
385  PyModuleDef_HEAD_INIT,
386  .m_name = "msgbus",
387  .m_methods = BPy_msgbus_methods,
388 };
389 
390 PyObject *BPY_msgbus_module(void)
391 {
392  PyObject *submodule;
393 
394  submodule = PyModule_Create(&_bpy_msgbus_def);
395 
396  return submodule;
397 }
398 
struct wmMsgBus * CTX_wm_message_bus(const bContext *C)
Definition: context.c:746
#define BLI_assert(a)
Definition: BLI_assert.h:58
#define UNUSED(x)
#define C
Definition: RandGen.cpp:39
struct bContext * BPY_context_get(void)
void bpy_context_clear(struct bContext *C, const PyGILState_STATE *gilstate)
void bpy_context_set(struct bContext *C, PyGILState_STATE *gilstate)
static struct PyMethodDef BPy_msgbus_methods[]
Definition: bpy_msgbus.c:368
#define BPY_MSGBUS_USER_DATA_LEN
Definition: bpy_msgbus.c:150
PyDoc_STRVAR(bpy_msgbus_subscribe_rna_doc, ".. function:: subscribe_rna(key, owner, args, notify, options=set())\n" "\n" BPY_MSGBUS_RNA_MSGKEY_DOC " :arg owner: Handle for this subscription (compared by identity).\n" " :type owner: Any type.\n" " :arg options: Change the behavior of the subscriber.\n" "\n" " - ``PERSISTENT`` when set, the subscriber will be kept when remapping ID data.\n" "\n" " :type options: set of str.\n")
static PyObject * bpy_msgbus_clear_by_owner(PyObject *UNUSED(self), PyObject *py_owner)
Definition: bpy_msgbus.c:360
static int py_msgbus_rna_key_from_py(PyObject *py_sub, wmMsgParams_RNA *msg_key_params, const char *error_prefix)
Definition: bpy_msgbus.c:70
static PyObject * bpy_msgbus_subscribe_rna(PyObject *UNUSED(self), PyObject *args, PyObject *kw)
Definition: bpy_msgbus.c:218
static PyObject * bpy_msgbus_publish_rna(PyObject *UNUSED(self), PyObject *args, PyObject *kw)
Definition: bpy_msgbus.c:324
static void bpy_msgbus_notify(bContext *C, wmMsgSubscribeKey *UNUSED(msg_key), wmMsgSubscribeValue *msg_val)
Definition: bpy_msgbus.c:153
static void bpy_msgbus_subscribe_value_free_data(struct wmMsgSubscribeKey *UNUSED(msg_key), struct wmMsgSubscribeValue *msg_val)
Definition: bpy_msgbus.c:192
#define BPY_MSGBUS_RNA_MSGKEY_DOC
Definition: bpy_msgbus.c:51
static struct PyModuleDef _bpy_msgbus_def
Definition: bpy_msgbus.c:384
PyObject * BPY_msgbus_module(void)
Definition: bpy_msgbus.c:390
int pyrna_set_to_enum_bitfield(const EnumPropertyItem *items, PyObject *value, int *r_value, const char *error_prefix)
Definition: bpy_rna.c:1363
bool pyrna_write_check(void)
Definition: bpy_rna.c:362
StructRNA * pyrna_struct_as_srna(PyObject *self, const bool parent, const char *error_prefix)
Definition: bpy_rna.c:7849
void pyrna_write_set(bool val)
Definition: bpy_rna.c:367
#define PYRNA_STRUCT_CHECK_INT(obj)
Definition: bpy_rna.h:90
#define PYRNA_PROP_CHECK_INT(obj)
Definition: bpy_rna.h:101
#define BPy_StructRNA_Check(v)
Definition: bpy_rna.h:80
#define BPy_PropertyRNA_Check(v)
Definition: bpy_rna.h:82
CCL_NAMESPACE_BEGIN struct Options options
void * user_data
#define BaseMathObject_CheckExact(v)
Definition: mathutils.h:78
void PyC_Err_PrintWithFunc(PyObject *py_func)
#define PyTuple_SET_ITEMS(op_arg,...)
return ret
const char * RNA_struct_identifier(const StructRNA *type)
Definition: rna_access.c:723
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
Definition: rna_access.c:866
PropertyRNA * prop
Definition: bpy_rna.h:143
PyObject_HEAD PointerRNA ptr
Definition: bpy_rna.h:142
PyObject_HEAD PointerRNA ptr
Definition: bpy_rna.h:125
struct StructRNA * type
Definition: RNA_types.h:51
const PropertyRNA * prop
wmMsgSubscribeValueFreeDataFn free_data
wmMsgNotifyFn notify
void WM_msgbus_clear_by_owner(struct wmMsgBus *mbus, void *owner)
void WM_msg_dump(struct wmMsgBus *mbus, const char *info_str)
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_publish_rna_params(struct wmMsgBus *mbus, const wmMsgParams_RNA *msg_key_params)