added scons option BF_WITH_PYTHON (defined as DISABLE_PYTHON)
[blender-staging.git] / source / blender / nodes / intern / SHD_nodes / SHD_dynamic.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. 
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2007 Blender Foundation.
21  * All rights reserved.
22  *
23  * The Original Code is: all of this file.
24  *
25  * Contributor(s): Nathan Letwory
26  *
27  * ***** END GPL LICENSE BLOCK *****
28  */
29
30 #ifndef DISABLE_PYTHON
31 #include <Python.h>
32 #include <compile.h>
33 #include <eval.h>
34 #endif
35
36 #include "DNA_text_types.h"
37 #include "BKE_text.h"
38 #include "BKE_utildefines.h"
39
40 #ifndef DISABLE_PYTHON
41 #include "api2_2x/Node.h"
42 #include "api2_2x/gen_utils.h"
43 #include "BPY_extern.h"
44 #endif
45
46 #include "../SHD_util.h"
47
48 static void node_dynamic_setup(bNode *node);
49 static void node_dynamic_exec_cb(void *data, bNode *node, bNodeStack **in, bNodeStack **out);
50 static void node_dynamic_free_storage_cb(bNode *node);
51
52 #ifndef DISABLE_PYTHON
53 static PyObject *init_dynamicdict(void) {
54         PyObject *newscriptdict;
55         PyGILState_STATE gilstate = PyGILState_Ensure();
56
57         newscriptdict= PyDict_New();
58
59         PyDict_SetItemString(newscriptdict, "__builtins__", PyEval_GetBuiltins());
60         EXPP_dict_set_item_str(newscriptdict, "__name__", PyString_FromString("__main__"));
61
62         PyGILState_Release(gilstate);
63
64         return newscriptdict;
65 }
66 #endif
67
68 static bNodeType *node_dynamic_find_typeinfo(ListBase *list, ID *id)
69 {
70         bNodeType *ntype = list->first;
71
72         while(ntype) {
73                 if (ntype->type == NODE_DYNAMIC && ntype->id == id)
74                         break;
75                 ntype = ntype->next;
76         }
77
78         return ntype; /* NULL if doesn't exist */
79 }
80
81 static void node_dynamic_free_typeinfo_sockets(bNodeType *tinfo)
82 {
83         bNodeSocketType *sock;
84
85         if (!tinfo) return;
86
87         if (tinfo->inputs) {
88                 sock = tinfo->inputs;
89                 while (sock->type != -1) {
90                         MEM_freeN(sock->name);
91                         sock++;
92                 }
93                 MEM_freeN(tinfo->inputs);
94                 tinfo->inputs = NULL;
95         }
96         if (tinfo->outputs) {
97                 sock = tinfo->outputs;
98                 while (sock->type != -1) {
99                         MEM_freeN(sock->name);
100                         sock++;
101                 }
102                 MEM_freeN(tinfo->outputs);
103                 tinfo->outputs = NULL;
104         }
105 }
106
107 static void node_dynamic_free_typeinfo(bNodeType *tinfo)
108 {
109         if (!tinfo) return;
110
111         node_dynamic_free_typeinfo_sockets(tinfo);
112
113         if (tinfo->name) { MEM_freeN(tinfo->name); }
114
115         MEM_freeN(tinfo);
116 }
117
118 static void node_dynamic_free_sockets(bNode *node)
119 {
120         BLI_freelistN(&node->inputs);
121         BLI_freelistN(&node->outputs);
122 }
123
124 /* For now we just remove the socket links. It's the safest
125  * route, since an update in the script may change completely the
126  * inputs and outputs. Trying to recreate the node links would be
127  * nicer for pynode authors, though. */
128 static void node_dynamic_update_socket_links(bNode *node, bNodeTree *ntree)
129 {
130         if (ntree) {
131                 nodeVerifyType(ntree, node);
132         }
133         else {
134                 Material *ma;
135
136                 for (ma= G.main->mat.first; ma; ma= ma->id.next) {
137                         if (ma->nodetree) {
138                                 bNode *nd;
139                                 for (nd= ma->nodetree->nodes.first; nd; nd = nd->next) {
140                                         if (nd == node) nodeVerifyType(ma->nodetree, node);
141                                 }
142                         }
143                 }
144         }
145 }
146
147 static void node_dynamic_free_storage_cb(bNode *node)
148 {
149 #ifndef DISABLE_PYTHON
150         NodeScriptDict *nsd;
151         PyObject *pydict;
152         BPy_Node *pynode;
153
154         if (!node->storage) return;
155         nsd = (NodeScriptDict *)(node->storage);
156         pydict = nsd->dict;
157         if (pydict) {
158                 Py_DECREF(pydict);
159         }
160         pynode = nsd->node;
161         if (pynode) {
162                 Py_DECREF(pynode);
163         }
164 #endif
165         MEM_freeN(node->storage);
166         node->storage = NULL;
167 }
168
169 /* Disable pynode when its script fails */
170 static void node_dynamic_disable(bNode *node)
171 {
172         node->custom1 = 0;
173         node->custom1 = BSET(node->custom1, NODE_DYNAMIC_ERROR);
174 }
175
176 /* Disable all pynodes using the given text (script) id */
177 static void node_dynamic_disable_all_by_id(ID *id)
178 {
179 #ifndef DISABLE_PYTHON
180         Material *ma; /* XXX hardcoded for shaders */
181
182         for (ma= G.main->mat.first; ma; ma= ma->id.next) {
183                 if (ma->nodetree) {
184                         bNode *nd;
185                         bNodeTree *ntree = ma->nodetree;
186                         for (nd= ntree->nodes.first; nd; nd= nd->next) {
187                                 if (nd->id == id) {
188                                         nd->custom1 = 0;
189                                         nd->custom1 = BSET(nd->custom1, NODE_DYNAMIC_ERROR);
190                                 }
191                         }
192                 }
193         }
194 #endif
195 }
196
197 static void node_rem_socklist_links(bNodeTree *ntree, ListBase *lb)
198 {
199         bNodeLink *link, *next;
200         bNodeSocket *sock;
201
202         if (!lb) return;
203
204         for (sock= lb->first; sock; sock= sock->next) {
205                 for (link= ntree->links.first; link; link= next) {
206                         next= link->next;
207                         if (link->fromsock==sock || link->tosock==sock) {
208                                 nodeRemLink(ntree, link);
209                         }
210                 }
211         }
212 }
213
214 /* XXX hardcoded for shaders */
215 static void node_dynamic_rem_all_links(bNodeType *tinfo)
216 {
217         Material *ma;
218         int in, out;
219
220         in = tinfo->inputs ? 1 : 0;
221         out = tinfo->outputs ? 1 : 0;
222
223         if (!in && !out) return;
224
225         for (ma= G.main->mat.first; ma; ma= ma->id.next) {
226                 if (ma->nodetree) {
227                         bNode *nd;
228                         bNodeTree *ntree = ma->nodetree;
229                         for (nd= ntree->nodes.first; nd; nd= nd->next) {
230                                 if (nd->typeinfo == tinfo) {
231                                         if (in)
232                                                 node_rem_socklist_links(ntree, &nd->inputs);
233                                         if (out)
234                                                 node_rem_socklist_links(ntree, &nd->outputs);
235                                 }
236                         }
237                 }
238         }
239 }
240
241 /* node_dynamic_reset: clean a pynode, getting rid of all
242  * data dynamically created for it. */
243 static void node_dynamic_reset(bNode *node, int unlink_text)
244 {
245         bNodeType *tinfo, *tinfo_default;
246         Material *ma;
247
248         tinfo = node->typeinfo;
249         tinfo_default = node_dynamic_find_typeinfo(&node_all_shaders, NULL);
250
251         if ((tinfo == tinfo_default) && unlink_text) {
252                 ID *textID = node->id;
253         /* already at default (empty) state, which happens if this node's
254          * script failed to parse at the first stage: definition. We're here
255          * because its text was removed from Blender. */
256                 for (ma= G.main->mat.first; ma; ma= ma->id.next) {
257                         if (ma->nodetree) {
258                                 bNode *nd;
259                                 for (nd= ma->nodetree->nodes.first; nd; nd = nd->next) {
260                                         if (nd->id == textID) {
261                                                 nd->id = NULL;
262                                                 nd->custom1 = 0;
263                                                 nd->custom1 = BSET(nd->custom1, NODE_DYNAMIC_NEW);
264                                                 BLI_strncpy(nd->name, "Dynamic", 8);
265                                                 return;
266                                         }
267                                 }
268                         }
269                 }
270         }
271
272         node_dynamic_rem_all_links(tinfo);
273         node_dynamic_free_typeinfo_sockets(tinfo);
274
275         /* reset all other XXX shader nodes sharing this typeinfo */
276         for (ma= G.main->mat.first; ma; ma= ma->id.next) {
277                 if (ma->nodetree) {
278                         bNode *nd;
279                         for (nd= ma->nodetree->nodes.first; nd; nd = nd->next) {
280                                 if (nd->typeinfo == tinfo) {
281                                         node_dynamic_free_storage_cb(nd);
282                                         node_dynamic_free_sockets(nd);
283                                         //node_dynamic_update_socket_links(nd, ma->nodetree);
284                                         nd->typeinfo = tinfo_default;
285                                         if (unlink_text) {
286                                                 nd->id = NULL;
287                                                 nd->custom1 = 0;
288                                                 nd->custom1 = BSET(nd->custom1, NODE_DYNAMIC_NEW);
289                                                 BLI_strncpy(nd->name, "Dynamic", 8);
290                                         }
291                                 }
292                         }
293                 }
294         }
295
296         /* XXX hardcoded for shaders: */
297         if (tinfo->id) { BLI_remlink(&node_all_shaders, tinfo); }
298         node_dynamic_free_typeinfo(tinfo);
299 }
300
301 /* Special case of the above function: for working pynodes
302  * that were saved on a .blend but fail for some reason when
303  * the file is opened. We need this because pynodes are initialized
304  * before G.main. */
305 static void node_dynamic_reset_loaded(bNode *node)
306 {
307         bNodeType *tinfo = node->typeinfo;
308
309         node_dynamic_rem_all_links(tinfo);
310         node_dynamic_free_typeinfo_sockets(tinfo);
311         node_dynamic_free_storage_cb(node);
312         /* XXX hardcoded for shaders: */
313         if (tinfo->id) { BLI_remlink(&node_all_shaders, tinfo); }
314
315         node_dynamic_free_typeinfo(tinfo);
316         node->typeinfo = node_dynamic_find_typeinfo(&node_all_shaders, NULL);
317 }
318
319 int nodeDynamicUnlinkText(ID *txtid) {
320         Material *ma;
321         bNode *nd;
322
323         /* find one node that uses this text */
324         for (ma= G.main->mat.first; ma; ma= ma->id.next) {
325                 if (ma->nodetree) {
326                         for (nd= ma->nodetree->nodes.first; nd; nd = nd->next) {
327                                 if ((nd->type == NODE_DYNAMIC) && (nd->id == txtid)) {
328                                         node_dynamic_reset(nd, 1); /* found, reset all */
329                                         return 1;
330                                 }
331                         }
332                 }
333         }
334         return 0; /* no pynodes used this text */
335 }
336
337 static void node_dynamic_pyerror_print(bNode *node)
338 {
339 #ifndef DISABLE_PYTHON
340         PyGILState_STATE gilstate = PyGILState_Ensure();
341
342         fprintf(stderr, "\nError in dynamic node script \"%s\":\n", node->name);
343         if (PyErr_Occurred()) { PyErr_Print(); }
344         else { fprintf(stderr, "Not a valid dynamic node Python script.\n"); }
345
346         PyGILState_Release(gilstate);
347 #endif
348 }
349
350 static void node_dynamic_register_type(bNode *node)
351 {
352         nodeRegisterType(&node_all_shaders, node->typeinfo);
353         /* nodeRegisterType copied it to a new one, so we
354          * free the typeinfo itself, but not what it
355          * points to: */
356         MEM_freeN(node->typeinfo);
357         node->typeinfo = node_dynamic_find_typeinfo(&node_all_shaders, node->id);
358         MEM_freeN(node->typeinfo->name);
359         node->typeinfo->name = BLI_strdup(node->name);
360 }
361
362 #ifndef DISABLE_PYTHON
363 /* node_dynamic_get_pynode:
364  * Find the pynode definition from the script */
365 static PyObject *node_dynamic_get_pynode(PyObject *dict)
366 {
367         PyObject *key= NULL;
368         Py_ssize_t pos = 0;
369         PyObject *value = NULL;
370
371         /* script writer specified a node? */
372         value = PyDict_GetItemString(dict, "__node__");
373
374         if (value) {
375                 if (PyObject_TypeCheck(value, &PyType_Type)) {
376                         Py_INCREF(value);
377                         return value;
378                 }
379                 else {
380                         PyErr_SetString(PyExc_TypeError,
381                                 "expected class object derived from Scripted node");
382                         return NULL;
383                 }
384         }
385
386         /* case not, search for it in the script's global dictionary */
387         while (PyDict_Next(dict, &pos, &key, &value)) {
388                 /* skip names we know belong to other available objects */
389                 if (strcmp("Socket", PyString_AsString(key)) == 0)
390                         continue;
391                 else if (strcmp("Scripted", PyString_AsString(key)) == 0)
392                         continue;
393                 /* naive: we grab the first ob of type 'type': */
394                 else if (PyObject_TypeCheck(value, &PyType_Type)) {
395                         Py_INCREF(value);
396                         return value;
397                 }
398         }
399
400         PyErr_SetString(PyExc_TypeError,
401                 "no PyNode definition found in the script!");
402         return NULL;
403 }
404 #endif /* DISABLE_PYTHON */
405
406 static int node_dynamic_parse(struct bNode *node)
407 {
408 #ifdef DISABLE_PYTHON
409         return -1;
410 #else
411         PyObject *dict= NULL;
412         PyObject *pynode_data= NULL;
413         PyObject *pynode= NULL;
414         PyObject *args= NULL;
415         NodeScriptDict *nsd = NULL;
416         PyObject *pyresult = NULL;
417         char *buf = NULL;
418         int is_valid_script = 0;
419         PyGILState_STATE gilstate;
420
421         if (!node->id || !node->storage)
422                 return 0;
423
424         /* READY, no need to be here */
425         if (BTST(node->custom1, NODE_DYNAMIC_READY))
426                 return 0;
427
428         /* for threading */
429         gilstate = PyGILState_Ensure();
430
431         nsd = (NodeScriptDict *)node->storage;
432
433         dict = (PyObject *)(nsd->dict);
434         buf = txt_to_buf((Text *)node->id);
435
436         pyresult = PyRun_String(buf, Py_file_input, dict, dict);
437
438         MEM_freeN(buf);
439
440         if (!pyresult) {
441                 if (BTST(node->custom1, NODE_DYNAMIC_LOADED)) {
442                         node_dynamic_disable(node);
443                 } else {
444                 node_dynamic_disable_all_by_id(node->id);
445                 }
446                 node_dynamic_pyerror_print(node);
447                 PyGILState_Release(gilstate);
448                 return -1;
449         }
450
451         Py_DECREF(pyresult);
452
453         pynode_data = node_dynamic_get_pynode(dict);
454
455         if (pynode_data) {
456                 BPy_NodeSocketLists *socklists = Node_CreateSocketLists(node);
457
458                 args = Py_BuildValue("(O)", socklists);
459
460                 /* init it to get the input and output sockets */
461                 pynode = PyObject_Call(pynode_data, args, NULL);
462
463                 Py_DECREF(pynode_data);
464                 Py_DECREF(socklists);
465                 Py_DECREF(args);
466
467                 if (!PyErr_Occurred() && pynode && pytype_is_pynode(pynode)) {
468                         InitNode((BPy_Node *)(pynode), node);
469                         nsd->node = pynode;
470                         node->typeinfo->execfunc = node_dynamic_exec_cb;
471                         is_valid_script = 1;
472
473                         /* for NEW, LOADED, REPARSE */
474                         if (BNTST(node->custom1, NODE_DYNAMIC_ADDEXIST)) {
475                                 node->typeinfo->pydict = dict;
476                                 node->typeinfo->pynode = pynode;
477                                 node->typeinfo->id = node->id;
478                                 if (BNTST(node->custom1, NODE_DYNAMIC_LOADED))
479                                         nodeAddSockets(node, node->typeinfo);
480                                 if (BNTST(node->custom1, NODE_DYNAMIC_REPARSE))
481                                         node_dynamic_register_type(node);
482                         }
483
484                         node->custom1 = 0;
485                         node->custom1 = BSET(node->custom1, NODE_DYNAMIC_READY);
486                 }
487         }
488
489         PyGILState_Release(gilstate);
490
491         if (!is_valid_script) { /* not a valid pynode script */
492                 node_dynamic_disable_all_by_id(node->id);
493                 node_dynamic_pyerror_print(node);
494                 return -1;
495         }
496
497         return 0;
498 #endif
499 }
500
501 /* node_dynamic_setup: prepare for execution (state: NODE_DYNAMIC_READY)
502  * pynodes already linked to a script (node->id != NULL). */
503 static void node_dynamic_setup(bNode *node)
504 {
505 #ifndef DISABLE_PYTHON
506         NodeScriptDict *nsd = NULL;
507         bNodeTree *nodetree = NULL;
508         bNodeType *ntype = NULL;
509         PyGILState_STATE gilstate;
510
511         /* Possible cases:
512          * NEW
513          * ADDEXIST
514          * LOADED
515          * REPARSE
516          * ERROR
517          * READY
518          */
519
520         /* NEW, but not linked to a script: link default (empty) typeinfo */
521         if (!node->id) {
522                 node->typeinfo = node_dynamic_find_typeinfo(&node_all_shaders,
523                                 NULL);
524                 return;
525         }
526
527         /* READY, no need to be here */
528         if (BTST(node->custom1, NODE_DYNAMIC_READY))
529                 return;
530
531         gilstate = PyGILState_Ensure();
532
533         /* ERROR, reset to (empty) defaults */
534         if (BCLR(node->custom1, NODE_DYNAMIC_ERROR) == 0) {
535                 node_dynamic_reset(node, 0);
536                 PyGILState_Release(gilstate);
537                 return;
538         }
539
540         /* User asked to update this pynode, prepare it for reparsing */
541         if (BTST(node->custom1, NODE_DYNAMIC_REPARSE)) {
542                 int needs_parsing = 1;
543
544                 node->custom1 = BSET(node->custom1, NODE_DYNAMIC_NEW);
545
546                 if (BTST(node->custom1, NODE_DYNAMIC_ERROR)) {
547                         node->custom1 = BCLR(node->custom1, NODE_DYNAMIC_REPARSE);
548                         ntype = node_dynamic_find_typeinfo(&node_all_shaders, node->id);
549
550                         if (ntype) {
551                                 node->typeinfo = ntype;
552                                 node->custom1 = BSET(node->custom1, NODE_DYNAMIC_ADDEXIST);
553                                 node->custom1 = BCLR(node->custom1, NODE_DYNAMIC_ERROR);
554                                 needs_parsing = 0;
555                         }
556                         else { nodeMakeDynamicType(node); }
557
558                 } else {
559                         node_dynamic_rem_all_links(node->typeinfo);
560                         node_dynamic_free_typeinfo_sockets(node->typeinfo);
561                         node_dynamic_update_socket_links(node, NULL);
562                         node_dynamic_free_storage_cb(node);
563                 }
564
565                 if (needs_parsing) {
566                         nsd = MEM_callocN(sizeof(NodeScriptDict), "node script dictionary");
567                         nsd->dict = init_dynamicdict();
568                         node->storage = nsd;
569                         /* prepared, now reparse: */
570                         node_dynamic_parse(node);
571                         PyGILState_Release(gilstate);
572                         return;
573                 }
574         }
575         else if (BTST(node->custom1, NODE_DYNAMIC_LOADED)) {
576                 /* when loading from a .blend we don't have G.main yet, so we
577                  * quickly abuse node->storage in ntreeInitTypes (node.c) to have
578                  * our nodetree ptr (needed if a pynode script that worked before
579                  * saving the .blend for some reason fails upon loading): */
580                 nodetree = (bNodeTree *)node->storage;
581                 node->storage = NULL;
582         }
583
584         if (node->storage)
585                 fprintf(stderr, "\nDEBUG: PYNODES ERROR: non NULL node->storage in node_dynamic_setup()\n");
586
587         nsd = MEM_callocN(sizeof(NodeScriptDict), "node script dictionary");
588         node->storage = nsd;
589         
590         /* NEW, LOADED or REPARSE */
591         if (BNTST(node->custom1, NODE_DYNAMIC_ADDEXIST)) {
592                 /* check if there's already a bNodeType linked to this script */
593                 /* (XXX hardcoded for shader nodes for now) */
594                 ntype = node_dynamic_find_typeinfo(&node_all_shaders, node->id);
595
596                 if (ntype) { /* if so, reuse it */
597                         node->typeinfo = ntype;
598                         /* so this is actually an ADDEXIST type */
599                         node->custom1 = BSET(node->custom1, NODE_DYNAMIC_ADDEXIST);
600                 }
601                 else { /* create bNodeType for this pynode */
602                         nodeMakeDynamicType(node);
603                         nsd->dict = init_dynamicdict();
604                         if ((node_dynamic_parse(node) == -1) && nodetree) {
605                                 node_dynamic_reset_loaded(node);
606                         }
607                         PyGILState_Release(gilstate);
608                         return;
609                 }
610         }
611
612         /* ADDEXIST: new pynode linked to an already registered dynamic type,
613          * we just reuse existing py dict and pynode */
614         nsd->dict = node->typeinfo->pydict;
615         nsd->node = node->typeinfo->pynode;
616
617         Py_INCREF((PyObject *)(nsd->dict));
618         Py_INCREF((PyObject *)(nsd->node));
619
620         if (BTST(node->custom1, NODE_DYNAMIC_NEW)) {
621                 nodeAddSockets(node, node->typeinfo);
622                 node->custom1 = BCLR(node->custom1, NODE_DYNAMIC_NEW);
623         }
624
625         node->custom1 = BCLR(node->custom1, NODE_DYNAMIC_ADDEXIST);
626         node->custom1 = BSET(node->custom1, NODE_DYNAMIC_READY);
627
628         PyGILState_Release(gilstate);
629 #endif /* DISABLE_PYTHON */
630         return;
631 }
632
633 /* node_dynamic_init_cb callback: called when a pynode is created.
634  * The pynode type is passed via node->custom2. It can be:
635  *  0: for loaded empty nodes
636  *  NODE_DYNAMIC_MENU: for the default Dynamic node type
637  *  > NODE_DYNAMIC_MENU: for the new types defined by scripts
638 */
639 static void node_dynamic_init_cb(bNode *node) {
640         int type = node->custom2;
641
642         node->custom2 = 0;
643
644         if (type >= NODE_DYNAMIC_MENU) {
645                 node->custom1 = 0;
646
647                 if (type == NODE_DYNAMIC_MENU) {
648                         node->custom1 = BSET(node->custom1, NODE_DYNAMIC_NEW);
649                         return;
650                 }
651
652                 node->custom1 = BSET(node->custom1, NODE_DYNAMIC_ADDEXIST);
653                 node->id = node->typeinfo->id;
654         }
655
656         node_dynamic_setup(node);
657 }
658
659 /* node_dynamic_copy_cb: pynode copy callback */
660 static void node_dynamic_copy_cb(bNode *orig_node, bNode *new_node)
661 {
662 #ifdef DISABLE_PYTHON
663         return;
664 #else
665         NodeScriptDict *nsd;
666         PyGILState_STATE gilstate;
667
668         if (!orig_node->storage) return;
669
670         nsd = (NodeScriptDict *)(orig_node->storage);
671         new_node->storage = MEM_dupallocN(orig_node->storage);
672
673         gilstate = PyGILState_Ensure();
674
675         if (nsd->node)
676                 Py_INCREF((PyObject *)(nsd->node));
677         if (nsd->dict)
678                 Py_INCREF((PyObject *)(nsd->dict));
679
680         PyGILState_Release(gilstate);
681 #endif
682 }
683
684 /* node_dynamic_exec_cb: the execution callback called per pixel
685  * during rendering. */
686 static void node_dynamic_exec_cb(void *data, bNode *node, bNodeStack **in, bNodeStack **out) {
687 #ifdef DISABLE_PYTHON
688         return;
689 #else
690         BPy_Node *mynode = NULL;
691         NodeScriptDict *nsd = NULL;
692         PyObject *pyresult = NULL;
693         PyObject *args = NULL;
694         ShadeInput *shi;
695         PyGILState_STATE gilstate;
696
697         if (!node->id)
698                 return;
699
700         /*if (G.scene->r.threads > 1)
701                 return;*/
702
703         if (BTST2(node->custom1, NODE_DYNAMIC_NEW, NODE_DYNAMIC_REPARSE)) {
704                 node_dynamic_setup(node);
705                 return;
706         }
707
708         if (BTST(node->custom1, NODE_DYNAMIC_ERROR)) {
709                 if (node->storage) node_dynamic_setup(node);
710                 return;
711         }
712
713         if (BTST(node->custom1, NODE_DYNAMIC_READY)) {
714                 nsd = (NodeScriptDict *)node->storage;
715                 mynode = (BPy_Node *)(nsd->node);
716
717
718                 if (mynode && PyCallable_Check((PyObject *)mynode)) {
719
720                         gilstate = PyGILState_Ensure();
721
722                         mynode->node = node;
723                         shi = ((ShaderCallData *)data)->shi;
724
725                         Node_SetStack(mynode, in, NODE_INPUTSTACK);
726                         Node_SetStack(mynode, out, NODE_OUTPUTSTACK);
727                         Node_SetShi(mynode, shi);
728
729                         args=Py_BuildValue("()");
730                         pyresult= PyObject_Call((PyObject *)mynode, args, NULL);
731                         Py_DECREF(args);
732
733                         if (!pyresult) {
734                                 PyGILState_Release(gilstate);
735                                 node_dynamic_disable_all_by_id(node->id);
736                                 node_dynamic_pyerror_print(node);
737                                 node_dynamic_setup(node);
738                                 return;
739                         }
740                         Py_DECREF(pyresult);
741                         PyGILState_Release(gilstate);
742                 }
743         }
744 #endif
745 }
746
747 bNodeType node_dynamic_typeinfo = {
748         /* next, prev  */       NULL, NULL,
749         /* type code   */       NODE_DYNAMIC,
750         /* name        */       "Dynamic",
751         /* width+range */       150, 60, 300,
752         /* class+opts  */       NODE_CLASS_OP_DYNAMIC, NODE_OPTIONS,
753         /* input sock  */       NULL,
754         /* output sock */       NULL,
755         /* storage     */       "NodeScriptDict",
756         /* execfunc    */       node_dynamic_exec_cb,
757         /* butfunc     */       NULL,
758         /* initfunc    */       node_dynamic_init_cb,
759         /* freefunc    */       node_dynamic_free_storage_cb,
760         /* copyfunc    */       node_dynamic_copy_cb,
761         /* id          */       NULL
762 };
763
764