Blender  V3.3
bpy_app_handlers.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
11 #include "BLI_utildefines.h"
12 #include <Python.h>
13 
14 #include "BKE_callbacks.h"
15 
16 #include "RNA_access.h"
17 #include "RNA_types.h"
18 #include "bpy_app_handlers.h"
19 #include "bpy_rna.h"
20 
21 #include "../generic/python_utildefines.h"
22 
23 #include "BPY_extern.h"
24 
26  struct PointerRNA **pointers,
27  const int pointers_num,
28  void *arg);
29 
30 static PyTypeObject BlenderAppCbType;
31 
35 static PyStructSequence_Field app_cb_info_fields[] = {
36  {"frame_change_pre",
37  "Called after frame change for playback and rendering, before any data is evaluated for the "
38  "new frame. This makes it possible to change data and relations (for example swap an object "
39  "to another mesh) for the new frame. Note that this handler is **not** to be used as 'before "
40  "the frame changes' event. The dependency graph is not available in this handler, as data "
41  "and relations may have been altered and the dependency graph has not yet been updated for "
42  "that."},
43  {"frame_change_post",
44  "Called after frame change for playback and rendering, after the data has been evaluated "
45  "for the new frame."},
46  {"render_pre", "on render (before)"},
47  {"render_post", "on render (after)"},
48  {"render_write", "on writing a render frame (directly after the frame is written)"},
49  {"render_stats", "on printing render statistics"},
50  {"render_init", "on initialization of a render job"},
51  {"render_complete", "on completion of render job"},
52  {"render_cancel", "on canceling a render job"},
53  {"load_pre", "on loading a new blend file (before)"},
54  {"load_post", "on loading a new blend file (after)"},
55  {"save_pre", "on saving a blend file (before)"},
56  {"save_post", "on saving a blend file (after)"},
57  {"undo_pre", "on loading an undo step (before)"},
58  {"undo_post", "on loading an undo step (after)"},
59  {"redo_pre", "on loading a redo step (before)"},
60  {"redo_post", "on loading a redo step (after)"},
61  {"depsgraph_update_pre", "on depsgraph update (pre)"},
62  {"depsgraph_update_post", "on depsgraph update (post)"},
63  {"version_update", "on ending the versioning code"},
64  {"load_factory_preferences_post", "on loading factory preferences (after)"},
65  {"load_factory_startup_post", "on loading factory startup (after)"},
66  {"xr_session_start_pre", "on starting an xr session (before)"},
67  {"annotation_pre", "on drawing an annotation (before)"},
68  {"annotation_post", "on drawing an annotation (after)"},
69  {"object_bake_pre", "before starting a bake job"},
70  {"object_bake_complete", "on completing a bake job; will be called in the main thread"},
71  {"object_bake_cancel", "on canceling a bake job; will be called in the main thread"},
72  {"composite_pre", "on a compositing background job (before)"},
73  {"composite_post", "on a compositing background job (after)"},
74  {"composite_cancel", "on a compositing background job (cancel)"},
75 
76 /* sets the permanent tag */
77 #define APP_CB_OTHER_FIELDS 1
78  {"persistent",
79  "Function decorator for callback functions not to be removed when loading new files"},
80 
81  {NULL},
82 };
83 
84 static PyStructSequence_Desc app_cb_info_desc = {
85  "bpy.app.handlers", /* name */
86  "This module contains callback lists", /* doc */
87  app_cb_info_fields, /* fields */
89 };
90 
91 #if 0
92 # if (BKE_CB_EVT_TOT != ARRAY_SIZE(app_cb_info_fields))
93 # error "Callbacks are out of sync"
94 # endif
95 #endif
96 
97 /* --------------------------------------------------------------------------*/
98 /* permanent tagging code */
99 #define PERMINENT_CB_ID "_bpy_persistent"
100 
101 static PyObject *bpy_app_handlers_persistent_new(PyTypeObject *UNUSED(type),
102  PyObject *args,
103  PyObject *UNUSED(kwds))
104 {
105  PyObject *value;
106 
107  if (!PyArg_ParseTuple(args, "O:bpy.app.handlers.persistent", &value)) {
108  return NULL;
109  }
110 
111  if (PyFunction_Check(value)) {
112  PyObject **dict_ptr = _PyObject_GetDictPtr(value);
113  if (dict_ptr == NULL) {
114  PyErr_SetString(PyExc_ValueError,
115  "bpy.app.handlers.persistent wasn't able to "
116  "get the dictionary from the function passed");
117  return NULL;
118  }
119 
120  /* set id */
121  if (*dict_ptr == NULL) {
122  *dict_ptr = PyDict_New();
123  }
124 
125  PyDict_SetItemString(*dict_ptr, PERMINENT_CB_ID, Py_None);
126 
127  Py_INCREF(value);
128  return value;
129  }
130 
131  PyErr_SetString(PyExc_ValueError, "bpy.app.handlers.persistent expected a function");
132  return NULL;
133 }
134 
135 /* dummy type because decorators can't be PyCFunctions */
136 static PyTypeObject BPyPersistent_Type = {
137 
138 #if defined(_MSC_VER)
139  PyVarObject_HEAD_INIT(NULL, 0)
140 #else
141  PyVarObject_HEAD_INIT(&PyType_Type, 0)
142 #endif
143 
144  "persistent", /* tp_name */
145  0, /* tp_basicsize */
146  0, /* tp_itemsize */
147  /* methods */
148  0, /* tp_dealloc */
149  0, /* tp_print */
150  0, /* tp_getattr */
151  0, /* tp_setattr */
152  0, /* tp_reserved */
153  0, /* tp_repr */
154  0, /* tp_as_number */
155  0, /* tp_as_sequence */
156  0, /* tp_as_mapping */
157  0, /* tp_hash */
158  0, /* tp_call */
159  0, /* tp_str */
160  0, /* tp_getattro */
161  0, /* tp_setattro */
162  0, /* tp_as_buffer */
163  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
164  0, /* tp_doc */
165  0, /* tp_traverse */
166  0, /* tp_clear */
167  0, /* tp_richcompare */
168  0, /* tp_weaklistoffset */
169  0, /* tp_iter */
170  0, /* tp_iternext */
171  0, /* tp_methods */
172  0, /* tp_members */
173  0, /* tp_getset */
174  0, /* tp_base */
175  0, /* tp_dict */
176  0, /* tp_descr_get */
177  0, /* tp_descr_set */
178  0, /* tp_dictoffset */
179  0, /* tp_init */
180  0, /* tp_alloc */
181  bpy_app_handlers_persistent_new, /* tp_new */
182  0, /* tp_free */
183 };
184 
185 static PyObject *py_cb_array[BKE_CB_EVT_TOT] = {NULL};
186 
187 static PyObject *make_app_cb_info(void)
188 {
189  PyObject *app_cb_info;
190  int pos;
191 
192  app_cb_info = PyStructSequence_New(&BlenderAppCbType);
193  if (app_cb_info == NULL) {
194  return NULL;
195  }
196 
197  for (pos = 0; pos < BKE_CB_EVT_TOT; pos++) {
198  if (app_cb_info_fields[pos].name == NULL) {
199  Py_FatalError("invalid callback slots 1");
200  }
201  PyStructSequence_SET_ITEM(app_cb_info, pos, (py_cb_array[pos] = PyList_New(0)));
202  }
204  Py_FatalError("invalid callback slots 2");
205  }
206 
207  /* custom function */
208  PyStructSequence_SET_ITEM(app_cb_info, pos++, (PyObject *)&BPyPersistent_Type);
209 
210  return app_cb_info;
211 }
212 
213 PyObject *BPY_app_handlers_struct(void)
214 {
215  PyObject *ret;
216 
217 #if defined(_MSC_VER)
218  BPyPersistent_Type.ob_base.ob_base.ob_type = &PyType_Type;
219 #endif
220 
221  if (PyType_Ready(&BPyPersistent_Type) < 0) {
222  BLI_assert_msg(0, "error initializing 'bpy.app.handlers.persistent'");
223  }
224 
225  PyStructSequence_InitType(&BlenderAppCbType, &app_cb_info_desc);
226 
227  ret = make_app_cb_info();
228 
229  /* prevent user from creating new instances */
230  BlenderAppCbType.tp_init = NULL;
231  BlenderAppCbType.tp_new = NULL;
232  BlenderAppCbType.tp_hash = (hashfunc)
233  _Py_HashPointer; /* without this we can't do set(sys.modules) T29635. */
234 
235  /* assign the C callbacks */
236  if (ret) {
237  static bCallbackFuncStore funcstore_array[BKE_CB_EVT_TOT] = {{NULL}};
238  bCallbackFuncStore *funcstore;
239  int pos = 0;
240 
241  for (pos = 0; pos < BKE_CB_EVT_TOT; pos++) {
242  funcstore = &funcstore_array[pos];
243  funcstore->func = bpy_app_generic_callback;
244  funcstore->alloc = 0;
245  funcstore->arg = POINTER_FROM_INT(pos);
246  BKE_callback_add(funcstore, pos);
247  }
248  }
249 
250  return ret;
251 }
252 
253 void BPY_app_handlers_reset(const bool do_all)
254 {
255  PyGILState_STATE gilstate;
256  int pos = 0;
257 
258  gilstate = PyGILState_Ensure();
259 
260  if (do_all) {
261  for (pos = 0; pos < BKE_CB_EVT_TOT; pos++) {
262  /* clear list */
263  PyList_SetSlice(py_cb_array[pos], 0, PY_SSIZE_T_MAX, NULL);
264  }
265  }
266  else {
267  /* save string conversion thrashing */
268  PyObject *perm_id_str = PyUnicode_FromString(PERMINENT_CB_ID);
269 
270  for (pos = 0; pos < BKE_CB_EVT_TOT; pos++) {
271  /* clear only items without PERMINENT_CB_ID */
272  PyObject *ls = py_cb_array[pos];
273  Py_ssize_t i;
274 
275  for (i = PyList_GET_SIZE(ls) - 1; i >= 0; i--) {
276  PyObject *item = PyList_GET_ITEM(ls, i);
277 
278  if (PyMethod_Check(item)) {
279  PyObject *item_test = PyMethod_GET_FUNCTION(item);
280  if (item_test) {
281  item = item_test;
282  }
283  }
284 
285  PyObject **dict_ptr;
286  if (PyFunction_Check(item) && (dict_ptr = _PyObject_GetDictPtr(item)) && (*dict_ptr) &&
287  (PyDict_GetItem(*dict_ptr, perm_id_str) != NULL)) {
288  /* keep */
289  }
290  else {
291  /* remove */
292  // PySequence_DelItem(ls, i); /* more obvious but slower */
293  PyList_SetSlice(ls, i, i + 1, NULL);
294  }
295  }
296  }
297 
298  Py_DECREF(perm_id_str);
299  }
300 
301  PyGILState_Release(gilstate);
302 }
303 
304 static PyObject *choose_arguments(PyObject *func, PyObject *args_all, PyObject *args_single)
305 {
306  if (!PyFunction_Check(func)) {
307  return args_all;
308  }
309  PyCodeObject *code = (PyCodeObject *)PyFunction_GetCode(func);
310  if (code->co_argcount == 1) {
311  return args_single;
312  }
313  return args_all;
314 }
315 
316 /* the actual callback - not necessarily called from py */
318  struct PointerRNA **pointers,
319  const int pointers_num,
320  void *arg)
321 {
322  PyObject *cb_list = py_cb_array[POINTER_AS_INT(arg)];
323  if (PyList_GET_SIZE(cb_list) > 0) {
324  const PyGILState_STATE gilstate = PyGILState_Ensure();
325 
326  const int num_arguments = 2;
327  PyObject *args_all = PyTuple_New(num_arguments); /* save python creating each call */
328  PyObject *args_single = PyTuple_New(1);
329  PyObject *func;
330  PyObject *ret;
331  Py_ssize_t pos;
332 
333  /* setup arguments */
334  for (int i = 0; i < pointers_num; ++i) {
335  PyTuple_SET_ITEM(args_all, i, pyrna_struct_CreatePyObject(pointers[i]));
336  }
337  for (int i = pointers_num; i < num_arguments; ++i) {
338  PyTuple_SET_ITEM(args_all, i, Py_INCREF_RET(Py_None));
339  }
340 
341  if (pointers_num == 0) {
342  PyTuple_SET_ITEM(args_single, 0, Py_INCREF_RET(Py_None));
343  }
344  else {
345  PyTuple_SET_ITEM(args_single, 0, pyrna_struct_CreatePyObject(pointers[0]));
346  }
347 
348  /* Iterate the list and run the callbacks
349  * NOTE: don't store the list size since the scripts may remove themselves. */
350  for (pos = 0; pos < PyList_GET_SIZE(cb_list); pos++) {
351  func = PyList_GET_ITEM(cb_list, pos);
352  PyObject *args = choose_arguments(func, args_all, args_single);
353  ret = PyObject_Call(func, args, NULL);
354  if (ret == NULL) {
355  /* Don't set last system variables because they might cause some
356  * dangling pointers to external render engines (when exception
357  * happens during rendering) which will break logic of render pipeline
358  * which expects to be the only user of render engine when rendering
359  * is finished.
360  */
361  PyErr_PrintEx(0);
362  PyErr_Clear();
363  }
364  else {
365  Py_DECREF(ret);
366  }
367  }
368 
369  Py_DECREF(args_all);
370  Py_DECREF(args_single);
371 
372  PyGILState_Release(gilstate);
373  }
374 }
void BKE_callback_add(bCallbackFuncStore *funcstore, eCbEvent evt)
Definition: callbacks.c:72
@ BKE_CB_EVT_TOT
#define BLI_assert_msg(a, msg)
Definition: BLI_assert.h:53
#define ARRAY_SIZE(arr)
#define POINTER_FROM_INT(i)
#define UNUSED(x)
#define POINTER_AS_INT(i)
_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 type
int main(int argc, char *argv[])
static PyObject * bpy_app_handlers_persistent_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *UNUSED(kwds))
static PyStructSequence_Desc app_cb_info_desc
static PyObject * choose_arguments(PyObject *func, PyObject *args_all, PyObject *args_single)
static PyObject * make_app_cb_info(void)
#define APP_CB_OTHER_FIELDS
static PyTypeObject BPyPersistent_Type
#define PERMINENT_CB_ID
PyObject * BPY_app_handlers_struct(void)
void bpy_app_generic_callback(struct Main *main, struct PointerRNA **pointers, const int pointers_num, void *arg)
void BPY_app_handlers_reset(const bool do_all)
static PyStructSequence_Field app_cb_info_fields[]
static PyObject * py_cb_array[BKE_CB_EVT_TOT]
static PyTypeObject BlenderAppCbType
PyObject * pyrna_struct_CreatePyObject(PointerRNA *ptr)
Definition: bpy_rna.c:7505
uint pos
return ret
Definition: BKE_main.h:121
void(* func)(struct Main *, struct PointerRNA **, int num_pointers, void *arg)