This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/bpython/intern/BPY_main.c
Kent Mein d0e346d544 updated .c files to include:
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

Just need to finish cpp files now :)

Kent
--
mein@cs.umn.edu
2002-11-25 12:02:15 +00:00

677 lines
16 KiB
C

/**
* blenkernel/py_main.c
* (cleaned up somewhat nzc apr-2001)
* $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.
*
* The Original Code is: all of this file.
*
* Contributor(s): none yet.
*
* ***** END GPL/BL DUAL LICENSE BLOCK *****
*
*/
/* NOTE: all externally callable routines have the prefix BPY_
-- see also ../include/BPY_extern.h */
#include "BPY_main.h"
#include "BPY_modules.h"
#include "BPY_macros.h"
#include "DNA_space_types.h"
#include "b_interface.h"
#include "mydevice.h"
#include "import.h"
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
/* PROTOS */
extern void init_frozenmodules(void); // frozen module library
extern void initmxTextTools(void);
extern void inittess(void); // tesselator module
void init_ourImport(void);
/* GLOBALS */
PyObject* ErrorObject = NULL;
PyObject* callback = NULL;
PyObject* callbackArgs = NULL;
PyObject* blenderprogname = NULL;
ID* script_link_id = NULL;
/*------------------------------------------------------------------------*/
/* START PYTHON (from creator.c) */
void INITMODULE(BLENDERMODULE)(void);
struct _inittab blendermodules[] = {
#ifndef SHAREDMODULE // Blender module can alternatively be compiled shared
#ifdef STATIC_TEXTTOOLS // see api.h
{ "mxTextTools" , initmxTextTools },
#endif
{ MODNAME(BLENDERMODULE) , INITMODULE(BLENDERMODULE) },
#endif
#ifdef NO_RELEASE
{ "tess" , inittess }, // GLU tesselator wrapper module
#endif
{ 0, 0}
};
/* hack to make sure, inittab is extended only first time */
static short g_is_extended = 0;
/** (Re)initializes the Python Interpreter.
* This function should be only called if the Python interpreter
* was not yet initialized (check Py_IsInitialized() )
*/
static void initBPythonInterpreter(void)
{
Py_Initialize();
init_ourImport(); /* our own import, later: security */
if (!BPY_CHECKFLAG(G_NOFROZEN)) {
init_frozenmodules(); /* initialize frozen modules unless disabled */
}
init_syspath();
}
/** This function initializes Blender Python. It should be called only
* once at start, which is currently not the case (GameEngine Python).
* Therefore, it contains some dirty workarounds. They will be thrown
* into the grachten once the different APIs are merged into something
* more consistent.
*
*/
void BPY_start_python(void)
{
Py_SetProgramName("blender");
if (BPY_DEBUGFLAG) {
Py_VerboseFlag = 1;
Py_DebugFlag = 1;
} else {
#ifndef EXPERIMENTAL
Py_FrozenFlag = 1; /* no warnings about non set PYTHONHOME */
Py_NoSiteFlag = 1; /* disable auto site module import */
#endif
}
if (!g_is_extended) {
g_is_extended = 1;
PyImport_ExtendInittab(blendermodules); /* extend builtin module table */
}
initBPythonInterpreter();
#ifdef NO_RELEASE
if (PyRun_SimpleString("import startup"))
{
BPY_warn(("init script not found, continuing anyway\n"));
PyErr_Clear();
return;
}
#endif
}
/** Ends the Python interpreter. This cleans up all global variables
* Blender-Python descriptor objects will (MUST!) decref on their
* raw blender objects, so this function should be called more or less
* immediately before garbage collection actions.
*/
void BPY_end_python(void)
{
Py_Finalize();
}
void BPY_free_compiled_text(Text* text)
{
if (!text->compiled) return;
Py_DECREF((PyObject*) text->compiled);
text->compiled = NULL;
}
void syspath_append(PyObject *dir)
{
PyObject *m, *d;
PyObject *o;
PyErr_Clear();
m = PyImport_ImportModule("sys");
d = PyModule_GetDict(m);
o = PyDict_GetItemString(d, "path");
if (!PyList_Check(o)) {
return;
}
PyList_Append(o, dir);
if (PyErr_Occurred()) {
Py_FatalError("could not build sys.path");
}
Py_DECREF(m);
}
/* build blender specific system path for external modules */
void init_syspath(void)
{
PyObject *path;
PyObject *m, *d;
PyObject *p;
char *c;
char execdir[PATH_MAXCHAR], *progname;
int n;
path = Py_BuildValue("s", bprogname);
m = PyImport_ImportModule(MODNAME(BLENDERMODULE) ".sys");
if (m) {
d = PyModule_GetDict(m);
PyDict_SetItemString(d, "progname", path);
Py_DECREF(m);
} else {
BPY_debug(("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);
execdir[n] = '\0';
p = Py_BuildValue("s", execdir);
syspath_append(p); /* append to module search path */
/* set Blender.sys.progname */
} else {
BPY_debug(("Warning: could not determine argv[0] path\n"));
}
/* TODO look for the blender executable in the search path */
BPY_debug(("append to syspath: %s\n", U.pythondir));
if (U.pythondir) {
p = Py_BuildValue("s", U.pythondir);
syspath_append(p); /* append to module search path */
}
BPY_debug(("append done\n"));
}
#define FILENAME_LENGTH 24
typedef struct _ScriptError {
char filename[FILENAME_LENGTH];
int lineno;
} ScriptError;
ScriptError g_script_error;
int BPY_Err_getLinenumber()
{
return g_script_error.lineno;
}
const char *BPY_Err_getFilename()
{
return g_script_error.filename;
}
/** Returns (PyString) filename from a traceback object */
PyObject *traceback_getFilename(PyObject *tb)
{
PyObject *v;
v = PyObject_GetAttrString(tb, "tb_frame"); Py_DECREF(v);
v = PyObject_GetAttrString(v, "f_code"); Py_DECREF(v);
v = PyObject_GetAttrString(v, "co_filename");
return v;
}
/** Blender Python error handler. This catches the error and stores
* filename and line number in a global
*/
void BPY_Err_Handle(Text *text)
{
PyObject *exception, *err, *tb, *v;
PyErr_Fetch(&exception, &err, &tb);
if (!exception && !tb) {
printf("FATAL: spurious exception\n");
return;
}
strcpy(g_script_error.filename, getName(text));
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");
g_script_error.lineno = PyInt_AsLong(v);
Py_XDECREF(v);
return;
} else {
PyErr_NormalizeException(&exception, &err, &tb);
PyErr_Restore(exception, err, tb); // takes away reference!
PyErr_Print();
tb = PySys_GetObject("last_traceback");
Py_INCREF(tb);
// 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)), getName(text)))
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);
}
}
/** Runs a Python string in the global name space of the given dictionary
'globaldict' */
static PyObject *newGlobalDictionary(void)
{
PyObject *d = PyDict_New();
PyDict_SetItemString(d, "__builtins__", PyEval_GetBuiltins());
PyDict_SetItemString(d, "__name__", PyString_FromString("__main__"));
return d;
}
static void releaseGlobalDictionary(PyObject *d)
{
BPY_debug(("--- CLEAR namespace\n"));
PyDict_Clear(d);
Py_DECREF(d); // release dictionary
}
PyObject *BPY_runPython(Text *text, PyObject *globaldict)
{
PyObject *ret;
char* buf = NULL;
if (!text->compiled)
{
buf = txt_to_buf(text);
/* bah, what a filthy hack -- removed */
/* strcat(buf, "\n"); */
text->compiled = Py_CompileString(buf, getName(text), Py_file_input);
MEM_freeN(buf);
if (PyErr_Occurred())
{
BPY_free_compiled_text(text);
return 0;
}
}
BPY_debug(("Run Python script \"%s\" ...\n", getName(text)));
ret = PyEval_EvalCode(text->compiled, globaldict, globaldict);
return ret;
}
/** This function is executed whenever ALT+PKEY is pressed -> drawtext.c
It returns the global namespace dictionary of the script context
(which is created newly when CLEAR_NAMESPACE is defined).
This may be stored in the SpaceText instance to give control over
namespace persistence. Remember that the same script may be executed
in several windows..
Namespace persistence is desired for scripts that use the GUI and
store callbacks to the current script.
*/
PyObject *BPY_txt_do_python(SpaceText *st)
{
PyObject* d = NULL;
PyObject *ret;
Text *text = st->text;
if (!text)
{
return NULL;
}
/* TODO: make this an option: */
#ifdef CLEAR_NAMESPACE
BPY_debug(("--- enable clear namespace\n"));
st->flags |= ST_CLEAR_NAMESPACE;
#endif
#ifdef CLEAR_NAMESPACE
d = newGlobalDictionary();
#else
d = PyModule_GetDict(PyImport_AddModule("__main__"));
#endif
ret = BPY_runPython(text, d);
if (!ret) {
#ifdef CLEAR_NAMESPACE
releaseGlobalDictionary(d);
#endif
BPY_Err_Handle(text);
Py_Finalize();
initBPythonInterpreter();
return NULL;
}
else
Py_DECREF(ret);
/* The following lines clear the global name space of the python
* interpreter. This is desired to release objects after execution
* of a script (remember that each wrapper object increments the refcount
* of the Blender Object.
* Exception: scripts that use the GUI rely on the
* persistent global namespace, so they need a workaround: The namespace
* is released when the GUI is exit.
* See opy_draw.c:Method_Register()
*
*/
#ifdef CLEAR_NAMESPACE
if (st->flags & ST_CLEAR_NAMESPACE) {
releaseGlobalDictionary(d);
garbage_collect(getGlobal()->main);
}
#endif
return d;
}
/****************************************/
/* SCRIPTLINKS */
static void do_all_scriptlist(ListBase* list, short event)
{
ID *id;
id = list->first;
while (id)
{
BPY_do_pyscript (id, event);
id = id->next;
}
}
void BPY_do_all_scripts(short event)
{
do_all_scriptlist(getObjectList(), event);
do_all_scriptlist(getLampList(), event);
do_all_scriptlist(getCameraList(), event);
do_all_scriptlist(getMaterialList(), event);
do_all_scriptlist(getWorldList(), event);
BPY_do_pyscript(&scene_getCurrent()->id, event);
}
char *event_to_name(short event)
{
switch (event) {
case SCRIPT_FRAMECHANGED:
return "FrameChanged";
case SCRIPT_ONLOAD:
return "OnLoad";
case SCRIPT_REDRAW:
return "Redraw";
default:
return "Unknown";
}
}
void BPY_do_pyscript(ID *id, short event)
{
int i, offset;
char evName[24] = "";
char* structname = NULL;
ScriptLink* scriptlink;
PyObject *globaldict;
switch(GET_ID_TYPE(id)) {
case ID_OB: structname= "Object"; break;
case ID_LA: structname= "Lamp"; break;
case ID_CA: structname= "Camera"; break;
case ID_MA: structname= "Material"; break;
case ID_WO: structname= "World"; break;
case ID_SCE: structname= "Scene"; break;
default: return;
}
offset = BLO_findstruct_offset(structname, "scriptlink");
if (offset < 0)
{
BPY_warn(("Internal error, unable to find script link\n"));
return;
}
scriptlink = (ScriptLink*) (((char*)id) + offset);
/* no script provided */
if (!scriptlink->totscript) return;
/* Debugging output */
switch (event)
{
case SCRIPT_FRAMECHANGED:
strcpy(evName, "SCRIPT_FRAMECHANGED");
BPY_debug(("do_pyscript(%s, %s)\n", getIDName(id), evName));
break;
case SCRIPT_ONLOAD:
strcpy(evName, "SCRIPT_ONLOAD");
BPY_debug(("do_pyscript(%s, %s)\n", getIDName(id), evName));
break;
case SCRIPT_REDRAW:
strcpy(evName, "SCRIPT_REDRAW");
BPY_debug(("do_pyscript(%s, %s)\n", getIDName(id), evName));
break;
default:
BPY_debug(("do_pyscript(): This should not happen !!!"));
break;
}
/* END DEBUGGING */
#ifndef SHAREDMODULE
set_scriptlinks(id, event);
#endif
disable_where_script(1);
for (i = 0; i < scriptlink->totscript; i++)
{
if (scriptlink->flag[i] == event && scriptlink->scripts[i])
{
BPY_debug(("Evaluate script \"%s\" ...\n",
getIDName(scriptlink->scripts[i])));
script_link_id = id;
#ifdef CLEAR_NAMESPACE
globaldict = newGlobalDictionary();
#else
globaldict = PyModule_GetDict(PyImport_AddModule("__main__"));
#endif
BPY_runPython((Text*) scriptlink->scripts[i], globaldict);
#ifdef CLEAR_NAMESPACE
releaseGlobalDictionary(globaldict);
#endif
script_link_id = NULL;
BPY_debug(("... done\n"));
}
}
#ifndef SHAREDMODULE
release_scriptlinks(id);
#endif
disable_where_script(0);
}
void BPY_clear_bad_scriptlink(ID *id, Text *byebye)
{
ScriptLink* scriptlink;
int offset = -1;
char* structname = NULL;
int i;
switch (GET_ID_TYPE(id)) {
case ID_OB: structname = "Object"; break;
case ID_LA: structname = "Lamp"; break;
case ID_CA: structname = "Camera"; break;
case ID_MA: structname = "Material"; break;
case ID_WO: structname = "World"; break;
case ID_SCE: structname = "Scene"; break;
}
if (!structname) return;
offset= BLO_findstruct_offset(structname, "scriptlink");
if (offset<0) return;
scriptlink= (ScriptLink *) (((char *)id) + offset);
for(i=0; i<scriptlink->totscript; i++)
if ((Text*)scriptlink->scripts[i] == byebye)
scriptlink->scripts[i] = NULL;
}
void BPY_clear_bad_scriptlist(ListBase *list, Text *byebye)
{
ID *id;
id= list->first;
while (id)
{
BPY_clear_bad_scriptlink(id, byebye);
id= id->next;
}
}
void BPY_clear_bad_scriptlinks(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);
}
void BPY_free_scriptlink(ScriptLink *slink)
{
if (slink->totscript)
{
if(slink->flag) MEM_freeN(slink->flag);
if(slink->scripts) MEM_freeN(slink->scripts);
}
}
void BPY_copy_scriptlink(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);
}
}
/*
* Python alien graphics format conversion framework
*
* $Id$
*
*
*
*/
/* import importloader module with registered importers */
#include "BPY_extern.h"
#include "Python.h"
int BPY_call_importloader(char *name)
{
PyObject *mod, *tmp, *meth, *args;
int i, success = 0;
init_syspath();
mod = PyImport_ImportModule("Converter.importloader");
if (mod) {
meth = PyObject_GetAttrString(mod, "process"); // new ref
args = Py_BuildValue("(s)", name);
tmp = PyEval_CallObject(meth, args);
Py_DECREF(meth);
if (PyErr_Occurred()) {
PyErr_Print();
}
if (tmp) {
i = PyInt_AsLong(tmp);
if (i)
success = 1;
Py_DECREF(tmp);
}
Py_DECREF(mod);
} else {
PyErr_Print();
BPY_warn(("couldn't import 'importloader' \n"));
}
return success;
}
// more to come...