From de65f674480ebfeab651d9668131e7ab0d310484 Mon Sep 17 00:00:00 2001
From: Jonathan Wilkes <jon.w.wilkes@gmail.com>
Date: Mon, 19 Oct 2015 22:35:38 -0400
Subject: [PATCH] add [event] class for canvas field

---
 pd/src/g_canvas.c   | 17 +++++++++++++
 pd/src/g_scalar.c   | 32 +++++++++++++++--------
 pd/src/g_template.c | 62 ++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 99 insertions(+), 12 deletions(-)

diff --git a/pd/src/g_canvas.c b/pd/src/g_canvas.c
index d881b906c..40806943a 100644
--- a/pd/src/g_canvas.c
+++ b/pd/src/g_canvas.c
@@ -360,6 +360,13 @@ void canvasgop__clickhook(t_scalehandle *sh, int newstate);
 void canvasgop__motionhook(t_scalehandle *sh,t_floatarg f1, t_floatarg f2);
 extern void glist_setlastxy(t_glist *gl, int xval, int yval);
 
+/* These globals are used to set state for the "canvas" field in a
+   struct. We try below to make sure they only get set for the toplevel
+   canvas, and then set them to NULL for everything else. */
+t_symbol *canvas_field_templatesym; /* for "canvas" data type */
+t_word *canvas_field_vec;           /* for "canvas" data type */
+t_gpointer *canvas_field_gp;        /* parent for "canvas" data type */
+
     /* make a new glist.  It will either be a "root" canvas or else
     it appears as a "text" object in another window (canvas_getcurrent() 
     tells us which.) */
@@ -465,6 +472,16 @@ t_canvas *canvas_new(void *dummy, t_symbol *sel, int argc, t_atom *argv)
 
     x->u_queue = canvas_undo_init(x);
     //glist_setlastxy(x, 20, 20);
+
+    x->gl_templatesym = canvas_field_templatesym;
+    x->gl_vec = canvas_field_vec;
+    if (canvas_field_gp) gpointer_copy(canvas_field_gp, &x->gl_gp);
+
+    // unset the globals in case this was a canvas field
+    canvas_field_templatesym = NULL;
+    canvas_field_vec = NULL;
+    canvas_field_gp = NULL;
+
     return(x);
 }
 
diff --git a/pd/src/g_scalar.c b/pd/src/g_scalar.c
index 0e0561283..c53b45b04 100644
--- a/pd/src/g_scalar.c
+++ b/pd/src/g_scalar.c
@@ -18,6 +18,10 @@ t_class *scalar_class;
 
 void pd_doloadbang(void);
 
+extern t_symbol *canvas_field_templatesym; /* for "canvas" data type */
+extern t_word *canvas_field_vec;           /* for "canvas" data type */
+extern t_gpointer *canvas_field_gp;        /* parent for "canvas" data type */
+
 void word_init(t_word *data, t_template *template, t_gpointer *gp)
 {
     int i, nitems = template->t_n;
@@ -36,6 +40,23 @@ void word_init(t_word *data, t_template *template, t_gpointer *gp)
         }
         else if (type == DT_LIST)
         {
+            /* we feed these values to global vars so that we can 
+               read them from inside canvas_new.  This is very hacky, 
+               but I couldn't figure out a better way to do it. */
+            canvas_field_templatesym = template->t_sym;
+            /* this is bad-- we're storing a reference to a position in
+               a dynamically allocated byte array when realloc can potentially
+               move this data.  Essentially, we're depending on gcc to never
+               move it, which is a bad assumption.  Unfortunately gpointers
+               do the same thing, and I haven't heard back from Miller yet
+               on how he plans to deal with this problem. Hopefully that same
+               solution will be usable here. */
+            canvas_field_vec = data;
+            /* Here too we're being dangerous-- I'm copying the gpointer
+               without recounting, and I'm not unsetting the one that's 
+               part of the _glist struct (t_gpointer gl_gp). */
+            canvas_field_gp = gp;
+
             /* copied from glob_evalfile... */
             t_pd *x = 0;
             /* even though binbuf_evalfile appears to take care of dspstate,
@@ -62,17 +83,6 @@ void word_init(t_word *data, t_template *template, t_gpointer *gp)
 
             wp->w_list = canvas_getcurrent();
             wp->w_list->gl_templatesym = template->t_sym;
-            /* this is bad-- we're storing a reference to a position in
-               a dynamically allocated byte array when realloc can potentially
-               move this data.  Essentially, we're depending on gcc to never
-               move it, which is a bad assumption.  Unfortunately gpointers
-               do the same thing, and I haven't heard back from Miller yet
-               on how he plans to deal with this problem. Hopefully that same
-               solution will be usable here. */
-            wp->w_list->gl_vec = data;
-            /* Here too we're being dangerous-- I'm not unsetting this
-               gpointer yet. */
-            gpointer_copy(gp, &wp->w_list->gl_gp);
             /* make the parent glist the parent of our canvas field */
             wp->w_list->gl_owner = gp->gp_stub->gs_un.gs_glist;
 
diff --git a/pd/src/g_template.c b/pd/src/g_template.c
index 0ecb62ea5..1d972bd4b 100644
--- a/pd/src/g_template.c
+++ b/pd/src/g_template.c
@@ -4149,9 +4149,10 @@ static int draw_click(t_gobj *z, t_glist *glist,
 
 void draw_notify(t_canvas *x, t_symbol *s, int argc, t_atom *argv)
 {
+    char canvas_field_namebuf[20];
+    t_symbol *canvas_field_event;
     t_symbol *scalarsym = atom_getsymbolarg(0, argc--, argv++);
     t_symbol *drawcommand_sym = atom_getsymbolarg(0, argc--, argv++);
-    t_symbol *event_name = atom_getsymbolarg(0, argc--, argv++);
     t_scalar *sc;
     t_object *ob = 0;
     if (scalarsym->s_thing)
@@ -4161,6 +4162,20 @@ void draw_notify(t_canvas *x, t_symbol *s, int argc, t_atom *argv)
         error("draw_notify: can't get scalar from symbol");
         return;
     }
+
+    /* Generate the symbol that would be bound by any [event] inside
+       a canvas field.  If there's any in existence, forward the event
+       notification. pd_bind takes care of the details of this-- if 
+       there are multiple [event] objects it will dispatch to each */
+    sprintf(canvas_field_namebuf, "%lx_event", (long unsigned int)sc->sc_vec);
+    canvas_field_event = gensym(canvas_field_namebuf);
+    t_pd *target = canvas_field_event->s_thing;
+    if (target)
+        pd_forwardmess(target, argc, argv);
+
+    /* need to revisit popping this off the args, it's a little confusing... */
+    t_symbol *event_name = atom_getsymbolarg(0, argc--, argv++);
+
     if (drawcommand_sym->s_thing)
     {
         t_pd *drawcommand = (t_pd *)drawcommand_sym->s_thing;
@@ -4406,6 +4421,50 @@ static void draw_setup(void)
         gensym("y2"), A_GIMME, 0);
 }
 
+/* ------------------------------ event --------------------------------- */
+/* This is a very simple class used to dispatch events inside a
+   canvas field. */
+
+t_class *event_class;
+
+typedef struct _event
+{
+    t_object x_obj;
+    t_symbol *x_bindsym;
+} t_event;
+
+static void event_anything(t_event *x, t_symbol *s, int argc, t_atom *argv)
+{
+    outlet_anything(x->x_obj.ob_outlet, s, argc, argv);
+}
+
+static void *event_new(void)
+{
+    char namebuf[20];
+    t_event *x = (t_event *)pd_new(event_class);
+    t_canvas *c = canvas_getcurrent();
+    if (c->gl_vec)
+    {
+        sprintf(namebuf, "%lx_event", (long unsigned int)c->gl_vec);
+        x->x_bindsym = gensym(namebuf);
+        pd_bind(&x->x_obj.ob_pd, x->x_bindsym);
+    }
+    outlet_new(&x->x_obj, &s_anything);
+    return (x);
+}
+
+static void event_free(t_event *x)
+{
+    pd_unbind(&x->x_obj.ob_pd, x->x_bindsym);
+}
+
+void event_setup(void)
+{
+    event_class = class_new(gensym("event"), (t_newmethod)event_new,
+        (t_method)event_free, sizeof(t_event), 0, 0);
+    class_addanything(event_class, event_anything);
+}
+
 /* ---------------- curves and polygons (joined segments) ---------------- */
 
 /*
@@ -7749,6 +7808,7 @@ void g_template_setup(void)
     gtemplate_setup();
     curve_setup();
     draw_setup();
+    event_setup();
     plot_setup();
     drawnumber_setup();
     drawsymbol_setup();
-- 
GitLab