/* * $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, Joseph Gilbert * * ***** END GPL/BL DUAL LICENSE BLOCK ***** */ #include #include /* for open, close in Blender_Load */ #include #ifndef WIN32 #include #else #include "BLI_winstuff.h" #include #endif #include /* exit_editmode() */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* for SPACE_VIEW3D */ #include /* for SPACE_VIEW3D */ #include #include #include #include "gen_utils.h" #include "modules.h" #include "../BPY_extern.h" /* for bpy_gethome() */ #include "../BPY_menus.h" /* to update menus */ /**********************************************************/ /* Python API function prototypes for the Blender module. */ /**********************************************************/ static PyObject *Blender_Set( PyObject * self, PyObject * args ); static PyObject *Blender_Get( PyObject * self, PyObject * args ); static PyObject *Blender_Redraw( PyObject * self, PyObject * args ); static PyObject *Blender_Quit( PyObject * self ); static PyObject *Blender_Load( PyObject * self, PyObject * args ); static PyObject *Blender_Save( PyObject * self, PyObject * args ); static PyObject *Blender_UpdateMenus( PyObject * self); /*****************************************************************************/ /* The following string definitions are used for documentation strings. */ /* In Python these will be written to the console when doing a */ /* Blender.__doc__ */ /*****************************************************************************/ static char Blender_Set_doc[] = "(request, data) - Update settings in Blender\n\ \n\ (request) A string identifying the setting to change\n\ 'curframe' - Sets the current frame using the number in data"; static char Blender_Get_doc[] = "(request) - Retrieve settings from Blender\n\ \n\ (request) A string indentifying the data to be returned\n\ 'curframe' - Returns the current animation frame\n\ 'curtime' - Returns the current animation time\n\ 'staframe' - Returns the start frame of the animation\n\ 'endframe' - Returns the end frame of the animation\n\ 'filename' - Returns the name of the last file read or written\n\ 'homedir' - Returns Blender's home dir\n\ 'datadir' - Returns the dir where scripts can save their data, if available\n\ 'scriptsdir' - Returns the main dir where scripts are kept, if available\n\ 'uscriptsdir' - Returns the user defined dir for scripts, if available\n\ 'version' - Returns the Blender version number"; static char Blender_Redraw_doc[] = "() - Redraw all 3D windows"; static char Blender_Quit_doc[] = "() - Quit Blender. The current data is saved as 'quit.blend' before leaving."; static char Blender_Load_doc[] = "(filename) - Load the given file.\n\ Supported formats:\n\ Blender, DXF, Inventor 1.0 ASCII, VRML 1.0 asc, STL, Videoscape, radiogour.\n\ \n\ Notes:\n\ 1 - () - an empty argument loads the default .B.blend file;\n\ 2 - if the substring '.B.blend' occurs inside 'filename', the default\n\ .B.blend file is loaded;\n\ 3 - If a Blender file is loaded the script ends immediately.\n\ 4 - The current data is always preserved as an autosave file, for safety;\n\ 5 - This function only works if the script where it's executed is the\n\ only one running at the moment."; static char Blender_Save_doc[] = "(filename) - Save data to a file based on the filename's extension.\n\ Supported are: Blender's .blend and the builtin exporters:\n\ VRML 1.0 (.wrl), Videoscape (.obj), DXF (.dxf) and STL (.stl)\n\ (filename) - A filename with one of the supported extensions.\n\ Note 1: 'filename' should not contain the substring \".B.blend\" in it.\n\ Note 2: only .blend raises an error if file wasn't saved.\n\ \tYou can use Blender.sys.exists(filename) to make sure the file was saved\n\ \twhen writing to one of the other formats."; static char Blender_UpdateMenus_doc[] = "() - Update the menus where scripts are registered. Only needed for\n\ scripts that save other new scripts in the default or user defined folders."; /*****************************************************************************/ /* Python method structure definition. */ /*****************************************************************************/ static struct PyMethodDef Blender_methods[] = { {"Set", Blender_Set, METH_VARARGS, Blender_Set_doc}, {"Get", Blender_Get, METH_VARARGS, Blender_Get_doc}, {"Redraw", Blender_Redraw, METH_VARARGS, Blender_Redraw_doc}, {"Quit", ( PyCFunction ) Blender_Quit, METH_NOARGS, Blender_Quit_doc}, {"Load", Blender_Load, METH_VARARGS, Blender_Load_doc}, {"Save", Blender_Save, METH_VARARGS, Blender_Save_doc}, {"UpdateMenus", ( PyCFunction ) Blender_UpdateMenus, METH_NOARGS, Blender_UpdateMenus_doc}, {NULL, NULL, 0, NULL} }; /*****************************************************************************/ /* Global variables */ /*****************************************************************************/ PyObject *g_blenderdict; /*****************************************************************************/ /* Function: Blender_Set */ /* Python equivalent: Blender.Set */ /*****************************************************************************/ static PyObject *Blender_Set( PyObject * self, PyObject * args ) { char *name; PyObject *arg; int framenum; if( !PyArg_ParseTuple( args, "sO", &name, &arg ) ) { /* TODO: Do we need to generate a nice error message here? */ return ( NULL ); } if( StringEqual( name, "curframe" ) ) { if( !PyArg_Parse( arg, "i", &framenum ) ) { /* TODO: Do we need to generate a nice error message here? */ return ( NULL ); } G.scene->r.cfra = framenum; update_for_newframe( ); } else { return ( EXPP_ReturnPyObjError( PyExc_AttributeError, "bad request identifier" ) ); } return ( EXPP_incr_ret( Py_None ) ); } /*****************************************************************************/ /* Function: Blender_Get */ /* Python equivalent: Blender.Get */ /*****************************************************************************/ static PyObject *Blender_Get( PyObject * self, PyObject * args ) { PyObject *object; PyObject *dict; char *str; if( !PyArg_ParseTuple( args, "O", &object ) ) { /* TODO: Do we need to generate a nice error message here? */ return ( NULL ); } if( PyString_Check( object ) ) { str = PyString_AsString( object ); if( StringEqual( str, "curframe" ) ) { return ( PyInt_FromLong( G.scene->r.cfra ) ); } if( StringEqual( str, "curtime" ) ) { return ( PyFloat_FromDouble ( frame_to_float( G.scene->r.cfra ) ) ); } if( StringEqual( str, "staframe" ) ) { return ( PyInt_FromLong( G.scene->r.sfra ) ); } if( StringEqual( str, "endframe" ) ) { return ( PyInt_FromLong( G.scene->r.efra ) ); } if( StringEqual( str, "filename" ) ) { return ( PyString_FromString( G.sce ) ); } if( StringEqual( str, "homedir" ) ) { if( BLI_exists( bpy_gethome() )) return PyString_FromString( bpy_gethome() ); else return EXPP_incr_ret( Py_None ); } if( StringEqual( str, "datadir" ) ) { char datadir[FILE_MAXDIR]; BLI_make_file_string( "/", datadir, bpy_gethome( ), "bpydata/" ); if( BLI_exists( datadir ) ) return PyString_FromString( datadir ); else return EXPP_incr_ret( Py_None ); } if( StringEqual( str, "scriptsdir" ) ) { char scriptsdir[FILE_MAXDIR]; BLI_make_file_string( "/", scriptsdir, bpy_gethome( ), "scripts/" ); if( BLI_exists( scriptsdir ) ) return PyString_FromString( scriptsdir ); else return EXPP_incr_ret( Py_None ); } if( StringEqual( str, "uscriptsdir" ) ) { if( BLI_exists( U.pythondir ) ) return PyString_FromString( U.pythondir ); else return EXPP_incr_ret( Py_None ); } /* According to the old file (opy_blender.c), the following if statement is a quick hack and needs some clean up. */ if( StringEqual( str, "vrmloptions" ) ) { dict = PyDict_New( ); PyDict_SetItemString( dict, "twoside", PyInt_FromLong( U. vrmlflag & USER_VRML_TWOSIDED ) ); PyDict_SetItemString( dict, "layers", PyInt_FromLong( U. vrmlflag & USER_VRML_LAYERS ) ); PyDict_SetItemString( dict, "autoscale", PyInt_FromLong( U. vrmlflag & USER_VRML_AUTOSCALE ) ); return ( dict ); } /* End 'quick hack' part. */ if( StringEqual( str, "version" ) ) { return ( PyInt_FromLong( G.version ) ); } /* TODO: Do we want to display a usefull message here that the requested data is unknown? else { return (EXPP_ReturnPyObjError (..., "message") ); } */ } else { return ( EXPP_ReturnPyObjError( PyExc_AttributeError, "expected string argument" ) ); } return ( EXPP_ReturnPyObjError( PyExc_AttributeError, "bad request identifier" ) ); } /*****************************************************************************/ /* Function: Blender_Redraw */ /* Python equivalent: Blender.Redraw */ /*****************************************************************************/ static PyObject *Blender_Redraw( PyObject * self, PyObject * args ) { int wintype = SPACE_VIEW3D; if( !PyArg_ParseTuple( args, "|i", &wintype ) ) { return EXPP_ReturnPyObjError( PyExc_TypeError, "expected int argument (or nothing)" ); } return M_Window_Redraw( self, Py_BuildValue( "(i)", wintype ) ); } /*****************************************************************************/ /* Function: Blender_Quit */ /* Python equivalent: Blender.Quit */ /*****************************************************************************/ static PyObject *Blender_Quit( PyObject * self ) { BIF_write_autosave( ); /* save the current data first */ exit_usiblender( ); /* renames last autosave to quit.blend */ Py_INCREF( Py_None ); return Py_None; } /** * Blender.Load * loads Blender's .blend, DXF, radiogour(?), STL, Videoscape, * Inventor 1.0 ASCII, VRML 1.0 asc. */ static PyObject *Blender_Load( PyObject * self, PyObject * args ) { char *fname = NULL; int keep_oldfname = 0; Script *script = NULL; char str[32], name[FILE_MAXDIR]; int file, is_blend_file = 0; if( !PyArg_ParseTuple( args, "|si", &fname, &keep_oldfname ) ) return EXPP_ReturnPyObjError( PyExc_TypeError, "expected filename and optional int or nothing as arguments" ); if( fname ) { if( strlen( fname ) > FILE_MAXDIR ) /* G.main->name's max length */ return EXPP_ReturnPyObjError( PyExc_AttributeError, "filename too long!" ); else if( !BLI_exists( fname ) ) return EXPP_ReturnPyObjError( PyExc_AttributeError, "requested file doesn't exist!" ); if( keep_oldfname ) BLI_strncpy( name, G.sce, FILE_MAXDIR ); } /* We won't let a new .blend file be loaded if there are still other * scripts running, since loading a new file will close and remove them. */ if( G.main->script.first != G.main->script.last ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "there are other scripts running at the Scripts win, close them first!" ); if( fname ) { file = open( fname, O_BINARY | O_RDONLY ); if( file <= 0 ) { return EXPP_ReturnPyObjError( PyExc_RuntimeError, "cannot open file!" ); } else { read( file, str, 31 ); close( file ); if( strncmp( str, "BLEN", 4 ) == 0 ) is_blend_file = 1; } } else is_blend_file = 1; /* no arg given means default: .B.blend */ if( is_blend_file ) { int during_slink = during_scriptlink( ); /* when loading a .blend file from a scriptlink, the scriptlink pointer * in BPY_do_pyscript becomes invalid during a loop. Inform it here. * Also do not allow a nested scriptlink (called from inside another) * to load .blend files, to avoid nasty problems. */ if( during_slink >= 1 ) { if( during_slink == 1 ) disable_where_scriptlink( -1 ); else { return EXPP_ReturnPyObjError ( PyExc_EnvironmentError, "Blender.Load: cannot load .blend files from a nested scriptlink." ); } } /* trick: mark the script so that its script struct won't be freed after * the script is executed (to avoid a double free warning on exit): */ script = G.main->script.first; if( script ) script->flags |= SCRIPT_GUI; BIF_write_autosave( ); /* for safety let's preserve the current data */ } if( G.obedit ) exit_editmode( 1 ); /* for safety, any filename with .B.blend is considered the default one. * It doesn't seem necessary to compare file attributes (like st_ino and * st_dev, according to the glibc info pages) to find out if the given * filename, that may have been given with a twisted misgiving path, is the * default one for sure. Taking any .B.blend file as the default is good * enough here. Note: the default file requires extra clean-up done by * BIF_read_homefile: freeing the user theme data. */ if( !fname || ( strstr( fname, ".B.blend" ) && is_blend_file ) ) BIF_read_homefile( ); else BIF_read_file( fname ); if( fname && keep_oldfname ) { /*BLI_strncpy(G.main->name, name, FILE_MAXDIR); */ BLI_strncpy( G.sce, name, FILE_MAXDIR ); } Py_INCREF( Py_None ); return Py_None; } static PyObject *Blender_Save( PyObject * self, PyObject * args ) { char *fname = NULL; int overwrite = 0, len = 0; char *error = NULL; Library *li; if( !PyArg_ParseTuple( args, "s|i", &fname, &overwrite ) ) return EXPP_ReturnPyObjError( PyExc_TypeError, "expected filename and optional int (overwrite flag) as arguments" ); for( li = G.main->library.first; li; li = li->id.next ) { if( BLI_streq( li->name, fname ) ) { return EXPP_ReturnPyObjError( PyExc_AttributeError, "cannot overwrite used library" ); } } /* for safety, any filename with .B.blend is considered the default one * and not accepted here. */ if( strstr( fname, ".B.blend" ) ) return EXPP_ReturnPyObjError( PyExc_AttributeError, "filename can't contain the substring \".B.blend\" in it." ); len = strlen( fname ); if( len > FILE_MAXFILE ) return EXPP_ReturnPyObjError( PyExc_AttributeError, "filename is too long!" ); else if( BLI_exists( fname ) && !overwrite ) return EXPP_ReturnPyObjError( PyExc_AttributeError, "file already exists and overwrite flag was not given." ); disable_where_script( 1 ); /* to avoid error popups in the write_* functions */ if( BLI_testextensie( fname, ".blend" ) ) { if( G.fileflags & G_AUTOPACK ) packAll( ); if( !BLO_write_file( fname, G.fileflags, &error ) ) { disable_where_script( 0 ); return EXPP_ReturnPyObjError( PyExc_SystemError, error ); } } else if( BLI_testextensie( fname, ".dxf" ) ) write_dxf( fname ); else if( BLI_testextensie( fname, ".stl" ) ) write_stl( fname ); else if( BLI_testextensie( fname, ".wrl" ) ) write_vrml( fname ); else if( BLI_testextensie( fname, ".obj" ) ) write_videoscape( fname ); else { disable_where_script( 0 ); return EXPP_ReturnPyObjError( PyExc_AttributeError, "unknown file extension." ); } disable_where_script( 0 ); Py_INCREF( Py_None ); return Py_None; } static PyObject * Blender_UpdateMenus( PyObject * self ) { BPyMenu_RemoveAllEntries(); if (BPyMenu_Init(1) == -1) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "invalid scripts dir"); Py_INCREF( Py_None ); return Py_None; } /*****************************************************************************/ /* Function: initBlender */ /*****************************************************************************/ void M_Blender_Init( void ) { PyObject *module; PyObject *dict; g_blenderdict = NULL; /* TODO: create a docstring for the Blender module */ module = Py_InitModule3( "Blender", Blender_methods, NULL ); types_InitAll( ); /* set all our pytypes to &PyType_Type */ dict = PyModule_GetDict( module ); g_blenderdict = dict; Py_INCREF( Py_False ); PyDict_SetItemString( dict, "bylink", Py_False ); Py_INCREF( Py_None ); PyDict_SetItemString( dict, "link", Py_None ); PyDict_SetItemString( dict, "event", PyString_FromString( "" ) ); PyDict_SetItemString( dict, "Types", Types_Init( ) ); PyDict_SetItemString( dict, "sys", sys_Init( ) ); PyDict_SetItemString( dict, "Registry", Registry_Init( ) ); PyDict_SetItemString( dict, "Scene", Scene_Init( ) ); PyDict_SetItemString( dict, "Object", Object_Init( ) ); PyDict_SetItemString( dict, "Material", Material_Init( ) ); PyDict_SetItemString( dict, "Camera", Camera_Init( ) ); PyDict_SetItemString( dict, "Lamp", Lamp_Init( ) ); PyDict_SetItemString( dict, "Lattice", Lattice_Init( ) ); PyDict_SetItemString( dict, "Curve", Curve_Init( ) ); PyDict_SetItemString( dict, "Armature", Armature_Init( ) ); PyDict_SetItemString( dict, "Ipo", Ipo_Init( ) ); PyDict_SetItemString( dict, "IpoCurve", IpoCurve_Init( ) ); PyDict_SetItemString( dict, "Metaball", Metaball_Init( ) ); PyDict_SetItemString( dict, "Image", Image_Init( ) ); PyDict_SetItemString( dict, "Window", Window_Init( ) ); PyDict_SetItemString( dict, "Draw", Draw_Init( ) ); PyDict_SetItemString( dict, "BGL", BGL_Init( ) ); PyDict_SetItemString( dict, "Effect", Effect_Init( ) ); PyDict_SetItemString( dict, "Text", Text_Init( ) ); PyDict_SetItemString( dict, "World", World_Init( ) ); PyDict_SetItemString( dict, "Texture", Texture_Init( ) ); PyDict_SetItemString( dict, "NMesh", NMesh_Init( ) ); PyDict_SetItemString( dict, "Noise", Noise_Init( ) ); PyDict_SetItemString( dict, "Mathutils", Mathutils_Init( ) ); PyDict_SetItemString( dict, "Library", Library_Init( ) ); PyDict_SetItemString( dict, "Sound", Sound_Init( ) ); PyDict_SetItemString( dict, "CurNurb", CurNurb_Init( ) ); PyModule_AddIntConstant( module, "TRUE", 1 ); PyModule_AddIntConstant( module, "FALSE", 0 ); }