/* Copyright (c) 2003 krzYszcz and others. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* LATER handle stcriptlet's named arguments */ #include #include #include "m_pd.h" #include "g_canvas.h" #include "common/loud.h" #include "hammer/file.h" #include "hammer/gui.h" #include "common/props.h" #include "toxy/scriptlet.h" #include "build_counter" //#define TOT_DEBUG /* probably much more than needed for canvas messages from gui */ #define TOTSPY_MAXSIZE 32 typedef struct _tot { t_object x_ob; t_glist *x_glist; /* containing glist */ t_symbol *x_cvremote; /* null if containing glist is our destination */ t_symbol *x_cvname; t_symbol *x_cvpathname; /* see tot_getpathname() */ t_symbol *x_visedpathname; /* see tot__vised() */ t_symbol *x_target; int x_warned; t_scriptlet *x_persistent; t_scriptlet *x_transient; t_outlet *x_out2; t_outlet *x_out4; t_symbol *x_defname; /* file name (if given as a creation arg) */ t_hammerfile *x_filehandle; t_pd *x_guidetached; t_pd *x_guisink; struct _totspy *x_spy; } t_tot; typedef struct _totspy { t_pd ts_pd; int ts_on; t_canvas *ts_cv; t_symbol *ts_target; t_symbol *ts_qsym; double ts_lasttime; t_symbol *ts_selector; t_atom ts_outbuf[TOTSPY_MAXSIZE]; t_outlet *ts_out3; } t_totspy; static t_class *tot_class; static t_class *totspy_class; static t_class *totsink_class; static t_class *tot_guiconnect_class = 0; static t_symbol *tot_ps_qpush; static t_symbol *tot_ps_query; static t_canvas *tot_getcanvas(t_tot *x, int complain) { t_canvas *cv = 0; t_glist *glist = (x->x_cvremote ? (t_glist *)pd_findbyclass(x->x_cvremote, canvas_class) : x->x_glist); if (glist) cv = glist_getcanvas(glist); else if (complain) loud_error((t_pd *)x, "bad canvas name '%s'", x->x_cvname->s_name); if (!x->x_warned && !x->x_cvremote) { x->x_warned = 1; if (!cv) cv = x->x_glist; /* redundant */ loud_warning((t_pd *)x, "using containing canvas ('%s')", cv->gl_name->s_name); } return (cv); } static t_canvas *tot_cvhook(t_pd *z) { return (tot_getcanvas((t_tot *)z, 1)); } static t_symbol *tot_dogetpathname(t_tot *x, int visedonly, int complain) { t_canvas *cv = tot_getcanvas(x, complain); if (cv) { if (visedonly && !glist_isvisible(cv)) return (0); else if (cv == x->x_glist) /* containing glist is our destination, and we are not in a gop */ return (x->x_cvpathname); else { char buf[32]; sprintf(buf, ".x%x.c", (int)cv); return (gensym(buf)); } } else return (0); } static t_symbol *tot_getpathname(t_tot *x, int complain) { return (tot_dogetpathname(x, 0, complain)); } static t_symbol *tot_getvisedpathname(t_tot *x, int complain) { return (tot_dogetpathname(x, 1, complain)); } static void tot_reset(t_tot *x) { scriptlet_reset(x->x_persistent); } static void tot_prealloc(t_tot *x, t_floatarg f) { int reqsize = (int)f; scriptlet_prealloc(x->x_persistent, reqsize, 1); scriptlet_prealloc(x->x_transient, reqsize, 1); /* LATER rethink */ } static void tot_add(t_tot *x, t_symbol *s, int ac, t_atom *av) { scriptlet_add(x->x_persistent, 0, 0, ac, av); } static void tot_addnext(t_tot *x, t_symbol *s, int ac, t_atom *av) { scriptlet_setseparator(x->x_persistent, '\n'); scriptlet_add(x->x_persistent, 0, 0, ac, av); } static void tot_push(t_tot *x, t_symbol *s, int ac, t_atom *av) { if (scriptlet_evaluate(x->x_persistent, x->x_transient, 1, ac, av, 0)) { if (s == tot_ps_qpush) scriptlet_qpush(x->x_transient); else scriptlet_push(x->x_transient); } } static void tot_tot(t_tot *x, t_symbol *s, int ac, t_atom *av) { if (ac) { t_scriptlet *sp = x->x_transient; scriptlet_reset(sp); scriptlet_add(sp, 1, 1, ac, av); if (s == tot_ps_query) scriptlet_qpush(sp); else scriptlet_push(sp); } } static void tot_dooutput(t_tot *x, t_outlet *op, t_symbol *s, int ac, t_atom *av) { if (ac == 1) { if (av->a_type == A_FLOAT) outlet_float(op, av->a_w.w_float); else if (av->a_type == A_SYMBOL) outlet_symbol(op, av->a_w.w_symbol); } else if (ac) { if (av->a_type == A_FLOAT) outlet_list(op, &s_list, ac, av); else if (av->a_type == A_SYMBOL) outlet_anything(op, av->a_w.w_symbol, ac - 1, av + 1); } else outlet_bang(op); } static void tot__reply(t_tot *x, t_symbol *s, int ac, t_atom *av) { tot_dooutput(x, ((t_object *)x)->ob_outlet, s, ac, av); } static void tot__callback(t_tot *x, t_symbol *s, int ac, t_atom *av) { tot_dooutput(x, x->x_out2, s, ac, av); } /* LATER use properties in widgetbehavior (if gop visibility rules change) */ static void tot_click(t_tot *x, t_floatarg xpos, t_floatarg ypos, t_floatarg shift, t_floatarg ctrl, t_floatarg alt) { int nleft; char *head = scriptlet_getcontents(x->x_persistent, &nleft); char buf[MAXPDSTRING + 1]; buf[MAXPDSTRING] = 0; hammereditor_open(x->x_filehandle, "scriptlet editor"); while (nleft > 0) { if (nleft > MAXPDSTRING) { strncpy(buf, head, MAXPDSTRING); head += MAXPDSTRING; nleft -= MAXPDSTRING; } else { strncpy(buf, head, nleft); buf[nleft] = 0; nleft = 0; } hammereditor_append(x->x_filehandle, buf); } } /* This is called for all Map (f==1) and all Destroy (f==0) events, comming from any canvas. If visedpathname is zero, we assume our canvas does not exist. So we ignore everything, waiting for a Map event that fits tot_getpathname(). Once we spot it, we set visedpathname, and ignore everything, waiting for a Destroy event that fits visedpathname. Then we clear visedpathname, etc... */ static void tot__vised(t_tot *x, t_symbol *s, t_floatarg f) { int flag = f != 0.; #ifdef TOT_DEBUG t_symbol *pn = tot_getpathname(x, 0); post("tot__vised %s %g (pathname %s) ", s->s_name, f, (pn ? pn->s_name : "unknown")); #endif if (!x->x_visedpathname) { if (flag && s == tot_getpathname(x, 0)) { x->x_visedpathname = s; outlet_bang(x->x_out4); } } else if (!flag && s == x->x_visedpathname) x->x_visedpathname = 0; /* LATER reconsider reporting this */ } #ifdef TOT_DEBUG static void tot_debug(t_tot *x) { t_symbol *pn = tot_getpathname(x, 0); int sz; char *bp; post("containing glist: %x", x->x_glist); post("destination: %s", x->x_cvname->s_name); post("pathname%s %s", (pn ? ":" : ""), (pn ? pn->s_name : "unknown")); bp = scriptlet_getbuffer(x->x_transient, &sz); post("transient buffer (size %d):\n\"%s\"", sz, bp); bp = scriptlet_getbuffer(x->x_persistent, &sz); post("persistent buffer (size %d):\n\"%s\"", sz, bp); } #endif static void tot_readhook(t_pd *z, t_symbol *fn, int ac, t_atom *av) { scriptlet_read(((t_tot *)z)->x_persistent, fn); } static void tot_writehook(t_pd *z, t_symbol *fn, int ac, t_atom *av) { scriptlet_write(((t_tot *)z)->x_persistent, fn); } static void tot_read(t_tot *x, t_symbol *s) { if (s && s != &s_) scriptlet_read(x->x_persistent, s); else hammerpanel_open(x->x_filehandle, 0); } static void tot_write(t_tot *x, t_symbol *s) { if (s && s != &s_) scriptlet_write(x->x_persistent, s); else hammerpanel_save(x->x_filehandle, canvas_getdir(x->x_glist), x->x_defname); } static void tot_detach(t_tot *x) { t_canvas *cv = tot_getcanvas(x, 1); if (cv && glist_isvisible(cv)) { t_pd *gc; t_symbol *target; char buf[64]; sprintf(buf, ".x%x", (int)cv); target = gensym(buf); if (!tot_guiconnect_class) { gc = (t_pd *)guiconnect_new(0, gensym("tot")); tot_guiconnect_class = *gc; typedmess(gc, gensym("signoff"), 0, 0); } if (gc = pd_findbyclass(target, tot_guiconnect_class)) { x->x_guidetached = gc; pd_unbind(gc, target); pd_bind(x->x_guisink, target); } } } static void tot_attach(t_tot *x) { t_canvas *cv = tot_getcanvas(x, 1); if (cv && glist_isvisible(cv) && x->x_guidetached) { if (tot_guiconnect_class) { t_pd *gc; t_symbol *target; char buf[64]; sprintf(buf, ".x%x", (int)cv); target = gensym(buf); if (gc = pd_findbyclass(target, tot_guiconnect_class)) { } else { /* assuming nobody else detached it in the meantime... */ pd_unbind(x->x_guisink, target); pd_bind(x->x_guidetached, target); x->x_guidetached = 0; } } else bug("tot_attach"); } } static void tot_capture(t_tot *x, t_symbol *s, t_floatarg f) { t_totspy *ts = x->x_spy; if ((int)f) { t_canvas *cv = tot_getcanvas(x, 1); ts->ts_qsym = (s == &s_ ? 0 : s); if (cv != ts->ts_cv) { if (ts->ts_target) { pd_unbind((t_pd *)ts, ts->ts_target); ts->ts_cv = 0; ts->ts_target = 0; } if (cv) { char buf[64]; ts->ts_cv = cv; sprintf(buf, ".x%x", (int)cv); pd_bind((t_pd *)ts, ts->ts_target = gensym(buf)); } } ts->ts_on = (ts->ts_target != 0); if (ts->ts_on && ts->ts_qsym) { ts->ts_lasttime = clock_getlogicaltime(); ts->ts_selector = gensym("add"); SETFLOAT(&ts->ts_outbuf[0], 0); SETSYMBOL(&ts->ts_outbuf[1], ts->ts_qsym); SETSYMBOL(&ts->ts_outbuf[2], &s_); outlet_anything(ts->ts_out3, gensym("clear"), 0, 0); } } else ts->ts_on = 0; } static void totspy_anything(t_totspy *ts, t_symbol *s, int ac, t_atom *av) { if (ts->ts_on) { if (ts->ts_qsym) { int cnt = ac + 3; if (cnt < TOTSPY_MAXSIZE) { t_atom *ap = ts->ts_outbuf; ap++->a_w.w_float = (float)clock_gettimesince(ts->ts_lasttime); ap++; ap++->a_w.w_symbol = s; while (ac--) *ap++ = *av++; outlet_anything(ts->ts_out3, ts->ts_selector, cnt, ts->ts_outbuf); ts->ts_lasttime = clock_getlogicaltime(); } else loud_warning((t_pd *)ts, "unexpectedly long message (\"%s...\"), ignored", s->s_name); } else outlet_anything(ts->ts_out3, s, ac, av); } } static void totsink_anything(t_pd *x, t_symbol *s, int ac, t_atom *av) { /* nop */ } static void tot_free(t_tot *x) { pd_unbind((t_pd *)x, x->x_target); hammergui_unbindvised((t_pd *)x); hammerfile_free(x->x_filehandle); scriptlet_free(x->x_persistent); scriptlet_free(x->x_transient); if (x->x_spy->ts_target) pd_unbind((t_pd *)x->x_spy, x->x_spy->ts_target); pd_free((t_pd *)x->x_spy); pd_free(x->x_guisink); } static void *tot_new(t_symbol *s1, t_symbol *s2) { t_tot *x = (t_tot *)pd_new(tot_class); char buf[64]; sprintf(buf, "tot%x", (int)x); pd_bind((t_pd *)x, x->x_target = gensym(buf)); x->x_transient = scriptlet_new((t_pd *)x, x->x_target, x->x_target, 0, tot_cvhook); x->x_persistent = scriptlet_new((t_pd *)x, x->x_target, x->x_target, 0, tot_cvhook); x->x_glist = canvas_getcurrent(); if (s1 && s1 != &s_ && strcmp(s1->s_name, ".")) { x->x_cvremote = canvas_makebindsym(x->x_cvname = s1); x->x_cvpathname = 0; } else { x->x_warned = (s1 && *s1->s_name == '.'); /* do not warn if explicit */ x->x_cvremote = 0; x->x_cvname = x->x_glist->gl_name; sprintf(buf, ".x%x.c", (int)x->x_glist); x->x_cvpathname = gensym(buf); } outlet_new((t_object *)x, &s_anything); x->x_out2 = outlet_new((t_object *)x, &s_anything); x->x_spy = (t_totspy *)pd_new(totspy_class); x->x_spy->ts_on = 0; x->x_spy->ts_cv = 0; x->x_spy->ts_target = 0; x->x_spy->ts_qsym = 0; x->x_spy->ts_out3 = outlet_new((t_object *)x, &s_anything); x->x_out4 = outlet_new((t_object *)x, &s_bang); if (s2 && s2 != &s_) { x->x_defname = s2; scriptlet_read(x->x_persistent, s2); } else x->x_defname = &s_; x->x_filehandle = hammerfile_new((t_pd *)x, 0, tot_readhook, tot_writehook, 0); hammergui_bindvised((t_pd *)x); x->x_visedpathname = tot_getvisedpathname(x, 0); x->x_guidetached = 0; x->x_guisink = pd_new(totsink_class); return (x); } void tot_setup(void) { post("beware! this is tot %s, %s %s build...", TOXY_VERSION, loud_ordinal(TOXY_BUILD), TOXY_RELEASE); tot_ps_qpush = gensym("qpush"); tot_ps_query = gensym("query"); tot_class = class_new(gensym("tot"), (t_newmethod)tot_new, (t_method)tot_free, sizeof(t_tot), 0, A_DEFSYM, A_DEFSYM, 0); class_addmethod(tot_class, (t_method)tot_prealloc, gensym("prealloc"), A_FLOAT, 0); class_addmethod(tot_class, (t_method)tot_read, gensym("read"), A_DEFSYM, 0); class_addmethod(tot_class, (t_method)tot_write, gensym("write"), A_DEFSYM, 0); class_addmethod(tot_class, (t_method)tot_reset, gensym("reset"), 0); class_addmethod(tot_class, (t_method)tot_push, gensym("push"), A_GIMME, 0); class_addmethod(tot_class, (t_method)tot_push, gensym("qpush"), A_GIMME, 0); class_addmethod(tot_class, (t_method)tot_add, gensym("add"), A_GIMME, 0); class_addmethod(tot_class, (t_method)tot_addnext, gensym("addnext"), A_GIMME, 0); class_addmethod(tot_class, (t_method)tot_tot, gensym("tot"), A_GIMME, 0); class_addmethod(tot_class, (t_method)tot_tot, gensym("query"), A_GIMME, 0); class_addmethod(tot_class, (t_method)tot_detach, gensym("detach"), 0); class_addmethod(tot_class, (t_method)tot_attach, gensym("attach"), 0); class_addmethod(tot_class, (t_method)tot_capture, gensym("capture"), A_FLOAT, A_DEFSYM, 0); class_addmethod(tot_class, (t_method)tot__reply, gensym("_rp"), A_GIMME, 0); class_addmethod(tot_class, (t_method)tot__callback, gensym("_cb"), A_GIMME, 0); class_addmethod(tot_class, (t_method)tot_click, gensym("click"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); class_addmethod(tot_class, (t_method)tot__vised, gensym("_vised"), A_SYMBOL, A_FLOAT, 0); #ifdef TOT_DEBUG class_addmethod(tot_class, (t_method)tot_debug, gensym("debug"), 0); #endif hammerfile_setup(tot_class, 0); totspy_class = class_new(gensym("tot spy"), 0, 0, sizeof(t_totspy), CLASS_PD, 0); class_addanything(totspy_class, totspy_anything); totsink_class = class_new(gensym("tot sink"), 0, 0, sizeof(t_pd), CLASS_PD, 0); class_addanything(totsink_class, totsink_anything); }