From e3a15434b23932df5cbd09d15514c05f47012a46 Mon Sep 17 00:00:00 2001
From: Jonathan Wilkes <jon.w.wilkes@gmail.com>
Date: Fri, 14 Jul 2017 13:11:40 -0400
Subject: [PATCH] add some rudimentary mouse tracking objects so that the
 questionable C code in the various mouse-related classes can be replaced with
 abstractions

---
 pd/src/g_editor.c |  31 +++++++++++
 pd/src/x_gui.c    | 128 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 159 insertions(+)

diff --git a/pd/src/g_editor.c b/pd/src/g_editor.c
index 3fdf9473b..4ff0150d2 100644
--- a/pd/src/g_editor.c
+++ b/pd/src/g_editor.c
@@ -3756,11 +3756,29 @@ void canvas_doclick(t_canvas *x, int xpos, int ypos, int which,
     }
 }
 
+   // Dispatch mouseclick message to receiver (for legacy mouse event externals)
+void canvas_dispatch_mouseclick(t_float down, t_float xpos, t_float ypos,
+    t_float which)
+{
+    t_symbol *mouseclicksym = gensym("#mouseclick");
+    if (mouseclicksym->s_thing)
+    {
+        t_atom at[4];
+        SETFLOAT(at, down);
+        SETFLOAT(at+1, which);
+        SETFLOAT(at+2, xpos);
+        SETFLOAT(at+3, ypos);
+        pd_list(mouseclicksym->s_thing, &s_list, 4, at);
+    }
+}
+
 void canvas_mousedown(t_canvas *x, t_floatarg xpos, t_floatarg ypos,
     t_floatarg which, t_floatarg mod)
 {
     //fprintf(stderr,"canvas_mousedown %d\n", x->gl_editor->e_onmotion);
     canvas_doclick(x, xpos, ypos, which, mod, 1);
+    // now dispatch to any listeners
+    canvas_dispatch_mouseclick(1., xpos, ypos, which);
 }
 
 int canvas_isconnected (t_canvas *x, t_text *ob1, int n1,
@@ -4770,6 +4788,8 @@ void canvas_mouseup(t_canvas *x,
     if (canvas_last_glist_mod == -1)
         canvas_doclick(x, xpos, ypos, 0,
             (glob_shift + glob_ctrl*2 + glob_alt*4), 0);
+    // now dispatch to any click listeners
+    canvas_dispatch_mouseclick(0., xpos, ypos, which);
 }
 
 /* Cheap hack to simulate mouseup at the last x/y coord. We use this in
@@ -5150,6 +5170,7 @@ extern void graph_checkgop_rect(t_gobj *z, t_glist *glist,
 void canvas_motion(t_canvas *x, t_floatarg xpos, t_floatarg ypos,
     t_floatarg fmod)
 {
+    static t_symbol *mousemotionsym;
     //fprintf(stderr,"motion %d %d %d %d\n",
     //    (int)xpos, (int)ypos, (int)fmod, canvas_last_glist_mod);
     //fprintf(stderr,"canvas_motion=%d\n",x->gl_editor->e_onmotion);
@@ -5312,6 +5333,16 @@ void canvas_motion(t_canvas *x, t_floatarg xpos, t_floatarg ypos,
     //        x, (int)xpos, (int)ypos);
     //}
     x->gl_editor->e_lastmoved = 1;
+    // Dispatch to any listeners for the motion message
+    if (!mousemotionsym)
+        mousemotionsym = gensym("#mousemotion");
+    if (mousemotionsym->s_thing)
+    {
+        t_atom at[2];
+        SETFLOAT(at, xpos);
+        SETFLOAT(at+1, ypos);
+        pd_list(mousemotionsym->s_thing, &s_list, 2, at);
+    }
 }
 
 void canvas_startmotion(t_canvas *x)
diff --git a/pd/src/x_gui.c b/pd/src/x_gui.c
index f7f1fb70d..3386e8bda 100644
--- a/pd/src/x_gui.c
+++ b/pd/src/x_gui.c
@@ -478,6 +478,133 @@ static void key_setup(void)
     //class_sethelpsymbol(keyname_class, gensym("key"));
 }
 
+/* ------------------ mouse classes for legacy externals ------------------ */
+
+/* Every other legacy external library has some ad hoc code for getting
+   mouse state within a Pd patch. All of them have different weird interfaces
+   and some are outright buggy.
+
+   Most of these return screen coordinates. This is unfortunately more of
+   a pain than it should be in nw.js. Instead, we return window coordinates
+   and hope that this is good enough for the uses to which these external
+   classes have been put. At worst the user can make the relevant canvas
+   full screen and get the desired behavior (minus the offset for the menu).
+
+   Most of the uses for mouse coordinates seem to do with tutorials that map
+   x/y positions to amplitude, frequency, etc. So these classes should be
+   good enough to build abstractions to do an end run around the relevant
+   externals.
+*/
+
+static t_symbol *mousemotion_sym, *mouseclick_sym, *mousewheel_sym;
+static t_class *mousemotion_class, *mouseclick_class, *mousewheel_class;
+
+typedef struct _mousemotion
+{
+    t_object x_obj;
+    t_outlet *x_outlet1;
+    t_outlet *x_outlet2;
+} t_mousemotion;
+
+static void *mousemotion_new( void)
+{
+    t_mousemotion *x = (t_mousemotion *)pd_new(mousemotion_class);
+    x->x_outlet1 = outlet_new(&x->x_obj, &s_float);
+    x->x_outlet2 = outlet_new(&x->x_obj, &s_float);
+    pd_bind(&x->x_obj.ob_pd, mousemotion_sym);
+    return (x);
+}
+
+static void mousemotion_list(t_mousemotion *x, t_symbol *s, int argc,
+    t_atom *argv)
+{
+    outlet_float(x->x_outlet2, atom_getfloatarg(1, argc, argv));
+    outlet_float(x->x_outlet1, atom_getfloatarg(0, argc, argv));
+}
+
+static void mousemotion_free(t_mousemotion *x)
+{
+    pd_unbind(&x->x_obj.ob_pd, mousemotion_sym);
+}
+
+typedef struct _mouseclick
+{
+    t_object x_obj;
+    t_outlet *x_outlet1;
+    t_outlet *x_outlet2;
+    t_outlet *x_outlet3;
+    t_outlet *x_outlet4;
+} t_mouseclick;
+
+static void *mouseclick_new( void)
+{
+    t_mouseclick *x = (t_mouseclick *)pd_new(mouseclick_class);
+    x->x_outlet1 = outlet_new(&x->x_obj, &s_float);
+    x->x_outlet2 = outlet_new(&x->x_obj, &s_float);
+    x->x_outlet3 = outlet_new(&x->x_obj, &s_float);
+    x->x_outlet4 = outlet_new(&x->x_obj, &s_float);
+    pd_bind(&x->x_obj.ob_pd, mouseclick_sym);
+    return (x);
+}
+
+static void mouseclick_list(t_mouseclick *x, t_symbol *s, int argc,
+    t_atom *argv)
+{
+    outlet_float(x->x_outlet4, atom_getfloatarg(3, argc, argv));
+    outlet_float(x->x_outlet3, atom_getfloatarg(2, argc, argv));
+    outlet_float(x->x_outlet2, atom_getfloatarg(1, argc, argv));
+    outlet_float(x->x_outlet1, atom_getfloatarg(0, argc, argv));
+}
+
+static void mouseclick_free(t_mouseclick *x)
+{
+    pd_unbind(&x->x_obj.ob_pd, mouseclick_sym);
+}
+
+typedef struct _mousewheel
+{
+    t_object x_obj;
+} t_mousewheel;
+
+static void *mousewheel_new( void)
+{
+    t_mousewheel *x = (t_mousewheel *)pd_new(mousewheel_class);
+    outlet_new(&x->x_obj, &s_float);
+    pd_bind(&x->x_obj.ob_pd, mousewheel_sym);
+    return (x);
+}
+
+static void mousewheel_float(t_mousewheel *x, t_floatarg f)
+{
+    outlet_float(x->x_obj.ob_outlet, f);
+}
+
+static void mousewheel_free(t_mousewheel *x)
+{
+    pd_unbind(&x->x_obj.ob_pd, mousewheel_sym);
+}
+
+static void mouse_setup(void)
+{
+    mousemotion_class = class_new(gensym("mousemotion"),
+        (t_newmethod)mousemotion_new, (t_method)mousemotion_free,
+        sizeof(t_mousemotion), CLASS_NOINLET, 0);
+    class_addlist(mousemotion_class, mousemotion_list);
+    mousemotion_sym = gensym("#mousemotion");
+
+    mouseclick_class = class_new(gensym("mouseclick"),
+        (t_newmethod)mouseclick_new, (t_method)mouseclick_free,
+        sizeof(t_mouseclick), CLASS_NOINLET, 0);
+    class_addlist(mouseclick_class, mouseclick_list);
+    mouseclick_sym = gensym("#mouseclick");
+
+    mousewheel_class = class_new(gensym("mousewheel"),
+        (t_newmethod)mousewheel_new, (t_method)mousewheel_free,
+        sizeof(t_mousewheel), CLASS_NOINLET, 0);
+    class_addfloat(mousewheel_class, mousewheel_float);
+    mousewheel_sym = gensym("#mousewheel");
+}
+
 /* -------------------------- setup routine ------------------------------ */
 
 void x_gui_setup(void)
@@ -486,6 +613,7 @@ void x_gui_setup(void)
     openpanel_setup();
     savepanel_setup();
     key_setup();
+    mouse_setup();
     // jsarlo
     magicGlass_setup();
     // end jsarlo
-- 
GitLab