Blender V2.61 - r43446

bpy_app_handlers.c

Go to the documentation of this file.
00001 /*
00002  * ***** BEGIN GPL LICENSE BLOCK *****
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License
00006  * as published by the Free Software Foundation; either version 2
00007  * of the License, or (at your option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software Foundation,
00016  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  *
00018  * Contributor(s): Campbell Barton
00019  *
00020  * ***** END GPL LICENSE BLOCK *****
00021  */
00022 
00031 #include <Python.h>
00032 #include "BLI_utildefines.h"
00033 #include "BLI_callbacks.h"
00034 
00035 #include "RNA_types.h"
00036 #include "RNA_access.h"
00037 #include "bpy_rna.h"
00038 #include "bpy_app_handlers.h"
00039 
00040 void bpy_app_generic_callback(struct Main *main, struct ID *id, void *arg);
00041 
00042 static PyTypeObject BlenderAppCbType;
00043 
00044 static PyStructSequence_Field app_cb_info_fields[] = {
00045     {(char *)"frame_change_pre",  (char *)"Callback list - on frame change for playback and rendering (before)"},
00046     {(char *)"frame_change_post", (char *)"Callback list - on frame change for playback and rendering (after)"},
00047     {(char *)"render_pre",        (char *)"Callback list - on render (before)"},
00048     {(char *)"render_post",       (char *)"Callback list - on render (after)"},
00049     {(char *)"render_stats",      (char *)"Callback list - on printing render statistics"},
00050     {(char *)"load_pre",          (char *)"Callback list - on loading a new blend file (before)"},
00051     {(char *)"load_post",         (char *)"Callback list - on loading a new blend file (after)"},
00052     {(char *)"save_pre",          (char *)"Callback list - on saving a blend file (before)"},
00053     {(char *)"save_post",         (char *)"Callback list - on saving a blend file (after)"},
00054     {(char *)"scene_update_pre",  (char *)"Callback list - on updating the scenes data (before)"},
00055     {(char *)"scene_update_post", (char *)"Callback list - on updating the scenes data (after)"},
00056 
00057     /* sets the permanent tag */
00058 #   define APP_CB_OTHER_FIELDS 1
00059     {(char *)"persistent",        (char *)"Function decorator for callback functions not to be removed when loading new files"},
00060 
00061     {NULL}
00062 };
00063 
00064 static PyStructSequence_Desc app_cb_info_desc = {
00065     (char *)"bpy.app.handlers",     /* name */
00066     (char *)"This module contains callbacks",    /* doc */
00067     app_cb_info_fields,    /* fields */
00068     (sizeof(app_cb_info_fields) / sizeof(PyStructSequence_Field)) - 1
00069 };
00070 
00071 /*
00072 #if (BLI_CB_EVT_TOT != ((sizeof(app_cb_info_fields)/sizeof(PyStructSequence_Field))))
00073 #  error "Callbacks are out of sync"
00074 #endif
00075 */
00076 
00077 /* --------------------------------------------------------------------------*/
00078 /* permanent tagging code */
00079 #define PERMINENT_CB_ID "_bpy_persistent"
00080 
00081 static PyObject *bpy_app_handlers_persistent_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *UNUSED(kwds))
00082 {
00083     PyObject *value;
00084 
00085     if (!PyArg_ParseTuple(args, "O:bpy.app.handlers.persistent", &value))
00086         return NULL;
00087 
00088     if (PyFunction_Check(value)) {
00089         PyObject **dict_ptr = _PyObject_GetDictPtr(value);
00090         if (dict_ptr == NULL) {
00091             PyErr_SetString(PyExc_ValueError,
00092                             "bpy.app.handlers.persistent wasn't able to "
00093                             "get the dictionary from the function passed");
00094             return NULL;
00095         }
00096         else {
00097             /* set id */
00098             if (*dict_ptr == NULL) {
00099                 *dict_ptr = PyDict_New();
00100             }
00101 
00102             PyDict_SetItemString(*dict_ptr, PERMINENT_CB_ID, Py_None);
00103         }
00104 
00105         Py_INCREF(value);
00106         return value;
00107     }
00108     else {
00109         PyErr_SetString(PyExc_ValueError,
00110                         "bpy.app.handlers.persistent expected a function");
00111         return NULL;
00112     }
00113 }
00114 
00115 /* dummy type because decorators can't be PyCFunctions */
00116 static PyTypeObject BPyPersistent_Type = {
00117 
00118 #if defined(_MSC_VER) || defined(FREE_WINDOWS)
00119     PyVarObject_HEAD_INIT(NULL, 0)
00120 #else
00121     PyVarObject_HEAD_INIT(&PyType_Type, 0)
00122 #endif
00123 
00124     "persistent",                               /* tp_name */
00125     0,                                          /* tp_basicsize */
00126     0,                                          /* tp_itemsize */
00127     /* methods */
00128     0,                                          /* tp_dealloc */
00129     0,                                          /* tp_print */
00130     0,                                          /* tp_getattr */
00131     0,                                          /* tp_setattr */
00132     0,                                          /* tp_reserved */
00133     0,                                          /* tp_repr */
00134     0,                                          /* tp_as_number */
00135     0,                                          /* tp_as_sequence */
00136     0,                                          /* tp_as_mapping */
00137     0,                                          /* tp_hash */
00138     0,                                          /* tp_call */
00139     0,                                          /* tp_str */
00140     0,                                          /* tp_getattro */
00141     0,                                          /* tp_setattro */
00142     0,                                          /* tp_as_buffer */
00143     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
00144         Py_TPFLAGS_BASETYPE,                    /* tp_flags */
00145     0,                                          /* tp_doc */
00146     0,                                          /* tp_traverse */
00147     0,                                          /* tp_clear */
00148     0,                                          /* tp_richcompare */
00149     0,                                          /* tp_weaklistoffset */
00150     0,                                          /* tp_iter */
00151     0,                                          /* tp_iternext */
00152     0,                                          /* tp_methods */
00153     0,                                          /* tp_members */
00154     0,                                          /* tp_getset */
00155     0,                                          /* tp_base */
00156     0,                                          /* tp_dict */
00157     0,                                          /* tp_descr_get */
00158     0,                                          /* tp_descr_set */
00159     0,                                          /* tp_dictoffset */
00160     0,                                          /* tp_init */
00161     0,                                          /* tp_alloc */
00162     bpy_app_handlers_persistent_new,            /* tp_new */
00163     0,                                          /* tp_free */
00164 };
00165 
00166 static PyObject *py_cb_array[BLI_CB_EVT_TOT] = {NULL};
00167 
00168 static PyObject *make_app_cb_info(void)
00169 {
00170     PyObject *app_cb_info;
00171     int pos = 0;
00172 
00173     app_cb_info = PyStructSequence_New(&BlenderAppCbType);
00174     if (app_cb_info == NULL) {
00175         return NULL;
00176     }
00177 
00178     for (pos = 0; pos < BLI_CB_EVT_TOT; pos++) {
00179         if (app_cb_info_fields[pos].name == NULL) {
00180             Py_FatalError("invalid callback slots 1");
00181         }
00182         PyStructSequence_SET_ITEM(app_cb_info, pos, (py_cb_array[pos] = PyList_New(0)));
00183     }
00184     if (app_cb_info_fields[pos + APP_CB_OTHER_FIELDS].name != NULL) {
00185         Py_FatalError("invalid callback slots 2");
00186     }
00187 
00188     /* custom function */
00189     PyStructSequence_SET_ITEM(app_cb_info, pos++, (PyObject *)&BPyPersistent_Type);
00190 
00191     return app_cb_info;
00192 }
00193 
00194 PyObject *BPY_app_handlers_struct(void)
00195 {
00196     PyObject *ret;
00197 
00198 #if defined(_MSC_VER) || defined(FREE_WINDOWS)
00199     BPyPersistent_Type.ob_base.ob_base.ob_type = &PyType_Type;
00200 #endif
00201 
00202     if (PyType_Ready(&BPyPersistent_Type) < 0) {
00203         BLI_assert(!"error initializing 'bpy.app.handlers.persistent'");
00204     }
00205 
00206     PyStructSequence_InitType(&BlenderAppCbType, &app_cb_info_desc);
00207 
00208     ret = make_app_cb_info();
00209 
00210     /* prevent user from creating new instances */
00211     BlenderAppCbType.tp_init = NULL;
00212     BlenderAppCbType.tp_new = NULL;
00213     BlenderAppCbType.tp_hash = (hashfunc)_Py_HashPointer; /* without this we can't do set(sys.modules) [#29635] */
00214 
00215     /* assign the C callbacks */
00216     if (ret) {
00217         static bCallbackFuncStore funcstore_array[BLI_CB_EVT_TOT] = {{NULL}};
00218         bCallbackFuncStore *funcstore;
00219         int pos = 0;
00220 
00221         for (pos = 0; pos < BLI_CB_EVT_TOT; pos++) {
00222             funcstore = &funcstore_array[pos];
00223             funcstore->func = bpy_app_generic_callback;
00224             funcstore->alloc = 0;
00225             funcstore->arg = SET_INT_IN_POINTER(pos);
00226             BLI_add_cb(funcstore, pos);
00227         }
00228     }
00229 
00230     return ret;
00231 }
00232 
00233 void BPY_app_handlers_reset(const short do_all)
00234 {
00235     int pos = 0;
00236 
00237     if (do_all) {
00238     for (pos = 0; pos < BLI_CB_EVT_TOT; pos++) {
00239             /* clear list */
00240             PyList_SetSlice(py_cb_array[pos], 0, PY_SSIZE_T_MAX, NULL);
00241         }
00242     }
00243     else {
00244         /* save string conversion thrashing */
00245         PyObject *perm_id_str = PyUnicode_FromString(PERMINENT_CB_ID);
00246 
00247         for (pos = 0; pos < BLI_CB_EVT_TOT; pos++) {
00248             /* clear only items without PERMINENT_CB_ID */
00249             PyObject *ls = py_cb_array[pos];
00250             Py_ssize_t i;
00251 
00252             PyObject *item;
00253             PyObject **dict_ptr;
00254 
00255             for (i = PyList_GET_SIZE(ls) - 1; i >= 0; i--) {
00256 
00257                 if ( (PyFunction_Check((item = PyList_GET_ITEM(ls, i)))) &&
00258                      (dict_ptr = _PyObject_GetDictPtr(item)) &&
00259                      (*dict_ptr) &&
00260                      (PyDict_GetItem(*dict_ptr, perm_id_str) != NULL))
00261                 {
00262                     /* keep */
00263                 }
00264                 else {
00265                     /* remove */
00266                     /* PySequence_DelItem(ls, i); */ /* more obvious buw slower */
00267                     PyList_SetSlice(ls, i, i + 1, NULL);
00268                 }
00269             }
00270         }
00271 
00272         Py_DECREF(perm_id_str);
00273     }
00274 }
00275 
00276 /* the actual callback - not necessarily called from py */
00277 void bpy_app_generic_callback(struct Main *UNUSED(main), struct ID *id, void *arg)
00278 {
00279     PyObject *cb_list = py_cb_array[GET_INT_FROM_POINTER(arg)];
00280     Py_ssize_t cb_list_len;
00281     if ((cb_list_len = PyList_GET_SIZE(cb_list)) > 0) {
00282         PyGILState_STATE gilstate = PyGILState_Ensure();
00283 
00284         PyObject* args = PyTuple_New(1); // save python creating each call
00285         PyObject* func;
00286         PyObject* ret;
00287         Py_ssize_t pos;
00288 
00289         /* setup arguments */
00290         if (id) {
00291             PointerRNA id_ptr;
00292             RNA_id_pointer_create(id, &id_ptr);
00293             PyTuple_SET_ITEM(args, 0, pyrna_struct_CreatePyObject(&id_ptr));
00294         }
00295         else {
00296             PyTuple_SET_ITEM(args, 0, Py_None);
00297             Py_INCREF(Py_None);
00298         }
00299 
00300         // Iterate the list and run the callbacks
00301         for (pos = 0; pos < cb_list_len; pos++) {
00302             func = PyList_GET_ITEM(cb_list, pos);
00303             ret = PyObject_Call(func, args, NULL);
00304             if (ret == NULL) {
00305                 PyErr_Print();
00306                 PyErr_Clear();
00307             }
00308             else {
00309                 Py_DECREF(ret);
00310             }
00311         }
00312 
00313         Py_DECREF(args);
00314 
00315         PyGILState_Release(gilstate);
00316     }
00317 }