aboutsummaryrefslogtreecommitdiff
path: root/externals/grill/py/source
diff options
context:
space:
mode:
Diffstat (limited to 'externals/grill/py/source')
-rw-r--r--externals/grill/py/source/bound.cpp143
-rw-r--r--externals/grill/py/source/clmeth.cpp278
-rw-r--r--externals/grill/py/source/main.cpp212
-rw-r--r--externals/grill/py/source/main.h147
-rw-r--r--externals/grill/py/source/modmeth.cpp183
-rw-r--r--externals/grill/py/source/py.cpp327
-rw-r--r--externals/grill/py/source/pyargs.cpp131
-rw-r--r--externals/grill/py/source/pyext.cpp465
-rw-r--r--externals/grill/py/source/pyext.h127
-rw-r--r--externals/grill/py/source/register.cpp86
10 files changed, 2099 insertions, 0 deletions
diff --git a/externals/grill/py/source/bound.cpp b/externals/grill/py/source/bound.cpp
new file mode 100644
index 00000000..651bb52b
--- /dev/null
+++ b/externals/grill/py/source/bound.cpp
@@ -0,0 +1,143 @@
+/*
+
+py/pyext - python external object for PD and MaxMSP
+
+Copyright (c) 2002 Thomas Grill (xovo@gmx.net)
+For information on usage and redistribution, and for a DISCLAIMER OF ALL
+WARRANTIES, see the file, "license.txt," in this distribution.
+
+*/
+
+#include "pyext.h"
+#include "flinternal.h"
+
+t_class *pyext::px_class;
+pyext::py_proxy *pyext::px_head,*pyext::px_tail;
+
+void pyext::py_proxy::px_method(py_proxy *obj,const t_symbol *s,int argc,t_atom *argv)
+{
+ PY_LOCK
+
+ PyObject *args = MakePyArgs(s,AtomList(argc,argv),-1,obj->self != NULL);
+ PyObject *ret = PyObject_CallObject(obj->func,args);
+ if(!ret) {
+ PyErr_Print();
+ }
+ Py_XDECREF(ret);
+
+ PY_UNLOCK
+}
+
+
+PyObject *pyext::pyext_bind(PyObject *,PyObject *args)
+{
+ PyObject *self,*meth;
+ C *name;
+ if(!PyArg_ParseTuple(args, "OsO:pyext_bind", &self,&name,&meth))
+ post("py/pyext - Wrong arguments!");
+ else if(!PyInstance_Check(self) || !(PyMethod_Check(meth) || PyFunction_Check(meth))) {
+ post("py/pyext - Wrong argument types!");
+ }
+ else {
+ t_symbol *recv = gensym(name);
+/*
+ if(GetBound(recv))
+ post("py/pyext - Symbol \"%s\" is already hooked",GetString(recv));
+*/
+ // make a proxy object
+ py_proxy *px = (py_proxy *)object_new(px_class);
+ if(PyMethod_Check(meth)) {
+ PyObject *no = PyObject_GetAttrString(meth,"__name__");
+ meth = PyObject_GetAttr(self,no);
+ Py_DECREF(no);
+ }
+ px->init(recv,self,meth);
+
+ // add it to the list
+ if(px_tail) px_tail->nxt = px;
+ else px_head = px;
+ px_tail = px;
+
+ // Do bind
+ pd_bind(&px->obj.ob_pd,recv);
+
+ Py_INCREF(self); // self is borrowed reference
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyObject *pyext::pyext_unbind(PyObject *,PyObject *args)
+{
+ PyObject *self,*meth;
+ C *name;
+ if(!PyArg_ParseTuple(args, "OsO:pyext_bind", &self,&name,&meth))
+ post("py/pyext - Wrong arguments!");
+ else if(!PyInstance_Check(self) || !(PyMethod_Check(meth) || PyFunction_Check(meth))) {
+ post("py/pyext - Wrong argument types!");
+ }
+ else {
+ t_symbol *recv = gensym(name);
+ if(PyMethod_Check(meth)) {
+ PyObject *no = PyObject_GetAttrString(meth,"__name__");
+ meth = PyObject_GetAttr(self,no); // meth is given a new reference!
+ Py_DECREF(no);
+ }
+
+ // search proxy object
+ py_proxy *pp = NULL,*px = px_head;
+ while(px) {
+ py_proxy *pn = px->nxt;
+ if(recv == px->name && self == px->self && meth == px->func) {
+ if(pp)
+ pp->nxt = pn;
+ else
+ px_head = pn;
+ if(!pn) px_tail = pp;
+ break;
+ }
+ else pp = px;
+ px = pn;
+ }
+
+ // do unbind
+ if(px) {
+ pd_unbind(&px->obj.ob_pd,recv);
+ object_free(px->obj);
+
+ Py_DECREF(self);
+ if(PyMethod_Check(meth)) Py_DECREF(meth);
+ }
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+V pyext::ClearBinding()
+{
+ // search proxy object
+ py_proxy *pp = NULL,*px = px_head;
+ while(px) {
+ py_proxy *pn = px->nxt;
+ if(px->self == pyobj) {
+ if(pp)
+ pp->nxt = pn;
+ else
+ px_head = pn;
+ if(!pn) px_tail = pp;
+
+ Py_DECREF(px->self);
+ if(PyMethod_Check(px->func)) Py_DECREF(px->func);
+
+ pd_unbind(&px->obj.ob_pd,px->name);
+ object_free(px->obj);
+ }
+ else pp = px;
+ px = pn;
+ }
+}
+
+
diff --git a/externals/grill/py/source/clmeth.cpp b/externals/grill/py/source/clmeth.cpp
new file mode 100644
index 00000000..0a30d1f7
--- /dev/null
+++ b/externals/grill/py/source/clmeth.cpp
@@ -0,0 +1,278 @@
+/*
+
+py/pyext - python external object for PD and MaxMSP
+
+Copyright (c) 2002 Thomas Grill (xovo@gmx.net)
+For information on usage and redistribution, and for a DISCLAIMER OF ALL
+WARRANTIES, see the file, "license.txt," in this distribution.
+
+*/
+
+#include "pyext.h"
+
+
+PyMethodDef pyext::meth_tbl[] =
+{
+ {"__init__", pyext::pyext__init__, METH_VARARGS, "Constructor"},
+ {"__del__", pyext::pyext__del__, METH_VARARGS, "Destructor"},
+
+ {"_outlet", pyext::pyext_outlet, METH_VARARGS,"Send message to outlet"},
+ {"_tocanvas", pyext::pyext_tocanvas, METH_VARARGS,"Send message to canvas" },
+
+ { "_bind", pyext::pyext_bind, METH_VARARGS,"Bind function to a receiving symbol" },
+ { "_unbind", pyext::pyext_unbind, METH_VARARGS,"Unbind function from a receiving symbol" },
+#ifdef FLEXT_THREADS
+ { "_detach", pyext::pyext_detach, METH_VARARGS,"Set detach flag for called methods" },
+ { "_stop", pyext::pyext_stop, METH_VARARGS,"Stop running threads" },
+#endif
+ {NULL, NULL, 0, NULL} /* Sentinel */
+};
+
+PyMethodDef pyext::attr_tbl[] =
+{
+ { "__setattr__", pyext::pyext_setattr, METH_VARARGS,"Set class attribute" },
+ { "__getattr__", pyext::pyext_getattr, METH_VARARGS,"Get class attribute" },
+ { NULL, NULL,0,NULL },
+};
+
+
+const C *pyext::pyext_doc =
+ "py/pyext - python external object for PD and MaxMSP, (C)2002 Thomas Grill\n"
+ "\n"
+ "This is the pyext base class. Available methods:\n"
+ "_outlet(self,ix,args...): Send a message to an indexed outlet\n"
+ "_tocanvas(self,args...): Send a message to the parent canvas\n"
+#ifdef FLEXT_THREADS
+ "_detach(self,int): Define whether a called Python method has its own thread\n"
+#endif
+ "_bind(self,name,func): Bind a python function to a symbol\n"
+ "_unbind(self,name,func): Unbind a python function from a symbol\n"
+;
+
+PyObject* pyext::pyext__init__(PyObject *,PyObject *args)
+{
+// post("pyext.__init__ called");
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyObject* pyext::pyext__del__(PyObject *,PyObject *args)
+{
+// post("pyext.__del__ called");
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyObject* pyext::pyext_setattr(PyObject *,PyObject *args)
+{
+ PyObject *self,*name,*val,*ret = NULL;
+ if(!PyArg_ParseTuple(args, "OOO:test_foo", &self,&name,&val)) {
+ // handle error
+ ERRINTERNAL();
+ return NULL;
+ }
+
+ BL handled = false;
+ if(PyString_Check(name)) {
+ char* sname = PyString_AsString(name);
+ if (sname) {
+// post("pyext::setattr %s",sname);
+ }
+ }
+
+ if(!handled) {
+ if(PyInstance_Check(self))
+ PyDict_SetItem(((PyInstanceObject *)self)->in_dict, name,val);
+ else
+ ERRINTERNAL();
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyObject* pyext::pyext_getattr(PyObject *,PyObject *args)
+{
+ PyObject *self,*name,*ret = NULL;
+ if(!PyArg_ParseTuple(args, "OO:test_foo", &self,&name)) {
+ // handle error
+ ERRINTERNAL();
+ }
+
+ if(PyString_Check(name)) {
+ char* sname = PyString_AsString(name);
+ if (sname) {
+ if(!strcmp(sname,"_shouldexit")) {
+ pyext *ext = GetThis(self);
+ if(ext)
+ ret = PyLong_FromLong(ext->shouldexit?1:0);
+ }
+// post("pyext::getattr %s",sname);
+ }
+ }
+
+ if(!ret) {
+#if PY_VERSION_HEX >= 0x02020000
+ ret = PyObject_GenericGetAttr(self,name);
+#else
+ if(PyInstance_Check(self))
+ ret = PyDict_GetItem(((PyInstanceObject *)self)->in_dict,name);
+#endif
+ }
+ return ret;
+}
+
+
+//! Send message to outlet
+PyObject *pyext::pyext_outlet(PyObject *,PyObject *args)
+{
+ BL ok = false;
+ if(PySequence_Check(args)) {
+ PyObject *self = PySequence_GetItem(args,0);
+ PyObject *outl = PySequence_GetItem(args,1);
+ if(
+ self && PyInstance_Check(self) &&
+ outl && PyInt_Check(outl)
+ ) {
+ pyext *ext = GetThis(self);
+
+ I sz = PySequence_Size(args);
+ PyObject *val;
+ BL tp = sz == 3 && PySequence_Check(PySequence_GetItem(args,2));
+
+ if(tp)
+ val = PySequence_GetItem(args,2); // borrowed
+ else
+ val = PySequence_GetSlice(args,2,sz); // new ref
+
+ AtomList *lst = GetPyArgs(val);
+ if(lst) {
+ I o = PyInt_AsLong(outl);
+ if(o >= 1 && o <= ext->Outlets()) {
+ // by using the queue there is no immediate call of the next object
+ // deadlock would occur if this was another py/pyext object!
+ if(lst->Count() && IsSymbol((*lst)[0]))
+ ext->ToQueueAnything(o-1,GetSymbol((*lst)[0]),lst->Count()-1,lst->Atoms()+1);
+ else
+ ext->ToQueueList(o-1,*lst);
+ }
+ else
+ post("pyext: outlet index out of range");
+
+ ok = true;
+ }
+ else
+ post("py/pyext - No data to send");
+ if(lst) delete lst;
+
+ if(!tp) Py_DECREF(val);
+ }
+ }
+
+ if(!ok) post("pyext - Syntax: _outlet(self,outlet,args...)");
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+
+#ifdef FLEXT_THREADS
+//! Detach threads
+PyObject *pyext::pyext_detach(PyObject *,PyObject *args)
+{
+ PyObject *self;
+ int val;
+ if(!PyArg_ParseTuple(args, "Oi:pyext_detach",&self,&val)) {
+ // handle error
+ post("pyext - Syntax: _detach(self,[0/1])");
+ }
+ else {
+ pyext *ext = GetThis(self);
+ ext->m_detach(val != 0);
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+//! Stop running threads
+PyObject *pyext::pyext_stop(PyObject *,PyObject *args)
+{
+ PyObject *self;
+ int val = -1;
+ if(!PyArg_ParseTuple(args, "O|i:pyext_stop",&self,&val)) {
+ // handle error
+ post("pyext - Syntax: _stop(self,{wait time}");
+ }
+ else {
+ pyext *ext = GetThis(self);
+ I cnt = 0;
+ t_atom at;
+ if(val >= 0) flext::SetInt(at,val);
+ ext->m_stop(cnt,&at);
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+#endif
+
+//! Send message to canvas
+PyObject *pyext::pyext_tocanvas(PyObject *,PyObject *args)
+{
+ BL ok = false;
+ if(PySequence_Check(args)) {
+ PyObject *self = PySequence_GetItem(args,0);
+ if(self && PyInstance_Check(self)) {
+ pyext *ext = GetThis(self);
+
+#ifdef PD
+ I sz = PySequence_Size(args);
+ PyObject *val;
+ BL tp = sz == 2 && PySequence_Check(PyTuple_GetItem(args,1));
+
+ if(tp)
+ val = PySequence_GetItem(args,1); // borrowed
+ else
+ val = PySequence_GetSlice(args,1,sz); // new ref
+
+ AtomList *lst = GetPyArgs(val);
+ if(lst) {
+ t_glist *gl = ext->thisCanvas(); //canvas_getcurrent();
+ t_class **cl = (t_pd *)gl;
+ if(cl) {
+#ifdef PD
+ pd_forwardmess(cl,lst->Count(),lst->Atoms());
+#else
+ #pragma message ("Send is not implemented")
+#endif
+ }
+#ifdef _DEBUG
+ else
+ post("pyext - no parent canvas?!");
+#endif
+ ok = true;
+ }
+ else
+ post("py/pyext - No data to send");
+ if(lst) delete lst;
+
+ if(!tp) Py_DECREF(val);
+#else
+#pragma message ("Not implemented for MaxMSP")
+#endif
+ }
+ }
+
+ if(!ok) post("pyext - Syntax: _tocanvas(self,args...)");
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+
diff --git a/externals/grill/py/source/main.cpp b/externals/grill/py/source/main.cpp
new file mode 100644
index 00000000..65e1d274
--- /dev/null
+++ b/externals/grill/py/source/main.cpp
@@ -0,0 +1,212 @@
+/*
+
+py/pyext - python external object for PD and MaxMSP
+
+Copyright (c) 2002 Thomas Grill (xovo@gmx.net)
+For information on usage and redistribution, and for a DISCLAIMER OF ALL
+WARRANTIES, see the file, "license.txt," in this distribution.
+
+*/
+
+#include "main.h"
+
+
+V py::lib_setup()
+{
+ post("");
+ post("py/pyext %s - python script objects, (C)2002 Thomas Grill",PY__VERSION);
+ post("");
+
+ FLEXT_SETUP(pyobj);
+ FLEXT_SETUP(pyext);
+
+ pyref = 0;
+}
+
+FLEXT_LIB_SETUP(py,py::lib_setup)
+
+PyInterpreterState *py::pystate = NULL;
+
+
+I py::pyref = 0;
+PyObject *py::module_obj = NULL;
+PyObject *py::module_dict = NULL;
+
+
+py::py():
+ module(NULL),
+ detach(false),shouldexit(false),thrcount(0),
+ clk(NULL),stoptick(0)
+{
+ Lock();
+ // under Max/MSP: doesn't survive next line.....
+
+ if(!(pyref++)) {
+ Py_Initialize();
+
+ #ifdef FLEXT_THREADS
+ PyEval_InitThreads();
+
+ pystate = PyThreadState_Get()->interp;
+ #endif
+ // register/initialize pyext module only once!
+ module_obj = Py_InitModule(PYEXT_MODULE, func_tbl);
+ module_dict = PyModule_GetDict(module_obj);
+
+ PyModule_AddStringConstant(module_obj,"__doc__",(C *)py_doc);
+
+ #ifdef FLEXT_THREADS
+ pythrmain = PyEval_SaveThread();
+ #endif
+ }
+ else {
+ PY_LOCK
+ Py_INCREF(module_obj);
+ Py_INCREF(module_dict);
+ PY_UNLOCK
+ }
+
+ Unlock();
+
+ clk = clock_new(this,(t_method)tick);
+}
+
+py::~py()
+{
+ if(thrcount) {
+ shouldexit = true;
+
+ // Wait for a certain time
+ for(int i = 0; i < (PY_STOP_WAIT/PY_STOP_TICK) && thrcount; ++i) Sleep((F)(PY_STOP_TICK/1000.));
+
+ // Wait forever
+ post("%s - Waiting for thread termination!",thisName());
+ while(thrcount) Sleep(0.2f);
+ post("%s - Okay, all threads have terminated",thisName());
+ }
+
+/*
+ // don't unregister
+
+ Lock();
+
+ if(!(--pyref)) {
+ Py_DECREF(module_obj);
+ module_obj = NULL;
+ Py_DECREF(module_dict);
+ module_dict = NULL;
+
+ Py_XDECREF(module);
+
+// delete modules; modules = NULL;
+
+ PyEval_AcquireThread(pythrmain);
+ PyThreadState *new_state = PyThreadState_New(pystate); // must have lock
+ PyThreadState *prev_state = PyThreadState_Swap(new_state);
+
+ Py_Finalize();
+ }
+
+ Unlock();
+*/
+ if(clk) clock_free(clk);
+}
+
+
+V py::m_doc()
+{
+ if(dict) {
+ PyObject *docf = PyDict_GetItemString(dict,"__doc__"); // borrowed!!!
+ if(docf && PyString_Check(docf)) {
+ post("");
+ post(PyString_AsString(docf));
+ }
+ }
+}
+
+
+
+
+V py::SetArgs(I argc,t_atom *argv)
+{
+ // script arguments
+ C **sargv = new C *[argc+1];
+ for(int i = 0; i <= argc; ++i) {
+ sargv[i] = new C[256];
+ if(!i)
+ strcpy(sargv[i],thisName());
+ else
+ GetAString(argv[i-1],sargv[i],255);
+ }
+
+ // the arguments to the module are only recognized once! (at first use in a patcher)
+ PySys_SetArgv(argc+1,sargv);
+
+ for(int j = 0; j <= argc; ++j) delete[] sargv[j];
+ delete[] sargv;
+}
+
+V py::ImportModule(const C *name)
+{
+ if(!name) return;
+
+ module = PyImport_ImportModule((C *)name);
+ if (!module) {
+ PyErr_Print();
+ dict = NULL;
+ }
+ else
+ dict = PyModule_GetDict(module); // borrowed
+
+}
+
+
+V py::ReloadModule()
+{
+ if(module) {
+ PyObject *newmod = PyImport_ReloadModule(module);
+ if(!newmod) {
+ PyErr_Print();
+ // old module still exists?!
+// dict = NULL;
+ }
+ else {
+ Py_XDECREF(module);
+ module = newmod;
+ dict = PyModule_GetDict(module); // borrowed
+ }
+ }
+ else
+ post("%s - No module to reload",thisName());
+}
+
+V py::GetModulePath(const C *mod,C *dir,I len)
+{
+#ifdef PD
+ // uarghh... pd doesn't show it's path for extra modules
+
+ C *name;
+ I fd = open_via_path("",mod,".py",dir,&name,len,0);
+ if(fd > 0) close(fd);
+ else name = NULL;
+
+ // if dir is current working directory... name points to dir
+ if(dir == name) strcpy(dir,".");
+#elif defined(MAXMSP)
+ *dir = 0;
+#endif
+}
+
+V py::AddToPath(const C *dir)
+{
+ if(dir && *dir) {
+ PyObject *pobj = PySys_GetObject("path");
+ if(pobj && PyList_Check(pobj)) {
+ PyObject *ps = PyString_FromString(dir);
+ PyList_Append(pobj,ps);
+ }
+ PySys_SetObject("path",pobj);
+ }
+}
+
+
diff --git a/externals/grill/py/source/main.h b/externals/grill/py/source/main.h
new file mode 100644
index 00000000..66b332cf
--- /dev/null
+++ b/externals/grill/py/source/main.h
@@ -0,0 +1,147 @@
+/*
+
+py/pyext - python script object for PD and MaxMSP
+
+Copyright (c) 2002 Thomas Grill (xovo@gmx.net)
+For information on usage and redistribution, and for a DISCLAIMER OF ALL
+WARRANTIES, see the file, "license.txt," in this distribution.
+
+*/
+
+#ifndef __MAIN_H
+#define __MAIN_H
+
+#include <flext.h>
+#include <Python.h>
+#ifndef NT
+#include <unistd.h>
+#endif
+
+#if !defined(FLEXT_VERSION) || (FLEXT_VERSION < 400)
+#error You need at least flext version 0.4.0
+#endif
+
+#define PY__VERSION "0.1.1pre3"
+
+
+#define PYEXT_MODULE "pyext" // name for module
+#define PYEXT_CLASS "_class" // name for base class
+
+#define PY_STOP_WAIT 1000 // ms
+#define PY_STOP_TICK 10 // ms
+
+
+
+#define I int
+#define C char
+#define V void
+#define BL bool
+#define F float
+#define D double
+
+
+#include "main.h"
+
+class py:
+ public flext_base
+{
+ FLEXT_HEADER(py,flext_base)
+
+public:
+ py();
+ ~py();
+ static V lib_setup();
+
+ static PyObject *MakePyArgs(const t_symbol *s,const AtomList &args,I inlet = -1,BL withself = false);
+ static AtomList *GetPyArgs(PyObject *pValue,PyObject **self = NULL);
+
+protected:
+
+ V m_doc();
+
+ PyObject *module,*dict; // inherited user class module and associated dictionary
+
+ static I pyref;
+ static const C *py_doc;
+
+ V GetModulePath(const C *mod,C *dir,I len);
+ V AddToPath(const C *dir);
+ V SetArgs(I argc,t_atom *argv);
+ V ImportModule(const C *name);
+ V ReloadModule();
+
+ V Register(const C *reg);
+ V Unregister(const C *reg);
+ V Reregister(const C *reg);
+ virtual V Reload() = 0;
+
+ static BL IsAnything(const t_symbol *s) { return s && s != sym_bang && s != sym_float && s != sym_int && s != sym_symbol && s != sym_list && s != sym_pointer; }
+
+ enum retval { nothing,atom,sequ /*,tuple,list*/ };
+
+ // --- module stuff -----
+
+ static PyObject *module_obj,*module_dict;
+ static PyMethodDef func_tbl[];
+
+ static PyObject *py__doc__(PyObject *,PyObject *args);
+ static PyObject *py_send(PyObject *,PyObject *args);
+#ifdef FLEXT_THREADS
+ static PyObject *py_priority(PyObject *,PyObject *args);
+#endif
+
+ static PyObject *py_samplerate(PyObject *,PyObject *args);
+ static PyObject *py_blocksize(PyObject *,PyObject *args);
+ static PyObject *py_inchannels(PyObject *,PyObject *args);
+ static PyObject *py_outchannels(PyObject *,PyObject *args);
+
+ // ----thread stuff ------------
+
+ V m_detach(BL det) { detach = det; }
+ virtual V m_stop(int argc,t_atom *argv);
+
+ BL detach,shouldexit;
+ I thrcount;
+ t_clock *clk;
+ I stoptick;
+
+ static V tick(py *obj);
+
+public:
+ static PyInterpreterState *pystate;
+ PyThreadState *pythrmain;
+
+#ifdef FLEXT_THREADS
+ ThrMutex mutex;
+ V Lock() { mutex.Unlock(); }
+ V Unlock() { mutex.Unlock(); }
+#else
+ V Lock() {}
+ V Unlock() {}
+#endif
+
+protected:
+ // callbacks
+
+ FLEXT_CALLBACK_B(m_detach)
+ FLEXT_CALLBACK_V(m_stop)
+ FLEXT_CALLBACK(m_doc)
+};
+
+#ifdef FLEXT_THREADS
+#define PY_LOCK \
+ { \
+ PyThreadState *thrst = PyThreadState_New(pystate); \
+ PyEval_AcquireThread(thrst);
+
+#define PY_UNLOCK \
+ PyThreadState_Clear(thrst); /* must have lock */ \
+ PyEval_ReleaseThread(thrst); \
+ PyThreadState_Delete(thrst); /* needn't have lock */ \
+ }
+#else
+#define PY_LOCK
+#define PY_UNLOCK
+#endif
+
+#endif
diff --git a/externals/grill/py/source/modmeth.cpp b/externals/grill/py/source/modmeth.cpp
new file mode 100644
index 00000000..51e103ed
--- /dev/null
+++ b/externals/grill/py/source/modmeth.cpp
@@ -0,0 +1,183 @@
+/*
+
+py/pyext - python external object for PD and MaxMSP
+
+Copyright (c) 2002 Thomas Grill (xovo@gmx.net)
+For information on usage and redistribution, and for a DISCLAIMER OF ALL
+WARRANTIES, see the file, "license.txt," in this distribution.
+
+*/
+
+#include "main.h"
+
+
+// function table for module
+PyMethodDef py::func_tbl[] =
+{
+ { "_send", py::py_send, METH_VARARGS,"Send message to a named object" },
+#ifdef FLEXT_THREADS
+ { "_priority", py::py_priority, METH_VARARGS,"Set priority of current thread" },
+#endif
+
+ { "_samplerate", py::py_samplerate, 0,"Get system sample rate" },
+ { "_blocksize", py::py_blocksize, 0,"Get system block size" },
+ { "_inchannels", py::py_inchannels, 0,"Get number of audio in channels" },
+ { "_outchannels", py::py_outchannels, 0,"Get number of audio out channels" },
+
+ {NULL, NULL, 0, NULL} // sentinel
+};
+
+const C *py::py_doc =
+ "py/pyext - python external object for PD and MaxMSP, (C)2002 Thomas Grill\n"
+ "\n"
+ "This is the pyext module. Available function:\n"
+ "_send(args...): Send a message to a send symbol\n"
+#ifdef FLEXT_THREADS
+ "_priority(int): Raise/lower thread priority\n"
+#endif
+ "_samplerate(): Get system sample rate\n"
+ "_blocksize(): Get current blocksize\n"
+ "_inchannels(): Get number of audio in channels\n"
+ "_outchannels(): Get number of audio out channels\n"
+;
+
+
+
+V py::tick(py *th)
+{
+ th->Lock();
+
+ if(!th->thrcount) {
+ // all threads have stopped
+ th->shouldexit = false;
+ th->stoptick = 0;
+ }
+ else {
+ // still active threads
+ if(!--th->stoptick) {
+ post("%s - Threads couldn't be stopped entirely - %i remaining",th->thisName(),th->thrcount);
+ th->shouldexit = false;
+ }
+ else
+ // continue waiting
+ clock_delay(th->clk,PY_STOP_TICK);
+ }
+
+ th->Unlock();
+}
+
+V py::m_stop(int argc,t_atom *argv)
+{
+ if(thrcount) {
+ Lock();
+
+ I wait = PY_STOP_WAIT;
+ if(argc >= 1 && CanbeInt(argv[0])) wait = GetAInt(argv[0]);
+
+ I ticks = wait/PY_STOP_TICK;
+ if(stoptick) {
+ // already stopping
+ if(ticks < stoptick) stoptick = ticks;
+ }
+ else
+ stoptick = ticks;
+ shouldexit = true;
+ clock_delay(clk,PY_STOP_TICK);
+
+ Unlock();
+ }
+
+}
+
+PyObject *py::py_samplerate(PyObject *self,PyObject *args)
+{
+ return PyFloat_FromDouble(sys_getsr());
+}
+
+PyObject *py::py_blocksize(PyObject *self,PyObject *args)
+{
+ return PyLong_FromLong(sys_getblksize());
+}
+
+PyObject *py::py_inchannels(PyObject *self,PyObject *args)
+{
+#ifdef PD
+ I ch = sys_get_inchannels();
+#else // MAXMSP
+ I ch = sys_getch(); // not functioning
+#endif
+ return PyLong_FromLong(ch);
+}
+
+PyObject *py::py_outchannels(PyObject *self,PyObject *args)
+{
+#ifdef PD
+ I ch = sys_get_outchannels();
+#else // MAXMSP
+ I ch = sys_getch(); // not functioning
+#endif
+ return PyLong_FromLong(ch);
+}
+
+PyObject *py::py_send(PyObject *,PyObject *args)
+{
+ if(PySequence_Check(args)) {
+ PyObject *name = PySequence_GetItem(args,0); // borrowed
+ if(name && PyString_Check(name)) {
+ const t_symbol *recv = MakeSymbol(PyString_AsString(name));
+ I sz = PySequence_Size(args);
+ PyObject *val;
+ BL tp = sz == 2 && PySequence_Check(PySequence_GetItem(args,1));
+
+ if(tp)
+ val = PySequence_GetItem(args,1); // borrowed
+ else
+ val = PySequence_GetSlice(args,1,sz); // new ref
+
+ AtomList *lst = GetPyArgs(val);
+ if(lst) {
+// t_class **cl = (t_class **)GetBound(recv);
+ t_class **cl = (t_class **)recv->s_thing;
+ if(cl) {
+#ifdef PD
+ pd_forwardmess(cl,lst->Count(),lst->Atoms());
+#else
+ #pragma message ("Send is not implemented")
+#endif
+ }
+#ifdef _DEBUG
+ else
+ post("py/pyext - Receiver doesn't exist");
+#endif
+ }
+ else
+ post("py/pyext - No data to send");
+ if(lst) delete lst;
+
+ if(!tp) Py_DECREF(val);
+ }
+ else
+ post("py/pyext - Send name is invalid");
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+#ifdef FLEXT_THREADS
+PyObject *py::py_priority(PyObject *self,PyObject *args)
+{
+ int val;
+ if(!PyArg_ParseTuple(args, "i:py_priority", &val)) {
+ post("py/pyext - Syntax: _priority [int]");
+ }
+ else
+ ChangePriority(val);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+#endif
+
+
+
diff --git a/externals/grill/py/source/py.cpp b/externals/grill/py/source/py.cpp
new file mode 100644
index 00000000..83c5d9b5
--- /dev/null
+++ b/externals/grill/py/source/py.cpp
@@ -0,0 +1,327 @@
+/*
+
+py/pyext - python script object for PD and MaxMSP
+
+Copyright (c) 2002 Thomas Grill (xovo@gmx.net)
+For information on usage and redistribution, and for a DISCLAIMER OF ALL
+WARRANTIES, see the file, "license.txt," in this distribution.
+
+*/
+
+#include "main.h"
+
+
+class pyobj:
+ public py
+{
+ FLEXT_HEADER(pyobj,py)
+
+public:
+ pyobj(I argc,t_atom *argv);
+ ~pyobj();
+
+protected:
+ BL m_method_(I n,const t_symbol *s,I argc,t_atom *argv);
+
+ V work(const t_symbol *s,I argc,t_atom *argv);
+
+ V m_bang() { work(sym_bang,0,NULL); }
+ V m_reload();
+ V m_reload_(I argc,t_atom *argv);
+ V m_set(I argc,t_atom *argv);
+ V m_doc_();
+
+ virtual V m_help();
+
+ // methods for python arguments
+ V callwork(const t_symbol *s,I argc,t_atom *argv);
+
+ V m_py_list(I argc,t_atom *argv) { callwork(sym_list,argc,argv); }
+ V m_py_float(I argc,t_atom *argv) { callwork(sym_float,argc,argv); }
+ V m_py_int(I argc,t_atom *argv) { callwork(sym_int,argc,argv); }
+ V m_py_any(const t_symbol *s,I argc,t_atom *argv) { callwork(s,argc,argv); }
+
+ const t_symbol *funname;
+ PyObject *function;
+
+ virtual V Reload();
+
+ V SetFunction(const C *func);
+ V ResetFunction();
+
+private:
+
+ FLEXT_CALLBACK(m_bang)
+ FLEXT_CALLBACK(m_reload)
+ FLEXT_CALLBACK_V(m_reload_)
+ FLEXT_CALLBACK_V(m_set)
+ FLEXT_CALLBACK(m_doc_)
+
+ FLEXT_CALLBACK_V(m_py_float)
+ FLEXT_CALLBACK_V(m_py_list)
+ FLEXT_CALLBACK_V(m_py_int)
+ FLEXT_CALLBACK_A(m_py_any)
+
+#ifdef FLEXT_THREADS
+ FLEXT_THREAD_A(work)
+#else
+ FLEXT_CALLBACK_A(work)
+#endif
+};
+
+FLEXT_LIB_V("py",pyobj)
+
+
+pyobj::pyobj(I argc,t_atom *argv):
+ function(NULL),funname(NULL)
+{
+ PY_LOCK
+
+ AddInAnything(2);
+ AddOutAnything();
+
+ FLEXT_ADDBANG(0,m_bang);
+ FLEXT_ADDMETHOD_(0,"reload",m_reload_);
+ FLEXT_ADDMETHOD_(0,"reload.",m_reload);
+ FLEXT_ADDMETHOD_(0,"set",m_set);
+ FLEXT_ADDMETHOD_(0,"doc",m_doc);
+ FLEXT_ADDMETHOD_(0,"doc+",m_doc_);
+#ifdef FLEXT_THREADS
+ FLEXT_ADDMETHOD_(0,"detach",m_detach);
+ FLEXT_ADDMETHOD_(0,"stop",m_stop);
+#endif
+
+ FLEXT_ADDMETHOD_(1,"float",m_py_float);
+ FLEXT_ADDMETHOD_(1,"int",m_py_int);
+ FLEXT_ADDMETHOD(1,m_py_list);
+ FLEXT_ADDMETHOD(1,m_py_any);
+
+ if(argc > 2)
+ SetArgs(argc-2,argv+2);
+ else
+ SetArgs(0,NULL);
+
+ // init script module
+ if(argc >= 1) {
+ C dir[1024];
+ GetModulePath(GetString(argv[0]),dir,sizeof(dir));
+ // set script path
+ AddToPath(dir);
+
+ if(!IsString(argv[0]))
+ post("%s - script name argument is invalid",thisName());
+ else
+ ImportModule(GetString(argv[0]));
+ }
+
+ Register("_py");
+
+ if(argc >= 2) {
+ // set function name
+ if(!IsString(argv[1]))
+ post("%s - function name argument is invalid",thisName());
+ else {
+ // Set function
+ SetFunction(GetString(argv[1]));
+ }
+ }
+
+ PY_UNLOCK
+}
+
+pyobj::~pyobj()
+{
+ PY_LOCK
+ Unregister("_py");
+ PY_UNLOCK
+}
+
+
+
+
+BL pyobj::m_method_(I n,const t_symbol *s,I argc,t_atom *argv)
+{
+ if(n == 1)
+ post("%s - no method for type %s",thisName(),GetString(s));
+ return false;
+}
+
+V pyobj::m_reload()
+{
+ PY_LOCK
+
+ Unregister("_py");
+
+ ReloadModule();
+ Reregister("_py");
+ Register("_py");
+ SetFunction(funname?GetString(funname):NULL);
+
+ PY_UNLOCK
+}
+
+V pyobj::m_reload_(I argc,t_atom *argv)
+{
+ PY_LOCK
+ SetArgs(argc,argv);
+ PY_UNLOCK
+
+ m_reload();
+}
+
+V pyobj::m_set(I argc,t_atom *argv)
+{
+ PY_LOCK
+
+ I ix = 0;
+ if(argc >= 2) {
+ if(!IsString(argv[ix])) {
+ post("%s - script name is not valid",thisName());
+ return;
+ }
+ const C *sn = GetString(argv[ix]);
+
+ if(!module || !strcmp(sn,PyModule_GetName(module))) {
+ ImportModule(sn);
+ Register("_py");
+ }
+
+ ++ix;
+ }
+
+ if(!IsString(argv[ix]))
+ post("%s - function name is not valid",thisName());
+ else
+ SetFunction(GetString(argv[ix]));
+
+ PY_UNLOCK
+}
+
+
+V pyobj::m_doc_()
+{
+ PY_LOCK
+
+ if(function) {
+ PyObject *docf = PyObject_GetAttrString(function,"__doc__"); // borrowed!!!
+ if(docf && PyString_Check(docf)) {
+ post("");
+ post(PyString_AsString(docf));
+ }
+ }
+
+ PY_UNLOCK
+}
+
+
+V pyobj::m_help()
+{
+ post("");
+ post("py %s - python script object, (C)2002 Thomas Grill",PY__VERSION);
+#ifdef _DEBUG
+ post("compiled on " __DATE__ " " __TIME__);
+#endif
+
+ post("Arguments: %s [script name] [function name] {args...}",thisName());
+
+ post("Inlet 1:messages to control the py object");
+ post(" 2:call python function with message as argument(s)");
+ post("Outlet: 1:return values from python function");
+ post("Methods:");
+ post("\thelp: shows this help");
+ post("\tbang: call script without arguments");
+ post("\tset [script name] [function name]: set (script and) function name");
+ post("\treload {args...}: reload python script");
+ post("\treload. : reload with former arguments");
+ post("\tdoc: display module doc string");
+ post("\tdoc+: display function doc string");
+#ifdef FLEXT_THREADS
+ post("\tdetach 0/1: detach threads");
+ post("\tstop {wait time (ms)}: stop threads");
+#endif
+ post("");
+}
+
+V pyobj::ResetFunction()
+{
+ if(!module || !dict)
+ {
+ post("%s - No module loaded",thisName());
+ function = NULL;
+ return;
+ }
+
+ function = funname?PyDict_GetItemString(dict,(C *)GetString(funname)):NULL; // borrowed!!!
+ if(!function) {
+ PyErr_Clear();
+ if(funname) post("%s - Function %s could not be found",thisName(),GetString(funname));
+ }
+ else if(!PyFunction_Check(function)) {
+ post("%s - Object %s is not a function",thisName(),GetString(funname));
+ function = NULL;
+ }
+}
+
+V pyobj::SetFunction(const C *func)
+{
+ if(func) {
+ funname = MakeSymbol(func);
+ ResetFunction();
+ }
+ else
+ function = NULL,funname = NULL;
+}
+
+
+V pyobj::Reload()
+{
+ ResetFunction();
+}
+
+
+V pyobj::work(const t_symbol *s,I argc,t_atom *argv)
+{
+ AtomList *rargs = NULL;
+
+ ++thrcount;
+ PY_LOCK
+
+ if(function) {
+ PyObject *pArgs = MakePyArgs(s,AtomList(argc,argv));
+ PyObject *pValue = PyObject_CallObject(function, pArgs);
+
+ rargs = GetPyArgs(pValue);
+ if(!rargs) PyErr_Print();
+
+ Py_XDECREF(pArgs);
+ Py_XDECREF(pValue);
+ }
+ else {
+ post("%s: no function defined",thisName());
+ }
+
+ PY_UNLOCK
+ --thrcount;
+
+ if(rargs) {
+ // call to outlet _outside_ the Mutex lock!
+ // otherwise (if not detached) deadlock will occur
+ ToOutList(0,*rargs);
+ delete rargs;
+ }
+}
+
+V pyobj::callwork(const t_symbol *s,I argc,t_atom *argv)
+{
+ if(detach) {
+ if(shouldexit)
+ post("%s - New threads can't be launched now!",thisName());
+ else
+ if(!FLEXT_CALLMETHOD_A(work,s,argc,argv))
+ post("%s - Failed to launch thread!",thisName());
+ }
+ else
+ work(s,argc,argv);
+}
+
+
diff --git a/externals/grill/py/source/pyargs.cpp b/externals/grill/py/source/pyargs.cpp
new file mode 100644
index 00000000..be4adaa1
--- /dev/null
+++ b/externals/grill/py/source/pyargs.cpp
@@ -0,0 +1,131 @@
+/*
+
+py/pyext - python external object for PD and MaxMSP
+
+Copyright (c) 2002 Thomas Grill (xovo@gmx.net)
+For information on usage and redistribution, and for a DISCLAIMER OF ALL
+WARRANTIES, see the file, "license.txt," in this distribution.
+
+*/
+
+#include "main.h"
+
+
+PyObject *py::MakePyArgs(const t_symbol *s,const AtomList &args,I inlet,BL withself)
+{
+ PyObject *pArgs;
+
+ BL any = IsAnything(s);
+ pArgs = PyTuple_New(args.Count()+(any?1:0)+(inlet >= 0?1:0));
+
+ I pix = 0;
+
+ if(inlet >= 0) {
+ PyObject *pValue = PyInt_FromLong(inlet);
+
+ // reference stolen:
+ PyTuple_SetItem(pArgs, pix++, pValue);
+ }
+
+ I ix;
+ PyObject *tmp;
+ if(!withself || args.Count() < (any?1:2)) tmp = pArgs,ix = pix;
+ else tmp = PyTuple_New(args.Count()+(any?1:0)),ix = 0;
+
+ if(any) {
+ PyObject *pValue = PyString_FromString(GetString(s));
+
+ // reference stolen here:
+ PyTuple_SetItem(tmp, ix++, pValue);
+ }
+
+ for(I i = 0; i < args.Count(); ++i) {
+ PyObject *pValue = NULL;
+
+ if(IsFloat(args[i])) pValue = PyFloat_FromDouble((D)GetFloat(args[i]));
+ else if(IsInt(args[i])) pValue = PyInt_FromLong(GetInt(args[i]));
+ else if(IsSymbol(args[i])) pValue = PyString_FromString(GetString(args[i]));
+ else if(IsPointer(args[i])) pValue = NULL; // not handled
+
+ if(!pValue) {
+ post("py/pyext: cannot convert argument %i",any?i+1:i);
+ continue;
+ }
+
+ /* pValue reference stolen here: */
+ PyTuple_SetItem(tmp, ix++, pValue);
+ }
+
+ if(tmp != pArgs) {
+ PyTuple_SetItem(pArgs, pix++, tmp);
+#if PY_VERSION_HEX >= 0x02020000
+ _PyTuple_Resize(&pArgs,pix);
+#else
+ _PyTuple_Resize(&pArgs,pix,0);
+#endif
+ }
+
+ return pArgs;
+}
+
+flext::AtomList *py::GetPyArgs(PyObject *pValue,PyObject **self)
+{
+ if(pValue == NULL) return NULL;
+ AtomList *ret = NULL;
+
+ // analyze return value or tuple
+
+ I rargc = 0;
+ BL ok = true;
+ retval tp = nothing;
+
+ if(PyString_Check(pValue)) {
+ rargc = 1;
+ tp = atom;
+ }
+ else if(PySequence_Check(pValue)) {
+ rargc = PySequence_Size(pValue);
+ tp = sequ;
+ }
+ else {
+ rargc = 1;
+ tp = atom;
+ }
+
+ ret = new AtomList(rargc);
+
+ for(I ix = 0; ix < rargc; ++ix) {
+ PyObject *arg;
+ switch(tp) {
+ case sequ: arg = PySequence_GetItem(pValue,ix); break;
+ default: arg = pValue;
+ }
+
+ if(PyInt_Check(arg)) SetInt((*ret)[ix],PyInt_AsLong(arg));
+ else if(PyLong_Check(arg)) SetInt((*ret)[ix],PyLong_AsLong(arg));
+ else if(PyFloat_Check(arg)) SetFloat((*ret)[ix],(F)PyFloat_AsDouble(arg));
+ else if(PyString_Check(arg)) SetString((*ret)[ix],PyString_AsString(arg));
+ else if(ix == 0 && self && PyInstance_Check(arg)) {
+ // assumed to be self ... that should be checked _somehow_ !!!
+ *self = arg;
+ }
+ else {
+ PyObject *tp = PyObject_Type(arg);
+ PyObject *stp = tp?PyObject_Str(tp):NULL;
+ C *tmp = "";
+ if(stp) tmp = PyString_AsString(stp);
+ post("py/pyext: Could not convert argument %s",tmp);
+ Py_XDECREF(stp);
+ Py_XDECREF(tp);
+ ok = false;
+ }
+ // No DECREF for arg -> borrowed from pValue!
+ }
+
+ if(!ok) {
+ delete ret;
+ ret = NULL;
+ }
+ return ret;
+}
+
diff --git a/externals/grill/py/source/pyext.cpp b/externals/grill/py/source/pyext.cpp
new file mode 100644
index 00000000..8e971873
--- /dev/null
+++ b/externals/grill/py/source/pyext.cpp
@@ -0,0 +1,465 @@
+/*
+
+py/pyext - python script object for PD and MaxMSP
+
+Copyright (c) 2002 Thomas Grill (xovo@gmx.net)
+For information on usage and redistribution, and for a DISCLAIMER OF ALL
+WARRANTIES, see the file, "license.txt," in this distribution.
+
+*/
+
+#include "pyext.h"
+#include <flinternal.h>
+
+
+FLEXT_LIB_V("pyext",pyext)
+
+V pyext::setup(t_class *)
+{
+ px_head = px_tail = NULL;
+
+ px_class = class_new(gensym("pyext proxy"),NULL,NULL,sizeof(py_proxy),CLASS_PD|CLASS_NOINLET, A_NULL);
+ ::add_anything(px_class,py_proxy::px_method); // for other inlets
+}
+
+pyext *pyext::GetThis(PyObject *self)
+{
+ PyObject *th = PyObject_GetAttrString(self,"_this");
+ pyext *ret = th?(pyext *)PyLong_AsVoidPtr(th):NULL;
+ PyErr_Clear();
+ Py_XDECREF(th);
+ return ret;
+}
+
+
+I pyext::pyextref = 0;
+PyObject *pyext::class_obj = NULL;
+PyObject *pyext::class_dict = NULL;
+
+pyext::pyext(I argc,t_atom *argv):
+ pyobj(NULL),pythr(NULL),
+ inlets(0),outlets(0),
+ methname(NULL)
+{
+ PY_LOCK
+
+ if(!pyextref++) {
+ // register/initialize pyext base class along with module
+ class_dict = PyDict_New();
+ PyObject *className = PyString_FromString(PYEXT_CLASS);
+ PyMethodDef *def;
+
+ // add setattr/getattr to class
+ for(def = attr_tbl; def->ml_name; def++) {
+ PyObject *func = PyCFunction_New(def, NULL);
+ PyDict_SetItemString(class_dict, def->ml_name, func);
+ Py_DECREF(func);
+ }
+
+ class_obj = PyClass_New(NULL, class_dict, className);
+ PyDict_SetItemString(module_dict, PYEXT_CLASS,class_obj);
+ Py_DECREF(className);
+
+ // add methods to class
+ for (def = meth_tbl; def->ml_name != NULL; def++) {
+ PyObject *func = PyCFunction_New(def, NULL);
+ PyObject *method = PyMethod_New(func, NULL, class_obj);
+ PyDict_SetItemString(class_dict, def->ml_name, method);
+ Py_DECREF(func);
+ Py_DECREF(method);
+ }
+
+#if PY_VERSION_HEX >= 0x02020000
+ // not absolutely necessary, existent in python 2.2 upwards
+ // make pyext functions available in class scope
+ PyDict_Merge(class_dict,module_dict,0);
+#endif
+
+ PyDict_SetItemString(class_dict,"__doc__",PyString_FromString(pyext_doc));
+ }
+ else {
+ Py_INCREF(class_obj);
+ Py_INCREF(class_dict);
+ }
+
+ // init script module
+ if(argc >= 1) {
+ C dir[1024];
+#ifdef PD
+ // add dir of current patch to path
+ strcpy(dir,GetString(canvas_getdir(thisCanvas())));
+ AddToPath(dir);
+ // add current dir to path
+ strcpy(dir,GetString(canvas_getcurrentdir()));
+ AddToPath(dir);
+#else
+ #pragma message("Adding current dir to path is not implemented")
+#endif
+
+ GetModulePath(GetString(argv[0]),dir,sizeof(dir));
+ // add to path
+ AddToPath(dir);
+
+ if(!IsString(argv[0]))
+ post("%s - script name argument is invalid",thisName());
+ else {
+ SetArgs(0,NULL);
+ ImportModule(GetString(argv[0]));
+ }
+ }
+
+ Register("_pyext");
+
+// t_symbol *sobj = NULL;
+ if(argc >= 2) {
+ // object name
+ if(!IsString(argv[1]))
+ post("%s - object name argument is invalid",thisName());
+ else {
+ methname = GetSymbol(argv[1]);
+ }
+
+ args(argc-2,argv+2);
+ }
+
+ if(methname) {
+ SetClssMeth();
+
+ // now get number of inlets and outlets
+ inlets = 1,outlets = 1;
+
+ if(pyobj) {
+ PyObject *res;
+ res = PyObject_GetAttrString(pyobj,"_inlets"); // get ref
+ if(res) {
+ if(PyCallable_Check(res)) {
+ PyObject *fres = PyEval_CallObject(res,NULL);
+ Py_DECREF(res);
+ res = fres;
+ }
+ if(PyInt_Check(res))
+ inlets = PyInt_AsLong(res);
+ Py_DECREF(res);
+ }
+ else
+ PyErr_Clear();
+
+ res = PyObject_GetAttrString(pyobj,"_outlets"); // get ref
+ if(res) {
+ if(PyCallable_Check(res)) {
+ PyObject *fres = PyEval_CallObject(res,NULL);
+ Py_DECREF(res);
+ res = fres;
+ }
+ if(PyInt_Check(res))
+ outlets = PyInt_AsLong(res);
+ Py_DECREF(res);
+ }
+ else
+ PyErr_Clear();
+ }
+ }
+
+ PY_UNLOCK
+
+ AddInAnything(1+inlets);
+ AddOutAnything(outlets);
+
+ FLEXT_ADDMETHOD_(0,"reload.",m_reload);
+ FLEXT_ADDMETHOD_(0,"reload",m_reload_);
+ FLEXT_ADDMETHOD_(0,"doc",m_doc);
+ FLEXT_ADDMETHOD_(0,"doc+",m_doc_);
+
+#ifdef FLEXT_THREADS
+ FLEXT_ADDMETHOD_(0,"detach",m_detach);
+ FLEXT_ADDMETHOD_(0,"stop",m_stop);
+#endif
+
+ if(!pyobj)
+ InitProblem();
+}
+
+pyext::~pyext()
+{
+ PY_LOCK
+
+ ClearBinding();
+ Unregister("_pyext");
+
+ Py_XDECREF(pyobj);
+
+ Py_XDECREF(class_obj);
+ Py_XDECREF(class_dict);
+/*
+ // Don't unregister
+
+ if(!--pyextref) {
+ class_obj = NULL;
+ class_dict = NULL;
+ }
+*/
+ PY_UNLOCK
+}
+
+BL pyext::SetClssMeth() //I argc,t_atom *argv)
+{
+ // pyobj should already have been decref'd / cleared before getting here!!
+
+ if(module && methname) {
+ PyObject *pref = PyObject_GetAttrString(module,const_cast<C *>(GetString(methname)));
+ if(!pref)
+ PyErr_Print();
+ else if(PyClass_Check(pref)) {
+ // make instance, but don't call __init__
+ pyobj = PyInstance_NewRaw(pref,NULL);
+
+ Py_DECREF(pref);
+ if(pyobj == NULL)
+ PyErr_Print();
+ else {
+ // remember the this pointer
+ PyObject *th = PyLong_FromVoidPtr(this);
+ int ret = PyObject_SetAttrString(pyobj,"_this",th); // ref is taken
+
+ // call init now, after _this has been set, which is
+ // important for eventual callbacks from __init__ to c
+ PyObject *pargs = MakePyArgs(NULL,args,-1,true);
+ if (pargs == NULL) PyErr_Print();
+
+ PyObject *init;
+ init = PyObject_GetAttrString(pyobj,"__init__"); // get ref
+ if(init && PyCallable_Check(init)) {
+ PyObject *res = PyEval_CallObject(init,pargs);
+ if(!res)
+ PyErr_Print();
+ else
+ Py_DECREF(res);
+ }
+
+ Py_XDECREF(pargs);
+ }
+ }
+ else
+ post("%s - Type of \"%s\" is unhandled!",thisName(),GetString(methname));
+ return true;
+ }
+ else
+ return false;
+}
+
+V pyext::Reload()
+{
+ ClearBinding();
+ Py_XDECREF(pyobj);
+ // by here, the Python class destructor should have been called!
+
+ SetArgs(0,NULL);
+ ReloadModule();
+
+ SetClssMeth();
+}
+
+
+V pyext::m_reload()
+{
+ PY_LOCK
+
+ Unregister("_pyext"); // self
+
+ Reload();
+
+ Reregister("_pyext"); // the others
+ Register("_pyext"); // self
+
+ PY_UNLOCK
+}
+
+V pyext::m_reload_(I argc,t_atom *argv)
+{
+ args(argc,argv);
+ m_reload();
+}
+
+V pyext::m_doc_()
+{
+ if(pyobj) {
+ PY_LOCK
+
+ PyObject *docf = PyObject_GetAttrString(pyobj,"__doc__"); // borrowed!!!
+ if(docf && PyString_Check(docf)) {
+ post("");
+ post(PyString_AsString(docf));
+ }
+
+ PY_UNLOCK
+ }
+}
+
+
+
+
+BL pyext::m_method_(I n,const t_symbol *s,I argc,t_atom *argv)
+{
+ if(pyobj && n >= 1) {
+ return callwork(n,s,argc,argv);
+ }
+ else {
+ post("%s - no method for type '%s' into inlet %i",thisName(),GetString(s),n);
+ return false;
+ }
+}
+
+
+V pyext::m_help()
+{
+ post("");
+ post("pyext %s - python script object, (C)2002 Thomas Grill",PY__VERSION);
+#ifdef _DEBUG
+ post("compiled on " __DATE__ " " __TIME__);
+#endif
+
+ post("Arguments: %s [script name] [class name] {args...}",thisName());
+
+ post("Inlet 1: messages to control the pyext object");
+ post(" 2...: python inlets");
+ post("Outlets: python outlets");
+ post("Methods:");
+ post("\thelp: shows this help");
+ post("\treload {args...}: reload python script");
+ post("\treload. : reload with former arguments");
+ post("\tdoc: display module doc string");
+ post("\tdoc+: display class doc string");
+#ifdef FLEXT_THREADS
+ post("\tdetach 0/1: detach threads");
+ post("\tstop {wait time (ms)}: stop threads");
+#endif
+ post("");
+}
+
+PyObject *pyext::call(const C *meth,I inlet,const t_symbol *s,I argc,t_atom *argv)
+{
+ PyObject *ret = NULL;
+
+ PyObject *pmeth = PyObject_GetAttrString(pyobj,const_cast<char *>(meth)); /* fetch bound method */
+ if(pmeth == NULL) {
+ PyErr_Clear(); // no method found
+ }
+ else {
+ PyObject *pargs = MakePyArgs(s,AtomList(argc,argv),inlet?inlet:-1,true);
+ if(!pargs)
+ PyErr_Print();
+ else {
+ ret = PyEval_CallObject(pmeth, pargs);
+ if (ret == NULL) // function not found resp. arguments not matching
+#ifdef _DEBUG
+ PyErr_Print();
+#else
+ PyErr_Clear();
+#endif
+ else {
+// Py_DECREF(pres);
+ }
+
+ Py_DECREF(pargs);
+ }
+ Py_DECREF(pmeth);
+ }
+
+ return ret;
+}
+
+V pyext::work_wrapper(V *data)
+{
+ ++thrcount;
+#ifdef _DEBUG
+ if(!data)
+ post("%s - no data!",thisName());
+ else
+#endif
+ {
+ work_data *w = (work_data *)data;
+ work(w->n,w->Header(),w->Count(),w->Atoms());
+// delete w;
+ }
+ --thrcount;
+}
+
+BL pyext::callwork(I n,const t_symbol *s,I argc,t_atom *argv)
+{
+ if(detach) {
+ if(shouldexit) {
+ post("%s - Stopping.... new threads can't be launched now!",thisName());
+ return true;
+ }
+ else {
+ BL ret = FLEXT_CALLMETHOD_X(work_wrapper,new work_data(n,s,argc,argv));
+ if(!ret) post("%s - Failed to launch thread!",thisName());
+ return true;
+ }
+ }
+ else
+ return work(n,s,argc,argv);
+}
+
+BL pyext::work(I n,const t_symbol *s,I argc,t_atom *argv)
+{
+ BL retv = false;
+
+ PY_LOCK
+
+ PyObject *ret = NULL;
+ char *str = new char[strlen(GetString(s))+10];
+
+ {
+ // try tag/inlet
+ sprintf(str,"%s_%i",GetString(s),n);
+ ret = call(str,0,NULL,argc,argv);
+ }
+
+ if(!ret) {
+ // try anything/inlet
+ sprintf(str,"_anything_%i",n);
+ if(s == sym_bang && !argc) {
+ t_atom argv;
+ SetString(argv,"");
+ ret = call(str,0,s,1,&argv);
+ }
+ else
+ ret = call(str,0,s,argc,argv);
+ }
+ if(!ret) {
+ // try tag at any inlet
+ sprintf(str,"%s_",GetString(s));
+ ret = call(str,n,NULL,argc,argv);
+ }
+ if(!ret) {
+ // try anything at any inlet
+ strcpy(str,"_anything_");
+ if(s == sym_bang && !argc) {
+ t_atom argv;
+ SetString(argv,"");
+ ret = call(str,n,s,1,&argv);
+ }
+ else
+ ret = call(str,n,s,argc,argv);
+ }
+
+ if(!ret)
+ // no matching python method found
+ post("%s - no matching method found for '%s' into inlet %i",thisName(),GetString(s),n);
+
+ if(str) delete[] str;
+
+ if(ret) {
+ if(!PyObject_Not(ret)) post("%s - returned value is ignored",thisName());
+ Py_DECREF(ret);
+ retv = true;
+ }
+
+ PY_UNLOCK
+
+ return retv;
+}
+
+
+
diff --git a/externals/grill/py/source/pyext.h b/externals/grill/py/source/pyext.h
new file mode 100644
index 00000000..2aba0e8e
--- /dev/null
+++ b/externals/grill/py/source/pyext.h
@@ -0,0 +1,127 @@
+/*
+
+py/pyext - python external object for PD and MaxMSP
+
+Copyright (c) 2002 Thomas Grill (xovo@gmx.net)
+For information on usage and redistribution, and for a DISCLAIMER OF ALL
+WARRANTIES, see the file, "license.txt," in this distribution.
+
+*/
+
+#ifndef __PYEXT_H
+#define __PYEXT_H
+
+#include "main.h"
+
+class pyext:
+ public py
+{
+ FLEXT_HEADER_S(pyext,py,setup)
+
+public:
+ pyext(I argc,t_atom *argv);
+ ~pyext();
+
+ static PyObject *pyext__doc__(PyObject *,PyObject *args);
+ static PyObject *pyext__init__(PyObject *,PyObject *args);
+ static PyObject *pyext__del__(PyObject *,PyObject *args);
+
+ static PyObject *pyext_outlet(PyObject *,PyObject *args);
+ static PyObject *pyext_tocanvas(PyObject *,PyObject *args);
+
+ static PyObject *pyext_setattr(PyObject *,PyObject *args);
+ static PyObject *pyext_getattr(PyObject *,PyObject *args);
+
+ static PyObject *pyext_detach(PyObject *,PyObject *args);
+ static PyObject *pyext_stop(PyObject *,PyObject *args);
+
+ I Inlets() const { return inlets; }
+ I Outlets() const { return outlets; }
+
+protected:
+ BL m_method_(I n,const t_symbol *s,I argc,t_atom *argv);
+
+ BL work(I n,const t_symbol *s,I argc,t_atom *argv);
+
+ V m_reload();
+ V m_reload_(I argc,t_atom *argv);
+ V m_doc_();
+ virtual V m_help();
+
+ const t_symbol *methname;
+ PyObject *pyobj;
+ I inlets,outlets;
+
+private:
+ static V setup(t_class *);
+
+ static pyext *GetThis(PyObject *self);
+ V ClearBinding();
+ BL SetClssMeth(); //I argc,t_atom *argv);
+
+ AtomList args;
+
+ virtual V Reload();
+
+ static I pyextref;
+ static PyObject *class_obj,*class_dict;
+ static PyMethodDef attr_tbl[],meth_tbl[];
+ static const C *pyext_doc;
+
+ // -------- bound stuff ------------------
+
+ static t_class *px_class;
+
+ friend class py_proxy;
+
+ class py_proxy // no virtual table!
+ {
+ public:
+ t_object obj; // MUST reside at memory offset 0
+ PyObject *self,*func;
+ t_symbol *name;
+
+ py_proxy *nxt;
+
+ void init(t_symbol *n,PyObject *s,PyObject *f) { name = n,self = s,func = f,nxt = NULL; }
+// bool cmp(PyObject *s,PyObject *f) const { return self == s && func == f; }
+// void init(PyObject *s,char *f) { self = s,func = f,nxt = NULL; }
+// bool cmp(PyObject *s,char *f) const { return self == s && func == f; }
+ static void px_method(py_proxy *c,const t_symbol *s,int argc,t_atom *argv);
+ };
+ static py_proxy *px_head,*px_tail;
+
+ static PyObject *pyext_bind(PyObject *,PyObject *args);
+ static PyObject *pyext_unbind(PyObject *,PyObject *args);
+
+ // ---------------------------
+
+ PyObject *call(const C *meth,I inlet,const t_symbol *s,I argc,t_atom *argv);
+
+ V work_wrapper(void *data);
+ BL callwork(I n,const t_symbol *s,I argc,t_atom *argv);
+
+ class work_data:
+ public flext::AtomAnything
+ {
+ public:
+ work_data(I _n,const t_symbol *_s,I _argc,t_atom *_argv): n(_n),AtomAnything(_s,_argc,_argv) {}
+ I n;
+ };
+
+#ifdef FLEXT_THREADS
+ FLEXT_THREAD_X(work_wrapper)
+#else
+ FLEXT_CALLBACK_X(work_wrapper)
+#endif
+
+ PyThreadState *pythr;
+
+private:
+ FLEXT_CALLBACK(m_reload)
+ FLEXT_CALLBACK_V(m_reload_)
+ FLEXT_CALLBACK(m_doc_)
+};
+
+
+#endif \ No newline at end of file
diff --git a/externals/grill/py/source/register.cpp b/externals/grill/py/source/register.cpp
new file mode 100644
index 00000000..63a9f952
--- /dev/null
+++ b/externals/grill/py/source/register.cpp
@@ -0,0 +1,86 @@
+/*
+
+py/pyext - python external object for PD and MaxMSP
+
+Copyright (c) 2002 Thomas Grill (xovo@gmx.net)
+For information on usage and redistribution, and for a DISCLAIMER OF ALL
+WARRANTIES, see the file, "license.txt," in this distribution.
+
+*/
+
+#include "main.h"
+
+
+V py::Register(const C *regnm)
+{
+ if(module) {
+ // add this to module registry
+
+ PyObject *reg = PyDict_GetItemString(dict,(C *)regnm); // borrowed!!!
+ PyObject *add = Py_BuildValue("[i]",(long)this);
+ if(!reg || !PyList_Check(reg)) {
+ if(PyDict_SetItemString(dict,(C *)regnm,add)) {
+ post("%s - Could not set registry",thisName());
+ }
+ // Py_XDECREF(reg);
+ }
+ else {
+ PySequence_InPlaceConcat(reg,add);
+ }
+ }
+
+}
+
+V py::Unregister(const C *regnm)
+{
+ if(module) {
+ // remove this from module registry
+
+ PyObject *reg = PyDict_GetItemString(dict,(C *)regnm); // borrowed!!!
+ PyObject *add = Py_BuildValue("i",(int)this);
+ if(!reg || !PySequence_Check(reg))
+ post("%s - Registry not found!?",thisName());
+ else {
+ I ix = PySequence_Index(reg,add);
+ if(ix < 0) {
+ post("%s - This object not found in registry?!",thisName());
+ }
+ else {
+ PySequence_DelItem(reg,ix);
+ }
+ }
+ Py_DECREF(add);
+ }
+
+}
+
+V py::Reregister(const C *regnm)
+{
+ if(module) {
+ // remove this from module registry
+
+ PyObject *reg = PyDict_GetItemString(dict,(C *)regnm); // borrowed!!!
+
+ if(!reg || !PySequence_Check(reg))
+ post("%s - Registry not found!?",thisName());
+ else {
+ I cnt = PySequence_Size(reg);
+ for(I i = 0; i < cnt; ++i) {
+ PyObject *it = PySequence_GetItem(reg,i); // borrowed!!
+ if(!it || !PyInt_Check(it)) {
+ post("%s - Corrupt registry?!",thisName());
+ }
+ else {
+ py *th = (py *)PyInt_AsLong(it);
+ th->module = module;
+ th->dict = dict;
+ th->Reload();
+ }
+ }
+ }
+ }
+
+}
+
+
+