[PD-cvs] externals/gridflow/bridge placebo.rb, NONE, 1.1 puredata.c, NONE, 1.1 puredata.rb, NONE, 1.1

Mathieu Bouchard matju at users.sourceforge.net
Tue Oct 4 04:02:16 CEST 2005


Update of /cvsroot/pure-data/externals/gridflow/bridge
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv21117/bridge

Added Files:
	placebo.rb puredata.c puredata.rb 
Log Message:
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


--- NEW FILE: puredata.c ---
/*
	$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);
}



--- NEW FILE: placebo.rb ---
=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

--- NEW FILE: puredata.rb ---
=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





More information about the Pd-cvs mailing list