Blender V2.61 - r43446
|
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 }