a251bdeb15b23451a2819b79cda59aa7f3a898b0
[blender.git] / source / blender / python / intern / bpy_app_handlers.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * Contributor(s): Campbell Barton
19  *
20  * ***** END GPL LICENSE BLOCK *****
21  */
22
23 /** \file blender/python/intern/bpy_app_handlers.c
24  *  \ingroup pythonintern
25  *
26  * This file defines a 'PyStructSequence' accessed via 'bpy.app.handlers',
27  * which exposes various lists that the script author can add callback
28  * functions into (called via blenders generic BLI_cb api)
29  */
30
31 #include <Python.h>
32 #include "BLI_utildefines.h"
33 #include "BLI_callbacks.h"
34
35 #include "RNA_types.h"
36 #include "RNA_access.h"
37 #include "bpy_rna.h"
38 #include "bpy_app_handlers.h"
39
40 #include "../generic/python_utildefines.h"
41
42 #include "BPY_extern.h"
43
44 void bpy_app_generic_callback(struct Main *main, struct ID *id, void *arg);
45
46 static PyTypeObject BlenderAppCbType;
47
48 static PyStructSequence_Field app_cb_info_fields[] = {
49         {(char *)"frame_change_pre",      (char *)"on frame change for playback and rendering (before)"},
50         {(char *)"frame_change_post",     (char *)"on frame change for playback and rendering (after)"},
51         {(char *)"render_pre",            (char *)"on render (before)"},
52         {(char *)"render_post",           (char *)"on render (after)"},
53         {(char *)"render_write",          (char *)"on writing a render frame (directly after the frame is written)"},
54         {(char *)"render_stats",          (char *)"on printing render statistics"},
55         {(char *)"render_init",           (char *)"on initialization of a render job"},
56         {(char *)"render_complete",       (char *)"on completion of render job"},
57         {(char *)"render_cancel",         (char *)"on canceling a render job"},
58         {(char *)"load_pre",              (char *)"on loading a new blend file (before)"},
59         {(char *)"load_post",             (char *)"on loading a new blend file (after)"},
60         {(char *)"save_pre",              (char *)"on saving a blend file (before)"},
61         {(char *)"save_post",             (char *)"on saving a blend file (after)"},
62         {(char *)"undo_pre",              (char *)"on loading an undo step (before)"},
63         {(char *)"undo_post",             (char *)"on loading an undo step (after)"},
64         {(char *)"redo_pre",              (char *)"on loading a redo step (before)"},
65         {(char *)"redo_post",             (char *)"on loading a redo step (after)"},
66         {(char *)"depsgraph_update_pre",  (char *)"on depsgraph update (pre)"},
67         {(char *)"depsgraph_update_post", (char *)"on depsgraph update (post)"},
68         {(char *)"version_update",        (char *)"on ending the versioning code"},
69         {(char *)"load_factory_startup_post", (char *)"on loading factory startup (after)"},
70
71         /* sets the permanent tag */
72 #   define APP_CB_OTHER_FIELDS 1
73         {(char *)"persistent",        (char *)"Function decorator for callback functions not to be removed when loading new files"},
74
75         {NULL}
76 };
77
78 static PyStructSequence_Desc app_cb_info_desc = {
79         (char *)"bpy.app.handlers",     /* name */
80         (char *)"This module contains callback lists",  /* doc */
81         app_cb_info_fields,    /* fields */
82         ARRAY_SIZE(app_cb_info_fields) - 1
83 };
84
85 #if 0
86 #  if (BLI_CB_EVT_TOT != ARRAY_SIZE(app_cb_info_fields))
87 #    error "Callbacks are out of sync"
88 #  endif
89 #endif
90
91 /* --------------------------------------------------------------------------*/
92 /* permanent tagging code */
93 #define PERMINENT_CB_ID "_bpy_persistent"
94
95 static PyObject *bpy_app_handlers_persistent_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *UNUSED(kwds))
96 {
97         PyObject *value;
98
99         if (!PyArg_ParseTuple(args, "O:bpy.app.handlers.persistent", &value))
100                 return NULL;
101
102         if (PyFunction_Check(value)) {
103                 PyObject **dict_ptr = _PyObject_GetDictPtr(value);
104                 if (dict_ptr == NULL) {
105                         PyErr_SetString(PyExc_ValueError,
106                                         "bpy.app.handlers.persistent wasn't able to "
107                                         "get the dictionary from the function passed");
108                         return NULL;
109                 }
110                 else {
111                         /* set id */
112                         if (*dict_ptr == NULL) {
113                                 *dict_ptr = PyDict_New();
114                         }
115
116                         PyDict_SetItemString(*dict_ptr, PERMINENT_CB_ID, Py_None);
117                 }
118
119                 Py_INCREF(value);
120                 return value;
121         }
122         else {
123                 PyErr_SetString(PyExc_ValueError,
124                                 "bpy.app.handlers.persistent expected a function");
125                 return NULL;
126         }
127 }
128
129 /* dummy type because decorators can't be PyCFunctions */
130 static PyTypeObject BPyPersistent_Type = {
131
132 #if defined(_MSC_VER)
133         PyVarObject_HEAD_INIT(NULL, 0)
134 #else
135         PyVarObject_HEAD_INIT(&PyType_Type, 0)
136 #endif
137
138         "persistent",                               /* tp_name */
139         0,                                          /* tp_basicsize */
140         0,                                          /* tp_itemsize */
141         /* methods */
142         0,                                          /* tp_dealloc */
143         0,                                          /* tp_print */
144         0,                                          /* tp_getattr */
145         0,                                          /* tp_setattr */
146         0,                                          /* tp_reserved */
147         0,                                          /* tp_repr */
148         0,                                          /* tp_as_number */
149         0,                                          /* tp_as_sequence */
150         0,                                          /* tp_as_mapping */
151         0,                                          /* tp_hash */
152         0,                                          /* tp_call */
153         0,                                          /* tp_str */
154         0,                                          /* tp_getattro */
155         0,                                          /* tp_setattro */
156         0,                                          /* tp_as_buffer */
157         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
158         Py_TPFLAGS_BASETYPE,                        /* tp_flags */
159         0,                                          /* tp_doc */
160         0,                                          /* tp_traverse */
161         0,                                          /* tp_clear */
162         0,                                          /* tp_richcompare */
163         0,                                          /* tp_weaklistoffset */
164         0,                                          /* tp_iter */
165         0,                                          /* tp_iternext */
166         0,                                          /* tp_methods */
167         0,                                          /* tp_members */
168         0,                                          /* tp_getset */
169         0,                                          /* tp_base */
170         0,                                          /* tp_dict */
171         0,                                          /* tp_descr_get */
172         0,                                          /* tp_descr_set */
173         0,                                          /* tp_dictoffset */
174         0,                                          /* tp_init */
175         0,                                          /* tp_alloc */
176         bpy_app_handlers_persistent_new,            /* tp_new */
177         0,                                          /* tp_free */
178 };
179
180 static PyObject *py_cb_array[BLI_CB_EVT_TOT] = {NULL};
181
182 static PyObject *make_app_cb_info(void)
183 {
184         PyObject *app_cb_info;
185         int pos;
186
187         app_cb_info = PyStructSequence_New(&BlenderAppCbType);
188         if (app_cb_info == NULL) {
189                 return NULL;
190         }
191
192         for (pos = 0; pos < BLI_CB_EVT_TOT; pos++) {
193                 if (app_cb_info_fields[pos].name == NULL) {
194                         Py_FatalError("invalid callback slots 1");
195                 }
196                 PyStructSequence_SET_ITEM(app_cb_info, pos, (py_cb_array[pos] = PyList_New(0)));
197         }
198         if (app_cb_info_fields[pos + APP_CB_OTHER_FIELDS].name != NULL) {
199                 Py_FatalError("invalid callback slots 2");
200         }
201
202         /* custom function */
203         PyStructSequence_SET_ITEM(app_cb_info, pos++, (PyObject *)&BPyPersistent_Type);
204
205         return app_cb_info;
206 }
207
208 PyObject *BPY_app_handlers_struct(void)
209 {
210         PyObject *ret;
211
212 #if defined(_MSC_VER)
213         BPyPersistent_Type.ob_base.ob_base.ob_type = &PyType_Type;
214 #endif
215
216         if (PyType_Ready(&BPyPersistent_Type) < 0) {
217                 BLI_assert(!"error initializing 'bpy.app.handlers.persistent'");
218         }
219
220         PyStructSequence_InitType(&BlenderAppCbType, &app_cb_info_desc);
221
222         ret = make_app_cb_info();
223
224         /* prevent user from creating new instances */
225         BlenderAppCbType.tp_init = NULL;
226         BlenderAppCbType.tp_new = NULL;
227         BlenderAppCbType.tp_hash = (hashfunc)_Py_HashPointer; /* without this we can't do set(sys.modules) [#29635] */
228
229         /* assign the C callbacks */
230         if (ret) {
231                 static bCallbackFuncStore funcstore_array[BLI_CB_EVT_TOT] = {{NULL}};
232                 bCallbackFuncStore *funcstore;
233                 int pos = 0;
234
235                 for (pos = 0; pos < BLI_CB_EVT_TOT; pos++) {
236                         funcstore = &funcstore_array[pos];
237                         funcstore->func = bpy_app_generic_callback;
238                         funcstore->alloc = 0;
239                         funcstore->arg = POINTER_FROM_INT(pos);
240                         BLI_callback_add(funcstore, pos);
241                 }
242         }
243
244         return ret;
245 }
246
247 void BPY_app_handlers_reset(const short do_all)
248 {
249         PyGILState_STATE gilstate;
250         int pos = 0;
251
252         gilstate = PyGILState_Ensure();
253
254         if (do_all) {
255                 for (pos = 0; pos < BLI_CB_EVT_TOT; pos++) {
256                         /* clear list */
257                         PyList_SetSlice(py_cb_array[pos], 0, PY_SSIZE_T_MAX, NULL);
258                 }
259         }
260         else {
261                 /* save string conversion thrashing */
262                 PyObject *perm_id_str = PyUnicode_FromString(PERMINENT_CB_ID);
263
264                 for (pos = 0; pos < BLI_CB_EVT_TOT; pos++) {
265                         /* clear only items without PERMINENT_CB_ID */
266                         PyObject *ls = py_cb_array[pos];
267                         Py_ssize_t i;
268
269                         PyObject *item;
270                         PyObject **dict_ptr;
271
272                         for (i = PyList_GET_SIZE(ls) - 1; i >= 0; i--) {
273
274                                 if ((PyFunction_Check((item = PyList_GET_ITEM(ls, i)))) &&
275                                     (dict_ptr = _PyObject_GetDictPtr(item)) &&
276                                     (*dict_ptr) &&
277                                     (PyDict_GetItem(*dict_ptr, perm_id_str) != NULL))
278                                 {
279                                         /* keep */
280                                 }
281                                 else {
282                                         /* remove */
283                                         /* PySequence_DelItem(ls, i); */ /* more obvious buw slower */
284                                         PyList_SetSlice(ls, i, i + 1, NULL);
285                                 }
286                         }
287                 }
288
289                 Py_DECREF(perm_id_str);
290         }
291
292         PyGILState_Release(gilstate);
293 }
294
295 /* the actual callback - not necessarily called from py */
296 void bpy_app_generic_callback(struct Main *UNUSED(main), struct ID *id, void *arg)
297 {
298         PyObject *cb_list = py_cb_array[POINTER_AS_INT(arg)];
299         if (PyList_GET_SIZE(cb_list) > 0) {
300                 PyGILState_STATE gilstate = PyGILState_Ensure();
301
302                 PyObject *args = PyTuple_New(1);  /* save python creating each call */
303                 PyObject *func;
304                 PyObject *ret;
305                 Py_ssize_t pos;
306
307                 /* setup arguments */
308                 if (id) {
309                         PointerRNA id_ptr;
310                         RNA_id_pointer_create(id, &id_ptr);
311                         PyTuple_SET_ITEM(args, 0, pyrna_struct_CreatePyObject(&id_ptr));
312                 }
313                 else {
314                         PyTuple_SET_ITEM(args, 0, Py_INCREF_RET(Py_None));
315                 }
316
317                 /* Iterate the list and run the callbacks
318                  * note: don't store the list size since the scripts may remove themselves */
319                 for (pos = 0; pos < PyList_GET_SIZE(cb_list); pos++) {
320                         func = PyList_GET_ITEM(cb_list, pos);
321                         ret = PyObject_Call(func, args, NULL);
322                         if (ret == NULL) {
323                                 /* Don't set last system variables because they might cause some
324                                  * dangling pointers to external render engines (when exception
325                                  * happens during rendering) which will break logic of render pipeline
326                                  * which expects to be the only user of render engine when rendering
327                                  * is finished.
328                                  */
329                                 PyErr_PrintEx(0);
330                                 PyErr_Clear();
331                         }
332                         else {
333                                 Py_DECREF(ret);
334                         }
335                 }
336
337                 Py_DECREF(args);
338
339                 PyGILState_Release(gilstate);
340         }
341 }