diff options
author | N.N. <matju@users.sourceforge.net> | 2005-10-04 02:02:15 +0000 |
---|---|---|
committer | N.N. <matju@users.sourceforge.net> | 2005-10-04 02:02:15 +0000 |
commit | 5e2a1bc9e56003349e533f7e5841041ba5c04e28 (patch) | |
tree | ad040f6894d9383b732423a74420e732f62a66a5 /externals/gridflow/bridge | |
parent | 520a243c297175386ab31c78c84693a664934a69 (diff) |
starting to commit gridflow 0.8.0 ...
if you know how to use "cvs import" please mail me and i'll use it for 0.8.1
svn path=/trunk/; revision=3646
Diffstat (limited to 'externals/gridflow/bridge')
-rw-r--r-- | externals/gridflow/bridge/placebo.rb | 43 | ||||
-rw-r--r-- | externals/gridflow/bridge/puredata.c | 772 | ||||
-rw-r--r-- | externals/gridflow/bridge/puredata.rb | 194 |
3 files changed, 1009 insertions, 0 deletions
diff --git a/externals/gridflow/bridge/placebo.rb b/externals/gridflow/bridge/placebo.rb new file mode 100644 index 00000000..3d712d1d --- /dev/null +++ b/externals/gridflow/bridge/placebo.rb @@ -0,0 +1,43 @@ +=begin + $Id: placebo.rb,v 1.1 2005-10-04 02:02:13 matju Exp $ + + GridFlow + Copyright (c) 2001,2002,2003,2004,2005 by Mathieu Bouchard + + 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. + + See file ../COPYING for further informations on licensing terms. + + 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. +=end + +class Object + def self.dummy(sel) + self.module_eval "def #{sel}(*args) GridFlow.post \"dummy #{sel}: %s\", args.inspect end" + end +end + +module GridFlow + class<<self + # def add_creator_2(*args) post "dummy add_creator_2: %s", args.inspect end + dummy :add_creator_2 + def post_string(s) STDERR.puts s end + end + class Clock + def initialize(victim) @victim=victim end + dummy :delay + end + class Pointer + dummy :initialize + end +end diff --git a/externals/gridflow/bridge/puredata.c b/externals/gridflow/bridge/puredata.c new file mode 100644 index 00000000..dd6fd88f --- /dev/null +++ b/externals/gridflow/bridge/puredata.c @@ -0,0 +1,772 @@ +/* + $Id: puredata.c,v 1.1 2005-10-04 02:02:13 matju Exp $ + + GridFlow + Copyright (c) 2001,2002,2003,2004,2005 by Mathieu Bouchard + + 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. + + See file ../COPYING for further informations on licensing terms. + + 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. +*/ + +/* +'bridge_puredata.c' becomes 'gridflow.pd_linux' which loads Ruby and tells +Ruby to load the other 95% of Gridflow. GridFlow creates the FObject class +and then subclasses it many times, each time calling FObject.install(), +which tells the bridge to register a class in puredata. Puredata +objects have proxies for each of the non-first inlets so that any inlet +may receive any distinguished message. All inlets are typed as 'anything', +and the 'anything' method translates the whole message to Ruby objects and +tries to call a Ruby method of the proper name. +*/ + +#define IS_BRIDGE +#include "../base/grid.h.fcs" +/* resolving conflict: T_OBJECT will be PD's, not Ruby's */ +#undef T_OBJECT +#undef EXTERN +#include <m_pd.h> +#include <ctype.h> +#include <stdarg.h> +#include <unistd.h> +#include "g_canvas.h" + +/* **************************************************************** */ +struct BFObject; +struct FMessage { + BFObject *self; + int winlet; + t_symbol *selector; + int ac; + const t_atom *at; + bool is_init; +}; +#include <sys/time.h> +#include <signal.h> +#include <setjmp.h> +#define rb_sym_name rb_sym_name_r4j +static const char *rb_sym_name(Ruby sym) {return rb_id2name(SYM2ID(sym));} + +static BuiltinSymbols *syms; + +void CObject_freeee (void *victim) { + CObject *self = (CObject *)victim; + self->check_magic(); + if (!self->rself) { + fprintf(stderr,"attempt to free object that has no rself\n"); + abort(); + } + self->rself = 0; /* paranoia */ + delete self; +} + + +/* can't even refer to the other mGridFlow because we don't link + statically to the other gridflow.so */ +static Ruby mGridFlow2=0; +static Ruby mPointer=0; + +\class Pointer < CObject +struct Pointer : CObject { + void *p; + Pointer() { assert(!"DYING HORRIBLY"); } + Pointer(void *_p) : p(_p) {} + \decl Ruby ptr (); +}; +\def Ruby ptr () { return LONG2NUM(((long)p)); } +\classinfo { + IEVAL(rself, +"self.module_eval{" +"def inspect; p=('%08x'%ptr).gsub(/^\\.\\.f/,''); \"#<Pointer:#{p}>\" % ptr; end;" +"alias to_s inspect }" +);} +\end class Pointer +Ruby Pointer_s_new (void *ptr) { + return Data_Wrap_Struct(EVAL("GridFlow::Pointer"), 0, 0, new Pointer(ptr)); +} +void *Pointer_get (Ruby rself) { + DGS(Pointer); + return self->p; +} + +static Ruby make_error_message () { + char buf[1000]; + sprintf(buf,"%s: %s",rb_class2name(rb_obj_class(ruby_errinfo)), + rb_str_ptr(rb_funcall(ruby_errinfo,SI(to_s),0))); + Ruby ary = rb_ary_new2(2); + Ruby backtrace = rb_funcall(ruby_errinfo,SI(backtrace),0); + rb_ary_push(ary,rb_str_new2(buf)); + for (int i=0; i<2 && i<rb_ary_len(backtrace); i++) + rb_ary_push(ary,rb_funcall(backtrace,SI([]),1,INT2NUM(i))); +// rb_ary_push(ary,rb_funcall(rb_funcall(backtrace,SI(length),0),SI(to_s),0)); + return ary; +} + +static int ninlets_of (Ruby qlass) { + if (!rb_ivar_defined(qlass,SYM2ID(syms->iv_ninlets))) RAISE("no inlet count"); + return INT(rb_ivar_get(qlass,SYM2ID(syms->iv_ninlets))); +} + +static int noutlets_of (Ruby qlass) { + if (!rb_ivar_defined(qlass,SYM2ID(syms->iv_noutlets))) RAISE("no outlet count"); + return INT(rb_ivar_get(qlass,SYM2ID(syms->iv_noutlets))); +} + +#ifndef STACK_GROW_DIRECTION +#define STACK_GROW_DIRECTION -1 +#endif +extern "C" void Init_stack(VALUE *addr); +static VALUE *localize_sysstack () { + long bp; + sscanf(RUBY_STACK_END,"0x%08lx",&bp); + //fprintf(stderr,"old RUBY_STACK_END = %08lx\n",bp); + // HACK (2004.08.29: alx has a problem; i hope it doesn't get worse) + // this rounds to the last word of a 4k block + // cross fingers that no other OS does it too different + // !@#$ doesn't use STACK_GROW_DIRECTION + // bp=((bp+0xfff)&~0xfff)-sizeof(void*); + // GAAAH + bp=((bp+0xffff)&~0xffff)-sizeof(void*); + //fprintf(stderr,"new RUBY_STACK_END = %08lx\n",bp); + return (VALUE *)bp; +} + +//**************************************************************** +// BFObject + +struct BFObject : t_object { + int32 magic; // paranoia + Ruby rself; + int nin; // per object settings (not class) + int nout; // per object settings (not class) + t_outlet **out; + + void check_magic () { + if (magic != OBJECT_MAGIC) { + fprintf(stderr,"Object memory corruption! (ask the debugger)\n"); + for (int i=-1; i<=1; i++) { + fprintf(stderr,"this[0]=0x%08x\n",((int*)this)[i]); + } + raise(11); + } + } +}; + +static t_class *find_bfclass (t_symbol *sym) { + t_atom a[1]; + SETSYMBOL(a,sym); + char buf[4096]; + if (sym==&s_list) strcpy(buf,"list"); else atom_string(a,buf,sizeof(buf)); + Ruby v = rb_hash_aref(rb_ivar_get(mGridFlow2, SI(@fclasses)), rb_str_new2(buf)); + if (v==Qnil) { + post("GF: class not found: '%s'",buf); + return 0; + } + if (Qnil==rb_ivar_get(v,SI(@bfclass))) { + post("@bfclass missing for '%s'",buf); + return 0; + } + return FIX2PTR(t_class,rb_ivar_get(v,SI(@bfclass))); +} + +static t_class *BFProxy_class; + +struct BFProxy : t_object { + BFObject *parent; + int inlet; +}; + +static void Bridge_export_value(Ruby arg, t_atom *at) { + if (INTEGER_P(arg)) { + SETFLOAT(at,NUM2INT(arg)); + } else if (SYMBOL_P(arg)) { + const char *name = rb_sym_name(arg); + SETSYMBOL(at,gensym((char *)name)); + } else if (FLOAT_P(arg)) { + SETFLOAT(at,RFLOAT(arg)->value); + } else if (rb_obj_class(arg)==mPointer) { + SETPOINTER(at,(t_gpointer*)Pointer_get(arg)); + } else { + RAISE("cannot convert argument of class '%s'", + rb_str_ptr(rb_funcall(rb_funcall(arg,SI(class),0),SI(inspect),0))); + } +} + +static Ruby Bridge_import_value(const t_atom *at) { + t_atomtype t = at->a_type; + if (t==A_SYMBOL) { + return ID2SYM(rb_intern(at->a_w.w_symbol->s_name)); + } else if (t==A_FLOAT) { + return rb_float_new(at->a_w.w_float); + } else if (t==A_POINTER) { + return Pointer_s_new(at->a_w.w_gpointer); + } else { + return Qnil; /* unknown */ + } +} + +static Ruby BFObject_method_missing_1 (FMessage *fm) { + Ruby argv[fm->ac+2]; + argv[0] = INT2NUM(fm->winlet); + argv[1] = ID2SYM(rb_intern(fm->selector->s_name)); + for (int i=0; i<fm->ac; i++) argv[2+i] = Bridge_import_value(fm->at+i); + fm->self->check_magic(); + rb_funcall2(fm->self->rself,SI(send_in),fm->ac+2,argv); + return Qnil; +} + +static Ruby BFObject_rescue (FMessage *fm) { + Ruby error_array = make_error_message(); +// for (int i=0; i<rb_ary_len(error_array); i++) +// post("%s\n",rb_str_ptr(rb_ary_ptr(error_array)[i])); + if (fm->self) pd_error(fm->self,"%s",rb_str_ptr( + rb_funcall(error_array,SI(join),1,rb_str_new2("\n")))); + if (fm->self && fm->is_init) fm->self = 0; + return Qnil; +} + +static void BFObject_method_missing (BFObject *bself, +int winlet, t_symbol *selector, int ac, t_atom *at) { + FMessage fm = { bself, winlet, selector, ac, at, false }; + if (!bself->rself) { + pd_error(bself,"message to a dead object. (supposed to be impossible)"); + return; + } + rb_rescue2( + (RMethod)BFObject_method_missing_1,(Ruby)&fm, + (RMethod)BFObject_rescue,(Ruby)&fm, + rb_eException,0); +} + +static void BFObject_method_missing0 (BFObject *self, +t_symbol *s, int argc, t_atom *argv) { + BFObject_method_missing(self,0,s,argc,argv); +} + +static void BFProxy_method_missing(BFProxy *self, +t_symbol *s, int argc, t_atom *argv) { + BFObject_method_missing(self->parent,self->inlet,s,argc,argv); +} + +static Ruby BFObject_init_1 (FMessage *fm) { + Ruby argv[fm->ac+1]; + for (int i=0; i<fm->ac; i++) argv[i+1] = Bridge_import_value(fm->at+i); + + if (fm->selector==&s_list) { + argv[0] = rb_str_new2("list"); // pd is slightly broken here + } else { + argv[0] = rb_str_new2(fm->selector->s_name); + } + + Ruby rself = rb_funcall2(rb_const_get(mGridFlow2,SI(FObject)),SI([]),fm->ac+1,argv); + DGS(FObject); + self->bself = fm->self; + self->bself->rself = rself; + + int ninlets = self->bself->nin = ninlets_of(rb_funcall(rself,SI(class),0)); + int noutlets = self->bself->nout = noutlets_of(rb_funcall(rself,SI(class),0)); + + for (int i=1; i<ninlets; i++) { + BFProxy *p = (BFProxy *)pd_new(BFProxy_class); + p->parent = self->bself; + p->inlet = i; + inlet_new(self->bself, &p->ob_pd, 0,0); + } + self->bself->out = new t_outlet*[noutlets]; + for (int i=0; i<noutlets; i++) { + self->bself->out[i] = outlet_new(self->bself,&s_anything); + } + rb_funcall(rself,SI(initialize2),0); + return rself; +} + +static void *BFObject_init (t_symbol *classsym, int ac, t_atom *at) { + t_class *qlass = find_bfclass(classsym); + if (!qlass) return 0; + BFObject *bself = (BFObject *)pd_new(qlass); + bself->magic = OBJECT_MAGIC; + bself->check_magic(); + FMessage fm = { self: bself, winlet:-1, selector: classsym, + ac: ac, at: at, is_init: true }; + long r = rb_rescue2( + (RMethod)BFObject_init_1,(Ruby)&fm, + (RMethod)BFObject_rescue,(Ruby)&fm, + rb_eException,0); + return r==Qnil ? 0 : (void *)bself; // return NULL if broken object +} + +static void BFObject_delete_1 (FMessage *fm) { + fm->self->check_magic(); + if (fm->self->rself) { + rb_funcall(fm->self->rself,SI(delete),0); + } else { + post("BFObject_delete is NOT handling BROKEN object at %08x",(int)fm); + } +} + +static void BFObject_delete (BFObject *bself) { + bself->check_magic(); + FMessage fm = { self: bself, winlet:-1, selector: gensym("delete"), + ac: 0, at: 0, is_init: false }; + rb_rescue2( + (RMethod)BFObject_delete_1,(Ruby)&fm, + (RMethod)BFObject_rescue,(Ruby)&fm, + rb_eException,0); + bself->magic = 0xDeadBeef; +} + +/* **************************************************************** */ + +struct RMessage { + VALUE rself; + ID sel; + int argc; + VALUE *argv; +}; + +// this was called rb_funcall_rescue[...] but recently (ruby 1.8.2) +// got a conflict with a new function in ruby. + +VALUE rb_funcall_myrescue_1(RMessage *rm) { + return rb_funcall2(rm->rself,rm->sel,rm->argc,rm->argv); +} + +static Ruby rb_funcall_myrescue_2 (RMessage *rm) { + Ruby error_array = make_error_message(); +// for (int i=0; i<rb_ary_len(error_array); i++) +// post("%s\n",rb_str_ptr(rb_ary_ptr(error_array)[i])); + post("%s",rb_str_ptr(rb_funcall(error_array,SI(join),1,rb_str_new2("\n")))); + return Qnil; +} + +VALUE rb_funcall_myrescue(VALUE rself, ID sel, int argc, ...) { + va_list foo; + va_start(foo,argc); + VALUE argv[argc]; + for (int i=0; i<argc; i++) argv[i] = va_arg(foo,VALUE); + RMessage rm = { rself, sel, argc, argv }; + va_end(foo); + return rb_rescue2( + (RMethod)rb_funcall_myrescue_1,(Ruby)&rm, + (RMethod)rb_funcall_myrescue_2,(Ruby)&rm, + rb_eException,0); +} + +/* Call this to get a gobj's bounding rectangle in pixels */ +void bf_getrectfn(t_gobj *x, struct _glist *glist, +int *x1, int *y1, int *x2, int *y2) { + BFObject *bself = (BFObject*)x; + Ruby can = PTR2FIX(glist_getcanvas(glist)); + Ruby a = rb_funcall_myrescue(bself->rself,SI(pd_getrect),1,can); + if (TYPE(a)!=T_ARRAY || rb_ary_len(a)<4) { + post("bf_getrectfn: return value should be 4-element array"); + *x1=*y1=*x2=*y2=0; + return; + } + *x1 = INT(rb_ary_ptr(a)[0]); + *y1 = INT(rb_ary_ptr(a)[1]); + *x2 = INT(rb_ary_ptr(a)[2]); + *y2 = INT(rb_ary_ptr(a)[3]); +} + +/* and this to displace a gobj: */ +void bf_displacefn(t_gobj *x, struct _glist *glist, int dx, int dy) { + Ruby can = PTR2FIX(glist_getcanvas(glist)); + BFObject *bself = (BFObject *)x; + bself->te_xpix+=dx; + bself->te_ypix+=dy; + rb_funcall_myrescue(bself->rself,SI(pd_displace),3,can,INT2NUM(dx),INT2NUM(dy)); + canvas_fixlinesfor(glist, (t_text *)x); +} + +/* change color to show selection: */ +void bf_selectfn(t_gobj *x, struct _glist *glist, int state) { + Ruby can = PTR2FIX(glist_getcanvas(glist)); + rb_funcall_myrescue(((BFObject*)x)->rself,SI(pd_select),2,can,INT2NUM(state)); +} + +/* change appearance to show activation/deactivation: */ +void bf_activatefn(t_gobj *x, struct _glist *glist, int state) { + Ruby can = PTR2FIX(glist_getcanvas(glist)); + rb_funcall_myrescue(((BFObject*)x)->rself,SI(pd_activate),2,can,INT2NUM(state)); +} + +/* warn a gobj it's about to be deleted */ +void bf_deletefn(t_gobj *x, struct _glist *glist) { + Ruby can = PTR2FIX(glist_getcanvas(glist)); + rb_funcall_myrescue(((BFObject*)x)->rself,SI(pd_delete),1,can); + canvas_deletelinesfor(glist, (t_text *)x); +} + +/* making visible or invisible */ +void bf_visfn(t_gobj *x, struct _glist *glist, int flag) { + Ruby can = PTR2FIX(glist_getcanvas(glist)); + Ruby rself = ((BFObject*)x)->rself; + DGS(FObject); + self->check_magic(); + rb_funcall_myrescue(((BFObject*)x)->rself,SI(pd_vis),2,can,INT2NUM(flag)); +} + +/* field a mouse click (when not in "edit" mode) */ +int bf_clickfn(t_gobj *x, struct _glist *glist, +int xpix, int ypix, int shift, int alt, int dbl, int doit) { + Ruby can = PTR2FIX(glist_getcanvas(glist)); + Ruby ret = rb_funcall_myrescue(((BFObject*)x)->rself,SI(pd_click),7,can, + INT2NUM(xpix),INT2NUM(ypix), + INT2NUM(shift),INT2NUM(alt), + INT2NUM(dbl),INT2NUM(doit)); + if (TYPE(ret) == T_FIXNUM) return INT(ret); + post("bf_clickfn: expected Fixnum"); + return 0; +} + +/* save to a binbuf */ +void bf_savefn(t_gobj *x, t_binbuf *b) { + rb_funcall_myrescue(((BFObject*)x)->rself,SI(pd_save),1,Qnil); +} + +/* open properties dialog */ +void bf_propertiesfn(t_gobj *x, struct _glist *glist) { + Ruby can = PTR2FIX(glist_getcanvas(glist)); + rb_funcall_myrescue(((BFObject*)x)->rself,SI(pd_properties),1,can); +} + +/* get keypresses during focus */ +void bf_keyfn(void *x, t_floatarg fkey) { + rb_funcall_myrescue(((BFObject*)x)->rself,SI(pd_key),1,INT2NUM((int)fkey)); +} + +/* get motion diff during focus */ +void bf_motionfn(void *x, t_floatarg dx, t_floatarg dy) { + rb_funcall_myrescue(((BFObject*)x)->rself,SI(pd_motion),2, + INT2NUM((int)dx), INT2NUM((int)dy)); +} + +/* **************************************************************** */ + +static void BFObject_class_init_1 (t_class *qlass) { + class_addanything(qlass,(t_method)BFObject_method_missing0); +} + +\class FObject + +static Ruby FObject_s_install2(Ruby rself, Ruby name) { + if (TYPE(name)!=T_STRING) RAISE("name must be String"); + t_class *qlass = class_new(gensym(rb_str_ptr(name)), + (t_newmethod)BFObject_init, (t_method)BFObject_delete, + sizeof(BFObject), CLASS_DEFAULT, A_GIMME,0); + rb_ivar_set(rself, SI(@bfclass), PTR2FIX(qlass)); + FMessage fm = {0, -1, 0, 0, 0, false}; + rb_rescue2( + (RMethod)BFObject_class_init_1,(Ruby)qlass, + (RMethod)BFObject_rescue,(Ruby)&fm, + rb_eException,0); + return Qnil; +} + +static Ruby FObject_send_out2(int argc, Ruby *argv, Ruby rself) { +//\def Ruby send_out2(...) { + DGS(FObject); + BFObject *bself = self->bself; + if (!bself) { + //post("FObject#send_out2 : bself is NULL, rself=%08x",rself); + return Qnil; + } + bself->check_magic(); + Ruby qlass = rb_funcall(rself,SI(class),0); + int outlet = INT(argv[0]); + Ruby sym = argv[1]; + argc-=2; + argv+=2; + t_atom sel, at[argc]; + Bridge_export_value(sym,&sel); + for (int i=0; i<argc; i++) Bridge_export_value(argv[i],at+i); + t_outlet *out = bself->te_outlet; + outlet_anything(bself->out[outlet],atom_getsymbol(&sel),argc,at); + return Qnil; +} + +static Ruby FObject_s_set_help (Ruby rself, Ruby path) { + path = rb_funcall(path,SI(to_s),0); + Ruby qlassid = rb_ivar_get(rself,SI(@bfclass)); + if (qlassid==Qnil) RAISE("@bfclass==nil ???"); + t_class *qlass = FIX2PTR(t_class,qlassid); + class_sethelpsymbol(qlass,gensym(rb_str_ptr(path))); + return Qnil; +} + +static Ruby GridFlow_s_gui (int argc, Ruby *argv, Ruby rself) { + if (argc!=1) RAISE("bad args"); + Ruby command = rb_funcall(argv[0],SI(to_s),0); + sys_gui(rb_str_ptr(command)); + return Qnil; +} + +static Ruby GridFlow_s_bind (Ruby rself, Ruby argv0, Ruby argv1) { + if (TYPE(argv0)==T_STRING) { +#if PD_VERSION_INT < 37 + RAISE("requires Pd 0.37"); +#else + Ruby name = rb_funcall(argv0,SI(to_s),0); + Ruby qlassid = rb_ivar_get(rb_hash_aref(rb_ivar_get(mGridFlow2,SI(@fclasses)),name),SI(@bfclass)); + if (qlassid==Qnil) RAISE("no such class: %s",rb_str_ptr(name)); + pd_typedmess(&pd_objectmaker,gensym(rb_str_ptr(name)),0,0); + t_pd *o = pd_newest(); + pd_bind(o,gensym(rb_str_ptr(argv1))); +#endif + } else { + Ruby rself = argv0; + DGS(FObject); + t_symbol *s = gensym(rb_str_ptr(argv1)); + t_pd *o = (t_pd *)(self->bself); + //fprintf(stderr,"binding %08x to: \"%s\" (%08x %s)\n",o,rb_str_ptr(argv[1]),s,s->s_name); + pd_bind(o,s); + } + return Qnil; +} + +static Ruby FObject_s_gui_enable (Ruby rself) { + Ruby qlassid = rb_ivar_get(rself,SI(@bfclass)); + if (qlassid==Qnil) RAISE("no class id ?"); + t_widgetbehavior *wb = new t_widgetbehavior; + wb->w_getrectfn = bf_getrectfn; + wb->w_displacefn = bf_displacefn; + wb->w_selectfn = bf_selectfn; + wb->w_activatefn = bf_activatefn; + wb->w_deletefn = bf_deletefn; + wb->w_visfn = bf_visfn; + wb->w_clickfn = bf_clickfn; + //wb->w_savefn = bf_savefn; + //wb->w_propertiesfn = bf_propertiesfn; + class_setwidget(FIX2PTR(t_class,qlassid),wb); + return Qnil; +} + +static Ruby FObject_focus (Ruby rself, Ruby canvas_, Ruby x_, Ruby y_) { + DGS(FObject); + t_glist *canvas = FIX2PTR(t_glist,canvas_); + t_gobj *bself = (t_gobj *)(self->bself); + int x = INT(x_); + int y = INT(y_); + glist_grab(canvas,bself, bf_motionfn, bf_keyfn, x,y); + return Qnil; +} + +// doesn't use rself but still is aside FObject_focus for symmetry reasons. +static Ruby FObject_unfocus (Ruby rself, Ruby canvas_) { + DGS(FObject); + t_glist *canvas = FIX2PTR(t_glist,canvas_); + glist_grab(canvas,0,0,0,0,0); + return Qnil; +} + +static Ruby FObject_add_inlets (Ruby rself, Ruby n_) { + DGS(FObject); + if (!self->bself) RAISE("there is no bself"); + int n = INT(n_); + for (int i=self->bself->nin; i<self->bself->nin+n; i++) { + BFProxy *p = (BFProxy *)pd_new(BFProxy_class); + p->parent = self->bself; + p->inlet = i; + inlet_new(self->bself, &p->ob_pd, 0,0); + } + self->bself->nin+=n; + return Qnil; +} + +static Ruby FObject_add_outlets (Ruby rself, Ruby n_) { + DGS(FObject); + if (!self->bself) RAISE("there is no bself"); + int n = INT(n_); + t_outlet **oldouts = self->bself->out; + self->bself->out = new t_outlet*[self->bself->nout+n]; + memcpy(self->bself->out,oldouts,self->bself->nout*sizeof(t_outlet*)); + for (int i=self->bself->nout; i<self->bself->nout+n; i++) { + self->bself->out[i] = outlet_new(self->bself,&s_anything); + } + self->bself->nout+=n; + return Qnil; +} + +static Ruby bridge_add_to_menu (int argc, Ruby *argv, Ruby rself) { + if (argc!=1) RAISE("bad args"); + Ruby name = rb_funcall(argv[0],SI(to_s),0); + Ruby qlassid = rb_ivar_get(rb_hash_aref(rb_ivar_get(mGridFlow2,SI(@fclasses)),name),SI(@bfclass)); + if (qlassid==Qnil) RAISE("no such class: %s",rb_str_ptr(name)); + //!@#$ + return Qnil; +} + +static Ruby GridFlow_s_add_creator_2 (Ruby rself, Ruby name_) { + t_symbol *name = gensym(rb_str_ptr(rb_funcall(name_,SI(to_s),0))); + class_addcreator((t_newmethod)BFObject_init,name,A_GIMME,0); + return Qnil; +} + +static Ruby FObject_get_position (Ruby rself, Ruby canvas) { + DGS(FObject); + t_text *bself = (t_text *)(self->bself); + t_glist *c = FIX2PTR(t_glist,canvas); + float x0,y0; + if (c->gl_havewindow || !c->gl_isgraph) { + x0=bself->te_xpix; + y0=bself->te_ypix; + } + else { // graph-on-parent: have to zoom + float zx = float(c->gl_x2 - c->gl_x1) / (c->gl_screenx2 - c->gl_screenx1); + float zy = float(c->gl_y2 - c->gl_y1) / (c->gl_screeny2 - c->gl_screeny1); + x0 = glist_xtopixels(c, c->gl_x1 + bself->te_xpix*zx); + y0 = glist_ytopixels(c, c->gl_y1 + bself->te_ypix*zy); + } + Ruby a = rb_ary_new(); + rb_ary_push(a,INT2NUM((int)x0)); + rb_ary_push(a,INT2NUM((int)y0)); + return a; +} + +\classinfo {} +\end class FObject + +//**************************************************************** + +\class Clock < CObject +struct Clock : CObject { + t_clock *serf; + Ruby owner; /* copy of ptr that serf already has, for marking */ + \decl void set (double systime); + \decl void delay(double delaytime); + \decl void unset(); +}; + +void Clock_fn (Ruby rself) { rb_funcall_myrescue(rself,SI(call),0); } +void Clock_mark (Clock *self) { rb_gc_mark(self->owner); } +void Clock_free (Clock *self) { clock_free(self->serf); CObject_freeee(self); } + +Ruby Clock_s_new (Ruby qlass, Ruby owner) { + Clock *self = new Clock(); + self->rself = Data_Wrap_Struct(qlass, Clock_mark, Clock_free, self); + self->serf = clock_new((void*)owner,(t_method)Clock_fn); + self->owner = owner; + return self->rself; +} + +\def void set (double systime) { clock_set(serf, systime); } +\def void delay(double delaytime) { clock_delay(serf,delaytime); } +\def void unset() { clock_unset(serf); } + +\classinfo {} +\end class Clock + +//**************************************************************** + +Ruby GridFlow_s_post_string (Ruby rself, Ruby string) { + if (TYPE(string)!=T_STRING) RAISE("not a string!"); + post("%s",rb_str_ptr(string)); + return Qnil; +} + +#define SDEF(_name1_,_name2_,_argc_) \ + rb_define_singleton_method(mGridFlow2,_name1_,(RMethod)GridFlow_s_##_name2_,_argc_) + +Ruby gf_bridge_init (Ruby rself) { + Ruby ver = EVAL("GridFlow::GF_VERSION"); + if (strcmp(rb_str_ptr(ver), GF_VERSION) != 0) { + RAISE("GridFlow version mismatch: " + "main library is '%s'; bridge is '%s'", + rb_str_ptr(ver), GF_VERSION); + } + syms = FIX2PTR(BuiltinSymbols,rb_ivar_get(mGridFlow2,SI(@bsym))); + Ruby fo = EVAL("GridFlow::FObject"); + rb_define_singleton_method(fo,"install2",(RMethod)FObject_s_install2,1); + rb_define_singleton_method(fo,"gui_enable", (RMethod)FObject_s_gui_enable, 0); + rb_define_singleton_method(fo,"set_help", (RMethod)FObject_s_set_help, 1); + rb_define_method(fo,"get_position",(RMethod)FObject_get_position,1); + rb_define_method(fo,"send_out2", (RMethod)FObject_send_out2,-1); + rb_define_method(fo,"send_out2", (RMethod)FObject_send_out2,-1); + rb_define_method(fo,"add_inlets", (RMethod)FObject_add_inlets, 1); + rb_define_method(fo,"add_outlets", (RMethod)FObject_add_outlets, 1); + rb_define_method(fo,"unfocus", (RMethod)FObject_unfocus, 1); + rb_define_method(fo, "focus", (RMethod)FObject_focus, 3); + + SDEF("post_string",post_string,1); + SDEF("add_creator_2",add_creator_2,1); + SDEF("gui",gui,-1); + SDEF("bind",bind,2); + // SDEF("add_to_menu",add_to_menu,-1); + + \startall + rb_define_singleton_method(EVAL("GridFlow::Clock" ),"new", (RMethod)Clock_s_new, 1); + rb_define_singleton_method(EVAL("GridFlow::Pointer"),"new", (RMethod)Pointer_s_new, 1); + mPointer = EVAL("GridFlow::Pointer"); + EVAL("class<<GridFlow;attr_accessor :config; end"); + EVAL("GridFlow.config = {'PUREDATA_PATH' => %{"PUREDATA_PATH"}}"); + return Qnil; +} + +struct BFGridFlow : t_object {}; + +t_class *bindpatcher; +static void *bindpatcher_init (t_symbol *classsym, int ac, t_atom *at) { + t_pd *bself = pd_new(bindpatcher); + if (ac!=1 || at->a_type != A_SYMBOL) { + post("bindpatcher: oops"); + } else { + t_symbol *s = atom_getsymbol(at); + post("binding patcher to: %s",s->s_name); + pd_bind((t_pd *)canvas_getcurrent(),s); + } + return bself; +} + +extern "C" void gridflow_setup () { + char *foo[] = {"Ruby-for-PureData","-w","-e",";"}; + post("setting up Ruby-for-PureData..."); +/* + post("pd_getfilename() = %s", pd_getfilename()->s_name); + post("pd_getdirname() = %s", pd_getdirname()->s_name); + post("canvas_getcurrentdir() = %p", canvas_getcurrentdir()); + char *dirresult = new char[242]; + char *nameresult; + int fd = open_via_path("","gridflow",".so",dirresult,&nameresult,242,1); + post("open_via_path: fd=%d dirresult=\"%s\" nameresult=\"%s\"",fd,dirresult,nameresult); + delete[] dirresult; +*/ + ruby_init(); + Init_stack(localize_sysstack()); + ruby_options(COUNT(foo),foo); + post("we are using Ruby version %s",rb_str_ptr(EVAL("RUBY_VERSION"))); + Ruby cData = rb_const_get(rb_cObject,SI(Data)); + BFProxy_class = class_new(gensym("ruby_proxy"), + NULL,NULL,sizeof(BFProxy),CLASS_PD|CLASS_NOINLET, A_NULL); + class_addanything(BFProxy_class,BFProxy_method_missing); + rb_define_singleton_method(cData,"gf_bridge_init", + (RMethod)gf_bridge_init,0); + + mGridFlow2 = EVAL( + "module GridFlow; class<<self; attr_reader :bridge_name; end; " + "@bridge_name = 'puredata'; self end"); + post("(done)"); + if (! + EVAL("begin require 'gridflow'; true; rescue Exception => e;\ + STDERR.puts \"[#{e.class}] [#{e.message}]:\n#{e.backtrace.join'\n'}\"; false; end")) + { + post("ERROR: Cannot load GridFlow-for-Ruby (gridflow.so)\n"); + return; + } + bindpatcher = class_new(gensym("bindpatcher"), + (t_newmethod)bindpatcher_init, 0, sizeof(t_object),CLASS_DEFAULT,A_GIMME,0); +} + + diff --git a/externals/gridflow/bridge/puredata.rb b/externals/gridflow/bridge/puredata.rb new file mode 100644 index 00000000..fffda1c2 --- /dev/null +++ b/externals/gridflow/bridge/puredata.rb @@ -0,0 +1,194 @@ +=begin + $Id: puredata.rb,v 1.1 2005-10-04 02:02:13 matju Exp $ + + GridFlow + Copyright (c) 2001,2002 by Mathieu Bouchard + + 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. + + See file ../COPYING for further informations on licensing terms. + + 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. +=end + +# <matju> alx1: in puredata.rb, just after the header, you have a %w() block, +# and in it you write the name of your object, and if your helpfile is not +# named like your object, then you add an equal sign and the filename + +#!@#$ DON'T PUT ABSTRACTIONS IN THE %w() !!! +# @mouse=help_mouse @motion_detection=help_motion_detect @fade=help_fade +# @apply_colormap_channelwise @checkers @complex_sq @contrast +# @posterize @ravel @greyscale_to_rgb @rgb_to_greyscale @solarize @spread +#rgb_to_yuv=#rgb_to_yuv_and_#yuv_to_rgb +#yuv_to_rgb=#rgb_to_yuv_and_#yuv_to_rgb +#clip #contrast #fade #numop #remap_image + +# NEW help files +#!@#$ (what's #+-help.pd ? #print-help2.pd ?) +%w( + # #cast #dim #reverse + #pack=#unpack-#pack + #unpack=#unpack-#pack + renamefile + #in plotter_control + listelement exec ls #print unix_time +).each {|name| + if name =~ /=/ then name,file = name.split(/=/) else file = name end + begin + x = GridFlow.fclasses[name] + x.set_help "gridflow/flow_classes/#{file}-help.pd" + rescue Exception => e + GridFlow.post "for [#{name}], #{e.class}: #{e}" # + ":\n" + e.backtrace.join("\n") + end +} + +# OLD help files +%w( + @cast + @convolve @downscale_by @draw_polygon @export=@importexport + @finished @fold @for @global @grade + @import=@importexport @inner=@foldinnerouter + @in=@inout @join @layer @outer=@foldinnerouter @out=@inout + @! @perspective printargs @print @redim + rubyprint @scale_by @scale_to @scan + @store +).each {|name| + if name =~ /=/ then name,file = name.split(/=/) else file = name end + begin + GridFlow.fclasses[name].set_help "gridflow/#{file}.pd" + rescue Exception => e + GridFlow.post "ruby #{e.class}: #{e}:\n" + e.backtrace.join("\n") + end +} + +#GridFlow.gui "frame .controls.gridflow -relief ridge -borderwidth 2\n" +#GridFlow.gui "button .controls.gridflow.button -text FOO\n" +#GridFlow.gui "pack .controls.gridflow.button -side left\n" +#GridFlow.gui "pack .controls.gridflow -side right\n" + +GridFlow.gui %q{ + +if {[catch { + # pd 0.37 + menu .mbar.gridflow -tearoff $pd_tearoff + .mbar add cascade -label "GridFlow" -menu .mbar.gridflow + set gfmenu .mbar.gridflow +}]} { + # pd 0.36 + ###the problem is that GridFlow.bind requires 0.37 +} +catch { +$gfmenu add command -label "profiler_dump" -command {pd "gridflow profiler_dump;"} +$gfmenu add command -label "profiler_reset" -command {pd "gridflow profiler_reset;"} +$gfmenu add command -label "formats" -command {pd "gridflow formats;"} +} + +if {[string length [info command post]] == 0} { + proc post {x} {puts $x} +} + +# if not running Impd: +if {[string length [info command listener_new]] == 0} { +# start of part duplicated from Impd +proc listener_new {self name} { + global _ + set _($self:hist) {} + set _($self:histi) 0 + frame $self + label $self.label -text "$name: " + entry $self.entry -width 40 +# entry $self.count -width 5 + pack $self.label -side left + pack $self.entry -side left -fill x -expand yes +# pack $self.count -side left + pack $self -fill x -expand no + bind $self.entry <Up> "listener_up $self" + bind $self.entry <Down> "listener_down $self" +} + +proc listener_up {self} { + global _ + if {$_($self:histi) > 0} {set _($self:histi) [expr -1+$_($self:histi)]} + $self.entry delete 0 end + $self.entry insert 0 [lindex $_($self:hist) $_($self:histi)] + $self.entry icursor end +# $self.count delete 0 end +# $self.count insert 0 "$_($self:histi)/[llength $_($self:hist)]" +} + +proc listener_down {self} { + global _ + if {$_($self:histi) < [llength $_($self:hist)]} {incr _($self:histi)} + $self.entry delete 0 end + $self.entry insert 0 [lindex $_($self:hist) $_($self:histi)] + $self.entry icursor end +# $self.count delete 0 end +# $self.count insert 0 "$_($self:histi)/[llength $_($self:hist)]" +} + +proc listener_append {self v} { + global _ + lappend _($self:hist) $v + set _($self:histi) [llength $_($self:hist)] +} + +proc tcl_eval {} { + set l [.tcl.entry get] + post "tcl: $l" + post "returns: [eval $l]" + listener_append .tcl [.tcl.entry get] + .tcl.entry delete 0 end + +} +if {[catch { + listener_new .tcl "Tcl" + bind .tcl.entry <Return> {tcl_eval} +}]} { + listener_new .tcl "Tcl" {tcl_eval} +} + + +} +# end of part duplicated from Impd + +proc ruby_eval {} { + set l {} + foreach c [split [.ruby.entry get] ""] {lappend l [scan $c %c]} + pd "gridflow eval $l;" + listener_append .ruby [.ruby.entry get] + .ruby.entry delete 0 end +} + +if {[catch { + listener_new .ruby "Ruby" + bind .ruby.entry <Return> {ruby_eval} +}]} { + listener_new .ruby "Ruby" {ruby_eval} +} + +} # GridFlow.gui + +if false +GridFlow.gui %q{ +catch { + if {[file exists ${pd_guidir}/lib/gridflow/icons/peephole.gif]} { + global pd_guidir + image create photo icon_peephole -file ${pd_guidir}/lib/gridflow/icons/peephole.gif + global butt + button_bar_add peephole {guiext peephole} + } { + puts $stderr GAAAH + } +} +} # GridFlow.gui +end |