/* * $Id$ * * ***** BEGIN GPL/BL DUAL 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. The Blender * Foundation also sells licenses for use in proprietary software under * the Blender License. See http://www.blender.org/BL/ for information * about this. * * 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 * * ***** END GPL/BL DUAL LICENSE BLOCK ***** */ #ifdef HAVE_CONFIG_H #include #endif #include #include "compile.h" /* for the PyCodeObject */ #include "eval.h" /* for PyEval_EvalCode */ #include #include #include /* for BLI_last_slash() */ #include /* for pupmenu() */ #include #include #include #include #include #include #include /* during_scriptlink() */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for U.pythondir */ #include "BPY_extern.h" #include "BPY_menus.h" #include "api2_2x/EXPP_interface.h" #include "api2_2x/constant.h" #include "api2_2x/modules.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" /************************************************************************* * Structure definitions **************************************************************************/ #define FILENAME_LENGTH 24 typedef struct _ScriptError { char filename[FILENAME_LENGTH]; int lineno; } ScriptError; /**************************************************************************** * Global variables ****************************************************************************/ ScriptError g_script_error; /*************************************************************************** * Function prototypes ***************************************************************************/ PyObject *RunPython( Text * text, PyObject * globaldict ); char *GetName( Text * text ); PyObject *CreateGlobalDictionary( void ); void ReleaseGlobalDictionary( PyObject * dict ); void DoAllScriptsFromList( ListBase * list, short event ); PyObject *importText( char *name ); void init_ourImport( void ); PyObject *blender_import( PyObject * self, PyObject * args ); /**************************************************************************** * Description: This function will initialise Python and all the implemented * api variations. * Notes: Currently only the api for 2.2x will be initialised. ****************************************************************************/ void BPY_start_python( void ) { bpy_registryDict = PyDict_New( ); /* check comment at start of this file */ if( !bpy_registryDict ) printf( "Error: Couldn't create the Registry Python Dictionary!" ); /* TODO: Shouldn't "blender" be replaced by PACKAGE ?? (config.h) */ 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. */ printf( "Checking for Python install...\n" ); fflush( stdout ); Py_Initialize( ); init_ourImport( ); initBlenderApi2_2x( ); init_syspath( ); return; } /*****************************************************************************/ /* Description: This function will terminate the Python interpreter */ /*****************************************************************************/ void BPY_end_python( void ) { if( bpy_registryDict ) { Py_DECREF( bpy_registryDict ); bpy_registryDict = NULL; } 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, *dict, *path, *dir; PyErr_Clear( ); dir = Py_BuildValue( "s", dirname ); mod_sys = PyImport_ImportModule( "sys" ); /* new ref */ dict = PyModule_GetDict( mod_sys ); /* borrowed ref */ path = PyDict_GetItemString( dict, "path" ); /* borrowed ref */ if( !PyList_Check( path ) ) return; PyList_Append( path, dir ); if( PyErr_Occurred( ) ) Py_FatalError( "could not build sys.path" ); Py_DECREF( mod_sys ); } void init_syspath( void ) { PyObject *path; PyObject *mod, *d; PyObject *p; char *c, *progname; char execdir[FILE_MAXDIR + FILE_MAXFILE]; /*defines from DNA_space_types.h */ int n; path = Py_BuildValue( "s", bprogname ); mod = PyImport_ImportModule( "Blender.sys" ); if( mod ) { d = PyModule_GetDict( mod ); PyDict_SetItemString( d, "progname", path ); Py_DECREF( mod ); } else printf( "Warning: could not set Blender.sys.progname\n" ); progname = BLI_last_slash( bprogname ); /* looks for the last dir separator */ c = Py_GetPath( ); /* get python system path */ PySys_SetPath( c ); /* initialize */ 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 */ /* set Blender.sys.progname */ } else printf( "Warning: could not determine argv[0] path\n" ); /* * bring in the site module so we can add * site-package dirs to sys.path */ mod = PyImport_ImportModule( "site" ); /* new ref */ if( mod ) { PyObject *item; int size = 0; int index; printf( "Installed Python found!\n" ); /* get the value of 'sitedirs' from the module */ /* the ref man says GetDict() never fails!!! */ d = PyModule_GetDict( mod ); /* borrowed ref */ p = PyDict_GetItemString( d, "sitedirs" ); /* borrowed ref */ if( p ) { /* we got our string */ /* append each item in sitedirs list to path */ size = PyList_Size( p ); for( index = 0; index < size; index++ ) { item = PySequence_GetItem( p, index ); /* new ref */ if( item ) syspath_append( PyString_AsString ( item ) ); } } Py_DECREF( mod ); } else { /* import 'site' failed */ PyErr_Clear( ); 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 * set argv[0] to the Blender exe */ mod = PyImport_ImportModule( "sys" ); /* new ref */ if( mod ) { d = PyModule_GetDict( mod ); /* borrowed ref */ PyDict_SetItemString( d, "executable", Py_BuildValue( "s", bprogname ) ); /* in the future this can be extended to have more argv's if needed: */ PyDict_SetItemString( d, "argv", Py_BuildValue( "[s]", bprogname ) ); Py_DECREF( mod ); } } /**************************************************************************** * 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 ) { PyObject *result, *dict; if( U.pythondir && U.pythondir[0] != '\0' ) syspath_append( U.pythondir ); /* append to module search path */ BPyMenu_Init( 0 ); /* get dynamic menus (registered scripts) data */ dict = PyDict_New( ); /* here we check if the user has (some of) the expected modules */ if( dict ) { char *s = "import chunk, gzip, math, os, struct, string"; result = PyRun_String( s, Py_eval_input, dict, dict ); if( !result ) { PyErr_Clear( ); /*XXX print msg about this, point to readme.html */ } else Py_DECREF( result ); Py_DECREF( dict ); } } /**************************************************************************** * 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 */ /*****************************************************************************/ PyObject *traceback_getFilename( PyObject * tb ) { PyObject *v; /* co_filename is in f_code, which is in tb_frame, which is in tb */ v = PyObject_GetAttrString( tb, "tb_frame" ); Py_XDECREF( v ); v = PyObject_GetAttrString( v, "f_code" ); Py_XDECREF( v ); v = PyObject_GetAttrString( v, "co_filename" ); return v; } /**************************************************************************** * Description: Blender Python error handler. This catches the error and * stores filename and line number in a global *****************************************************************************/ void BPY_Err_Handle( char *script_name ) { PyObject *exception, *err, *tb, *v; if( !script_name ) { printf( "Error: script has NULL name\n" ); return; } PyErr_Fetch( &exception, &err, &tb ); 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_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( ); 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" ); 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."' */ while( 1 ) { v = PyObject_GetAttrString( tb, "tb_next" ); if( v == Py_None || strcmp( PyString_AsString ( traceback_getFilename( v ) ), script_name ) ) { break; } Py_DECREF( tb ); tb = v; } v = PyObject_GetAttrString( tb, "tb_lineno" ); g_script_error.lineno = PyInt_AsLong( v ); Py_XDECREF( v ); v = traceback_getFilename( tb ); strncpy( g_script_error.filename, PyString_AsString( v ), FILENAME_LENGTH ); Py_XDECREF( v ); Py_DECREF( tb ); } 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; 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: */ 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, GetName( text ) ); 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, GetName( text ), strlen( GetName( text ) ) + 1 ); script->id.us = 1; script->flags = SCRIPT_RUNNING; script->py_draw = NULL; script->py_event = NULL; script->py_button = NULL; py_dict = CreateGlobalDictionary( ); script->py_globaldict = py_dict; info = ( BPy_constant * ) M_constant_New( ); if( info ) { constant_insert( info, "name", PyString_FromString( script->id.name + 2 ) ); Py_INCREF( Py_None ); constant_insert( info, "arg", Py_None ); PyDict_SetItemString( 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; if( G.main->script.first ) free_libblock( &G.main->script, script ); 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 ); } } return 1; /* normal return */ } /**************************************************************************** * Description: The original function of this name has been refactored * into BPY_txt_do_python_Text. That version is needed for the command * line support for Python. This is here to keep the interface the * same and reduce code changes elsewhere. ****************************************************************************/ int BPY_txt_do_python( struct SpaceText *st ) { return BPY_txt_do_python_Text( st->text ); } /**************************************************************************** * Description: Called from command line to run a Python script * automatically. ****************************************************************************/ void BPY_run_python_script( char *fn ) { Text *text = NULL; int is_blenText = 0; if( !BLI_exists( fn ) ) { /* if there's no such filename ... */ text = G.main->text.first; /* try an already existing Blender Text */ while( text ) { if( !strcmp( fn, text->id.name + 2 ) ) break; text = text->id.next; } if( !text ) { printf( "\nError: no such file or Blender text -- %s.\n", fn ); return; } else is_blenText = 1; /* fn is already a Blender Text */ } if( !is_blenText ) text = add_text( fn ); if( text == NULL ) { printf( "Error in BPY_run_python_script: couldn't create Blender text " "from %s\n", fn ); // Chris: On Windows if I continue I just get a segmentation // violation. To get a baseline file I exit here. exit( 2 ); } 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_blenText ) free_libblock( &G.main->text, text ); } /**************************************************************************** * 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 ) { PyObject *py_dict, *py_res, *pyarg = NULL; BPy_constant *info; BPyMenu *pym; BPySubMenu *pysm; FILE *fp = NULL; char *buffer, *s; char filestr[FILE_MAXDIR + FILE_MAXFILE]; char dirname[FILE_MAXDIR]; Script *script = G.main->script.first; int len; pym = BPyMenu_GetEntry( menutype, ( short ) event ); if( !pym ) return 0; 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; pyarg = PyString_FromString( pysm->arg ); } else return 0; } } if( !pyarg ) { /* no submenus */ Py_INCREF( Py_None ); pyarg = Py_None; } if( pym->dir ) /* script is in U.pythondir */ BLI_make_file_string( "/", filestr, U.pythondir, pym->filename ); else { /* script is in ~/.blender/scripts/ */ BLI_make_file_string( "/", dirname, bpy_gethome( ), "scripts" ); BLI_make_file_string( "/", filestr, dirname, pym->filename ); } fp = fopen( filestr, "rb" ); if( !fp ) { printf( "Error loading script: couldn't open file %s\n", filestr ); return 0; } /* Create a new script structure and initialize it: */ script = alloc_libblock( &G.main->script, ID_SCRIPT, pym->name ); if( !script ) { printf( "couldn't allocate memory for Script struct!" ); fclose( fp ); 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 3 were handled in header_info.c */ case PYMENU_EXPORT: case PYMENU_HELP: case PYMENU_WIZARDS: 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; } script->id.us = 1; script->flags = SCRIPT_RUNNING; script->py_draw = NULL; script->py_event = NULL; script->py_button = NULL; py_dict = CreateGlobalDictionary( ); script->py_globaldict = py_dict; info = ( BPy_constant * ) M_constant_New( ); if( info ) { constant_insert( info, "name", PyString_FromString( script->id.name + 2 ) ); constant_insert( info, "arg", pyarg ); PyDict_SetItemString( py_dict, "__script__", ( PyObject * ) info ); } /* 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 */ fseek( fp, 0L, SEEK_END ); len = ftell( fp ); fseek( fp, 0L, SEEK_SET ); buffer = MEM_mallocN( len + 2, "pyfilebuf" ); /* len+2 to add '\n\0' */ len = fread( buffer, 1, len, fp ); buffer[len] = '\n'; /* fix syntax error in files w/o eol */ buffer[len + 1] = '\0'; /* fast clean-up of dos cr/lf line endings: change '\r' to space */ /* we also have to check for line splitters: '\\' */ /* to avoid possible syntax errors on dos files on win */ /**/ /* but first make sure we won't disturb memory below &buffer[0]: */ if( *buffer == '\r' ) *buffer = ' '; /* now handle the whole buffer */ for( s = buffer + 1; *s != '\0'; s++ ) { if( *s == '\r' ) { if( *( s - 1 ) == '\\' ) { /* special case: long lines split with '\': */ *( s - 1 ) = ' '; /* we write ' \', because '\ ' is a syntax error */ *s = '\\'; } else *s = ' '; /* not a split line, just replace '\r' with ' ' */ } } fclose( fp ); /* run the string buffer */ py_res = PyRun_String( buffer, Py_file_input, py_dict, py_dict ); MEM_freeN( buffer ); if( !py_res ) { /* Failed execution of the script */ BPY_Err_Handle( script->id.name + 2 ); PyErr_Print( ); ReleaseGlobalDictionary( py_dict ); if( G.main->script.first ) free_libblock( &G.main->script, script ); error( "Python script error: check console" ); 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( 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. */ } } } return 1; /* normal return */ } /***************************************************************************** * Description: * Notes: *****************************************************************************/ void BPY_free_compiled_text( struct Text *text ) { if( !text->compiled ) return; Py_DECREF( ( PyObject * ) text->compiled ); text->compiled = NULL; return; } /***************************************************************************** * Description: This function frees a finished (flags == 0) script. *****************************************************************************/ void BPY_free_finished_script( Script * script ) { if( !script ) return; if( PyErr_Occurred( ) ) { /* if script ended after filesel */ PyErr_Print( ); /* eventual errors are handled now */ error( "Python script error: check console" ); } free_libblock( &G.main->script, script ); return; } 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 ); } } } } } } } void BPY_clear_script( Script * script ) { PyObject *dict; if( !script ) return; Py_XDECREF( ( PyObject * ) script->py_draw ); Py_XDECREF( ( PyObject * ) script->py_event ); Py_XDECREF( ( PyObject * ) script->py_button ); dict = script->py_globaldict; if( dict ) { PyDict_Clear( dict ); Py_DECREF( dict ); /* Release dictionary. */ script->py_globaldict = NULL; } unlink_script( script ); } /*****************************************************************************/ /* 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 ) { 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 ); 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; } } static PyObject *ID_asPyObject( ID * id ) { switch ( MAKE_ID2( id->name[0], id->name[1] ) ) { case ID_OB: return Object_CreatePyObject( ( Object * ) id ); case ID_LA: return Lamp_CreatePyObject( ( Lamp * ) id ); case ID_CA: return Camera_CreatePyObject( ( Camera * ) id ); case ID_MA: return Material_CreatePyObject( ( Material * ) id ); case ID_WO: return World_CreatePyObject( ( World * ) id ); case ID_SCE: return Scene_CreatePyObject( ( Scene * ) id ); default: Py_INCREF( Py_None ); return Py_None; } } 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 = ID_getScriptlink( id ); if( scriptlink && scriptlink->totscript ) { PyObject *dict; PyObject *ret; int index, during_slink = during_scriptlink( ); /* invalid scriptlinks (new .blend was just loaded), return */ if( during_slink < 0 ) 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( during_slink ); /* set globals in Blender module to identify scriptlink */ Py_INCREF( Py_True ); PyDict_SetItemString( g_blenderdict, "bylink", Py_True ); PyDict_SetItemString( g_blenderdict, "link", ID_asPyObject( id ) ); PyDict_SetItemString( g_blenderdict, "event", PyString_FromString( event_to_name ( event ) ) ); 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( during_slink - 1 ); /* cleanup bylink flag and clear link so PyObject * can be released */ Py_INCREF( Py_False ); PyDict_SetItemString( g_blenderdict, "bylink", Py_False ); Py_INCREF( Py_None ); PyDict_SetItemString( g_blenderdict, "link", Py_None ); PyDict_SetItemString( g_blenderdict, "event", PyString_FromString( "" ) ); } } /***************************************************************************** * Description: * Notes: *****************************************************************************/ void BPY_free_scriptlink( struct ScriptLink *slink ) { if( slink->totscript ) { if( slink->flag ) MEM_freeN( slink->flag ); if( slink->scripts ) MEM_freeN( slink->scripts ); } return; } /***************************************************************************** * 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. *****************************************************************************/ 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, GetName( text ), Py_file_input ); MEM_freeN( buf ); if( PyErr_Occurred( ) ) { BPY_free_compiled_text( text ); return NULL; } } return PyEval_EvalCode( text->compiled, globaldict, globaldict ); } /***************************************************************************** * Description: This function returns the value of the name field of the * given Text struct. *****************************************************************************/ char *GetName( Text * text ) { return ( text->id.name + 2 ); } /***************************************************************************** * Description: This function creates a new Python dictionary object. *****************************************************************************/ PyObject *CreateGlobalDictionary( void ) { PyObject *dict = PyDict_New( ); PyDict_SetItemString( dict, "__builtins__", PyEval_GetBuiltins( ) ); PyDict_SetItemString( dict, "__name__", PyString_FromString( "__main__" ) ); return dict; } /***************************************************************************** * Description: This function deletes a given Python dictionary object. *****************************************************************************/ 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. *****************************************************************************/ void DoAllScriptsFromList( ListBase * list, short event ) { ID *id; id = list->first; while( id != NULL ) { BPY_do_pyscript( id, event ); id = id->next; } return; } PyObject *importText( char *name ) { Text *text; char *txtname; char *buf = NULL; int namelen = strlen( name ); txtname = malloc( namelen + 3 + 1 ); if( !txtname ) return NULL; memcpy( txtname, name, namelen ); memcpy( &txtname[namelen], ".py", 4 ); text = ( Text * ) & ( G.main->text.first ); while( text ) { if( !strcmp( txtname, GetName( text ) ) ) break; text = text->id.next; } if( !text ) { free( txtname ); return NULL; } if( !text->compiled ) { buf = txt_to_buf( text ); text->compiled = Py_CompileString( buf, GetName( text ), Py_file_input ); MEM_freeN( buf ); if( PyErr_Occurred( ) ) { PyErr_Print( ); BPY_free_compiled_text( text ); free( txtname ); return NULL; } } free( txtname ); return PyImport_ExecCodeModule( name, text->compiled ); } static PyMethodDef bimport[] = { {"blimport", blender_import, METH_VARARGS, "our own import"} }; PyObject *blender_import( PyObject * self, PyObject * args ) { PyObject *exception, *err, *tb; char *name; PyObject *globals = NULL, *locals = NULL, *fromlist = NULL; PyObject *m; if( !PyArg_ParseTuple( args, "s|OOO:bimport", &name, &globals, &locals, &fromlist ) ) return NULL; m = PyImport_ImportModuleEx( name, globals, locals, fromlist ); if( m ) return m; else PyErr_Fetch( &exception, &err, &tb ); /*restore for probable later use */ m = importText( name ); if( m ) { /* found module, ignore above exception */ PyErr_Clear( ); Py_XDECREF( exception ); Py_XDECREF( err ); Py_XDECREF( tb ); printf( "imported from text buffer...\n" ); } else { PyErr_Restore( exception, err, tb ); } return m; } void init_ourImport( void ) { PyObject *m, *d; PyObject *import = PyCFunction_New( bimport, NULL ); m = PyImport_AddModule( "__builtin__" ); d = PyModule_GetDict( m ); PyDict_SetItemString( d, "__import__", import ); } /* this makes sure BLI_gethome() returns a path with '.blender' appended * Besides, this function now either returns userhome/.blender (if it exists) * or blenderInstallDir/.blender/ otherwise */ char *bpy_gethome( ) { static char homedir[FILE_MAXDIR]; char bprogdir[FILE_MAXDIR]; char *s; int i; if( homedir[0] != '\0' ) return homedir; /* no need to search twice */ s = BLI_gethome( ); if( strstr( s, ".blender" ) ) PyOS_snprintf( homedir, FILE_MAXDIR, s ); else BLI_make_file_string( "/", homedir, s, ".blender/" ); /* if userhome/.blender/ exists, return it */ if( BLI_exists( homedir ) ) return homedir; /* otherwise, use argv[0] (bprogname) to get .blender/ in * Blender's installation dir */ s = BLI_last_slash( bprogname ); i = s - bprogname + 1; PyOS_snprintf( bprogdir, i, bprogname ); BLI_make_file_string( "/", homedir, bprogdir, ".blender/" ); return homedir; }