New Bpy type Text3d for accessing Blender's Font objects.
[blender.git] / source / blender / python / api2_2x / Blender.c
1 /* 
2  * $Id$
3  * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version. The Blender
9  * Foundation also sells licenses for use in proprietary software under
10  * the Blender License.  See http://www.blender.org/BL/ for information
11  * about this.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 59 Temple Place - Suite 330, Boston, MA        02111-1307, USA.
21  *
22  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
23  * All rights reserved.
24  *
25  * This is a new part of Blender.
26  *
27  * Contributor(s): Michel Selten, Willian P. Germano, Joseph Gilbert
28  *
29  * ***** END GPL/BL DUAL LICENSE BLOCK *****
30 */
31
32 #include <Python.h>
33 #include <stdio.h>
34
35 /* for open, close in Blender_Load */
36 #include <fcntl.h>
37 #ifndef WIN32
38 #include <unistd.h>
39 #else
40 #include "BLI_winstuff.h"
41 #include <io.h>
42 #endif
43
44 #include <BDR_editobject.h>     /* exit_editmode() */
45 #include <BIF_usiblender.h>
46 #include <BLI_blenlib.h>
47 #include <BLO_writefile.h>
48 #include <BKE_exotic.h>
49 #include <BKE_global.h>
50 #include <BKE_packedFile.h>
51 #include <BKE_object.h>
52 #include <BPI_script.h>
53 #include <BSE_headerbuttons.h>
54 #include <DNA_ID.h>
55 #include <DNA_object_types.h>
56 #include <DNA_scene_types.h>
57 #include <DNA_screen_types.h>   /* for SPACE_VIEW3D */
58 #include <DNA_space_types.h>    /* for SPACE_VIEW3D */
59 #include <DNA_userdef_types.h>
60 #include <BKE_ipo.h>
61 #include <blendef.h>
62
63
64
65 #include "gen_utils.h"
66 #include "modules.h"
67 #include "../BPY_extern.h"      /* for bpy_gethome() */
68 #include "../BPY_menus.h"       /* to update menus */
69
70
71 /**********************************************************/
72 /* Python API function prototypes for the Blender module.       */
73 /**********************************************************/
74 static PyObject *Blender_Set( PyObject * self, PyObject * args );
75 static PyObject *Blender_Get( PyObject * self, PyObject * args );
76 static PyObject *Blender_Redraw( PyObject * self, PyObject * args );
77 static PyObject *Blender_Quit( PyObject * self );
78 static PyObject *Blender_Load( PyObject * self, PyObject * args );
79 static PyObject *Blender_Save( PyObject * self, PyObject * args );
80 static PyObject *Blender_UpdateMenus( PyObject * self);
81
82 /*****************************************************************************/
83 /* The following string definitions are used for documentation strings.  */
84 /* In Python these will be written to the console when doing a           */
85 /* Blender.__doc__       */
86 /*****************************************************************************/
87 static char Blender_Set_doc[] =
88         "(request, data) - Update settings in Blender\n\
89 \n\
90 (request) A string identifying the setting to change\n\
91         'curframe'      - Sets the current frame using the number in data";
92
93 static char Blender_Get_doc[] = "(request) - Retrieve settings from Blender\n\
94 \n\
95 (request) A string indentifying the data to be returned\n\
96         'curframe'      - Returns the current animation frame\n\
97         'curtime'       - Returns the current animation time\n\
98         'staframe'      - Returns the start frame of the animation\n\
99         'endframe'      - Returns the end frame of the animation\n\
100         'filename'      - Returns the name of the last file read or written\n\
101         'homedir' - Returns Blender's home dir\n\
102         'datadir' - Returns the dir where scripts can save their data, if available\n\
103         'scriptsdir' - Returns the main dir where scripts are kept, if available\n\
104         'uscriptsdir' - Returns the user defined dir for scripts, if available\n\
105         'version'       - Returns the Blender version number";
106
107 static char Blender_Redraw_doc[] = "() - Redraw all 3D windows";
108
109 static char Blender_Quit_doc[] =
110         "() - Quit Blender.  The current data is saved as 'quit.blend' before leaving.";
111
112 static char Blender_Load_doc[] = "(filename) - Load the given file.\n\
113 Supported formats:\n\
114 Blender, DXF, Inventor 1.0 ASCII, VRML 1.0 asc, STL, Videoscape, radiogour.\n\
115 \n\
116 Notes:\n\
117 1 - () - an empty argument loads the default .B.blend file;\n\
118 2 - if the substring '.B.blend' occurs inside 'filename', the default\n\
119 .B.blend file is loaded;\n\
120 3 - If a Blender file is loaded the script ends immediately.\n\
121 4 - The current data is always preserved as an autosave file, for safety;\n\
122 5 - This function only works if the script where it's executed is the\n\
123 only one running at the moment.";
124
125 static char Blender_Save_doc[] =
126         "(filename) - Save data to a file based on the filename's extension.\n\
127 Supported are: Blender's .blend and the builtin exporters:\n\
128 VRML 1.0 (.wrl), Videoscape (.obj), DXF (.dxf) and STL (.stl)\n\
129 (filename) - A filename with one of the supported extensions.\n\
130 Note 1: 'filename' should not contain the substring \".B.blend\" in it.\n\
131 Note 2: only .blend raises an error if file wasn't saved.\n\
132 \tYou can use Blender.sys.exists(filename) to make sure the file was saved\n\
133 \twhen writing to one of the other formats.";
134
135 static char Blender_UpdateMenus_doc[] =
136         "() - Update the menus where scripts are registered.  Only needed for\n\
137 scripts that save other new scripts in the default or user defined folders.";
138
139 /*****************************************************************************/
140 /* Python method structure definition.           */
141 /*****************************************************************************/
142 static struct PyMethodDef Blender_methods[] = {
143         {"Set", Blender_Set, METH_VARARGS, Blender_Set_doc},
144         {"Get", Blender_Get, METH_VARARGS, Blender_Get_doc},
145         {"Redraw", Blender_Redraw, METH_VARARGS, Blender_Redraw_doc},
146         {"Quit", ( PyCFunction ) Blender_Quit, METH_NOARGS, Blender_Quit_doc},
147         {"Load", Blender_Load, METH_VARARGS, Blender_Load_doc},
148         {"Save", Blender_Save, METH_VARARGS, Blender_Save_doc},
149         {"UpdateMenus", ( PyCFunction ) Blender_UpdateMenus, METH_NOARGS,
150          Blender_UpdateMenus_doc},
151         {NULL, NULL, 0, NULL}
152 };
153
154 /*****************************************************************************/
155 /* Global variables      */
156 /*****************************************************************************/
157 PyObject *g_blenderdict;
158
159 /*****************************************************************************/
160 /* Function:    Blender_Set              */
161 /* Python equivalent:   Blender.Set              */
162 /*****************************************************************************/
163 static PyObject *Blender_Set( PyObject * self, PyObject * args )
164 {
165         char *name;
166         PyObject *arg;
167         int framenum;
168
169         if( !PyArg_ParseTuple( args, "sO", &name, &arg ) ) {
170                 /* TODO: Do we need to generate a nice error message here? */
171                 return ( NULL );
172         }
173
174         if( StringEqual( name, "curframe" ) ) {
175                 if( !PyArg_Parse( arg, "i", &framenum ) ) {
176                         /* TODO: Do we need to generate a nice error message here? */
177                         return ( NULL );
178                 }
179
180                 G.scene->r.cfra = framenum;
181
182                 update_for_newframe(  );
183         } else {
184                 return ( EXPP_ReturnPyObjError( PyExc_AttributeError,
185                                                 "bad request identifier" ) );
186         }
187         return ( EXPP_incr_ret( Py_None ) );
188 }
189
190 /*****************************************************************************/
191 /* Function:            Blender_Get      */
192 /* Python equivalent:   Blender.Get              */
193 /*****************************************************************************/
194 static PyObject *Blender_Get( PyObject * self, PyObject * args )
195 {
196         PyObject *object;
197         PyObject *dict;
198         char *str;
199
200         if( !PyArg_ParseTuple( args, "O", &object ) ) {
201                 /* TODO: Do we need to generate a nice error message here? */
202                 return ( NULL );
203         }
204
205         if( PyString_Check( object ) ) {
206                 str = PyString_AsString( object );
207
208                 if( StringEqual( str, "curframe" ) ) {
209                         return ( PyInt_FromLong( G.scene->r.cfra ) );
210                 }
211                 if( StringEqual( str, "curtime" ) ) {
212                         return ( PyFloat_FromDouble
213                                  ( frame_to_float( G.scene->r.cfra ) ) );
214                 }
215                 if( StringEqual( str, "staframe" ) ) {
216                         return ( PyInt_FromLong( G.scene->r.sfra ) );
217                 }
218                 if( StringEqual( str, "endframe" ) ) {
219                         return ( PyInt_FromLong( G.scene->r.efra ) );
220                 }
221                 if( StringEqual( str, "filename" ) ) {
222                         return ( PyString_FromString( G.sce ) );
223                 }
224                 if( StringEqual( str, "homedir" ) ) {
225                         if( BLI_exists( bpy_gethome() ))
226                                 return PyString_FromString( bpy_gethome() );
227                         else
228                                 return EXPP_incr_ret( Py_None );
229                 }
230
231                 if( StringEqual( str, "datadir" ) ) {
232                         char datadir[FILE_MAXDIR];
233                         BLI_make_file_string( "/", datadir, bpy_gethome(  ),
234                                               "bpydata/" );
235                         if( BLI_exists( datadir ) )
236                                 return PyString_FromString( datadir );
237                         else
238                                 return EXPP_incr_ret( Py_None );
239                 }
240                 if( StringEqual( str, "scriptsdir" ) ) {
241                         char scriptsdir[FILE_MAXDIR];
242                         BLI_make_file_string( "/", scriptsdir, bpy_gethome(  ),
243                                               "scripts/" );
244                         if( BLI_exists( scriptsdir ) )
245                                 return PyString_FromString( scriptsdir );
246                         else
247                                 return EXPP_incr_ret( Py_None );
248                 }
249                 if( StringEqual( str, "uscriptsdir" ) ) {
250                         if( BLI_exists( U.pythondir ) )
251                                 return PyString_FromString( U.pythondir );
252                         else
253                                 return EXPP_incr_ret( Py_None );
254                 }
255                 /* According to the old file (opy_blender.c), the following if
256                    statement is a quick hack and needs some clean up. */
257                 if( StringEqual( str, "vrmloptions" ) ) {
258                         dict = PyDict_New(  );
259
260                         PyDict_SetItemString( dict, "twoside",
261                                               PyInt_FromLong( U.
262                                                               vrmlflag &
263                                                               USER_VRML_TWOSIDED ) );
264
265                         PyDict_SetItemString( dict, "layers",
266                                               PyInt_FromLong( U.
267                                                               vrmlflag &
268                                                               USER_VRML_LAYERS ) );
269
270                         PyDict_SetItemString( dict, "autoscale",
271                                               PyInt_FromLong( U.
272                                                               vrmlflag &
273                                                               USER_VRML_AUTOSCALE ) );
274
275                         return ( dict );
276                 }               /* End 'quick hack' part. */
277                 if( StringEqual( str, "version" ) ) {
278                         return ( PyInt_FromLong( G.version ) );
279                 }
280                 /* TODO: Do we want to display a usefull message here that the
281                    requested data is unknown?
282                    else
283                    {
284                    return (EXPP_ReturnPyObjError (..., "message") );
285                    }
286                  */
287         } else {
288                 return ( EXPP_ReturnPyObjError( PyExc_AttributeError,
289                                                 "expected string argument" ) );
290         }
291
292         return ( EXPP_ReturnPyObjError( PyExc_AttributeError,
293                                         "bad request identifier" ) );
294 }
295
296 /*****************************************************************************/
297 /* Function:            Blender_Redraw           */
298 /* Python equivalent:   Blender.Redraw                   */
299 /*****************************************************************************/
300 static PyObject *Blender_Redraw( PyObject * self, PyObject * args )
301 {
302         int wintype = SPACE_VIEW3D;
303
304         if( !PyArg_ParseTuple( args, "|i", &wintype ) ) {
305                 return EXPP_ReturnPyObjError( PyExc_TypeError,
306                                               "expected int argument (or nothing)" );
307         }
308
309         return M_Window_Redraw( self, Py_BuildValue( "(i)", wintype ) );
310 }
311
312 /*****************************************************************************/
313 /* Function:            Blender_Quit             */
314 /* Python equivalent:   Blender.Quit                     */
315 /*****************************************************************************/
316 static PyObject *Blender_Quit( PyObject * self )
317 {
318         BIF_write_autosave(  ); /* save the current data first */
319
320         exit_usiblender(  );    /* renames last autosave to quit.blend */
321
322         Py_INCREF( Py_None );
323         return Py_None;
324 }
325
326 /**
327  * Blender.Load
328  * loads Blender's .blend, DXF, radiogour(?), STL, Videoscape,
329  * Inventor 1.0 ASCII, VRML 1.0 asc.
330  */
331 static PyObject *Blender_Load( PyObject * self, PyObject * args )
332 {
333         char *fname = NULL;
334         int keep_oldfname = 0;
335         Script *script = NULL;
336         char str[32], name[FILE_MAXDIR];
337         int file, is_blend_file = 0;
338
339         if( !PyArg_ParseTuple( args, "|si", &fname, &keep_oldfname ) )
340                 return EXPP_ReturnPyObjError( PyExc_TypeError,
341                                               "expected filename and optional int or nothing as arguments" );
342
343         if( fname ) {
344                 if( strlen( fname ) > FILE_MAXDIR )     /* G.main->name's max length */
345                         return EXPP_ReturnPyObjError( PyExc_AttributeError,
346                                                       "filename too long!" );
347                 else if( !BLI_exists( fname ) )
348                         return EXPP_ReturnPyObjError( PyExc_AttributeError,
349                                                       "requested file doesn't exist!" );
350
351                 if( keep_oldfname )
352                         BLI_strncpy( name, G.sce, FILE_MAXDIR );
353         }
354
355         /* We won't let a new .blend file be loaded if there are still other
356          * scripts running, since loading a new file will close and remove them. */
357
358         if( G.main->script.first != G.main->script.last )
359                 return EXPP_ReturnPyObjError( PyExc_RuntimeError,
360                                               "there are other scripts running at the Scripts win, close them first!" );
361
362         if( fname ) {
363                 file = open( fname, O_BINARY | O_RDONLY );
364
365                 if( file <= 0 ) {
366                         return EXPP_ReturnPyObjError( PyExc_RuntimeError,
367                                                       "cannot open file!" );
368                 } else {
369                         read( file, str, 31 );
370                         close( file );
371
372                         if( strncmp( str, "BLEN", 4 ) == 0 )
373                                 is_blend_file = 1;
374                 }
375         } else
376                 is_blend_file = 1;      /* no arg given means default: .B.blend */
377
378         if( is_blend_file ) {
379                 int during_slink = during_scriptlink(  );
380
381                 /* when loading a .blend file from a scriptlink, the scriptlink pointer
382                  * in BPY_do_pyscript becomes invalid during a loop.  Inform it here.
383                  * Also do not allow a nested scriptlink (called from inside another)
384                  * to load .blend files, to avoid nasty problems. */
385                 if( during_slink >= 1 ) {
386                         if( during_slink == 1 )
387                                 disable_where_scriptlink( -1 );
388                         else {
389                                 return EXPP_ReturnPyObjError
390                                         ( PyExc_EnvironmentError,
391                                           "Blender.Load: cannot load .blend files from a nested scriptlink." );
392                         }
393                 }
394
395                 /* trick: mark the script so that its script struct won't be freed after
396                  * the script is executed (to avoid a double free warning on exit): */
397                 script = G.main->script.first;
398                 if( script )
399                         script->flags |= SCRIPT_GUI;
400
401                 BIF_write_autosave(  ); /* for safety let's preserve the current data */
402         }
403
404         if( G.obedit )
405                 exit_editmode( 1 );
406
407         /* for safety, any filename with .B.blend is considered the default one.
408          * It doesn't seem necessary to compare file attributes (like st_ino and
409          * st_dev, according to the glibc info pages) to find out if the given
410          * filename, that may have been given with a twisted misgiving path, is the
411          * default one for sure.  Taking any .B.blend file as the default is good
412          * enough here.  Note: the default file requires extra clean-up done by
413          * BIF_read_homefile: freeing the user theme data. */
414         if( !fname || ( strstr( fname, ".B.blend" ) && is_blend_file ) )
415                 BIF_read_homefile(  );
416         else
417                 BIF_read_file( fname );
418
419         if( fname && keep_oldfname ) {
420                 /*BLI_strncpy(G.main->name, name, FILE_MAXDIR); */
421                 BLI_strncpy( G.sce, name, FILE_MAXDIR );
422         }
423
424         Py_INCREF( Py_None );
425         return Py_None;
426 }
427
428 static PyObject *Blender_Save( PyObject * self, PyObject * args )
429 {
430         char *fname = NULL;
431         int overwrite = 0, len = 0;
432         char *error = NULL;
433         Library *li;
434
435         if( !PyArg_ParseTuple( args, "s|i", &fname, &overwrite ) )
436                 return EXPP_ReturnPyObjError( PyExc_TypeError,
437                                               "expected filename and optional int (overwrite flag) as arguments" );
438
439         for( li = G.main->library.first; li; li = li->id.next ) {
440                 if( BLI_streq( li->name, fname ) ) {
441                         return EXPP_ReturnPyObjError( PyExc_AttributeError,
442                                                       "cannot overwrite used library" );
443                 }
444         }
445
446         /* for safety, any filename with .B.blend is considered the default one
447          * and not accepted here. */
448         if( strstr( fname, ".B.blend" ) )
449                 return EXPP_ReturnPyObjError( PyExc_AttributeError,
450                                               "filename can't contain the substring \".B.blend\" in it." );
451
452         len = strlen( fname );
453
454         if( len > FILE_MAXFILE )
455                 return EXPP_ReturnPyObjError( PyExc_AttributeError,
456                                               "filename is too long!" );
457         else if( BLI_exists( fname ) && !overwrite )
458                 return EXPP_ReturnPyObjError( PyExc_AttributeError,
459                                               "file already exists and overwrite flag was not given." );
460
461         disable_where_script( 1 );      /* to avoid error popups in the write_* functions */
462
463         if( BLI_testextensie( fname, ".blend" ) ) {
464                 if( G.fileflags & G_AUTOPACK )
465                         packAll(  );
466                 if( !BLO_write_file( fname, G.fileflags, &error ) ) {
467                         disable_where_script( 0 );
468                         return EXPP_ReturnPyObjError( PyExc_SystemError,
469                                                       error );
470                 }
471         } else if( BLI_testextensie( fname, ".dxf" ) )
472                 write_dxf( fname );
473         else if( BLI_testextensie( fname, ".stl" ) )
474                 write_stl( fname );
475         else if( BLI_testextensie( fname, ".wrl" ) )
476                 write_vrml( fname );
477         else if( BLI_testextensie( fname, ".obj" ) )
478                 write_videoscape( fname );
479         else {
480                 disable_where_script( 0 );
481                 return EXPP_ReturnPyObjError( PyExc_AttributeError,
482                                               "unknown file extension." );
483         }
484
485         disable_where_script( 0 );
486
487         Py_INCREF( Py_None );
488         return Py_None;
489 }
490
491 static PyObject * Blender_UpdateMenus( PyObject * self )
492 {
493
494         BPyMenu_RemoveAllEntries();
495
496         if (BPyMenu_Init(1) == -1)
497                 return EXPP_ReturnPyObjError( PyExc_RuntimeError,
498                         "invalid scripts dir");
499
500         Py_INCREF( Py_None );
501         return Py_None;
502 }
503
504 /*****************************************************************************/
505 /* Function:            initBlender              */
506 /*****************************************************************************/
507 void M_Blender_Init( void )
508 {
509         PyObject *module;
510         PyObject *dict;
511
512         g_blenderdict = NULL;
513
514         module = Py_InitModule3( "Blender", Blender_methods,
515                 "The main Blender module" );
516
517         types_InitAll(  );      /* set all our pytypes to &PyType_Type */
518
519         dict = PyModule_GetDict( module );
520         g_blenderdict = dict;
521
522         PyDict_SetItemString( dict, "bylink", EXPP_incr_ret_False() );
523         PyDict_SetItemString( dict, "link", EXPP_incr_ret ( Py_None ) );
524         PyDict_SetItemString( dict, "event", PyString_FromString( "" ) );
525
526         PyDict_SetItemString( dict, "Types", Types_Init(  ) );
527         PyDict_SetItemString( dict, "sys", sys_Init(  ) );
528         PyDict_SetItemString( dict, "Registry", Registry_Init(  ) );
529         PyDict_SetItemString( dict, "Scene", Scene_Init(  ) );
530         PyDict_SetItemString( dict, "Object", Object_Init(  ) );
531         PyDict_SetItemString( dict, "Material", Material_Init(  ) );
532         PyDict_SetItemString( dict, "Camera", Camera_Init(  ) );
533         PyDict_SetItemString( dict, "Lamp", Lamp_Init(  ) );
534         PyDict_SetItemString( dict, "Lattice", Lattice_Init(  ) );
535         PyDict_SetItemString( dict, "Curve", Curve_Init(  ) );
536         PyDict_SetItemString( dict, "Armature", Armature_Init(  ) );
537         PyDict_SetItemString( dict, "Ipo", Ipo_Init(  ) );
538         PyDict_SetItemString( dict, "IpoCurve", IpoCurve_Init(  ) );
539         PyDict_SetItemString( dict, "Metaball", Metaball_Init(  ) );
540         PyDict_SetItemString( dict, "Image", Image_Init(  ) );
541         PyDict_SetItemString( dict, "Window", Window_Init(  ) );
542         PyDict_SetItemString( dict, "Draw", Draw_Init(  ) );
543         PyDict_SetItemString( dict, "BGL", BGL_Init(  ) );
544         PyDict_SetItemString( dict, "Effect", Effect_Init(  ) );
545         PyDict_SetItemString( dict, "Text", Text_Init(  ) );
546         PyDict_SetItemString( dict, "Text3d", Text3d_Init(  ) );
547         PyDict_SetItemString( dict, "World", World_Init(  ) );
548         PyDict_SetItemString( dict, "Texture", Texture_Init(  ) );
549         PyDict_SetItemString( dict, "NMesh", NMesh_Init(  ) );
550         PyDict_SetItemString( dict, "Noise", Noise_Init(  ) );
551         PyDict_SetItemString( dict, "Mathutils", Mathutils_Init(  ) );
552         PyDict_SetItemString( dict, "Library", Library_Init(  ) );
553         PyDict_SetItemString( dict, "Sound", Sound_Init(  ) );
554
555         PyDict_SetItemString( dict, "CurNurb", CurNurb_Init(  ) );
556
557         PyModule_AddIntConstant( module, "TRUE", 1 );
558         PyModule_AddIntConstant( module, "FALSE", 0 );
559 }