2876 lines
77 KiB
C
2876 lines
77 KiB
C
/*
|
|
* $Id$
|
|
*
|
|
* ***** BEGIN GPL LICENSE BLOCK *****
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
|
|
* All rights reserved.
|
|
*
|
|
* This is a new part of Blender.
|
|
*
|
|
* Contributor(s): Michel Selten, Willian P. Germano, Stephen Swaney,
|
|
* Chris Keith, Chris Want, Ken Hughes
|
|
*
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
*/
|
|
|
|
#include <Python.h>
|
|
|
|
#include "compile.h" /* for the PyCodeObject */
|
|
#include "eval.h" /* for PyEval_EvalCode */
|
|
#include "BLI_blenlib.h" /* for BLI_last_slash() */
|
|
#include "BIF_interface.h" /* for pupmenu() */
|
|
#include "BIF_space.h"
|
|
#include "BIF_screen.h"
|
|
#include "BIF_toolbox.h"
|
|
#include "BKE_action.h" /* for get_pose_channel() */
|
|
#include "BKE_library.h"
|
|
#include "BKE_object.h" /* during_scriptlink() */
|
|
#include "BKE_text.h"
|
|
#include "BKE_constraint.h" /* for bConstraintOb */
|
|
#include "BKE_idprop.h"
|
|
|
|
#include "DNA_curve_types.h" /* for struct IpoDriver */
|
|
#include "DNA_ID.h" /* ipo driver */
|
|
#include "DNA_object_types.h" /* ipo driver */
|
|
#include "DNA_constraint_types.h" /* for pyconstraint */
|
|
|
|
#include "DNA_screen_types.h"
|
|
#include "DNA_userdef_types.h" /* for U.pythondir */
|
|
#include "MEM_guardedalloc.h"
|
|
#include "BPY_extern.h"
|
|
#include "BPY_menus.h"
|
|
#include "DNA_space_types.h"
|
|
#include "BKE_global.h"
|
|
#include "BKE_main.h"
|
|
#include "BKE_armature.h"
|
|
#include "BKE_depsgraph.h"
|
|
#include "api2_2x/EXPP_interface.h"
|
|
#include "api2_2x/constant.h"
|
|
#include "api2_2x/gen_utils.h"
|
|
#include "api2_2x/gen_library.h" /* GetPyObjectFromID */
|
|
#include "api2_2x/BGL.h"
|
|
#include "api2_2x/Blender.h"
|
|
#include "api2_2x/Camera.h"
|
|
#include "api2_2x/Draw.h"
|
|
#include "api2_2x/Object.h"
|
|
#include "api2_2x/Registry.h"
|
|
#include "api2_2x/Pose.h"
|
|
#include "api2_2x/Mathutils.h"
|
|
#include "api2_2x/bpy.h" /* for the new "bpy" module */
|
|
#include "api2_2x/bpy_internal_import.h"
|
|
|
|
/*these next two are for pyconstraints*/
|
|
#include "api2_2x/IDProp.h"
|
|
#include "api2_2x/matrix.h"
|
|
|
|
/* for scriptlinks */
|
|
#include "DNA_lamp_types.h"
|
|
#include "DNA_camera_types.h"
|
|
#include "DNA_world_types.h"
|
|
#include "DNA_scene_types.h"
|
|
#include "DNA_material_types.h"
|
|
|
|
/* bpy_registryDict is declared in api2_2x/Registry.h and defined
|
|
* in api2_2x/Registry.c
|
|
* This Python dictionary will be used to store data that scripts
|
|
* choose to preserve after they are executed, so user changes can be
|
|
* restored next time the script is used. Check the Blender.Registry module.
|
|
*/
|
|
/*#include "api2_2x/Registry.h" */
|
|
|
|
/* for pydrivers (ipo drivers defined by one-line Python expressions) */
|
|
PyObject *bpy_pydriver_Dict = NULL;
|
|
PyObject *bpy_orig_syspath_List = NULL;
|
|
|
|
|
|
static void BPY_Err_Clear(void)
|
|
{
|
|
/* Added in 2.48a, the last_traceback can reference Objects for example, increasing
|
|
* their user count. Not to mention holding references to wrapped data.
|
|
* This is especially bad when the PyObject for the wrapped data is free'd, after blender
|
|
* has alredy dealocated the pointer */
|
|
PySys_SetObject( "last_traceback", NULL);
|
|
|
|
PyErr_Clear();
|
|
}
|
|
|
|
/*
|
|
* set up a weakref list for Armatures
|
|
* creates list in __main__ module dict
|
|
*/
|
|
|
|
static int setup_armature_weakrefs()
|
|
{
|
|
PyObject *maindict;
|
|
PyObject *main_module;
|
|
PyObject *list;
|
|
PyObject *list_name = PyString_FromString(ARM_WEAKREF_LIST_NAME);
|
|
|
|
main_module = PyImport_AddModule( "__main__");
|
|
if(main_module){
|
|
maindict= PyModule_GetDict(main_module);
|
|
|
|
/* check if there is already a dict entry for the armature weakrefs,
|
|
* and delete if so before making another one */
|
|
|
|
if (PyDict_DelItem(maindict, list_name)==-1)
|
|
PyErr_Clear();
|
|
|
|
list= PyList_New(0);
|
|
if (PyDict_SetItem(maindict, list_name, list) == -1){
|
|
PyErr_Print();
|
|
BPY_Err_Clear();
|
|
Py_DECREF(list);
|
|
Py_DECREF(list_name);
|
|
return 0;
|
|
}
|
|
Py_DECREF(list); /* the dict owns it now */
|
|
}
|
|
Py_DECREF(list_name);
|
|
return 1;
|
|
}
|
|
|
|
/* Declares the modules and their initialization functions
|
|
* These are TOP-LEVEL modules e.g. import `module` - there is no
|
|
* support for packages here e.g. import `package.module` */
|
|
|
|
static struct _inittab BPy_Inittab_Modules[] = {
|
|
{"Blender", M_Blender_Init},
|
|
{"bpy", m_bpy_init},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
/*************************************************************************
|
|
* Structure definitions
|
|
**************************************************************************/
|
|
#define FILENAME_LENGTH 24
|
|
|
|
typedef struct _ScriptError {
|
|
char filename[FILENAME_LENGTH];
|
|
int lineno;
|
|
} ScriptError;
|
|
|
|
/****************************************************************************
|
|
* Global variables
|
|
****************************************************************************/
|
|
ScriptError g_script_error;
|
|
|
|
/***************************************************************************
|
|
* Function prototypes
|
|
***************************************************************************/
|
|
static PyObject *RunPython( Text * text, PyObject * globaldict );
|
|
static PyObject *CreateGlobalDictionary( void );
|
|
static void ReleaseGlobalDictionary( PyObject * dict );
|
|
static void DoAllScriptsFromList( ListBase * list, short event );
|
|
static void init_ourImport( void );
|
|
static void init_ourReload( void );
|
|
|
|
|
|
static void BPY_Err_Handle( char *script_name );
|
|
static PyObject *traceback_getFilename( PyObject * tb );
|
|
|
|
/****************************************************************************
|
|
* Description: This function will start the interpreter and load all modules
|
|
* as well as search for a python installation.
|
|
****************************************************************************/
|
|
void BPY_start_python( int argc, char **argv )
|
|
{
|
|
PyThreadState *py_tstate = NULL;
|
|
static int argc_copy = 0;
|
|
static char **argv_copy = NULL;
|
|
int first_time = argc;
|
|
|
|
/* we keep a copy of the values of argc and argv so that the game engine
|
|
* can call BPY_start_python(0, NULL) whenever a game ends, without having
|
|
* to know argc and argv there (in source/blender/src/space.c) */
|
|
if( first_time ) {
|
|
argc_copy = argc;
|
|
argv_copy = argv;
|
|
}
|
|
|
|
//stuff for Registry module
|
|
bpy_registryDict = PyDict_New( );/* check comment at start of this file */
|
|
if( !bpy_registryDict )
|
|
printf( "Warning: Couldn't create the Registry Python Dictionary!" );
|
|
Py_SetProgramName( "blender" );
|
|
|
|
/* Py_Initialize() will attempt to import the site module and
|
|
* print an error if not found. See init_syspath() for the
|
|
* rest of our init msgs.
|
|
*/
|
|
|
|
/* print Python version
|
|
* Py_GetVersion() returns a ptr to a static string "9.9.9 (aaaa..."
|
|
*/
|
|
{
|
|
int count = 3; /* a nice default for major.minor. example 2.5 */
|
|
const char *version = Py_GetVersion();
|
|
/* we know a blank is there somewhere! */
|
|
char *blank_ptr = strchr( version, ' ');
|
|
if(blank_ptr)
|
|
count = blank_ptr - version;
|
|
|
|
printf( "Compiled with Python version %.*s.\n", count, version );
|
|
}
|
|
|
|
|
|
//Initialize the TOP-LEVEL modules
|
|
PyImport_ExtendInittab(BPy_Inittab_Modules);
|
|
|
|
//Start the interpreter
|
|
Py_Initialize( );
|
|
|
|
PySys_SetArgv( argc_copy, argv_copy );
|
|
/* Initialize thread support (also acquires lock) */
|
|
PyEval_InitThreads();
|
|
|
|
//Overrides __import__
|
|
init_ourImport( );
|
|
init_ourReload( );
|
|
|
|
//init a global dictionary
|
|
g_blenderdict = NULL;
|
|
|
|
//Look for a python installation
|
|
init_syspath( first_time ); /* not first_time: some msgs are suppressed */
|
|
py_tstate = PyGILState_GetThisThreadState();
|
|
PyEval_ReleaseThread(py_tstate);
|
|
|
|
return;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Description: This function will terminate the Python interpreter */
|
|
/*****************************************************************************/
|
|
void BPY_end_python( void )
|
|
{
|
|
Script *script = NULL;
|
|
Script *next_script = NULL;
|
|
|
|
PyGILState_Ensure(); /* finalizing, no need to grab the state */
|
|
|
|
if( bpy_registryDict ) {
|
|
Py_DECREF( bpy_registryDict );
|
|
bpy_registryDict = NULL;
|
|
}
|
|
|
|
if( bpy_pydriver_Dict ) {
|
|
Py_DECREF( bpy_pydriver_Dict );
|
|
bpy_pydriver_Dict = NULL;
|
|
}
|
|
|
|
if( bpy_orig_syspath_List ) {
|
|
Py_DECREF( bpy_orig_syspath_List );
|
|
bpy_orig_syspath_List = NULL;
|
|
}
|
|
|
|
/* Freeing all scripts here prevents problems with the order in which
|
|
* Python is finalized and G.main is freed in exit_usiblender() */
|
|
for (script = G.main->script.first; script; script = next_script) {
|
|
next_script = script->id.next;
|
|
free_libblock( &G.main->script, script );
|
|
}
|
|
|
|
Py_Finalize( );
|
|
|
|
BPyMenu_RemoveAllEntries( ); /* freeing bpymenu mem */
|
|
|
|
/* a script might've opened a .blend file but didn't close it, so: */
|
|
EXPP_Library_Close( );
|
|
|
|
return;
|
|
}
|
|
|
|
void syspath_append( char *dirname )
|
|
{
|
|
PyObject *mod_sys= NULL, *dict= NULL, *path= NULL, *dir= NULL;
|
|
short ok=1;
|
|
|
|
mod_sys = PyImport_ImportModule( "sys" ); /* new ref */
|
|
|
|
if (mod_sys) {
|
|
dict = PyModule_GetDict( mod_sys ); /* borrowed ref */
|
|
path = PyDict_GetItemString( dict, "path" ); /* borrowed ref */
|
|
if ( !PyList_Check( path ) ) {
|
|
ok = 0;
|
|
}
|
|
} else {
|
|
/* cant get the sys module */
|
|
/* PyErr_Clear(); is called below */
|
|
ok = 0;
|
|
}
|
|
|
|
dir = PyString_FromString( dirname );
|
|
|
|
if (ok && PySequence_Contains(path, dir)==0) { /* Only add if we need to */
|
|
if (PyList_Append( path, dir ) != 0) /* decref below */
|
|
ok = 0; /* append failed */
|
|
}
|
|
|
|
if( (ok==0) || PyErr_Occurred( ) )
|
|
fprintf(stderr, "Warning: could import or build sys.path\n" );
|
|
|
|
PyErr_Clear();
|
|
Py_DECREF( dir );
|
|
Py_XDECREF( mod_sys );
|
|
}
|
|
|
|
void init_syspath( int first_time )
|
|
{
|
|
PyObject *mod, *d;
|
|
char *progname;
|
|
char execdir[FILE_MAXDIR]; /*defines from DNA_space_types.h */
|
|
|
|
int n;
|
|
|
|
mod = PyImport_ImportModule( "Blender.sys" );
|
|
|
|
if( mod ) {
|
|
d = PyModule_GetDict( mod );
|
|
EXPP_dict_set_item_str( d, "progname", PyString_FromString( bprogname ) );
|
|
Py_DECREF( mod );
|
|
} else {
|
|
printf( "Warning: could not set Blender.sys\n" );
|
|
PyErr_Clear();
|
|
}
|
|
|
|
progname = BLI_last_slash( bprogname ); /* looks for the last dir separator */
|
|
|
|
n = progname - bprogname;
|
|
if( n > 0 ) {
|
|
strncpy( execdir, bprogname, n );
|
|
if( execdir[n - 1] == '.' )
|
|
n--; /*fix for when run as ./blender */
|
|
execdir[n] = '\0';
|
|
|
|
syspath_append( execdir ); /* append to module search path */
|
|
} else {
|
|
printf( "Warning: could not determine argv[0] path\n" );
|
|
}
|
|
|
|
/*
|
|
attempt to import 'site' module as a check for valid
|
|
python install found.
|
|
*/
|
|
|
|
printf("Checking for installed Python... "); /* appears after msg "Compiled with Python 2.x" */
|
|
mod = PyImport_ImportModule( "site" ); /* new ref */
|
|
|
|
if( mod ) {
|
|
printf("got it!\n");
|
|
Py_DECREF( mod );
|
|
} else { /* import 'site' failed */
|
|
PyErr_Clear( );
|
|
if( first_time ) {
|
|
printf( "No installed Python found.\n" );
|
|
printf( "Only built-in modules are available. Some scripts may not run.\n" );
|
|
printf( "Continuing happily.\n" );
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* initialize the sys module
|
|
* set sys.executable to the Blender exe
|
|
*/
|
|
|
|
mod = PyImport_ImportModule( "sys" ); /* new ref */
|
|
|
|
if( mod ) {
|
|
d = PyModule_GetDict( mod ); /* borrowed ref */
|
|
EXPP_dict_set_item_str( d, "executable", Py_BuildValue( "s", bprogname ) );
|
|
|
|
if (bpy_orig_syspath_List == NULL) {
|
|
/* backup the original sys.path to rebuild later */
|
|
PyObject *syspath = PyDict_GetItemString( d, "path" ); /* borrowed ref */
|
|
if (bpy_orig_syspath_List) { /* This should never happen but just incase, be nice */
|
|
Py_DECREF(bpy_orig_syspath_List);
|
|
}
|
|
bpy_orig_syspath_List = PyList_GetSlice(syspath, 0, PyList_Size(syspath));
|
|
}
|
|
|
|
Py_DECREF( mod );
|
|
} else{
|
|
PyErr_Clear( );
|
|
printf("Warning: import of sys module failed\n");
|
|
}
|
|
}
|
|
|
|
void BPY_rebuild_syspath( void )
|
|
{
|
|
PyObject *mod, *dict, *syspath;
|
|
char dirpath[FILE_MAX];
|
|
char *sdir = NULL;
|
|
PyGILState_STATE gilstate = PyGILState_Ensure();
|
|
|
|
mod = PyImport_ImportModule( "sys" );
|
|
if (!mod) {
|
|
printf("Warning: could not import python sys module. some modules may not import.\n");
|
|
PyErr_Clear( );
|
|
PyGILState_Release(gilstate);
|
|
return;
|
|
}
|
|
|
|
if (!bpy_orig_syspath_List) { /* should never happen */
|
|
printf("Warning: cant refresh python path, bpy_orig_syspath_List is NULL\n");
|
|
Py_DECREF(mod);
|
|
PyGILState_Release(gilstate);
|
|
return;
|
|
}
|
|
|
|
dict = PyModule_GetDict( mod ); /* borrowed ref */
|
|
|
|
/* Reset sys.path */
|
|
syspath = PyDict_GetItemString( dict, "path" ); /* borrowed ref */
|
|
PyList_SetSlice(syspath, 0, PyList_Size(syspath), bpy_orig_syspath_List);
|
|
|
|
if(U.pythondir[0] != '\0' ) {
|
|
char modpath[FILE_MAX];
|
|
int upyslen = strlen(U.pythondir);
|
|
BLI_strncpy(dirpath, U.pythondir, FILE_MAX);
|
|
|
|
/* check if user pydir ends with a slash and, if so, remove the slash
|
|
* (for eventual implementations of c library's stat function that might
|
|
* not like it) */
|
|
#ifdef WIN32
|
|
if (upyslen > 3) {
|
|
#else
|
|
if (upyslen > 1) {
|
|
#endif
|
|
if (dirpath[upyslen-1] == '\\' || dirpath[upyslen-1] == '/') {
|
|
dirpath[upyslen-1] = '\0';
|
|
}
|
|
}
|
|
|
|
BLI_convertstringcode(dirpath, G.sce);
|
|
syspath_append(dirpath); /* append to module search path */
|
|
BLI_join_dirfile( modpath, dirpath, "bpymodules" );
|
|
if (BLI_exists(modpath)) syspath_append(modpath);
|
|
}
|
|
|
|
sdir = bpy_gethome(1);
|
|
if (sdir) {
|
|
syspath_append(sdir);
|
|
BLI_make_file_string("/", dirpath, sdir, "bpymodules");
|
|
if (BLI_exists(dirpath)) syspath_append(dirpath);
|
|
}
|
|
|
|
Py_DECREF(mod);
|
|
PyGILState_Release(gilstate);
|
|
}
|
|
|
|
int BPY_path_update( void )
|
|
{
|
|
BPyMenu_RemoveAllEntries(); /* free old data */
|
|
BPY_rebuild_syspath();
|
|
if (BPyMenu_Init(1) == -1) { /* re-eval scripts registration in menus */
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Description: This function finishes Python initialization in Blender.
|
|
|
|
Because U.pythondir (user defined dir for scripts) isn't
|
|
initialized when BPY_start_Python needs to be executed, we
|
|
postpone adding U.pythondir to sys.path and also BPyMenus
|
|
(mechanism to register scripts in Blender menus) for when
|
|
that dir info is available.
|
|
****************************************************************************/
|
|
void BPY_post_start_python( void )
|
|
{
|
|
PyGILState_STATE gilstate = PyGILState_Ensure();
|
|
|
|
BPY_rebuild_syspath();
|
|
BPyMenu_Init( 0 ); /* get dynamic menus (registered scripts) data */
|
|
|
|
PyGILState_Release(gilstate);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Description: This function will return the linenumber on which an error
|
|
* has occurred in the Python script.
|
|
****************************************************************************/
|
|
int BPY_Err_getLinenumber( void )
|
|
{
|
|
return g_script_error.lineno;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Description: This function will return the filename of the python script. */
|
|
/*****************************************************************************/
|
|
const char *BPY_Err_getFilename( void )
|
|
{
|
|
return g_script_error.filename;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Description: Return PyString filename from a traceback object */
|
|
/*****************************************************************************/
|
|
static PyObject *traceback_getFilename( PyObject * tb )
|
|
{
|
|
PyObject *v = NULL;
|
|
|
|
/* co_filename is in f_code, which is in tb_frame, which is in tb */
|
|
|
|
v = PyObject_GetAttrString( tb, "tb_frame" );
|
|
if (v) {
|
|
Py_DECREF( v );
|
|
v = PyObject_GetAttrString( v, "f_code" );
|
|
if (v) {
|
|
Py_DECREF( v );
|
|
v = PyObject_GetAttrString( v, "co_filename" );
|
|
}
|
|
}
|
|
|
|
if (v) return v;
|
|
else return PyString_FromString("unknown");
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Description: Blender Python error handler. This catches the error and
|
|
* stores filename and line number in a global
|
|
*****************************************************************************/
|
|
static void BPY_Err_Handle( char *script_name )
|
|
{
|
|
PyObject *exception, *err, *tb, *v;
|
|
|
|
if( !script_name ) {
|
|
printf( "Error: script has NULL name\n" );
|
|
BPY_Err_Clear();
|
|
return;
|
|
}
|
|
|
|
PyErr_Fetch( &exception, &err, &tb );
|
|
|
|
if (!script_name) script_name = "untitled";
|
|
//if( !exception && !tb ) {
|
|
// printf( "FATAL: spurious exception\n" );
|
|
// return;
|
|
//}
|
|
|
|
strcpy( g_script_error.filename, script_name );
|
|
|
|
if( exception
|
|
&& PyErr_GivenExceptionMatches( exception, PyExc_SyntaxError ) ) {
|
|
/* no traceback available when SyntaxError */
|
|
PyErr_NormalizeException( &exception, &err, &tb );
|
|
PyErr_Restore( exception, err, tb ); /* takes away reference! */
|
|
PyErr_Print( );
|
|
v = PyObject_GetAttrString( err, "lineno" );
|
|
if( v ) {
|
|
g_script_error.lineno = PyInt_AsLong( v );
|
|
Py_DECREF( v );
|
|
} else {
|
|
g_script_error.lineno = -1;
|
|
}
|
|
/* this avoids an abort in Python 2.3's garbage collecting:
|
|
PyErr_Clear() */
|
|
BPY_Err_Clear(); /* Calls PyErr_Clear as well */
|
|
return;
|
|
} else {
|
|
PyErr_NormalizeException( &exception, &err, &tb );
|
|
PyErr_Restore( exception, err, tb ); /* takes away reference! */
|
|
PyErr_Print( );
|
|
tb = PySys_GetObject( "last_traceback" );
|
|
|
|
if( !tb ) {
|
|
printf( "\nCan't get traceback\n" );
|
|
BPY_Err_Clear(); /* incase there is still some data hanging about */
|
|
return;
|
|
}
|
|
|
|
Py_INCREF( tb );
|
|
|
|
/* From old bpython BPY_main.c:
|
|
* 'check traceback objects and look for last traceback in the
|
|
* same text file. This is used to jump to the line of where the
|
|
* error occured. "If the error occured in another text file or module,
|
|
* the last frame in the current file is adressed."'
|
|
*/
|
|
|
|
for(;;) {
|
|
PyObject *py_fn= NULL;
|
|
v = PyObject_GetAttrString( tb, "tb_next" );
|
|
|
|
if( !v || v == Py_None ||
|
|
strcmp(PyString_AsString((py_fn= traceback_getFilename(v))), script_name)) {
|
|
Py_XDECREF(py_fn);
|
|
break;
|
|
}
|
|
Py_XDECREF( py_fn );
|
|
Py_DECREF( tb );
|
|
tb = v;
|
|
}
|
|
|
|
v = PyObject_GetAttrString( tb, "tb_lineno" );
|
|
if (v) {
|
|
g_script_error.lineno = PyInt_AsLong(v);
|
|
Py_DECREF(v);
|
|
}
|
|
v = traceback_getFilename( tb );
|
|
if (v) {
|
|
strncpy( g_script_error.filename, PyString_AsString( v ),
|
|
FILENAME_LENGTH );
|
|
Py_DECREF(v);
|
|
}
|
|
Py_DECREF( tb );
|
|
}
|
|
|
|
BPY_Err_Clear();
|
|
return;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Description: This function executes the script passed by st.
|
|
* Notes: It is called by blender/src/drawtext.c when a Blender user
|
|
* presses ALT+PKEY in the script's text window.
|
|
*****************************************************************************/
|
|
int BPY_txt_do_python_Text( struct Text *text )
|
|
{
|
|
PyObject *py_dict, *py_result;
|
|
BPy_constant *info;
|
|
char textname[24];
|
|
Script *script = G.main->script.first;
|
|
PyGILState_STATE gilstate;
|
|
|
|
if( !text )
|
|
return 0;
|
|
|
|
/* check if this text is already running */
|
|
while( script ) {
|
|
if( !strcmp( script->id.name + 2, text->id.name + 2 ) ) {
|
|
/* if this text is already a running script,
|
|
* just move to it: */
|
|
if (!G.background) {
|
|
SpaceScript *sc;
|
|
newspace( curarea, SPACE_SCRIPT );
|
|
sc = curarea->spacedata.first;
|
|
sc->script = script;
|
|
return 1;
|
|
}
|
|
}
|
|
script = script->id.next;
|
|
}
|
|
|
|
/* Create a new script structure and initialize it: */
|
|
script = alloc_libblock( &G.main->script, ID_SCRIPT, text->id.name+2 );
|
|
|
|
if( !script ) {
|
|
printf( "couldn't allocate memory for Script struct!" );
|
|
return 0;
|
|
}
|
|
|
|
/* if in the script Blender.Load(blendfile) is not the last command,
|
|
* an error after it will call BPY_Err_Handle below, but the text struct
|
|
* will have been deallocated already, so we need to copy its name here.
|
|
*/
|
|
BLI_strncpy( textname, text->id.name+2, 21 );
|
|
|
|
script->id.us = 1;
|
|
script->flags = SCRIPT_RUNNING;
|
|
script->py_draw = NULL;
|
|
script->py_event = NULL;
|
|
script->py_button = NULL;
|
|
script->py_browsercallback = NULL;
|
|
strncpy(script->scriptname, text->id.name+2, sizeof(script->scriptname));
|
|
gilstate = PyGILState_Ensure();
|
|
|
|
py_dict = CreateGlobalDictionary( );
|
|
|
|
if( !setup_armature_weakrefs()){
|
|
printf("Oops - weakref dict\n");
|
|
PyGILState_Release(gilstate);
|
|
return 0;
|
|
}
|
|
|
|
script->py_globaldict = py_dict;
|
|
|
|
info = ( BPy_constant * ) PyConstant_New( );
|
|
if( info ) {
|
|
PyConstant_Insert( info, "name",
|
|
PyString_FromString( script->id.name + 2 ) );
|
|
Py_INCREF( Py_None );
|
|
PyConstant_Insert( info, "arg", Py_None );
|
|
EXPP_dict_set_item_str( py_dict, "__script__",
|
|
( PyObject * ) info );
|
|
}
|
|
|
|
py_result = RunPython( text, py_dict ); /* Run the script */
|
|
|
|
if( !py_result ) { /* Failed execution of the script */
|
|
|
|
BPY_Err_Handle( textname );
|
|
ReleaseGlobalDictionary( py_dict );
|
|
script->py_globaldict = NULL;
|
|
free_libblock( &G.main->script, script );
|
|
PyGILState_Release(gilstate);
|
|
return 0;
|
|
} else {
|
|
Py_DECREF( py_result );
|
|
script->flags &= ~SCRIPT_RUNNING;
|
|
if( !script->flags ) {
|
|
ReleaseGlobalDictionary( py_dict );
|
|
script->py_globaldict = NULL;
|
|
free_libblock( &G.main->script, script );
|
|
}
|
|
}
|
|
|
|
PyGILState_Release(gilstate);
|
|
|
|
return 1; /* normal return */
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Description: Called from command line to run a Python script
|
|
* automatically. The script can be a file or a Blender Text in the current
|
|
* .blend.
|
|
****************************************************************************/
|
|
void BPY_run_python_script( const char *fn )
|
|
{
|
|
char filename[FILE_MAXDIR + FILE_MAXFILE];
|
|
Text *text = NULL;
|
|
int is_blender_text = 0;
|
|
|
|
BLI_strncpy(filename, fn, FILE_MAXDIR + FILE_MAXFILE);
|
|
|
|
if (!BLI_exists(filename))
|
|
BLI_convertstringcwd(filename);
|
|
|
|
if (!BLI_exists(filename)) { /* if there's no such filename ... */
|
|
/* try an already existing Blender Text.
|
|
* use 'fn' rather then filename for this since were looking for
|
|
* internal text
|
|
*/
|
|
text = G.main->text.first;
|
|
|
|
while (text) {
|
|
if (!strcmp(fn, text->id.name + 2)) break;
|
|
text = text->id.next;
|
|
}
|
|
|
|
if (text == NULL) {
|
|
printf("\nError: no such file or Blender text -- %s.\n", fn);
|
|
return;
|
|
}
|
|
else is_blender_text = 1; /* fn is already a Blender Text */
|
|
}
|
|
|
|
else {
|
|
/* use filename here since we know it exists,
|
|
* 'fn' may have been a relative path
|
|
*/
|
|
text = add_text(filename);
|
|
|
|
if (text == NULL) {
|
|
printf("\nError in BPY_run_python_script:\n"
|
|
"couldn't create Blender text from \"%s\"\n", filename);
|
|
/* Chris: On Windows if I continue I just get a segmentation
|
|
* violation. To get a baseline file I exit here. */
|
|
exit(2);
|
|
/* return; */
|
|
}
|
|
}
|
|
|
|
if (BPY_txt_do_python_Text(text) != 1) {
|
|
printf("\nError executing Python script from command-line:\n"
|
|
"%s (at line %d).\n", fn, BPY_Err_getLinenumber());
|
|
}
|
|
|
|
if (!is_blender_text) {
|
|
/* We can't simply free the text, since the script might have called
|
|
* Blender.Load() to load a new .blend, freeing previous data.
|
|
* So we check if the pointer is still valid. */
|
|
if (BLI_findindex(&G.main->text, text) != -1) {
|
|
free_libblock(&G.main->text, text);
|
|
}
|
|
}
|
|
}
|
|
|
|
int BPY_run_script(Script *script)
|
|
{
|
|
PyObject *py_dict, *py_res, *pyarg;
|
|
Text *text = NULL;
|
|
BPy_constant *info;
|
|
|
|
PyGILState_STATE gilstate = PyGILState_Ensure();
|
|
|
|
if (!BLI_exists(script->scriptname)) {
|
|
/* The file dosnt exist, maybe this blend file was made on some other persons computer? */
|
|
char fname[FILE_MAX];
|
|
char fpath[FILE_MAX];
|
|
char ftmp[FILE_MAX];
|
|
char *bpyhome = bpy_gethome(1);
|
|
|
|
if (bpyhome) {
|
|
BLI_strncpy(ftmp, script->scriptname, sizeof(ftmp));
|
|
BLI_split_dirfile_basic(ftmp, NULL, fname); /* get the filename only - fname */
|
|
BLI_strncpy(fpath, bpy_gethome(1), sizeof(fpath));
|
|
BLI_add_slash(fpath);
|
|
strcat(fpath, fname);
|
|
|
|
if (BLI_exists(fpath)) {
|
|
strncpy(script->scriptname, fpath, sizeof(script->scriptname));
|
|
} else {
|
|
bpyhome = NULL; /* a bit dodgy, this is so the line below runs */
|
|
}
|
|
}
|
|
|
|
if (bpyhome == NULL && U.pythondir[0]) {
|
|
BLI_make_file_string("/", fpath, U.pythondir, fname);
|
|
if (BLI_exists(fpath)) {
|
|
strncpy(script->scriptname, fpath, sizeof(script->scriptname));
|
|
}
|
|
}
|
|
|
|
/* cant find the file?, fallback to text block */
|
|
if (!BLI_exists(script->scriptname)) {
|
|
for (text=G.main->text.first; text; text=text->id.next) {
|
|
if (strcmp(script->scriptname, text->id.name+2)==0) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (text) {
|
|
Py_INCREF( Py_None );
|
|
pyarg = Py_None;
|
|
} else {
|
|
if (!BLI_exists(script->scriptname)) {
|
|
printf( "Script does not exist %s\n", script->scriptname );
|
|
free_libblock( &G.main->script, script );
|
|
PyGILState_Release(gilstate);
|
|
return 0;
|
|
}
|
|
|
|
if( script->scriptarg[0] == '\0' ) { /* no submenus */
|
|
Py_INCREF( Py_None );
|
|
pyarg = Py_None;
|
|
} else {
|
|
pyarg = PyString_FromString( script->scriptarg );
|
|
}
|
|
}
|
|
|
|
script->id.us = 1;
|
|
script->flags = SCRIPT_RUNNING;
|
|
script->py_draw = NULL;
|
|
script->py_event = NULL;
|
|
script->py_button = NULL;
|
|
script->py_browsercallback = NULL;
|
|
|
|
py_dict = CreateGlobalDictionary( );
|
|
|
|
script->py_globaldict = py_dict;
|
|
|
|
if( !setup_armature_weakrefs()){
|
|
printf("Oops - weakref dict\n");
|
|
free_libblock( &G.main->script, script );
|
|
ReleaseGlobalDictionary( py_dict );
|
|
PyGILState_Release(gilstate);
|
|
return 0;
|
|
}
|
|
|
|
info = ( BPy_constant * ) PyConstant_New( );
|
|
if( info ) {
|
|
PyConstant_Insert( info, "name",
|
|
PyString_FromString( script->id.name + 2 ) );
|
|
PyConstant_Insert( info, "arg", pyarg );
|
|
EXPP_dict_set_item_str( py_dict, "__script__",
|
|
( PyObject * ) info );
|
|
}
|
|
|
|
if (text) {
|
|
py_res = RunPython( text, py_dict );
|
|
} else {
|
|
char pystring[sizeof(script->scriptname) + 15];
|
|
sprintf(pystring, "execfile(r'%s')", script->scriptname);
|
|
py_res = PyRun_String( pystring, Py_file_input, py_dict, py_dict );
|
|
}
|
|
|
|
if( !py_res ) { /* Failed execution of the script */
|
|
/* Previously we used PyRun_File to run directly the code on a FILE
|
|
* object, but as written in the Python/C API Ref Manual, chapter 2,
|
|
* 'FILE structs for different C libraries can be different and
|
|
* incompatible'.
|
|
* So now we load the script file data to a buffer */
|
|
BPY_Err_Handle( script->id.name + 2 );
|
|
ReleaseGlobalDictionary( py_dict );
|
|
script->py_globaldict = NULL;
|
|
free_libblock( &G.main->script, script );
|
|
error_pyscript( );
|
|
|
|
PyGILState_Release(gilstate);
|
|
return 0;
|
|
} else {
|
|
Py_DECREF( py_res );
|
|
script->flags &= ~SCRIPT_RUNNING;
|
|
|
|
if( !script->flags ) {
|
|
ReleaseGlobalDictionary( py_dict );
|
|
script->py_globaldict = NULL;
|
|
free_libblock( &G.main->script, script );
|
|
|
|
/* special case: called from the menu in the Scripts window
|
|
* we have to change sc->script pointer, since it'll be freed here.*/
|
|
if (!G.background) {
|
|
if( curarea->spacetype == SPACE_SCRIPT ) {
|
|
SpaceScript *sc = curarea->spacedata.first;
|
|
sc->script = G.main->script.first; /* can be null, which is ok ... */
|
|
/* ... meaning no other script is running right now. */
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
PyGILState_Release(gilstate);
|
|
return 1;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Description: This function executes the script chosen from a menu.
|
|
* Notes: It is called by the ui code in src/header_???.c when a user
|
|
* clicks on a menu entry that refers to a script.
|
|
* Scripts are searched in the BPyMenuTable, using the given
|
|
* menutype and event values to know which one was chosen.
|
|
*****************************************************************************/
|
|
int BPY_menu_do_python( short menutype, int event )
|
|
{
|
|
BPyMenu *pym;
|
|
pym = BPyMenu_GetEntry( menutype, ( short ) event );
|
|
return BPY_menu_invoke( pym, menutype );
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Description: This function executes the script by its shortcut.
|
|
* Notes: It is called by the ui code in src/???.c when a user presses an
|
|
* unassigned key combination. Scripts are searched in the BPyMenuTable,
|
|
* using the given menutype and event values to know which one to invoke.
|
|
*****************************************************************************/
|
|
int BPY_menu_do_shortcut( short menutype, unsigned short key, unsigned short qual )
|
|
{
|
|
BPyMenu *pym;
|
|
pym = BPyMenu_GetEntry( menutype, 0 );
|
|
|
|
while ( pym ) {
|
|
if ( pym->key && pym->key == key && pym->qual == qual ) {
|
|
return BPY_menu_invoke( pym, menutype );
|
|
}
|
|
pym = pym->next;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Description: This function executes the script described by a menu item.
|
|
*****************************************************************************/
|
|
int BPY_menu_invoke( BPyMenu *pym, short menutype )
|
|
{
|
|
char *argstr = NULL;
|
|
BPySubMenu *pysm;
|
|
char scriptname[21];
|
|
Script *script = NULL;
|
|
int ret, len;
|
|
PyGILState_STATE gilstate;
|
|
char filestr[FILE_MAX];
|
|
|
|
if( !pym )
|
|
return 0;
|
|
|
|
gilstate = PyGILState_Ensure();
|
|
|
|
if( pym->version > G.version )
|
|
notice( "Version mismatch: script was written for Blender %d. "
|
|
"It may fail with yours: %d.", pym->version,
|
|
G.version );
|
|
|
|
/* if there are submenus, let the user choose one from a pupmenu that we
|
|
* create here.*/
|
|
pysm = pym->submenus;
|
|
if( pysm ) {
|
|
char *pupstr;
|
|
int arg;
|
|
|
|
pupstr = BPyMenu_CreatePupmenuStr( pym, menutype );
|
|
|
|
if( pupstr ) {
|
|
arg = pupmenu( pupstr );
|
|
MEM_freeN( pupstr );
|
|
|
|
if( arg >= 0 ) {
|
|
while( arg-- )
|
|
pysm = pysm->next;
|
|
argstr = pysm->arg;
|
|
} else {
|
|
PyGILState_Release(gilstate);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( pym->dir ) { /* script is in U.pythondir */
|
|
char upythondir[FILE_MAX];
|
|
|
|
/* dirs in Blender can be "//", which has a special meaning */
|
|
BLI_strncpy(upythondir, U.pythondir, FILE_MAX);
|
|
BLI_convertstringcode(upythondir, G.sce); /* if so, this expands it */
|
|
BLI_make_file_string( "/", filestr, upythondir, pym->filename );
|
|
}
|
|
else { /* script is in default scripts dir */
|
|
char *scriptsdir = bpy_gethome(1);
|
|
|
|
if (!scriptsdir) {
|
|
printf("Error loading script: can't find default scripts dir!");
|
|
PyGILState_Release(gilstate);
|
|
return 0;
|
|
}
|
|
|
|
BLI_make_file_string( "/", filestr, scriptsdir, pym->filename );
|
|
}
|
|
|
|
BLI_strncpy(scriptname, pym->name, 21);
|
|
len = strlen(scriptname) - 1;
|
|
/* by convention, scripts that open the file browser or have submenus
|
|
* display '...'. Here we remove them from the datablock name */
|
|
while ((len > 0) && scriptname[len] == '.') {
|
|
scriptname[len] = '\0';
|
|
len--;
|
|
}
|
|
|
|
/* Create a new script structure and initialize it: */
|
|
script = alloc_libblock( &G.main->script, ID_SCRIPT, scriptname );
|
|
|
|
if( !script ) {
|
|
printf( "couldn't allocate memory for Script struct!" );
|
|
PyGILState_Release(gilstate);
|
|
return 0;
|
|
}
|
|
|
|
/* let's find a proper area for an eventual script gui:
|
|
* (still experimenting here, need definition on which win
|
|
* each group will be put to code this properly) */
|
|
switch ( menutype ) {
|
|
|
|
case PYMENU_IMPORT: /* first 4 were handled in header_info.c */
|
|
case PYMENU_EXPORT:
|
|
case PYMENU_HELP:
|
|
case PYMENU_RENDER:
|
|
case PYMENU_WIZARDS:
|
|
case PYMENU_SCRIPTTEMPLATE:
|
|
case PYMENU_TEXTPLUGIN:
|
|
case PYMENU_MESHFACEKEY:
|
|
break;
|
|
|
|
default:
|
|
if( curarea->spacetype != SPACE_SCRIPT ) {
|
|
ScrArea *sa = NULL;
|
|
|
|
sa = find_biggest_area_of_type( SPACE_BUTS );
|
|
if( sa ) {
|
|
if( ( 1.5 * sa->winx ) < sa->winy )
|
|
sa = NULL; /* too narrow? */
|
|
}
|
|
|
|
if( !sa )
|
|
sa = find_biggest_area_of_type( SPACE_SCRIPT );
|
|
if( !sa )
|
|
sa = find_biggest_area_of_type( SPACE_TEXT );
|
|
if( !sa )
|
|
sa = find_biggest_area_of_type( SPACE_IMAGE ); /* group UV */
|
|
if( !sa )
|
|
sa = find_biggest_area_of_type( SPACE_VIEW3D );
|
|
|
|
if( !sa )
|
|
sa = find_biggest_area( );
|
|
|
|
areawinset( sa->win );
|
|
}
|
|
break;
|
|
}
|
|
|
|
strncpy(script->scriptname, filestr, sizeof(script->scriptname));
|
|
if (argstr!=NULL && argstr[0] != '\0')
|
|
strncpy(script->scriptarg, argstr, sizeof(script->scriptarg));
|
|
|
|
ret = BPY_run_script(script);
|
|
|
|
return 1; /* normal return */
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Description:
|
|
* Notes:
|
|
*****************************************************************************/
|
|
void BPY_free_compiled_text( struct Text *text )
|
|
{
|
|
if( text->compiled ) {
|
|
Py_DECREF( ( PyObject * ) text->compiled );
|
|
text->compiled = NULL;
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Description: This function frees a finished (flags == 0) script.
|
|
*****************************************************************************/
|
|
void BPY_free_finished_script( Script * script )
|
|
{
|
|
PyGILState_STATE gilstate;
|
|
|
|
if( !script )
|
|
return;
|
|
|
|
gilstate = PyGILState_Ensure();
|
|
|
|
if( PyErr_Occurred( ) ) { /* if script ended after filesel */
|
|
PyErr_Print( ); /* eventual errors are handled now */
|
|
BPY_Err_Clear( );
|
|
error_pyscript( );
|
|
}
|
|
|
|
PyGILState_Release(gilstate);
|
|
|
|
free_libblock( &G.main->script, script );
|
|
return;
|
|
}
|
|
|
|
static void unlink_script( Script * script )
|
|
{ /* copied from unlink_text in drawtext.c */
|
|
bScreen *scr;
|
|
ScrArea *area;
|
|
SpaceLink *sl;
|
|
|
|
for( scr = G.main->screen.first; scr; scr = scr->id.next ) {
|
|
for( area = scr->areabase.first; area; area = area->next ) {
|
|
for( sl = area->spacedata.first; sl; sl = sl->next ) {
|
|
if( sl->spacetype == SPACE_SCRIPT ) {
|
|
SpaceScript *sc = ( SpaceScript * ) sl;
|
|
|
|
if( sc->script == script ) {
|
|
sc->script = NULL;
|
|
|
|
if( sc == area->spacedata.first ) {
|
|
scrarea_queue_redraw( area );
|
|
}
|
|
|
|
if (sc->but_refs) {
|
|
BPy_Set_DrawButtonsList(sc->but_refs);
|
|
BPy_Free_DrawButtonsList();
|
|
sc->but_refs = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* This is called from free_libblock( &G.main->script, script ); */
|
|
void BPY_clear_script( Script * script )
|
|
{
|
|
PyObject *dict;
|
|
PyGILState_STATE gilstate;
|
|
|
|
if( !script )
|
|
return;
|
|
|
|
gilstate = PyGILState_Ensure();
|
|
|
|
if (!Py_IsInitialized()) {
|
|
printf("\nError: trying to free script data after finalizing Python!");
|
|
printf("\nScript name: %s\n", script->id.name+2);
|
|
PyGILState_Release(gilstate);
|
|
return;
|
|
}
|
|
|
|
Py_XDECREF( ( PyObject * ) script->py_draw );
|
|
Py_XDECREF( ( PyObject * ) script->py_event );
|
|
Py_XDECREF( ( PyObject * ) script->py_button );
|
|
Py_XDECREF( ( PyObject * ) script->py_browsercallback );
|
|
script->py_draw = NULL;
|
|
script->py_event = NULL;
|
|
script->py_button = NULL;
|
|
script->py_browsercallback = NULL;
|
|
script->scriptname[0] = '\0';
|
|
script->scriptarg[0] = '\0';
|
|
|
|
dict = script->py_globaldict;
|
|
|
|
if( dict ) {
|
|
PyDict_Clear( dict );
|
|
Py_DECREF( dict ); /* Release dictionary. */
|
|
script->py_globaldict = NULL;
|
|
}
|
|
|
|
PyGILState_Release(gilstate);
|
|
|
|
unlink_script( script );
|
|
}
|
|
|
|
/* PyDrivers */
|
|
|
|
/* PyDrivers are Ipo Drivers governed by expressions written in Python.
|
|
* Expressions here are one-liners that evaluate to a float value. */
|
|
|
|
/* For faster execution we keep a special dictionary for pydrivers, with
|
|
* the needed modules and aliases. */
|
|
static int bpy_pydriver_create_dict(void)
|
|
{
|
|
PyObject *d, *mod;
|
|
|
|
if (bpy_pydriver_Dict) return -1;
|
|
|
|
d = PyDict_New();
|
|
if (!d) return -1;
|
|
|
|
bpy_pydriver_Dict = d;
|
|
|
|
/* import some modules: builtins, Blender, math, Blender.noise */
|
|
|
|
PyDict_SetItemString(d, "__builtins__", PyEval_GetBuiltins()); /* borrow ref, ok */
|
|
|
|
mod = PyImport_ImportModule("Blender");
|
|
if (mod) {
|
|
PyDict_SetItemString(d, "Blender", mod);
|
|
PyDict_SetItemString(d, "b", mod);
|
|
Py_DECREF(mod); /* 2 refs above are cleared with the dict, only decref the ref from PyImport_ImportModule */
|
|
} else {
|
|
BPY_Err_Clear();
|
|
}
|
|
|
|
mod = PyImport_ImportModule("math");
|
|
if (mod) {
|
|
PyDict_Merge(d, PyModule_GetDict(mod), 0); /* 0 - dont overwrite existing values */
|
|
|
|
/* Only keep for backwards compat! - just import all math into root, they are standard */
|
|
PyDict_SetItemString(d, "math", mod);
|
|
PyDict_SetItemString(d, "m", mod);
|
|
Py_DECREF(mod); /* 2 refs above are cleared with the dict, only decref the ref from PyImport_ImportModule */
|
|
} else {
|
|
BPY_Err_Clear();
|
|
}
|
|
|
|
mod = PyImport_ImportModule("Blender.Noise");
|
|
if (mod) {
|
|
PyDict_SetItemString(d, "noise", mod);
|
|
PyDict_SetItemString(d, "n", mod);
|
|
Py_DECREF(mod); /* 2 refs above are cleared with the dict, only decref the ref from PyImport_ImportModule */
|
|
} else {
|
|
BPY_Err_Clear();
|
|
}
|
|
|
|
/* If there's a Blender text called pydrivers.py, import it.
|
|
* Users can add their own functions to this module. */
|
|
if (G.f&G_DOSCRIPTLINKS) {
|
|
int found; /* not used but needed as an arg */
|
|
mod = bpy_text_import("pydrivers", &found); /* can also use PyImport_Import() */
|
|
if (mod) {
|
|
PyDict_SetItemString(d, "pydrivers", mod);
|
|
PyDict_SetItemString(d, "p", mod);
|
|
Py_DECREF(mod); /* 2 refs above are cleared with the dict, only decref the ref from PyImport_ImportModule */
|
|
} else {
|
|
BPY_Err_Clear();
|
|
}
|
|
}
|
|
/* short aliases for some Get() functions: */
|
|
|
|
/* ob(obname) == Blender.Object.Get(obname) */
|
|
mod = PyImport_ImportModule("Blender.Object");
|
|
if (mod) {
|
|
PyObject *fcn = PyObject_GetAttrString(mod, "Get");
|
|
Py_DECREF(mod);
|
|
if (fcn) {
|
|
PyDict_SetItemString(d, "ob", fcn);
|
|
Py_DECREF(fcn);
|
|
}
|
|
} else {
|
|
BPY_Err_Clear();
|
|
}
|
|
|
|
/* TODO - change these */
|
|
/* me(meshname) == Blender.Mesh.Get(meshname) */
|
|
mod = PyImport_ImportModule("Blender.Mesh");
|
|
if (mod) {
|
|
PyObject *fcn = PyObject_GetAttrString(mod, "Get");
|
|
Py_DECREF(mod);
|
|
if (fcn) {
|
|
PyDict_SetItemString(d, "me", fcn);
|
|
Py_DECREF(fcn);
|
|
}
|
|
} else {
|
|
BPY_Err_Clear();
|
|
}
|
|
|
|
/* ma(matname) == Blender.Material.Get(matname) */
|
|
mod = PyImport_ImportModule("Blender.Material");
|
|
if (mod) {
|
|
PyObject *fcn = PyObject_GetAttrString(mod, "Get");
|
|
Py_DECREF(mod);
|
|
if (fcn) {
|
|
PyDict_SetItemString(d, "ma", fcn);
|
|
Py_DECREF(fcn);
|
|
}
|
|
} else {
|
|
BPY_Err_Clear();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* error return function for BPY_eval_pydriver */
|
|
static float pydriver_error(IpoDriver *driver) {
|
|
|
|
if (bpy_pydriver_oblist)
|
|
bpy_pydriver_freeList();
|
|
|
|
if (bpy_pydriver_Dict) { /* free the global dict used by pydrivers */
|
|
PyDict_Clear(bpy_pydriver_Dict);
|
|
Py_DECREF(bpy_pydriver_Dict);
|
|
bpy_pydriver_Dict = NULL;
|
|
}
|
|
|
|
driver->flag |= IPO_DRIVER_FLAG_INVALID; /* py expression failed */
|
|
|
|
if (driver->ob)
|
|
fprintf(stderr, "\nError in Ipo Driver: Object %s\nThis is the failed Python expression:\n'%s'\n\n", driver->ob->id.name+2, driver->name);
|
|
else
|
|
fprintf(stderr, "\nError in Ipo Driver: No Object\nThis is the failed Python expression:\n'%s'\n\n", driver->name);
|
|
|
|
PyErr_Print();
|
|
BPY_Err_Clear();
|
|
|
|
return 0.0f;
|
|
}
|
|
|
|
|
|
/********PyConstraints*********/
|
|
|
|
/* This function checks whether a text-buffer is a PyConstraint candidate.
|
|
* It uses simple text parsing that could be easily confused!
|
|
*/
|
|
int BPY_is_pyconstraint(Text *text)
|
|
{
|
|
TextLine *tline = text->lines.first;
|
|
|
|
if (tline && (tline->len > 10)) {
|
|
char *line = tline->line;
|
|
|
|
/* Expected format: #BPYCONSTRAINT
|
|
* The actual checks are forgiving, so slight variations also work. */
|
|
if (line && line[0] == '#' && strstr(line, "BPYCONSTRAINT")) return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* This function frees links from pyconstraints to a given text-buffer.
|
|
* Used when a text-buffer is unlinked!
|
|
*/
|
|
void BPY_free_pyconstraint_links(Text *text)
|
|
{
|
|
Object *ob;
|
|
bConstraint *con;
|
|
short update;
|
|
|
|
/*check all pyconstraints*/
|
|
for (ob=G.main->object.first; ob; ob=ob->id.next) {
|
|
update = 0;
|
|
if(ob->type==OB_ARMATURE && ob->pose) {
|
|
bPoseChannel *pchan;
|
|
for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
|
|
for (con = pchan->constraints.first; con; con=con->next) {
|
|
if (con->type==CONSTRAINT_TYPE_PYTHON) {
|
|
bPythonConstraint *data = con->data;
|
|
if (data->text==text) data->text = NULL;
|
|
update = 1;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (con = ob->constraints.first; con; con=con->next) {
|
|
if (con->type==CONSTRAINT_TYPE_PYTHON) {
|
|
bPythonConstraint *data = con->data;
|
|
if (data->text==text) data->text = NULL;
|
|
update = 1;
|
|
}
|
|
}
|
|
|
|
if (update) {
|
|
DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* This function is called to update PyConstraint data so that it is compatible with the script.
|
|
* Some of the allocating/freeing of memory for constraint targets occurs here, espcially
|
|
* if the number of targets changes.
|
|
*/
|
|
void BPY_pyconstraint_update(Object *owner, bConstraint *con)
|
|
{
|
|
bPythonConstraint *data= con->data;
|
|
|
|
if (data->text) {
|
|
/* script does exist. it is assumed that this is a valid pyconstraint script */
|
|
PyObject *globals;
|
|
PyObject *retval, *gval;
|
|
PyGILState_STATE gilstate;
|
|
int num, i;
|
|
|
|
/* clear the relevant flags first */
|
|
data->flag = 0;
|
|
|
|
gilstate = PyGILState_Ensure();
|
|
|
|
/* populate globals dictionary */
|
|
globals = CreateGlobalDictionary();
|
|
retval = RunPython(data->text, globals);
|
|
|
|
if (retval == NULL) {
|
|
BPY_Err_Handle(data->text->id.name);
|
|
ReleaseGlobalDictionary(globals);
|
|
data->flag |= PYCON_SCRIPTERROR;
|
|
PyGILState_Release(gilstate);
|
|
return;
|
|
}
|
|
|
|
Py_DECREF(retval);
|
|
retval = NULL;
|
|
|
|
/* try to find NUM_TARGETS */
|
|
gval = PyDict_GetItemString(globals, "NUM_TARGETS");
|
|
if ( (gval) && (num= PyInt_AsLong(gval)) ) {
|
|
/* NUM_TARGETS is defined... and non-zero */
|
|
bConstraintTarget *ct;
|
|
|
|
/* check if it is valid (just make sure it is not negative)
|
|
* TODO: PyInt_AsLong may return -1 as sign of invalid input...
|
|
*/
|
|
num = abs(num);
|
|
data->flag |= PYCON_USETARGETS;
|
|
|
|
/* check if the number of targets has changed */
|
|
if (num < data->tarnum) {
|
|
/* free a few targets */
|
|
num= data->tarnum - num;
|
|
for (i = 0; i < num; i++, data->tarnum--) {
|
|
ct= data->targets.last;
|
|
BLI_freelinkN(&data->targets, ct);
|
|
}
|
|
}
|
|
else if (num > data->tarnum) {
|
|
/* add a few targets */
|
|
num = num - data->tarnum;
|
|
for (i = 0; i < num; i++, data->tarnum++) {
|
|
ct= MEM_callocN(sizeof(bConstraintTarget), "PyConTarget");
|
|
BLI_addtail(&data->targets, ct);
|
|
}
|
|
}
|
|
|
|
/* validate targets */
|
|
con->flag &= ~CONSTRAINT_DISABLE;
|
|
for (ct= data->targets.first; ct; ct= ct->next) {
|
|
if (!exist_object(ct->tar)) {
|
|
ct->tar = NULL;
|
|
con->flag |= CONSTRAINT_DISABLE;
|
|
break;
|
|
}
|
|
|
|
if ((ct->tar == owner) && (ct->subtarget[0] != 0)) {
|
|
if (get_named_bone(get_armature(owner), ct->subtarget) == NULL) {
|
|
con->flag |= CONSTRAINT_DISABLE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* clear globals */
|
|
ReleaseGlobalDictionary(globals);
|
|
|
|
PyGILState_Release(gilstate);
|
|
|
|
return;
|
|
}
|
|
else {
|
|
/* NUM_TARGETS is not defined or equals 0 */
|
|
ReleaseGlobalDictionary(globals);
|
|
|
|
/* free all targets */
|
|
BLI_freelistN(&data->targets);
|
|
data->tarnum = 0;
|
|
data->flag &= ~PYCON_USETARGETS;
|
|
|
|
PyGILState_Release(gilstate);
|
|
|
|
return;
|
|
}
|
|
}
|
|
else {
|
|
/* no script, so clear any settings/data now */
|
|
data->tarnum = 0;
|
|
data->flag = 0;
|
|
con->flag &= ~CONSTRAINT_DISABLE;
|
|
|
|
BLI_freelistN(&data->targets);
|
|
|
|
/* supposedly this should still leave the base struct... */
|
|
IDP_FreeProperty(data->prop);
|
|
}
|
|
}
|
|
|
|
/* PyConstraints Evaluation Function (only called from evaluate_constraint)
|
|
* This function is responsible for modifying the ownermat that it is passed.
|
|
*/
|
|
void BPY_pyconstraint_eval(bPythonConstraint *con, bConstraintOb *cob, ListBase *targets)
|
|
{
|
|
PyObject *srcmat, *tarmat, *tarmats, *idprop;
|
|
PyObject *globals;
|
|
PyObject *gval;
|
|
PyObject *pyargs, *retval;
|
|
bConstraintTarget *ct;
|
|
MatrixObject *retmat;
|
|
int row, col, index;
|
|
PyGILState_STATE gilstate;
|
|
|
|
if (!con->text) return;
|
|
if (con->flag & PYCON_SCRIPTERROR) return;
|
|
|
|
gilstate = PyGILState_Ensure();
|
|
|
|
globals = CreateGlobalDictionary();
|
|
|
|
/* wrap blender-data as PyObjects for evaluation
|
|
* - we expose the owner's matrix as pymatrix
|
|
* - id-properties are wrapped using the id-properties pyapi
|
|
* - targets are presented as a list of matrices
|
|
*/
|
|
srcmat = newMatrixObject((float *)cob->matrix, 4, 4, Py_NEW);
|
|
idprop = BPy_Wrap_IDProperty(NULL, con->prop, NULL);
|
|
|
|
tarmats= PyList_New(con->tarnum);
|
|
for (ct=targets->first, index=0; ct; ct=ct->next, index++) {
|
|
tarmat = newMatrixObject((float *)ct->matrix, 4, 4, Py_NEW);
|
|
PyList_SET_ITEM(tarmats, index, tarmat);
|
|
}
|
|
|
|
if (!setup_armature_weakrefs()) {
|
|
fprintf(stderr, "Oops - weakref dict setup\n");
|
|
PyGILState_Release(gilstate);
|
|
|
|
return;
|
|
}
|
|
|
|
retval = RunPython(con->text, globals);
|
|
|
|
if (retval == NULL) {
|
|
BPY_Err_Handle(con->text->id.name);
|
|
con->flag |= PYCON_SCRIPTERROR;
|
|
|
|
/* free temp objects */
|
|
Py_DECREF(idprop);
|
|
Py_DECREF(srcmat);
|
|
Py_DECREF(tarmats);
|
|
|
|
ReleaseGlobalDictionary(globals);
|
|
|
|
PyGILState_Release(gilstate);
|
|
|
|
return;
|
|
}
|
|
|
|
Py_DECREF( retval );
|
|
retval = NULL;
|
|
|
|
gval = PyDict_GetItemString(globals, "doConstraint");
|
|
if (!gval) {
|
|
printf("ERROR: no doConstraint function in constraint!\n");
|
|
|
|
/* free temp objects */
|
|
Py_DECREF(idprop);
|
|
Py_DECREF(srcmat);
|
|
Py_DECREF(tarmats);
|
|
|
|
ReleaseGlobalDictionary(globals);
|
|
|
|
PyGILState_Release(gilstate);
|
|
|
|
return;
|
|
}
|
|
|
|
/* Now for the fun part! Try and find the functions we need. */
|
|
if (PyFunction_Check(gval)) {
|
|
pyargs = Py_BuildValue("OOO", srcmat, tarmats, idprop);
|
|
retval = PyObject_CallObject(gval, pyargs);
|
|
Py_DECREF(pyargs);
|
|
}
|
|
else {
|
|
printf("ERROR: doConstraint is supposed to be a function!\n");
|
|
con->flag |= PYCON_SCRIPTERROR;
|
|
|
|
Py_DECREF(idprop);
|
|
Py_DECREF(srcmat);
|
|
Py_DECREF(tarmats);
|
|
|
|
ReleaseGlobalDictionary(globals);
|
|
|
|
PyGILState_Release(gilstate);
|
|
|
|
return;
|
|
}
|
|
|
|
if (!retval) {
|
|
BPY_Err_Handle(con->text->id.name);
|
|
con->flag |= PYCON_SCRIPTERROR;
|
|
|
|
/* free temp objects */
|
|
Py_DECREF(idprop);
|
|
Py_DECREF(srcmat);
|
|
Py_DECREF(tarmats);
|
|
|
|
ReleaseGlobalDictionary(globals);
|
|
|
|
PyGILState_Release(gilstate);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
if (!PyObject_TypeCheck(retval, &matrix_Type)) {
|
|
printf("Error in PyConstraint - doConstraint: Function not returning a matrix!\n");
|
|
con->flag |= PYCON_SCRIPTERROR;
|
|
|
|
Py_DECREF(idprop);
|
|
Py_DECREF(srcmat);
|
|
Py_DECREF(tarmats);
|
|
Py_DECREF(retval);
|
|
|
|
ReleaseGlobalDictionary(globals);
|
|
|
|
PyGILState_Release(gilstate);
|
|
|
|
return;
|
|
}
|
|
|
|
retmat = (MatrixObject *)retval;
|
|
if (retmat->rowSize != 4 || retmat->colSize != 4) {
|
|
printf("Error in PyConstraint - doConstraint: Matrix returned is the wrong size!\n");
|
|
con->flag |= PYCON_SCRIPTERROR;
|
|
|
|
Py_DECREF(idprop);
|
|
Py_DECREF(srcmat);
|
|
Py_DECREF(tarmats);
|
|
Py_DECREF(retval);
|
|
|
|
ReleaseGlobalDictionary(globals);
|
|
|
|
PyGILState_Release(gilstate);
|
|
|
|
return;
|
|
}
|
|
|
|
/* this is the reverse of code taken from newMatrix() */
|
|
for(row = 0; row < 4; row++) {
|
|
for(col = 0; col < 4; col++) {
|
|
cob->matrix[row][col] = retmat->contigPtr[row*4+col];
|
|
}
|
|
}
|
|
|
|
/* free temp objects */
|
|
Py_DECREF(idprop);
|
|
Py_DECREF(srcmat);
|
|
Py_DECREF(tarmats);
|
|
Py_DECREF(retval);
|
|
|
|
/* clear globals */
|
|
ReleaseGlobalDictionary(globals);
|
|
|
|
PyGILState_Release(gilstate);
|
|
}
|
|
|
|
/* This evaluates the target matrix for each target the PyConstraint uses.
|
|
* NOTE: it only does one target at a time!
|
|
*/
|
|
void BPY_pyconstraint_target(bPythonConstraint *con, bConstraintTarget *ct)
|
|
{
|
|
PyObject *tar, *subtar;
|
|
PyObject *tarmat, *idprop;
|
|
PyObject *globals;
|
|
PyObject *gval;
|
|
PyObject *pyargs, *retval;
|
|
MatrixObject *retmat;
|
|
int row, col;
|
|
PyGILState_STATE gilstate;
|
|
|
|
if (!con->text) return;
|
|
if (con->flag & PYCON_SCRIPTERROR) return;
|
|
if (!ct) return;
|
|
|
|
gilstate = PyGILState_Ensure();
|
|
|
|
globals = CreateGlobalDictionary();
|
|
|
|
tar = Object_CreatePyObject(ct->tar);
|
|
if ((ct->tar) && (ct->tar->type==OB_ARMATURE)) {
|
|
bPoseChannel *pchan;
|
|
pchan = get_pose_channel(ct->tar->pose, ct->subtarget);
|
|
subtar = PyPoseBone_FromPosechannel(pchan);
|
|
}
|
|
else
|
|
subtar = PyString_FromString(ct->subtarget);
|
|
|
|
tarmat = newMatrixObject((float *)ct->matrix, 4, 4, Py_NEW);
|
|
idprop = BPy_Wrap_IDProperty( NULL, con->prop, NULL);
|
|
|
|
if (!setup_armature_weakrefs()) {
|
|
fprintf(stderr, "Oops - weakref dict setup\n");
|
|
PyGILState_Release(gilstate);
|
|
return;
|
|
}
|
|
|
|
retval = RunPython(con->text, globals);
|
|
|
|
if (retval == NULL) {
|
|
BPY_Err_Handle(con->text->id.name);
|
|
con->flag |= PYCON_SCRIPTERROR;
|
|
|
|
/* free temp objects */
|
|
Py_DECREF(tar);
|
|
Py_DECREF(subtar);
|
|
Py_DECREF(idprop);
|
|
Py_DECREF(tarmat);
|
|
|
|
ReleaseGlobalDictionary(globals);
|
|
|
|
PyGILState_Release(gilstate);
|
|
|
|
return;
|
|
}
|
|
|
|
Py_DECREF(retval);
|
|
retval = NULL;
|
|
|
|
/* try to find doTarget function to set the target matrix */
|
|
gval = PyDict_GetItemString(globals, "doTarget");
|
|
if (!gval) {
|
|
/* free temp objects */
|
|
Py_DECREF(tar);
|
|
Py_DECREF(subtar);
|
|
Py_DECREF(idprop);
|
|
Py_DECREF(tarmat);
|
|
|
|
ReleaseGlobalDictionary(globals);
|
|
|
|
PyGILState_Release(gilstate);
|
|
|
|
return;
|
|
}
|
|
|
|
/* Now for the fun part! Try and find the functions we need.*/
|
|
if (PyFunction_Check(gval)) {
|
|
pyargs = Py_BuildValue("OOOO", tar, subtar, tarmat, idprop);
|
|
retval = PyObject_CallObject(gval, pyargs);
|
|
Py_DECREF(pyargs);
|
|
}
|
|
else {
|
|
printf("ERROR: doTarget is supposed to be a function!\n");
|
|
con->flag |= PYCON_SCRIPTERROR;
|
|
|
|
Py_DECREF(tar);
|
|
Py_DECREF(subtar);
|
|
Py_DECREF(idprop);
|
|
Py_DECREF(tarmat);
|
|
|
|
ReleaseGlobalDictionary(globals);
|
|
|
|
PyGILState_Release(gilstate);
|
|
|
|
return;
|
|
}
|
|
|
|
if (!retval) {
|
|
BPY_Err_Handle(con->text->id.name);
|
|
con->flag |= PYCON_SCRIPTERROR;
|
|
|
|
|
|
/* free temp objects */
|
|
Py_DECREF(tar);
|
|
Py_DECREF(subtar);
|
|
Py_DECREF(idprop);
|
|
Py_DECREF(tarmat);
|
|
|
|
ReleaseGlobalDictionary(globals);
|
|
|
|
PyGILState_Release(gilstate);
|
|
|
|
return;
|
|
}
|
|
|
|
if (!PyObject_TypeCheck(retval, &matrix_Type)) {
|
|
con->flag |= PYCON_SCRIPTERROR;
|
|
|
|
Py_DECREF(tar);
|
|
Py_DECREF(subtar);
|
|
Py_DECREF(idprop);
|
|
Py_DECREF(tarmat);
|
|
Py_DECREF(retval);
|
|
|
|
ReleaseGlobalDictionary(globals);
|
|
|
|
PyGILState_Release(gilstate);
|
|
|
|
return;
|
|
}
|
|
|
|
retmat = (MatrixObject *)retval;
|
|
if (retmat->rowSize != 4 || retmat->colSize != 4) {
|
|
printf("Error in PyConstraint - doTarget: Matrix returned is the wrong size!\n");
|
|
con->flag |= PYCON_SCRIPTERROR;
|
|
|
|
Py_DECREF(tar);
|
|
Py_DECREF(subtar);
|
|
Py_DECREF(idprop);
|
|
Py_DECREF(tarmat);
|
|
Py_DECREF(retval);
|
|
|
|
ReleaseGlobalDictionary(globals);
|
|
|
|
PyGILState_Release(gilstate);
|
|
|
|
return;
|
|
}
|
|
|
|
/* this is the reverse of code taken from newMatrix() */
|
|
for(row = 0; row < 4; row++) {
|
|
for(col = 0; col < 4; col++) {
|
|
ct->matrix[row][col] = retmat->contigPtr[row*4+col];
|
|
}
|
|
}
|
|
|
|
/* free temp objects */
|
|
Py_DECREF(tar);
|
|
Py_DECREF(subtar);
|
|
Py_DECREF(idprop);
|
|
Py_DECREF(tarmat);
|
|
Py_DECREF(retval);
|
|
|
|
/* clear globals */
|
|
ReleaseGlobalDictionary(globals);
|
|
|
|
PyGILState_Release(gilstate);
|
|
}
|
|
|
|
/* This draws+handles the user-defined interface for editing pyconstraints idprops */
|
|
void BPY_pyconstraint_settings(void *arg1, void *arg2)
|
|
{
|
|
bPythonConstraint *con= (bPythonConstraint *)arg1;
|
|
PyObject *idprop;
|
|
PyObject *globals;
|
|
PyObject *gval;
|
|
PyObject *retval;
|
|
PyGILState_STATE gilstate;
|
|
|
|
if (!con->text) return;
|
|
if (con->flag & PYCON_SCRIPTERROR) return;
|
|
|
|
gilstate = PyGILState_Ensure();
|
|
|
|
globals = CreateGlobalDictionary();
|
|
|
|
idprop = BPy_Wrap_IDProperty( NULL, con->prop, NULL);
|
|
|
|
retval = RunPython(con->text, globals);
|
|
|
|
if (retval == NULL) {
|
|
BPY_Err_Handle(con->text->id.name);
|
|
ReleaseGlobalDictionary(globals);
|
|
con->flag |= PYCON_SCRIPTERROR;
|
|
|
|
/* free temp objects */
|
|
Py_DECREF(idprop);
|
|
|
|
PyGILState_Release(gilstate);
|
|
|
|
return;
|
|
}
|
|
|
|
if (retval) {Py_XDECREF( retval );}
|
|
retval = NULL;
|
|
|
|
gval = PyDict_GetItemString(globals, "getSettings");
|
|
if (!gval) {
|
|
printf("ERROR: no getSettings function in constraint!\n");
|
|
|
|
/* free temp objects */
|
|
ReleaseGlobalDictionary( globals );
|
|
Py_DECREF(idprop);
|
|
|
|
PyGILState_Release(gilstate);
|
|
|
|
return;
|
|
}
|
|
|
|
/* Now for the fun part! Try and find the functions we need. */
|
|
if (PyFunction_Check(gval)) {
|
|
retval = PyObject_CallFunction(gval, "O", idprop);
|
|
}
|
|
else {
|
|
printf("ERROR: getSettings is supposed to be a function!\n");
|
|
ReleaseGlobalDictionary( globals );
|
|
|
|
Py_DECREF(idprop);
|
|
|
|
PyGILState_Release(gilstate);
|
|
|
|
return;
|
|
}
|
|
|
|
if (!retval) {
|
|
BPY_Err_Handle(con->text->id.name);
|
|
con->flag |= PYCON_SCRIPTERROR;
|
|
|
|
/* free temp objects */
|
|
ReleaseGlobalDictionary(globals);
|
|
Py_DECREF(idprop);
|
|
|
|
PyGILState_Release(gilstate);
|
|
|
|
return;
|
|
}
|
|
else {
|
|
/* clear globals */
|
|
ReleaseGlobalDictionary(globals);
|
|
|
|
/* free temp objects */
|
|
Py_DECREF(idprop);
|
|
Py_DECREF(retval);
|
|
|
|
PyGILState_Release(gilstate);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Update function, it gets rid of pydrivers global dictionary, forcing
|
|
* BPY_pydriver_eval to recreate it. This function is used to force
|
|
* reloading the Blender text module "pydrivers.py", if available, so
|
|
* updates in it reach pydriver evaluation. */
|
|
void BPY_pydriver_update(void)
|
|
{
|
|
PyGILState_STATE gilstate = PyGILState_Ensure();
|
|
|
|
if (bpy_pydriver_Dict) { /* free the global dict used by pydrivers */
|
|
PyDict_Clear(bpy_pydriver_Dict);
|
|
Py_DECREF(bpy_pydriver_Dict);
|
|
bpy_pydriver_Dict = NULL;
|
|
}
|
|
|
|
PyGILState_Release(gilstate);
|
|
|
|
return;
|
|
}
|
|
|
|
/* for depsgraph.c, runs py expr once to collect all refs. made
|
|
* to objects (self refs. to the object that owns the py driver
|
|
* are not allowed). */
|
|
struct Object **BPY_pydriver_get_objects(IpoDriver *driver)
|
|
{
|
|
/*if (!driver || !driver->ob || driver->name[0] == '\0')
|
|
return NULL;*/
|
|
|
|
/*PyErr_Clear();*/
|
|
|
|
/* clear the flag that marks invalid python expressions */
|
|
driver->flag &= ~IPO_DRIVER_FLAG_INVALID;
|
|
|
|
/* tell we're running a pydriver, so Get() functions know they need
|
|
* to add the requested obj to our list */
|
|
bpy_pydriver_running(1);
|
|
|
|
/* append driver owner object as the 1st ob in the list;
|
|
* we put it there to make sure it is not itself referenced in
|
|
* its pydriver expression */
|
|
bpy_pydriver_appendToList(driver->ob);
|
|
|
|
/* this will append any other ob referenced in expr (driver->name)
|
|
* or set the driver's error flag if driver's py expression fails */
|
|
BPY_pydriver_eval(driver);
|
|
|
|
bpy_pydriver_running(0); /* ok, we're done */
|
|
|
|
return bpy_pydriver_obArrayFromList(); /* NULL if eval failed */
|
|
}
|
|
|
|
/* This evals py driver expressions, 'expr' is a Python expression that
|
|
* should evaluate to a float number, which is returned. */
|
|
float BPY_pydriver_eval(IpoDriver *driver)
|
|
{
|
|
char *expr = NULL;
|
|
PyObject *retval, *bpy_ob = NULL;
|
|
float result = 0.0f; /* default return */
|
|
int setitem_retval;
|
|
PyGILState_STATE gilstate;
|
|
|
|
if (!driver || (G.f&G_DOSCRIPTLINKS)==0) return result;
|
|
|
|
expr = driver->name; /* the py expression to be evaluated */
|
|
if (!expr || expr[0]=='\0') return result;
|
|
|
|
gilstate = PyGILState_Ensure();
|
|
|
|
if (!bpy_pydriver_Dict) {
|
|
if (bpy_pydriver_create_dict() != 0) {
|
|
fprintf(stderr, "Pydriver error: couldn't create Python dictionary");
|
|
PyGILState_Release(gilstate);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (driver->ob)
|
|
bpy_ob = Object_CreatePyObject(driver->ob);
|
|
|
|
if (!bpy_ob) {
|
|
Py_INCREF(Py_None);
|
|
bpy_ob = Py_None;
|
|
}
|
|
|
|
setitem_retval = EXPP_dict_set_item_str(bpy_pydriver_Dict, "self", bpy_ob);
|
|
|
|
if( !setup_armature_weakrefs()){
|
|
fprintf( stderr, "Oops - weakref dict setup\n");
|
|
PyGILState_Release(gilstate);
|
|
return result;
|
|
}
|
|
|
|
retval = PyRun_String(expr, Py_eval_input, bpy_pydriver_Dict,
|
|
bpy_pydriver_Dict);
|
|
|
|
if (retval == NULL) {
|
|
result = pydriver_error(driver);
|
|
PyGILState_Release(gilstate);
|
|
return result;
|
|
}
|
|
|
|
result = ( float )PyFloat_AsDouble( retval );
|
|
Py_DECREF(retval);
|
|
|
|
if (result == -1 && PyErr_Occurred()) {
|
|
result = pydriver_error(driver);
|
|
PyGILState_Release(gilstate);
|
|
return result;
|
|
}
|
|
|
|
/* remove 'self', since this dict is also used by py buttons */
|
|
if (setitem_retval == 0) PyDict_DelItemString(bpy_pydriver_Dict, "self");
|
|
|
|
/* all fine, make sure the "invalid expression" flag is cleared */
|
|
driver->flag &= ~IPO_DRIVER_FLAG_INVALID;
|
|
|
|
PyGILState_Release(gilstate);
|
|
|
|
return result;
|
|
}
|
|
|
|
/* Button Python Evaluation */
|
|
|
|
/* Python evaluation for gui buttons:
|
|
* users can write any valid Python expression (that evals to an int or float)
|
|
* inside Blender's gui number buttons and have them evaluated to their
|
|
* actual int or float value.
|
|
*
|
|
* The global dict used for pydrivers is also used here, so all imported
|
|
* modules for pydrivers (including the pydrivers.py Blender text) are
|
|
* available for button py eval, too. */
|
|
|
|
static int bpy_button_eval_error(char *expr) {
|
|
|
|
if (bpy_pydriver_oblist)
|
|
bpy_pydriver_freeList();
|
|
|
|
if (bpy_pydriver_Dict) { /* free the persistent global dict */
|
|
/* it's the same dict used by pydrivers */
|
|
PyDict_Clear(bpy_pydriver_Dict);
|
|
Py_DECREF(bpy_pydriver_Dict);
|
|
bpy_pydriver_Dict = NULL;
|
|
}
|
|
|
|
fprintf(stderr, "\nError in button evaluation:\nThis is the failed Python expression:\n'%s'\n\n", expr);
|
|
|
|
PyErr_Print();
|
|
BPY_Err_Clear();
|
|
|
|
return -1;
|
|
}
|
|
|
|
int BPY_button_eval(char *expr, double *value)
|
|
{
|
|
PyObject *retval, *floatval;
|
|
PyGILState_STATE gilstate;
|
|
int ret;
|
|
|
|
if (!value || !expr || expr[0]=='\0') return -1;
|
|
|
|
*value = 0.0; /* default value */
|
|
|
|
gilstate = PyGILState_Ensure();
|
|
|
|
if (!bpy_pydriver_Dict) {
|
|
if (bpy_pydriver_create_dict() != 0) {
|
|
fprintf(stderr,
|
|
"Button Python Eval error: couldn't create Python dictionary \n");
|
|
PyGILState_Release(gilstate);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
|
|
if( !setup_armature_weakrefs()){
|
|
fprintf(stderr, "Oops - weakref dict\n");
|
|
PyGILState_Release(gilstate);
|
|
return -1;
|
|
}
|
|
|
|
retval = PyRun_String(expr, Py_eval_input, bpy_pydriver_Dict,
|
|
bpy_pydriver_Dict);
|
|
|
|
if (retval == NULL) {
|
|
ret = bpy_button_eval_error(expr);
|
|
PyGILState_Release(gilstate);
|
|
return ret;
|
|
}
|
|
else {
|
|
floatval = PyNumber_Float(retval);
|
|
Py_DECREF(retval);
|
|
}
|
|
|
|
if (floatval == NULL) {
|
|
ret = bpy_button_eval_error(expr);
|
|
PyGILState_Release(gilstate);
|
|
return ret;
|
|
} else {
|
|
*value = (float)PyFloat_AsDouble(floatval);
|
|
Py_DECREF(floatval);
|
|
}
|
|
|
|
PyGILState_Release(gilstate);
|
|
|
|
return 0; /* successful exit */
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* ScriptLinks */
|
|
/*****************************************************************************/
|
|
|
|
/*****************************************************************************/
|
|
/* Description: */
|
|
/* Notes: Not implemented yet */
|
|
/*****************************************************************************/
|
|
void BPY_clear_bad_scriptlinks( struct Text *byebye )
|
|
{
|
|
/*
|
|
BPY_clear_bad_scriptlist(getObjectList(), byebye);
|
|
BPY_clear_bad_scriptlist(getLampList(), byebye);
|
|
BPY_clear_bad_scriptlist(getCameraList(), byebye);
|
|
BPY_clear_bad_scriptlist(getMaterialList(), byebye);
|
|
BPY_clear_bad_scriptlist(getWorldList(), byebye);
|
|
BPY_clear_bad_scriptlink(&scene_getCurrent()->id, byebye);
|
|
|
|
allqueue(REDRAWBUTSSCRIPT, 0);
|
|
*/
|
|
return;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Description: Loop through all scripts of a list of object types, and
|
|
* execute these scripts.
|
|
* For the scene, only the current active scene the scripts are
|
|
* executed (if any).
|
|
*****************************************************************************/
|
|
void BPY_do_all_scripts( short event, short anim )
|
|
{
|
|
/* during stills rendering we disable FRAMECHANGED events */
|
|
static char disable_frame_changed = 0;
|
|
|
|
if ((event == SCRIPT_FRAMECHANGED) && disable_frame_changed)
|
|
return;
|
|
|
|
DoAllScriptsFromList( &( G.main->object ), event );
|
|
DoAllScriptsFromList( &( G.main->lamp ), event );
|
|
DoAllScriptsFromList( &( G.main->camera ), event );
|
|
DoAllScriptsFromList( &( G.main->mat ), event );
|
|
DoAllScriptsFromList( &( G.main->world ), event );
|
|
|
|
BPY_do_pyscript( &( G.scene->id ), event );
|
|
|
|
/* Don't allow the Python Interpreter to release the GIL on
|
|
* its own, to guarantee PyNodes work properly. For Blender this
|
|
* is currently the best default behavior.
|
|
* The following code in C is equivalent in Python to:
|
|
* "import sys; sys.setcheckinterval(sys.maxint)" */
|
|
if (event == SCRIPT_RENDER) {
|
|
_Py_CheckInterval = PyInt_GetMax();
|
|
if (!anim)
|
|
disable_frame_changed = 1;
|
|
}
|
|
else if (event == SCRIPT_POSTRENDER) {
|
|
_Py_CheckInterval = 100; /* Python default */
|
|
disable_frame_changed = 0;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Description: Execute a Python script when an event occurs. The following
|
|
* events are possible: frame changed, load script and redraw.
|
|
* Only events happening to one of the following object types
|
|
* are handled: Object, Lamp, Camera, Material, World and
|
|
* Scene.
|
|
*****************************************************************************/
|
|
|
|
static ScriptLink *ID_getScriptlink( ID * id )
|
|
{
|
|
switch ( MAKE_ID2( id->name[0], id->name[1] ) ) {
|
|
case ID_OB:
|
|
return &( ( Object * ) id )->scriptlink;
|
|
case ID_LA:
|
|
return &( ( Lamp * ) id )->scriptlink;
|
|
case ID_CA:
|
|
return &( ( Camera * ) id )->scriptlink;
|
|
case ID_MA:
|
|
return &( ( Material * ) id )->scriptlink;
|
|
case ID_WO:
|
|
return &( ( World * ) id )->scriptlink;
|
|
case ID_SCE:
|
|
return &( ( Scene * ) id )->scriptlink;
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
int BPY_has_onload_script( void )
|
|
{
|
|
ScriptLink *slink = &G.scene->scriptlink;
|
|
int i;
|
|
|
|
if( !slink || !slink->totscript )
|
|
return 0;
|
|
|
|
for( i = 0; i < slink->totscript; i++ ) {
|
|
if( ( slink->flag[i] == SCRIPT_ONLOAD )
|
|
&& ( slink->scripts[i] != NULL ) )
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void BPY_do_pyscript( ID * id, short event )
|
|
{
|
|
ScriptLink *scriptlink;
|
|
|
|
if( !id ) return;
|
|
|
|
scriptlink = ID_getScriptlink( id );
|
|
|
|
if( scriptlink && scriptlink->totscript ) {
|
|
PyObject *value;
|
|
PyObject *dict;
|
|
PyObject *ret;
|
|
int index, during_slink = during_scriptlink( );
|
|
PyGILState_STATE gilstate;
|
|
|
|
/* invalid scriptlinks (new .blend was just loaded), return */
|
|
if( during_slink < 0 )
|
|
return;
|
|
|
|
gilstate = PyGILState_Ensure();
|
|
|
|
if( !setup_armature_weakrefs()){
|
|
printf("Oops - weakref dict, this is a bug\n");
|
|
PyGILState_Release(gilstate);
|
|
return;
|
|
}
|
|
|
|
value = GetPyObjectFromID( id );
|
|
if( !value){
|
|
printf("Oops - could not get a valid python object for Blender.link, this is a bug\n");
|
|
PyGILState_Release(gilstate);
|
|
return;
|
|
}
|
|
|
|
/* tell we're running a scriptlink. The sum also tells if this
|
|
* script is running nested inside another. Blender.Load needs
|
|
* this info to avoid trouble with invalid slink pointers. */
|
|
during_slink++;
|
|
disable_where_scriptlink( (short)during_slink );
|
|
|
|
/* set globals in Blender module to identify scriptlink */
|
|
PyDict_SetItemString( g_blenderdict, "bylink", Py_True);
|
|
EXPP_dict_set_item_str( g_blenderdict, "link", value );
|
|
EXPP_dict_set_item_str( g_blenderdict, "event",
|
|
PyString_FromString( event_to_name
|
|
( event ) ) );
|
|
if (event == SCRIPT_POSTRENDER) event = SCRIPT_RENDER;
|
|
|
|
for( index = 0; index < scriptlink->totscript; index++ ) {
|
|
if( ( scriptlink->flag[index] == event ) &&
|
|
( scriptlink->scripts[index] != NULL ) ) {
|
|
dict = CreateGlobalDictionary( );
|
|
ret = RunPython( ( Text * ) scriptlink->
|
|
scripts[index], dict );
|
|
ReleaseGlobalDictionary( dict );
|
|
|
|
if( !ret ) {
|
|
/* Failed execution of the script */
|
|
BPY_Err_Handle( scriptlink->
|
|
scripts[index]->name +
|
|
2 );
|
|
//BPY_end_python ();
|
|
//BPY_start_python ();
|
|
} else {
|
|
Py_DECREF( ret );
|
|
}
|
|
/* If a scriptlink has just loaded a new .blend file, the
|
|
* scriptlink pointer became invalid (see api2_2x/Blender.c),
|
|
* so we stop here. */
|
|
if( during_scriptlink( ) == -1 ) {
|
|
during_slink = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
disable_where_scriptlink( (short)(during_slink - 1) );
|
|
|
|
/* cleanup bylink flag and clear link so PyObject
|
|
* can be released
|
|
*/
|
|
PyDict_SetItemString(g_blenderdict, "bylink", Py_False);
|
|
PyDict_SetItemString( g_blenderdict, "link", Py_None );
|
|
EXPP_dict_set_item_str( g_blenderdict, "event", PyString_FromString( "" ) );
|
|
|
|
PyGILState_Release(gilstate);
|
|
}
|
|
}
|
|
|
|
|
|
/* SPACE HANDLERS */
|
|
|
|
/* These are special script links that can be assigned to ScrArea's to
|
|
* (EVENT type) receive events sent to a given space (and use or ignore them) or
|
|
* (DRAW type) be called after the space is drawn, to draw anything on top of
|
|
* the space area. */
|
|
|
|
/* How to add space handlers to other spaces:
|
|
* - add the space event defines to DNA_scriptlink_types.h, as done for
|
|
* 3d view: SPACEHANDLER_VIEW3D_EVENT, for example;
|
|
* - add the new defines to Blender.SpaceHandler dictionary in Blender.c;
|
|
* - check space.c for how to call the event handlers;
|
|
* - check drawview.c for how to call the draw handlers;
|
|
* - check header_view3d.c for how to add the "Space Handler Scripts" menu.
|
|
* Note: DRAW handlers should be called with 'event = 0', chech drawview.c */
|
|
|
|
int BPY_has_spacehandler(Text *text, ScrArea *sa)
|
|
{
|
|
ScriptLink *slink;
|
|
int index;
|
|
|
|
if (!sa || !text) return 0;
|
|
|
|
slink = &sa->scriptlink;
|
|
|
|
for (index = 0; index < slink->totscript; index++) {
|
|
if (slink->scripts[index] && (slink->scripts[index] == (ID *)text))
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int BPY_is_spacehandler(Text *text, char spacetype)
|
|
{
|
|
TextLine *tline = text->lines.first;
|
|
unsigned short type = 0;
|
|
|
|
if (tline && (tline->len > 10)) {
|
|
char *line = tline->line;
|
|
|
|
/* Expected format: # SPACEHANDLER.SPACE.TYPE
|
|
* Exs: # SPACEHANDLER.VIEW3D.DRAW
|
|
* The actual checks are forgiving, so slight variations also work. */
|
|
if (line && line[0] == '#' && strstr(line, "HANDLER")) {
|
|
line++; /* skip '#' */
|
|
|
|
/* only done for 3D View right now, trivial to add for others: */
|
|
switch (spacetype) {
|
|
case SPACE_VIEW3D:
|
|
line = strstr(line, "3D"); /* VIEW3D, 3DVIEW */
|
|
if (line) {
|
|
if (strstr(line, "DRAW")) type = SPACEHANDLER_VIEW3D_DRAW;
|
|
else {
|
|
line = strstr(line, "EVENT");
|
|
if (line) {
|
|
if (strstr(line, "ALL")) {
|
|
type = SPACEHANDLER_VIEW3D_EVENT_ALL;
|
|
} else { type = SPACEHANDLER_VIEW3D_EVENT; }
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return type; /* 0 if not a space handler */
|
|
}
|
|
|
|
int BPY_del_spacehandler(Text *text, ScrArea *sa)
|
|
{
|
|
ScriptLink *slink;
|
|
int i, j;
|
|
|
|
if (!sa || !text) return -1;
|
|
|
|
slink = &sa->scriptlink;
|
|
if (slink->totscript < 1) return -1;
|
|
|
|
for (i = 0; i < slink->totscript; i++) {
|
|
if (text == (Text *)slink->scripts[i]) {
|
|
|
|
for (j = i; j < slink->totscript - 1; j++) {
|
|
slink->flag[j] = slink->flag[j+1];
|
|
slink->scripts[j] = slink->scripts[j+1];
|
|
}
|
|
slink->totscript--;
|
|
/* like done in buttons_script.c we just free memory
|
|
* if all slinks have been removed -- less fragmentation,
|
|
* these should be quite small arrays */
|
|
if (slink->totscript == 0) {
|
|
if (slink->scripts) MEM_freeN(slink->scripts);
|
|
if (slink->flag) MEM_freeN(slink->flag);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int BPY_add_spacehandler(Text *text, ScrArea *sa, char spacetype)
|
|
{
|
|
unsigned short handlertype;
|
|
|
|
if (!sa || !text) return -1;
|
|
|
|
handlertype = (unsigned short)BPY_is_spacehandler(text, spacetype);
|
|
|
|
if (handlertype) {
|
|
ScriptLink *slink = &sa->scriptlink;
|
|
void *stmp, *ftmp;
|
|
|
|
/* extend slink */
|
|
|
|
stmp= slink->scripts;
|
|
slink->scripts= MEM_mallocN(sizeof(ID*)*(slink->totscript+1),
|
|
"spacehandlerscripts");
|
|
|
|
ftmp= slink->flag;
|
|
slink->flag= MEM_mallocN(sizeof(short*)*(slink->totscript+1),
|
|
"spacehandlerflags");
|
|
|
|
if (slink->totscript) {
|
|
memcpy(slink->scripts, stmp, sizeof(ID*)*(slink->totscript));
|
|
MEM_freeN(stmp);
|
|
|
|
memcpy(slink->flag, ftmp, sizeof(short)*(slink->totscript));
|
|
MEM_freeN(ftmp);
|
|
}
|
|
|
|
slink->scripts[slink->totscript] = (ID *)text;
|
|
slink->flag[slink->totscript]= handlertype;
|
|
|
|
slink->totscript++;
|
|
slink->actscript = slink->totscript;
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int BPY_do_spacehandlers( ScrArea *sa, unsigned short event,
|
|
short eventValue, unsigned short space_event )
|
|
{
|
|
ScriptLink *scriptlink;
|
|
int retval = 0;
|
|
short slink_event, spacehandlers_match;
|
|
PyGILState_STATE gilstate;
|
|
|
|
if (!sa || !(G.f & G_DOSCRIPTLINKS)) return 0;
|
|
|
|
scriptlink = &sa->scriptlink;
|
|
|
|
if (scriptlink->totscript > 0) {
|
|
PyObject *dict;
|
|
PyObject *ret;
|
|
int index, during_slink = during_scriptlink();
|
|
|
|
/* invalid scriptlinks (new .blend was just loaded), return */
|
|
if (during_slink < 0) return 0;
|
|
|
|
/* tell we're running a scriptlink. The sum also tells if this script
|
|
* is running nested inside another. Blender.Load needs this info to
|
|
* avoid trouble with invalid slink pointers.
|
|
* Update (test): allow EVENT space handlers to call file/image selectors,
|
|
* still disabled for DRAW space handlers: */
|
|
if (event == 0) { /* event = 0: DRAW space handler */
|
|
during_slink++;
|
|
disable_where_scriptlink( (short)during_slink );
|
|
}
|
|
|
|
gilstate = PyGILState_Ensure();
|
|
|
|
if( !setup_armature_weakrefs()){
|
|
printf("Oops - weakref dict, this is a bug\n");
|
|
PyGILState_Release(gilstate);
|
|
return 0;
|
|
}
|
|
|
|
/* set globals in Blender module to identify space handler scriptlink */
|
|
PyDict_SetItemString(g_blenderdict, "bylink", Py_True);
|
|
/* unlike normal scriptlinks, here Blender.link is int (space event type) */
|
|
EXPP_dict_set_item_str(g_blenderdict, "link", PyInt_FromLong(space_event));
|
|
/* note: DRAW space_events set event and val to 0 */
|
|
EXPP_dict_set_item_str(g_blenderdict, "event", PyInt_FromLong(event));
|
|
EXPP_dict_set_item_str(g_blenderdict, "eventValue", PyInt_FromLong(eventValue));
|
|
/* now run all assigned space handlers for this space and space_event */
|
|
for( index = 0; index < scriptlink->totscript; index++ ) {
|
|
|
|
spacehandlers_match = 0;
|
|
|
|
slink_event = scriptlink->flag[index];
|
|
if( slink_event == space_event )
|
|
spacehandlers_match = 1;
|
|
else if( ( space_event == SPACEHANDLER_VIEW3D_EVENT ) &&
|
|
( slink_event == SPACEHANDLER_VIEW3D_EVENT_ALL ) )
|
|
spacehandlers_match = 1;
|
|
|
|
/* for DRAW handlers: */
|
|
if (event == 0) {
|
|
glPushAttrib(GL_ALL_ATTRIB_BITS);
|
|
glMatrixMode(GL_PROJECTION);
|
|
glPushMatrix();
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glPushMatrix();
|
|
}
|
|
|
|
if( spacehandlers_match &&
|
|
( scriptlink->scripts[index] != NULL ) ) {
|
|
dict = CreateGlobalDictionary();
|
|
ret = RunPython( ( Text * ) scriptlink->scripts[index], dict );
|
|
ReleaseGlobalDictionary( dict );
|
|
|
|
if (!ret) { /* Failed execution of the script */
|
|
BPY_Err_Handle( scriptlink->scripts[index]->name+2 );
|
|
} else {
|
|
Py_DECREF(ret);
|
|
|
|
/* an EVENT type (event != 0) script can either accept an event or
|
|
* ignore it:
|
|
* if the script sets Blender.event to None it accepted it;
|
|
* otherwise the space's event handling callback that called us
|
|
* can go on processing the event */
|
|
if (event && (PyDict_GetItemString(g_blenderdict,"event") == Py_None))
|
|
retval = 1; /* event was swallowed */
|
|
}
|
|
|
|
/* If a scriptlink has just loaded a new .blend file, the
|
|
* scriptlink pointer became invalid (see api2_2x/Blender.c),
|
|
* so we stop here. */
|
|
if( during_scriptlink( ) == -1 ) {
|
|
during_slink = 1;
|
|
if (event == 0) glPopAttrib();
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* for DRAW handlers: */
|
|
if (event == 0) {
|
|
glMatrixMode(GL_PROJECTION);
|
|
glPopMatrix();
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glPopMatrix();
|
|
glPopAttrib();
|
|
disable_where_scriptlink( (short)(during_slink - 1) );
|
|
}
|
|
|
|
}
|
|
|
|
PyDict_SetItemString(g_blenderdict, "bylink", Py_False);
|
|
PyDict_SetItemString(g_blenderdict, "link", Py_None );
|
|
EXPP_dict_set_item_str(g_blenderdict, "event", PyString_FromString(""));
|
|
|
|
PyGILState_Release(gilstate);
|
|
}
|
|
|
|
/* retval:
|
|
* space_event is of type EVENT:
|
|
* 0 - event was returned,
|
|
* 1 - event was processed;
|
|
* space_event is of type DRAW:
|
|
* 0 always */
|
|
|
|
return retval;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Description:
|
|
* Notes:
|
|
*****************************************************************************/
|
|
void BPY_free_scriptlink( struct ScriptLink *slink )
|
|
{
|
|
if( slink->totscript ) {
|
|
if( slink->flag ) {
|
|
MEM_freeN( slink->flag );
|
|
slink->flag= NULL;
|
|
}
|
|
if( slink->scripts ) {
|
|
MEM_freeN( slink->scripts );
|
|
slink->scripts= NULL;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static int CheckAllSpaceHandlers(Text *text)
|
|
{
|
|
bScreen *screen;
|
|
ScrArea *sa;
|
|
ScriptLink *slink;
|
|
int fixed = 0;
|
|
|
|
for (screen = G.main->screen.first; screen; screen = screen->id.next) {
|
|
for (sa = screen->areabase.first; sa; sa = sa->next) {
|
|
slink = &sa->scriptlink;
|
|
if (!slink->totscript) continue;
|
|
if (BPY_del_spacehandler(text, sa) == 0) fixed++;
|
|
}
|
|
}
|
|
return fixed;
|
|
}
|
|
|
|
static int CheckAllScriptsFromList( ListBase * list, Text * text )
|
|
{
|
|
ID *id;
|
|
ScriptLink *scriptlink;
|
|
int index;
|
|
int fixed = 0;
|
|
|
|
id = list->first;
|
|
|
|
while( id != NULL ) {
|
|
scriptlink = ID_getScriptlink( id );
|
|
if( scriptlink && scriptlink->totscript ) {
|
|
for( index = 0; index < scriptlink->totscript; index++) {
|
|
if ((Text *)scriptlink->scripts[index] == text) {
|
|
scriptlink->scripts[index] = NULL;
|
|
fixed++;
|
|
}
|
|
}
|
|
}
|
|
id = id->next;
|
|
}
|
|
|
|
return fixed;
|
|
}
|
|
|
|
/* When a Text is deleted, we need to unlink it from eventual scriptlinks */
|
|
int BPY_check_all_scriptlinks( Text * text )
|
|
{
|
|
int fixed = 0;
|
|
fixed += CheckAllScriptsFromList( &( G.main->object ), text );
|
|
fixed += CheckAllScriptsFromList( &( G.main->lamp ), text );
|
|
fixed += CheckAllScriptsFromList( &( G.main->camera ), text );
|
|
fixed += CheckAllScriptsFromList( &( G.main->mat ), text );
|
|
fixed += CheckAllScriptsFromList( &( G.main->world ), text );
|
|
fixed += CheckAllScriptsFromList( &( G.main->scene ), text );
|
|
fixed += CheckAllSpaceHandlers(text);
|
|
|
|
return fixed;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Description:
|
|
* Notes:
|
|
*****************************************************************************/
|
|
void BPY_copy_scriptlink( struct ScriptLink *scriptlink )
|
|
{
|
|
void *tmp;
|
|
|
|
if( scriptlink->totscript ) {
|
|
|
|
tmp = scriptlink->scripts;
|
|
scriptlink->scripts =
|
|
MEM_mallocN( sizeof( ID * ) * scriptlink->totscript,
|
|
"scriptlistL" );
|
|
memcpy( scriptlink->scripts, tmp,
|
|
sizeof( ID * ) * scriptlink->totscript );
|
|
|
|
tmp = scriptlink->flag;
|
|
scriptlink->flag =
|
|
MEM_mallocN( sizeof( short ) * scriptlink->totscript,
|
|
"scriptlistF" );
|
|
memcpy( scriptlink->flag, tmp,
|
|
sizeof( short ) * scriptlink->totscript );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Description:
|
|
* Notes: Not implemented yet
|
|
*****************************************************************************/
|
|
int BPY_call_importloader( char *name )
|
|
{ /* XXX Should this function go away from Blender? */
|
|
printf( "In BPY_call_importloader(name=%s)\n", name );
|
|
return ( 0 );
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Private functions
|
|
*****************************************************************************/
|
|
|
|
/*****************************************************************************
|
|
* Description: This function executes the python script passed by text.
|
|
* The Python dictionary containing global variables needs to
|
|
* be passed in globaldict.
|
|
* NOTE: Make sure BPY_Err_Handle() runs if this returns NULL
|
|
* otherwise pointers can be left in sys.last_traceback that become invalid.
|
|
*****************************************************************************/
|
|
static PyObject *RunPython( Text * text, PyObject * globaldict )
|
|
{
|
|
char *buf = NULL;
|
|
|
|
/* The script text is compiled to Python bytecode and saved at text->compiled
|
|
* to speed-up execution if the user executes the script multiple times */
|
|
|
|
if( !text->compiled ) { /* if it wasn't already compiled, do it now */
|
|
buf = txt_to_buf( text );
|
|
|
|
text->compiled =
|
|
Py_CompileString( buf, text->id.name+2, Py_file_input );
|
|
|
|
MEM_freeN( buf );
|
|
|
|
if( PyErr_Occurred( ) ) {
|
|
BPY_free_compiled_text( text );
|
|
return NULL;
|
|
}
|
|
|
|
}
|
|
|
|
/* Without __file__ set the sys.argv[0] is used for the filename
|
|
* which ends up with lines from the blender binary being printed in the console */
|
|
PyDict_SetItemString(globaldict, "__file__", PyString_FromString(text->id.name+2));
|
|
|
|
return PyEval_EvalCode( text->compiled, globaldict, globaldict );
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Description: This function creates a new Python dictionary object.
|
|
*****************************************************************************/
|
|
static PyObject *CreateGlobalDictionary( void )
|
|
{
|
|
PyObject *dict = PyDict_New( );
|
|
|
|
PyDict_SetItemString( dict, "__builtins__", PyEval_GetBuiltins( ) );
|
|
EXPP_dict_set_item_str( dict, "__name__",
|
|
PyString_FromString( "__main__" ) );
|
|
|
|
return dict;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Description: This function deletes a given Python dictionary object.
|
|
*****************************************************************************/
|
|
static void ReleaseGlobalDictionary( PyObject * dict )
|
|
{
|
|
PyDict_Clear( dict );
|
|
Py_DECREF( dict ); /* Release dictionary. */
|
|
|
|
return;
|
|
}
|
|
|
|
/***************************************************************************
|
|
* Description: This function runs all scripts (if any) present in the
|
|
* list argument. The event by which the function has been
|
|
* called, is passed in the event argument.
|
|
*****************************************************************************/
|
|
static void DoAllScriptsFromList( ListBase * list, short event )
|
|
{
|
|
ID *id;
|
|
|
|
id = list->first;
|
|
|
|
while( id != NULL ) {
|
|
BPY_do_pyscript( id, event );
|
|
id = id->next;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
static void init_ourImport( void )
|
|
{
|
|
PyObject *m, *d;
|
|
PyObject *import = PyCFunction_New( bpy_import_meth, NULL );
|
|
|
|
m = PyImport_AddModule( "__builtin__" );
|
|
d = PyModule_GetDict( m );
|
|
|
|
EXPP_dict_set_item_str( d, "__import__", import );
|
|
}
|
|
|
|
static void init_ourReload( void )
|
|
{
|
|
PyObject *m, *d;
|
|
PyObject *reload = PyCFunction_New( bpy_reload_meth, NULL );
|
|
|
|
m = PyImport_AddModule( "__builtin__" );
|
|
d = PyModule_GetDict( m );
|
|
EXPP_dict_set_item_str( d, "reload", reload );
|
|
}
|
|
|
|
|
|
void BPY_scripts_clear_pyobjects( void )
|
|
{
|
|
Script *script;
|
|
for (script=G.main->script.first; script; script=script->id.next) {
|
|
Py_XDECREF((PyObject *)script->py_draw);
|
|
Py_XDECREF((PyObject *)script->py_event);
|
|
Py_XDECREF((PyObject *)script->py_button);
|
|
Py_XDECREF((PyObject *)script->py_browsercallback);
|
|
Py_XDECREF((PyObject *)script->py_globaldict);
|
|
SCRIPT_SET_NULL(script)
|
|
}
|
|
}
|
|
void error_pyscript( void )
|
|
{
|
|
error("Python script error: check console");
|
|
}
|