BPython:
[blender.git] / source / blender / python / api2_2x / Blender.c
1 /* 
2  *
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 #include <BIF_usiblender.h>
36 #include <BLI_blenlib.h>
37 #include <BLO_writefile.h>
38 #include <BKE_global.h>
39 #include <BKE_packedFile.h>
40 #include <BPI_script.h>
41 #include <BSE_headerbuttons.h>
42 #include <DNA_ID.h>
43 #include <DNA_object_types.h>
44 #include <DNA_scene_types.h>
45 #include <DNA_screen_types.h> /* for SPACE_VIEW3D */
46 #include <DNA_space_types.h> /* for SPACE_VIEW3D */
47 #include <DNA_userdef_types.h>
48 #include <BKE_ipo.h>
49
50 #include "gen_utils.h"
51 #include "modules.h"
52 #include "../BPY_extern.h" /* for bpy_gethome() */
53
54 /* From Window.h, used here by Blender_Redraw */
55 PyObject *M_Window_Redraw(PyObject *self, PyObject *args);
56
57 /**********************************************************/
58 /* Python API function prototypes for the Blender module.       */
59 /**********************************************************/
60 static PyObject *Blender_Set (PyObject *self, PyObject *args);
61 static PyObject *Blender_Get (PyObject *self, PyObject *args);
62 static PyObject *Blender_Redraw(PyObject *self, PyObject *args);
63 static PyObject *Blender_ReleaseGlobalDict(PyObject *self, PyObject *args);
64 static PyObject *Blender_Quit(PyObject *self);
65 static PyObject *Blender_Load(PyObject *self, PyObject *args);
66 static PyObject *Blender_Save(PyObject *self, PyObject *args);
67
68 /*****************************************************************************/
69 /* The following string definitions are used for documentation strings.                  */
70 /* In Python these will be written to the console when doing a                                                   */
71 /* Blender.__doc__                                                                                                                                                                                                                                       */
72 /*****************************************************************************/
73 static char Blender_Set_doc[] =
74 "(request, data) - Update settings in Blender\n\
75 \n\
76 (request) A string identifying the setting to change\n\
77         'curframe'      - Sets the current frame using the number in data";
78
79 static char Blender_Get_doc[] =
80 "(request) - Retrieve settings from Blender\n\
81 \n\
82 (request) A string indentifying the data to be returned\n\
83         'curframe'      - Returns the current animation frame\n\
84         'curtime'       - Returns the current animation time\n\
85         'staframe'      - Returns the start frame of the animation\n\
86         'endframe'      - Returns the end frame of the animation\n\
87         'filename'      - Returns the name of the last file read or written\n\
88         'datadir' - Returns the dir where scripts can save their data, if available\n\
89         'version'       - Returns the Blender version number";
90
91 static char Blender_Redraw_doc[] = "() - Redraw all 3D windows";
92
93 static char Blender_ReleaseGlobalDict_doc[] =
94 "Deprecated, please use the Blender.Registry module solution instead.";
95
96 static char Blender_Quit_doc[] =
97 "() - Quit Blender.  The current data is saved as 'quit.blend' before leaving.";
98
99 static char Blender_Load_doc[] =
100 "(filename) - Load the given .blend file.  If successful, the script is ended\n\
101 immediately.\n\
102 Notes:\n\
103 1 - () - an empty argument loads the default .B.blend file;\n\
104 2 - if the substring '.B.blend' occurs inside 'filename', the default\n\
105 .B.blend file is loaded;\n\
106 3 - The current data is always preserved as an autosave file, for safety;\n\
107 4 - This function only works if the script where it's executed is the\n\
108 only one running.";
109
110 static char Blender_Save_doc[] =
111 "(filename) - Save a .blend file with the given filename.\n\
112 (filename) - A file pathname that should not contain \".B.blend\" in it.";
113
114 /*****************************************************************************/
115 /* Python method structure definition.                                                                                                                                                   */
116 /*****************************************************************************/
117 static struct PyMethodDef Blender_methods[] = {
118         {"Set",          Blender_Set, METH_VARARGS, Blender_Set_doc},
119         {"Get",          Blender_Get, METH_VARARGS, Blender_Get_doc},
120         {"Redraw", Blender_Redraw, METH_VARARGS, Blender_Redraw_doc},
121         {"Quit",         (PyCFunction)Blender_Quit, METH_NOARGS, Blender_Quit_doc},
122         {"Load", Blender_Load, METH_VARARGS, Blender_Load_doc},
123         {"Save", Blender_Save, METH_VARARGS, Blender_Save_doc},
124         {"ReleaseGlobalDict", &Blender_ReleaseGlobalDict,
125                 METH_VARARGS, Blender_ReleaseGlobalDict_doc},
126         {NULL, NULL, 0, NULL}
127 };
128
129 /*****************************************************************************/
130 /* Global variables                                                                                                                                                                                                                                      */
131 /*****************************************************************************/
132 PyObject *g_blenderdict;
133
134 /*****************************************************************************/
135 /* Function:                                                    Blender_Set                                                                                                                                                              */
136 /* Python equivalent:                   Blender.Set                                                                                                                                                              */
137 /*****************************************************************************/
138 static PyObject *Blender_Set (PyObject *self, PyObject *args)
139 {
140         char                    * name;
141         PyObject        * arg;
142         int                                     framenum;
143                         
144         if (!PyArg_ParseTuple(args, "sO", &name, &arg))
145         {
146                 /* TODO: Do we need to generate a nice error message here? */
147                 return (NULL);
148         }
149
150         if (StringEqual (name, "curframe"))
151         {
152                 if (!PyArg_Parse(arg, "i", &framenum))
153                 {
154                 /* TODO: Do we need to generate a nice error message here? */
155                         return (NULL);
156                 }
157
158                 G.scene->r.cfra = framenum;
159
160                 update_for_newframe();
161         }
162         else
163         {
164                 return (EXPP_ReturnPyObjError (PyExc_AttributeError,
165                                                                                                                                                         "bad request identifier"));
166         }
167         return ( EXPP_incr_ret (Py_None) );
168 }
169
170 /*****************************************************************************/
171 /* Function:                                                    Blender_Get                                                                                                                                                              */
172 /* Python equivalent:                   Blender.Get                                                                                                                                                              */
173 /*****************************************************************************/
174 static PyObject *Blender_Get (PyObject *self, PyObject *args)
175 {
176         PyObject        * object;
177         PyObject        * dict;
178         char                    * str;
179                                 
180         if (!PyArg_ParseTuple (args, "O", &object))
181         {
182         /* TODO: Do we need to generate a nice error message here? */
183                 return (NULL);
184         }
185
186         if (PyString_Check (object))
187         {
188                 str = PyString_AsString (object);
189
190                 if (StringEqual (str, "curframe"))
191                 {
192                         return ( PyInt_FromLong (G.scene->r.cfra) );
193                 }
194                 if (StringEqual (str, "curtime"))
195                 {
196                         return ( PyFloat_FromDouble (frame_to_float (G.scene->r.cfra) ) );
197                 }
198                 if (StringEqual (str, "staframe"))
199                 {
200                         return ( PyInt_FromLong (G.scene->r.sfra) );
201                 }
202                 if (StringEqual (str, "endframe"))
203                 {
204                         return ( PyInt_FromLong (G.scene->r.efra) );
205                 }
206                 if (StringEqual (str, "filename"))
207                 {
208                         return ( PyString_FromString (G.sce) );
209                 }
210                 if (StringEqual (str, "datadir"))
211                 {
212                         char datadir[FILE_MAXDIR];
213                         BLI_make_file_string("/", datadir, bpy_gethome(), "bpydata/");
214                         if (BLI_exists(datadir)) return PyString_FromString(datadir);
215                         else return EXPP_incr_ret (Py_None);
216                 }
217                 /* According to the old file (opy_blender.c), the following if
218                          statement is a quick hack and needs some clean up. */
219                 if (StringEqual (str, "vrmloptions"))
220                 {
221                         dict = PyDict_New ();
222
223                         PyDict_SetItemString (dict, "twoside",
224                                                                         PyInt_FromLong (U.vrmlflag & USER_VRML_TWOSIDED));
225
226                         PyDict_SetItemString (dict, "layers",
227                                                                         PyInt_FromLong (U.vrmlflag & USER_VRML_LAYERS));
228
229                         PyDict_SetItemString (dict, "autoscale",
230                                                                         PyInt_FromLong (U.vrmlflag & USER_VRML_AUTOSCALE));
231
232                         return (dict);
233                 } /* End 'quick hack' part. */
234                 if (StringEqual (str, "version"))
235                 {
236                         return ( PyInt_FromLong (G.version) );
237                 }
238                 /* TODO: Do we want to display a usefull message here that the
239                                                                  requested data is unknown?
240                 else
241                 {
242                         return (EXPP_ReturnPyObjError (..., "message") );
243                 }
244                 */
245         }
246         else
247         {
248                 return (EXPP_ReturnPyObjError (PyExc_AttributeError,
249                                                                                                                                                 "expected string argument"));
250         }
251
252         return (EXPP_ReturnPyObjError (PyExc_AttributeError,
253                                                                                                                                 "bad request identifier"));
254 }
255
256 /*****************************************************************************/
257 /* Function:                                                    Blender_Redraw                                                                                                                                           */
258 /* Python equivalent:                   Blender.Redraw                                                                                                                                           */
259 /*****************************************************************************/
260 static PyObject *Blender_Redraw(PyObject *self, PyObject *args)
261 {
262         int wintype = SPACE_VIEW3D;
263
264         if (!PyArg_ParseTuple (args, "|i", &wintype))
265         {
266                 return EXPP_ReturnPyObjError (PyExc_TypeError,
267                                                                                                 "expected int argument (or nothing)");
268         }
269
270         return M_Window_Redraw(self, Py_BuildValue("(i)", wintype));
271 }
272
273 /*****************************************************************************/
274 /* Function:                                                    Blender_ReleaseGlobalDict                                                                                                        */
275 /* Python equivalent:                   Blender.ReleaseGlobalDict                                                                                                        */
276 /* Description:                                         Deprecated function.                                                                                                                     */
277 /*****************************************************************************/
278 static PyObject *Blender_ReleaseGlobalDict(PyObject *self, PyObject *args)
279 {
280         Py_INCREF(Py_None);
281         return Py_None;
282 }
283
284 /*****************************************************************************/
285 /* Function:                                                    Blender_Quit                                                                                                                                                     */
286 /* Python equivalent:                   Blender.Quit                                                                                                                                                     */
287 /*****************************************************************************/
288 static PyObject *Blender_Quit(PyObject *self)
289 {
290         BIF_write_autosave(); /* save the current data first */
291
292         exit_usiblender(); /* renames last autosave to quit.blend */
293
294         Py_INCREF(Py_None);
295         return Py_None;
296 }
297
298 static PyObject *Blender_Load(PyObject *self, PyObject *args)
299 {
300         char *fname = NULL;
301         Script *script = NULL;
302
303         if (!PyArg_ParseTuple(args, "|s", &fname))
304                 return EXPP_ReturnPyObjError(PyExc_TypeError,
305                         "expected filename string or nothing (for default file) as argument");
306
307         if (fname && !BLI_exists(fname))
308                 return EXPP_ReturnPyObjError(PyExc_AttributeError,
309                         "requested file doesn't exist!");
310
311         /* We won't let a new .blend file be loaded if there are still other
312          * scripts running, since loading a new file will close and remove them. */
313
314         if (G.main->script.first != G.main->script.last)
315                 return EXPP_ReturnPyObjError(PyExc_RuntimeError,
316                         "there are other scripts running at the Scripts win, close them first!");
317
318         /* trick: mark the script so that its script struct won't be freed after
319          * the script is executed (to avoid a double free warning on exit): */
320         script = G.main->script.first;
321         script->flags |= SCRIPT_GUI;
322
323         BIF_write_autosave(); /* for safety let's preserve the current data */
324
325         /* for safety, any filename with .B.blend is considered the default one.
326          * It doesn't seem necessary to compare file attributes (like st_ino and
327          * st_dev, according to the glibc info pages) to find out if the given
328          * filename, that may have been given with a twisted misgiving path, is the
329          * default one for sure.  Taking any .B.blend file as the default is good
330          * enough here.  Note: the default file requires extra clean-up done by
331          * BIF_read_homefile: freeing the user theme data. */
332         if (!fname || strstr(fname, ".B.blend"))
333                 BIF_read_homefile();
334         else
335                 BIF_read_file(fname);
336
337         Py_INCREF(Py_None);
338         return Py_None;
339 }
340
341 static PyObject *Blender_Save(PyObject *self, PyObject *args)
342 {
343         char *fname = NULL;
344         char savefname[FILE_MAXFILE];
345         int overwrite = 0, len = 0;
346         char *error = NULL;
347         Library *li;
348
349         if (!PyArg_ParseTuple(args, "s|i", &fname, &overwrite))
350                 return EXPP_ReturnPyObjError(PyExc_TypeError,
351                         "expected filename and optional int (overwrite flag) as arguments");
352
353         for (li = G.main->library.first; li; li = li->id.next) {
354                 if (BLI_streq(li->name, fname)) {
355                         return EXPP_ReturnPyObjError(PyExc_AttributeError,
356                                 "cannot overwrite used library");
357                 }
358         }
359         
360         /* for safety, any filename with .B.blend is considered the default one
361          * and not accepted here. */
362         if (strstr(fname, ".B.blend"))
363                 return EXPP_ReturnPyObjError(PyExc_AttributeError,
364                         "filename can't contain the substring \".B.blend\" in it.");
365
366         len = strlen(fname);
367
368         if (len > FILE_MAXFILE - 7) /* 6+1 for eventual .blend added below */
369                 return EXPP_ReturnPyObjError(PyExc_AttributeError,
370                         "filename is too long!");
371         else
372                 BLI_strncpy(savefname, fname, len + 1);
373
374         if (!strstr(fname, ".blend"))
375                 BLI_strncpy(savefname + len, ".blend", 7); /* 7: BLI_strncpy adds '\0'*/
376
377         if (BLI_exists(savefname) && !overwrite)
378                 return EXPP_ReturnPyObjError(PyExc_AttributeError,
379                         "file already exists and overwrite flag was not given.");
380
381         if (G.fileflags & G_AUTOPACK) packAll();
382
383         if (!BLO_write_file(savefname, G.fileflags, &error))
384                 return EXPP_ReturnPyObjError(PyExc_SystemError, error);
385
386         Py_INCREF(Py_None);
387         return Py_None;
388 }
389
390 /*****************************************************************************/
391 /* Function:                                                    initBlender                                                                                                                                                              */
392 /*****************************************************************************/
393 void M_Blender_Init (void)
394 {
395         PyObject                                * module;
396         PyObject                                * dict;
397
398         g_blenderdict = NULL;
399
400         /* TODO: create a docstring for the Blender module */
401         module = Py_InitModule3("Blender", Blender_methods, NULL);
402
403         types_InitAll(); /* set all our pytypes to &PyType_Type*/
404
405         dict = PyModule_GetDict (module);
406         g_blenderdict = dict;
407
408         Py_INCREF(Py_False);
409         PyDict_SetItemString(dict, "bylink", Py_False);
410         Py_INCREF(Py_None);
411         PyDict_SetItemString(dict, "link", Py_None);
412         PyDict_SetItemString(dict, "event", PyString_FromString(""));
413
414         PyDict_SetItemString (dict, "Types",            Types_Init());
415         PyDict_SetItemString (dict, "sys",                      sys_Init());
416         PyDict_SetItemString (dict, "Registry", Registry_Init());
417         PyDict_SetItemString (dict, "Scene",            Scene_Init());
418         PyDict_SetItemString (dict, "Object",           Object_Init());
419         PyDict_SetItemString (dict, "Material", Material_Init());
420         PyDict_SetItemString (dict, "Camera",           Camera_Init());
421         PyDict_SetItemString (dict, "Lamp",                     Lamp_Init());
422         PyDict_SetItemString (dict, "Lattice",  Lattice_Init());
423         PyDict_SetItemString (dict, "Curve",            Curve_Init());
424         PyDict_SetItemString (dict, "Armature", Armature_Init());
425         PyDict_SetItemString (dict, "Ipo",                      Ipo_Init());
426         PyDict_SetItemString (dict, "IpoCurve", IpoCurve_Init());
427         PyDict_SetItemString (dict, "Metaball", Metaball_Init());
428         PyDict_SetItemString (dict, "Image",            Image_Init());
429         PyDict_SetItemString (dict, "Window",           Window_Init());
430         PyDict_SetItemString (dict, "Draw",                     Draw_Init());
431         PyDict_SetItemString (dict, "BGL",                      BGL_Init());
432         PyDict_SetItemString (dict, "Effect",           Effect_Init());
433         PyDict_SetItemString (dict, "Text",                     Text_Init());
434         PyDict_SetItemString (dict, "World",            World_Init());
435         PyDict_SetItemString (dict, "Texture",  Texture_Init());
436         PyDict_SetItemString (dict, "NMesh",            NMesh_Init());
437         PyDict_SetItemString (dict, "Noise",            Noise_Init());
438         PyDict_SetItemString (dict, "Mathutils",Mathutils_Init());
439         PyDict_SetItemString (dict, "Library",  Library_Init());
440
441         PyModule_AddIntConstant(module, "TRUE",  1);
442         PyModule_AddIntConstant(module, "FALSE",  0);
443 }