Blender  V3.3
bpy_rna_callback.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
10 #include <Python.h>
11 
12 #include "../generic/py_capi_rna.h"
13 #include "../generic/python_utildefines.h"
14 
15 #include "DNA_space_types.h"
16 
17 #include "RNA_access.h"
18 #include "RNA_enum_types.h"
19 #include "RNA_prototypes.h"
20 
21 #include "BKE_screen.h"
22 
23 #include "WM_api.h"
24 
25 #include "ED_space_api.h"
26 
27 #include "BPY_extern.h" /* For public API. */
28 
29 #include "bpy_capi_utils.h"
30 #include "bpy_rna.h"
31 #include "bpy_rna_callback.h" /* Own include. */
32 
33 /* Use this to stop other capsules from being mis-used. */
34 static const char *rna_capsual_id = "RNA_HANDLE";
35 static const char *rna_capsual_id_invalid = "RNA_HANDLE_REMOVED";
36 
38  {REGION_DRAW_POST_PIXEL, "POST_PIXEL", 0, "Post Pixel", ""},
39  {REGION_DRAW_POST_VIEW, "POST_VIEW", 0, "Post View", ""},
40  {REGION_DRAW_PRE_VIEW, "PRE_VIEW", 0, "Pre View", ""},
41  {REGION_DRAW_BACKDROP, "BACKDROP", 0, "Backdrop", ""},
42  {0, NULL, 0, NULL, NULL},
43 };
44 
45 static void cb_region_draw(const bContext *C, ARegion *UNUSED(region), void *customdata)
46 {
47  PyObject *cb_func, *cb_args, *result;
48  PyGILState_STATE gilstate;
49 
50  bpy_context_set((bContext *)C, &gilstate);
51 
52  cb_func = PyTuple_GET_ITEM((PyObject *)customdata, 1);
53  cb_args = PyTuple_GET_ITEM((PyObject *)customdata, 2);
54  result = PyObject_CallObject(cb_func, cb_args);
55 
56  if (result) {
57  Py_DECREF(result);
58  }
59  else {
60  PyErr_Print();
61  PyErr_Clear();
62  }
63 
64  bpy_context_clear((bContext *)C, &gilstate);
65 }
66 
67 /* We could make generic utility */
68 static PyObject *PyC_Tuple_CopySized(PyObject *src, int len_dst)
69 {
70  PyObject *dst = PyTuple_New(len_dst);
71  const int len_src = PyTuple_GET_SIZE(src);
72  BLI_assert(len_src <= len_dst);
73  for (int i = 0; i < len_src; i++) {
74  PyObject *item = PyTuple_GET_ITEM(src, i);
75  PyTuple_SET_ITEM(dst, i, item);
76  Py_INCREF(item);
77  }
78  return dst;
79 }
80 
81 static void cb_wm_cursor_draw(bContext *C, int x, int y, void *customdata)
82 {
83  PyObject *cb_func, *cb_args, *result;
84  PyGILState_STATE gilstate;
85 
86  bpy_context_set((bContext *)C, &gilstate);
87 
88  cb_func = PyTuple_GET_ITEM((PyObject *)customdata, 1);
89  cb_args = PyTuple_GET_ITEM((PyObject *)customdata, 2);
90 
91  const int cb_args_len = PyTuple_GET_SIZE(cb_args);
92 
93  PyObject *cb_args_xy = PyTuple_New(2);
94  PyTuple_SET_ITEMS(cb_args_xy, PyLong_FromLong(x), PyLong_FromLong(y));
95 
96  PyObject *cb_args_with_xy = PyC_Tuple_CopySized(cb_args, cb_args_len + 1);
97  PyTuple_SET_ITEM(cb_args_with_xy, cb_args_len, cb_args_xy);
98 
99  result = PyObject_CallObject(cb_func, cb_args_with_xy);
100 
101  Py_DECREF(cb_args_with_xy);
102 
103  if (result) {
104  Py_DECREF(result);
105  }
106  else {
107  PyErr_Print();
108  PyErr_Clear();
109  }
110 
111  bpy_context_clear((bContext *)C, &gilstate);
112 }
113 
114 #if 0
115 PyObject *pyrna_callback_add(BPy_StructRNA *self, PyObject *args)
116 {
117  void *handle;
118 
119  PyObject *cb_func, *cb_args;
120  char *cb_event_str = NULL;
121  int cb_event;
122 
123  if (!PyArg_ParseTuple(args,
124  "OO!|s:bpy_struct.callback_add",
125  &cb_func,
126  &PyTuple_Type,
127  &cb_args,
128  &cb_event_str)) {
129  return NULL;
130  }
131 
132  if (!PyCallable_Check(cb_func)) {
133  PyErr_SetString(PyExc_TypeError, "callback_add(): first argument isn't callable");
134  return NULL;
135  }
136 
137  if (RNA_struct_is_a(self->ptr.type, &RNA_Region)) {
138  if (cb_event_str) {
140  region_draw_mode_items, cb_event_str, &cb_event, "bpy_struct.callback_add()") ==
141  -1) {
142  return NULL;
143  }
144  }
145  else {
146  cb_event = REGION_DRAW_POST_PIXEL;
147  }
148 
150  ((ARegion *)self->ptr.data)->type, cb_region_draw, (void *)args, cb_event);
151  Py_INCREF(args);
152  }
153  else {
154  PyErr_SetString(PyExc_TypeError, "callback_add(): type does not support callbacks");
155  return NULL;
156  }
157 
158  return PyCapsule_New((void *)handle, rna_capsual_id, NULL);
159 }
160 
161 PyObject *pyrna_callback_remove(BPy_StructRNA *self, PyObject *args)
162 {
163  PyObject *py_handle;
164  void *handle;
165  void *customdata;
166 
167  if (!PyArg_ParseTuple(args, "O!:callback_remove", &PyCapsule_Type, &py_handle)) {
168  return NULL;
169  }
170 
171  handle = PyCapsule_GetPointer(py_handle, rna_capsual_id);
172 
173  if (handle == NULL) {
174  PyErr_SetString(PyExc_ValueError,
175  "callback_remove(handle): NULL handle given, invalid or already removed");
176  return NULL;
177  }
178 
179  if (RNA_struct_is_a(self->ptr.type, &RNA_Region)) {
180  customdata = ED_region_draw_cb_customdata(handle);
181  Py_DECREF((PyObject *)customdata);
182 
183  ED_region_draw_cb_exit(((ARegion *)self->ptr.data)->type, handle);
184  }
185  else {
186  PyErr_SetString(PyExc_TypeError, "callback_remove(): type does not support callbacks");
187  return NULL;
188  }
189 
190  /* don't allow reuse */
191  PyCapsule_SetName(py_handle, rna_capsual_id_invalid);
192 
193  Py_RETURN_NONE;
194 }
195 #endif
196 
197 /* reverse of rna_Space_refine() */
199 {
200  if (srna == &RNA_SpaceView3D) {
201  return SPACE_VIEW3D;
202  }
203  if (srna == &RNA_SpaceGraphEditor) {
204  return SPACE_GRAPH;
205  }
206  if (srna == &RNA_SpaceOutliner) {
207  return SPACE_OUTLINER;
208  }
209  if (srna == &RNA_SpaceProperties) {
210  return SPACE_PROPERTIES;
211  }
212  if (srna == &RNA_SpaceFileBrowser) {
213  return SPACE_FILE;
214  }
215  if (srna == &RNA_SpaceImageEditor) {
216  return SPACE_IMAGE;
217  }
218  if (srna == &RNA_SpaceInfo) {
219  return SPACE_INFO;
220  }
221  if (srna == &RNA_SpaceSequenceEditor) {
222  return SPACE_SEQ;
223  }
224  if (srna == &RNA_SpaceTextEditor) {
225  return SPACE_TEXT;
226  }
227  if (srna == &RNA_SpaceDopeSheetEditor) {
228  return SPACE_ACTION;
229  }
230  if (srna == &RNA_SpaceNLA) {
231  return SPACE_NLA;
232  }
233  if (srna == &RNA_SpaceNodeEditor) {
234  return SPACE_NODE;
235  }
236  if (srna == &RNA_SpaceConsole) {
237  return SPACE_CONSOLE;
238  }
239  if (srna == &RNA_SpacePreferences) {
240  return SPACE_USERPREF;
241  }
242  if (srna == &RNA_SpaceClipEditor) {
243  return SPACE_CLIP;
244  }
245  if (srna == &RNA_SpaceSpreadsheet) {
246  return SPACE_SPREADSHEET;
247  }
248  return SPACE_EMPTY;
249 }
250 
251 static void cb_rna_capsule_destructor(PyObject *capsule)
252 {
253  PyObject *args = PyCapsule_GetContext(capsule);
254  Py_DECREF(args);
255 }
256 
257 PyObject *pyrna_callback_classmethod_add(PyObject *UNUSED(self), PyObject *args)
258 {
259  void *handle;
260  PyObject *cls;
261  PyObject *cb_func, *cb_args;
262  StructRNA *srna;
263 
264  if (PyTuple_GET_SIZE(args) < 2) {
265  PyErr_SetString(PyExc_ValueError, "handler_add(handler): expected at least 2 args");
266  return NULL;
267  }
268 
269  cls = PyTuple_GET_ITEM(args, 0);
270  if (!(srna = pyrna_struct_as_srna(cls, false, "handler_add"))) {
271  return NULL;
272  }
273  cb_func = PyTuple_GET_ITEM(args, 1);
274  if (!PyCallable_Check(cb_func)) {
275  PyErr_SetString(PyExc_TypeError, "first argument isn't callable");
276  return NULL;
277  }
278 
279  /* class specific callbacks */
280 
281  if (srna == &RNA_WindowManager) {
282  struct {
283  struct BPy_EnumProperty_Parse space_type_enum;
284  struct BPy_EnumProperty_Parse region_type_enum;
285  } params = {
286  .space_type_enum = {.items = rna_enum_space_type_items, .value = SPACE_TYPE_ANY},
287  .region_type_enum = {.items = rna_enum_region_type_items, .value = RGN_TYPE_ANY},
288  };
289 
290  if (!PyArg_ParseTuple(args,
291  "OOO!|O&O&:WindowManager.draw_cursor_add",
292  &cls,
293  &cb_func, /* already assigned, no matter */
294  &PyTuple_Type,
295  &cb_args,
297  &params.space_type_enum,
299  &params.region_type_enum)) {
300  return NULL;
301  }
302 
303  handle = WM_paint_cursor_activate(params.space_type_enum.value,
304  params.region_type_enum.value,
305  NULL,
307  (void *)args);
308  }
309  else if (RNA_struct_is_a(srna, &RNA_Space)) {
310  struct {
311  struct BPy_EnumProperty_Parse region_type_enum;
312  struct BPy_EnumProperty_Parse event_enum;
313  } params = {
314  .region_type_enum = {.items = rna_enum_region_type_items},
315  .event_enum = {.items = region_draw_mode_items},
316  };
317 
318  if (!PyArg_ParseTuple(args,
319  "OOO!O&O&:Space.draw_handler_add",
320  &cls,
321  &cb_func, /* already assigned, no matter */
322  &PyTuple_Type,
323  &cb_args,
325  &params.region_type_enum,
327  &params.event_enum)) {
328  return NULL;
329  }
330 
331  const eSpace_Type spaceid = rna_Space_refine_reverse(srna);
332  if (spaceid == SPACE_EMPTY) {
333  PyErr_Format(PyExc_TypeError, "unknown space type '%.200s'", RNA_struct_identifier(srna));
334  return NULL;
335  }
336 
337  SpaceType *st = BKE_spacetype_from_id(spaceid);
338  ARegionType *art = BKE_regiontype_from_id(st, params.region_type_enum.value);
339  if (art == NULL) {
340  PyErr_Format(
341  PyExc_TypeError, "region type %R not in space", params.region_type_enum.value_orig);
342  return NULL;
343  }
345  art, cb_region_draw, (void *)args, params.event_enum.value);
346  }
347  else {
348  PyErr_SetString(PyExc_TypeError, "callback_add(): type does not support callbacks");
349  return NULL;
350  }
351 
352  /* Keep the 'args' reference as long as the callback exists.
353  * This reference is decremented in #BPY_callback_screen_free and #BPY_callback_wm_free. */
354  Py_INCREF(args);
355 
356  PyObject *ret = PyCapsule_New((void *)handle, rna_capsual_id, NULL);
357 
358  /* Store 'args' in context as well for simple access. */
359  PyCapsule_SetDestructor(ret, cb_rna_capsule_destructor);
360  PyCapsule_SetContext(ret, args);
361  Py_INCREF(args);
362 
363  return ret;
364 }
365 
366 PyObject *pyrna_callback_classmethod_remove(PyObject *UNUSED(self), PyObject *args)
367 {
368  PyObject *cls;
369  PyObject *py_handle;
370  void *handle;
371  StructRNA *srna;
372  bool capsule_clear = false;
373  bool handle_removed = false;
374 
375  if (PyTuple_GET_SIZE(args) < 2) {
376  PyErr_SetString(PyExc_ValueError, "callback_remove(handler): expected at least 2 args");
377  return NULL;
378  }
379 
380  cls = PyTuple_GET_ITEM(args, 0);
381  if (!(srna = pyrna_struct_as_srna(cls, false, "callback_remove"))) {
382  return NULL;
383  }
384  py_handle = PyTuple_GET_ITEM(args, 1);
385  handle = PyCapsule_GetPointer(py_handle, rna_capsual_id);
386  if (handle == NULL) {
387  PyErr_SetString(PyExc_ValueError,
388  "callback_remove(handler): NULL handler given, invalid or already removed");
389  return NULL;
390  }
391 
392  if (srna == &RNA_WindowManager) {
393  if (!PyArg_ParseTuple(
394  args, "OO!:WindowManager.draw_cursor_remove", &cls, &PyCapsule_Type, &py_handle)) {
395  return NULL;
396  }
397  handle_removed = WM_paint_cursor_end(handle);
398  capsule_clear = true;
399  }
400  else if (RNA_struct_is_a(srna, &RNA_Space)) {
401  const char *error_prefix = "Space.draw_handler_remove";
402  struct {
403  struct BPy_EnumProperty_Parse region_type_enum;
404  } params = {
405  .region_type_enum = {.items = rna_enum_region_type_items},
406  };
407 
408  if (!PyArg_ParseTuple(args,
409  "OO!O&:Space.draw_handler_remove",
410  &cls,
411  &PyCapsule_Type,
412  &py_handle, /* already assigned, no matter */
414  &params.region_type_enum)) {
415  return NULL;
416  }
417 
418  const eSpace_Type spaceid = rna_Space_refine_reverse(srna);
419  if (spaceid == SPACE_EMPTY) {
420  PyErr_Format(PyExc_TypeError,
421  "%s: unknown space type '%.200s'",
422  error_prefix,
423  RNA_struct_identifier(srna));
424  return NULL;
425  }
426 
427  SpaceType *st = BKE_spacetype_from_id(spaceid);
428  ARegionType *art = BKE_regiontype_from_id(st, params.region_type_enum.value);
429  if (art == NULL) {
430  PyErr_Format(PyExc_TypeError,
431  "%s: region type %R not in space",
432  error_prefix,
433  params.region_type_enum.value_orig);
434  return NULL;
435  }
436  handle_removed = ED_region_draw_cb_exit(art, handle);
437  capsule_clear = true;
438  }
439  else {
440  PyErr_SetString(PyExc_TypeError, "callback_remove(): type does not support callbacks");
441  return NULL;
442  }
443 
444  /* When `handle_removed == false`: Blender has already freed the data
445  * (freeing screen data when loading a new file for example).
446  * This will have already decremented the user, so don't decrement twice. */
447  if (handle_removed == true) {
448  /* The handle has been removed, so decrement its custom-data. */
449  PyObject *handle_args = PyCapsule_GetContext(py_handle);
450  Py_DECREF(handle_args);
451  }
452 
453  /* don't allow reuse */
454  if (capsule_clear) {
455  PyCapsule_Destructor destructor_fn = PyCapsule_GetDestructor(py_handle);
456  if (destructor_fn) {
457  destructor_fn(py_handle);
458  PyCapsule_SetDestructor(py_handle, NULL);
459  }
460  PyCapsule_SetName(py_handle, rna_capsual_id_invalid);
461  }
462 
463  Py_RETURN_NONE;
464 }
465 
466 /* -------------------------------------------------------------------- */
470 static void cb_customdata_free(void *customdata)
471 {
472  PyObject *tuple = customdata;
473  bool use_gil = true; /* !PyC_IsInterpreterActive(); */
474 
475  PyGILState_STATE gilstate;
476  if (use_gil) {
477  gilstate = PyGILState_Ensure();
478  }
479 
480  Py_DECREF(tuple);
481 
482  if (use_gil) {
483  PyGILState_Release(gilstate);
484  }
485 }
486 
488 {
490 }
491 
493 {
495 }
496 
struct SpaceType * BKE_spacetype_from_id(int spaceid)
Definition: screen.c:353
struct ARegionType * BKE_regiontype_from_id(const struct SpaceType *st, int regionid)
#define BLI_assert(a)
Definition: BLI_assert.h:46
#define UNUSED(x)
#define RGN_TYPE_ANY
eSpace_Type
@ SPACE_TEXT
@ SPACE_CLIP
@ SPACE_ACTION
@ SPACE_CONSOLE
@ SPACE_OUTLINER
@ SPACE_NODE
@ SPACE_SPREADSHEET
@ SPACE_USERPREF
@ SPACE_FILE
@ SPACE_PROPERTIES
@ SPACE_NLA
@ SPACE_SEQ
@ SPACE_EMPTY
@ SPACE_IMAGE
@ SPACE_GRAPH
@ SPACE_VIEW3D
@ SPACE_INFO
#define SPACE_TYPE_ANY
#define REGION_DRAW_POST_VIEW
Definition: ED_space_api.h:62
#define REGION_DRAW_BACKDROP
Definition: ED_space_api.h:65
void * ED_region_draw_cb_activate(struct ARegionType *art, void(*draw)(const struct bContext *, struct ARegion *, void *), void *customdata, int type)
Definition: spacetypes.c:226
void ED_region_draw_cb_remove_by_type(struct ARegionType *art, void *draw_fn, void(*free)(void *))
Definition: spacetypes.c:275
#define REGION_DRAW_POST_PIXEL
Definition: ED_space_api.h:63
bool ED_region_draw_cb_exit(struct ARegionType *art, void *handle)
Definition: spacetypes.c:241
#define REGION_DRAW_PRE_VIEW
Definition: ED_space_api.h:64
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint y
#define C
Definition: RandGen.cpp:25
void bpy_context_clear(struct bContext *C, const PyGILState_STATE *gilstate)
void bpy_context_set(struct bContext *C, PyGILState_STATE *gilstate)
PyObject * self
Definition: bpy_driver.c:165
StructRNA * pyrna_struct_as_srna(PyObject *self, const bool parent, const char *error_prefix)
Definition: bpy_rna.c:7907
static eSpace_Type rna_Space_refine_reverse(StructRNA *srna)
static const char * rna_capsual_id_invalid
PyObject * pyrna_callback_classmethod_remove(PyObject *UNUSED(self), PyObject *args)
static PyObject * PyC_Tuple_CopySized(PyObject *src, int len_dst)
static void cb_customdata_free(void *customdata)
static void cb_region_draw(const bContext *C, ARegion *UNUSED(region), void *customdata)
static void cb_rna_capsule_destructor(PyObject *capsule)
static void cb_wm_cursor_draw(bContext *C, int x, int y, void *customdata)
void BPY_callback_screen_free(struct ARegionType *art)
PyObject * pyrna_callback_classmethod_add(PyObject *UNUSED(self), PyObject *args)
static const EnumPropertyItem region_draw_mode_items[]
static const char * rna_capsual_id
void BPY_callback_wm_free(struct wmWindowManager *wm)
SyclQueue void void * src
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
static const pxr::TfToken st("st", pxr::TfToken::Immortal)
int pyrna_enum_value_from_id(const EnumPropertyItem *item, const char *identifier, int *r_value, const char *error_prefix)
Definition: py_capi_rna.c:54
int pyrna_enum_value_parse_string(PyObject *o, void *p)
Definition: py_capi_rna.c:194
#define PyTuple_SET_ITEMS(op_arg,...)
return ret
const char * RNA_struct_identifier(const StructRNA *type)
Definition: rna_access.c:586
bool RNA_struct_is_a(const StructRNA *type, const StructRNA *srna)
Definition: rna_access.c:695
const EnumPropertyItem rna_enum_region_type_items[]
Definition: rna_screen.c:21
const EnumPropertyItem rna_enum_space_type_items[]
Definition: rna_space.c:86
bool WM_paint_cursor_end(wmPaintCursor *handle)
void WM_paint_cursor_remove_by_type(wmWindowManager *wm, void *draw_fn, void(*free)(void *))
wmPaintCursor * WM_paint_cursor_activate(short space_type, short region_type, bool(*poll)(bContext *C), wmPaintCursorDraw draw, void *customdata)