Skip to content

Commit dd69ce6

Browse files
committed
add mouse_enter() and mouse_exit() for Pd vanilla
with proxy function for canvas messages and clock for cleanup
1 parent 84e2362 commit dd69ce6

File tree

4 files changed

+155
-11
lines changed

4 files changed

+155
-11
lines changed

pd.lua

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,12 @@ pd._mouseevent = function (object, x, y, event_type)
115115
if event_type == 3 and type(obj.mouse_drag) == "function" then
116116
obj:mouse_drag(x, y)
117117
end
118+
if event_type == 4 and type(obj.mouse_enter) == "function" then
119+
obj:mouse_enter(x, y)
120+
end
121+
if event_type == 5 and type(obj.mouse_exit) == "function" then
122+
obj:mouse_exit(x, y)
123+
end
118124
end
119125
end
120126

pdlua.c

Lines changed: 138 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,15 @@ typedef struct pdlua_proxyclock
238238
struct pdlua *owner; /**< Object to forward messages to. */
239239
t_clock *clock; /** Pd clock to use. */
240240
} t_pdlua_proxyclock;
241+
242+
/** Proxy canvas object data. */
243+
typedef struct pdlua_proxycanvas
244+
{
245+
t_pd pd; /**< Minimal Pd object. */
246+
t_symbol *p_sym; /**< The name of the canvas. */
247+
struct pdlua *p_parent; /**< The parent object. */
248+
t_clock *p_clock; /**< clock for deferred cleanup */
249+
} t_pdlua_proxycanvas;
241250
/* prototypes*/
242251

243252
static const char *pdlua_reader (lua_State *L, void *rr, size_t *size);
@@ -263,6 +272,14 @@ static t_pdlua_proxyclock *pdlua_proxyclock_new (struct pdlua *owner);
263272
static void pdlua_proxyclock_setup (void);
264273
/** Dump an array of atoms into a Lua table. */
265274
static void pdlua_pushatomtable (int argc, t_atom *argv);
275+
/** Proxy canvas 'anything' method. */
276+
static void pdlua_proxycanvas_anything(t_pdlua_proxycanvas *p, t_symbol *s, int argc, t_atom *argv);
277+
/** Proxy canvas cleanup and deallocation. */
278+
static void pdlua_proxycanvas_free(t_pdlua_proxycanvas *p);
279+
/** Proxy canvas allocation and initialization. */
280+
static t_pdlua_proxycanvas *pdlua_proxycanvas_new(struct pdlua *owner, t_symbol *name);
281+
/** Register the proxy canvas class with Pd. */
282+
static void pdlua_proxycanvas_setup(void);
266283
/** Pd object constructor. */
267284
static t_pdlua *pdlua_new (t_symbol *s, int argc, t_atom *argv);
268285
/** Pd object destructor. */
@@ -346,12 +363,15 @@ void pdlua_setup (void);
346363
struct pdlua_proxyinlet;
347364
struct pdlua_proxyreceive;
348365
struct pdlua_proxyclock;
366+
struct pdlua_proxycanvas;
349367
/** Proxy inlet class pointer. */
350368
static t_class *pdlua_proxyinlet_class;
351369
/** Proxy receive class pointer. */
352370
static t_class *pdlua_proxyreceive_class;
353371
/** Proxy clock class pointer. */
354372
static t_class *pdlua_proxyclock_class;
373+
/** Proxy canvas class pointer. */
374+
static t_class *pdlua_proxycanvas_class;
355375

356376
/** Lua file reader callback. */
357377
static const char *pdlua_reader
@@ -378,6 +398,70 @@ static const char *pdlua_reader
378398
}
379399
}
380400

401+
static void pdlua_proxycanvas_anything(t_pdlua_proxycanvas *p, t_symbol *s, int argc, t_atom *argv) {
402+
#if !PLUGDATA
403+
// Early returns for invalid conditions
404+
if (!p->p_parent) return;
405+
if (s != gensym("motion")) return;
406+
if (argc != 3) return;
407+
408+
t_pdlua *x = p->p_parent;
409+
if (!x->has_gui || x->gfx.mouse_down) return;
410+
411+
float new_x = atom_getfloat(argv);
412+
float new_y = atom_getfloat(argv + 1);
413+
414+
int zoom = glist_getzoom(x->canvas);
415+
int obj_x = text_xpix(&x->pd, x->canvas);
416+
int obj_y = text_ypix(&x->pd, x->canvas);
417+
418+
int xpos = (new_x - obj_x) / zoom;
419+
int ypos = (new_y - obj_y) / zoom;
420+
421+
int inside = (xpos >= 0 && xpos < x->gfx.width &&
422+
ypos >= 0 && ypos < x->gfx.height);
423+
424+
// Handle state changes first
425+
if (!inside && x->gfx.mouse_inside) {
426+
pdlua_gfx_mouse_exit(x, xpos, ypos);
427+
x->gfx.mouse_inside = 0;
428+
} else if (inside && !x->gfx.mouse_inside) {
429+
pdlua_gfx_mouse_enter(x, xpos, ypos);
430+
x->gfx.mouse_inside = 1;
431+
}
432+
433+
// Only then send move event if we're inside
434+
if (inside) {
435+
pdlua_gfx_mouse_move(x, xpos, ypos);
436+
}
437+
438+
x->gfx.mouse_x = new_x;
439+
x->gfx.mouse_y = new_y;
440+
#endif
441+
}
442+
443+
static t_pdlua_proxycanvas *pdlua_proxycanvas_new(struct pdlua *owner, t_symbol *s) {
444+
if (!owner || !s || s == &s_) return NULL;
445+
446+
t_pdlua_proxycanvas *p = (t_pdlua_proxycanvas *)pd_new(pdlua_proxycanvas_class);
447+
if (!p) return NULL;
448+
449+
memset(p, 0, sizeof(t_pdlua_proxycanvas));
450+
p->pd = pdlua_proxycanvas_class;
451+
p->p_sym = s;
452+
p->p_parent = owner;
453+
p->p_clock = clock_new(p, (t_method)pdlua_proxycanvas_free);
454+
pd_bind(&p->pd, p->p_sym);
455+
456+
return p;
457+
}
458+
459+
static void pdlua_proxycanvas_free(t_pdlua_proxycanvas *p) {
460+
if (!p) return;
461+
if (p->p_sym) pd_unbind(&p->pd, p->p_sym);
462+
if (p->p_clock) clock_free(p->p_clock);
463+
}
464+
381465
/** Proxy inlet 'anything' method. */
382466
static void pdlua_proxyinlet_anything
383467
(
@@ -494,6 +578,19 @@ static void pdlua_proxyclock_setup(void)
494578
pdlua_proxyclock_class = class_new(gensym("pdlua proxy clock"), 0, 0, sizeof(t_pdlua_proxyclock), 0, 0);
495579
}
496580

581+
/** Setup the proxy class for canvas events. */
582+
static void pdlua_proxycanvas_setup(void)
583+
{
584+
pdlua_proxycanvas_class = class_new(
585+
gensym("pdlua proxy canvas"), 0,
586+
(t_method)pdlua_proxycanvas_free,
587+
sizeof(t_pdlua_proxycanvas),
588+
CLASS_NOINLET | CLASS_PD,
589+
0);
590+
if (pdlua_proxycanvas_class)
591+
class_addanything(pdlua_proxycanvas_class, (t_method)pdlua_proxycanvas_anything);
592+
}
593+
497594
/** Dump an array of atoms into a Lua table. */
498595
static void pdlua_pushatomtable
499596
(
@@ -714,9 +811,24 @@ static t_pdlua *pdlua_new
714811
if (lua_islightuserdata(__L(), -1))
715812
{
716813
object = lua_touserdata(__L(), -1);
814+
815+
// Create canvas proxy if we have GUI
816+
if (object->has_gui) {
817+
t_canvas *parent_canvas = glist_getcanvas(object->canvas);
818+
char buf[MAXPDSTRING];
819+
snprintf(buf, MAXPDSTRING-1, ".x%lx", (unsigned long)parent_canvas);
820+
object->gfx.proxycanvas = pdlua_proxycanvas_new(object, gensym(buf));
821+
if (!object->gfx.proxycanvas) {
822+
pd_error(NULL, "pdlua: failed to create canvas proxy");
823+
pd_free((t_pd *)object);
824+
lua_pop(__L(), 2);
825+
return NULL;
826+
}
827+
}
828+
717829
lua_pop(__L(), 2);/* pop the userdata and the global "pd" */
718830
PDLUA_DEBUG2("pdlua_new: before returning object %p stack top %d", object, lua_gettop(__L()));
719-
return object;
831+
return object;
720832
}
721833
else
722834
{
@@ -749,7 +861,6 @@ static void pdlua_free( t_pdlua *o /**< The object to destruct. */)
749861
}
750862

751863
void pdlua_vis(t_gobj *z, t_glist *glist, int vis){
752-
753864
t_pdlua* x = (t_pdlua *)z;
754865
// If there's no gui, use default text vis behavior
755866
if(!x->has_gui)
@@ -768,17 +879,25 @@ void pdlua_vis(t_gobj *z, t_glist *glist, int vis){
768879
}
769880
}
770881

771-
static void pdlua_delete(t_gobj *z, t_glist *glist){
772-
if(!((t_pdlua *)z)->has_gui)
882+
static void pdlua_delete(t_gobj *z, t_glist *glist) {
883+
t_pdlua *x = (t_pdlua *)z;
884+
if(!x->has_gui)
773885
{
774886
text_widgetbehavior.w_deletefn(z, glist);
775887
return;
776888
}
889+
777890
if(glist_isvisible(glist) && gobj_shouldvis(z, glist)) {
778891
pdlua_vis(z, glist, 0);
779892
}
780893

781894
canvas_deletelinesfor(glist, (t_text *)z);
895+
896+
if (x->gfx.proxycanvas) {
897+
// Schedule deferred cleanup (similar to receivecanvas external code)
898+
clock_delay(x->gfx.proxycanvas->p_clock, 0);
899+
x->gfx.proxycanvas = NULL;
900+
}
782901
}
783902

784903
#ifdef PURR_DATA // Purr Data uses an older version of this API
@@ -791,7 +910,6 @@ static void pdlua_motion(t_gobj *z, t_floatarg dx, t_floatarg dy,
791910
#if !PLUGDATA
792911
t_pdlua *x = (t_pdlua *)z;
793912
if (!x->has_gui) return;
794-
795913
#ifndef PURR_DATA
796914
// Handle mouse up immediately
797915
if (up && x->gfx.mouse_down) {
@@ -800,6 +918,14 @@ static void pdlua_motion(t_gobj *z, t_floatarg dx, t_floatarg dy,
800918
int ypos = (x->gfx.mouse_y - text_ypix(&x->pd, x->canvas)) / zoom;
801919
pdlua_gfx_mouse_up(x, xpos, ypos);
802920
x->gfx.mouse_down = 0;
921+
922+
// After mouse up, check if we need to send exit
923+
int inside = (xpos >= 0 && xpos < x->gfx.width &&
924+
ypos >= 0 && ypos < x->gfx.height);
925+
if (!inside && x->gfx.mouse_inside) {
926+
pdlua_gfx_mouse_exit(x, xpos, ypos);
927+
x->gfx.mouse_inside = 0;
928+
}
803929
return;
804930
}
805931
#endif
@@ -835,11 +961,11 @@ static int pdlua_click(t_gobj *z, t_glist *gl, int xpos, int ypos, int shift, in
835961
{
836962
pdlua_gfx_mouse_up(x, xpix, ypix);
837963
x->gfx.mouse_down = 0;
838-
} else {
839-
x->gfx.mouse_x = xpos;
840-
x->gfx.mouse_y = ypos;
841-
pdlua_gfx_mouse_move(x, xpix, ypix);
842964
}
965+
// no move events generated here
966+
// Let the proxy handle all move events
967+
x->gfx.mouse_x = xpos;
968+
x->gfx.mouse_y = ypos;
843969
}
844970
return 1;
845971
} else
@@ -3072,7 +3198,9 @@ void pdlua_setup(void)
30723198
PDLUA_DEBUG("pdlua pdlua_proxyreceive_setup done", 0);
30733199
pdlua_proxyclock_setup();
30743200
PDLUA_DEBUG("pdlua pdlua_proxyclock_setup done", 0);
3075-
if (! pdlua_proxyinlet_class || ! pdlua_proxyreceive_class || ! pdlua_proxyclock_class)
3201+
pdlua_proxycanvas_setup();
3202+
PDLUA_DEBUG("pdlua pdlua_proxycanvas_setup done", 0);
3203+
if (! pdlua_proxyinlet_class || ! pdlua_proxyreceive_class || ! pdlua_proxyclock_class || ! pdlua_proxycanvas_class)
30763204
{
30773205
pd_error(NULL, "lua: error creating proxy classes");
30783206
pd_error(NULL, "lua: loader will not be registered!");

pdlua.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,9 @@ typedef struct _pdlua_gfx
4343
char current_color[10]; // Keep track of current color
4444

4545
// Variables to keep track of mouse button state and drag position
46-
int mouse_x, mouse_y, mouse_down;
46+
int mouse_x, mouse_y, mouse_down, mouse_inside;
4747
int first_draw;
48+
struct pdlua_proxycanvas *proxycanvas;
4849
#else
4950
int current_layer;
5051
void(*plugdata_draw_callback)(void*, int, t_symbol*, int, t_atom*); // Callback to perform drawing in plugdata

pdlua_gfx.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,14 @@ void pdlua_gfx_mouse_drag(t_pdlua *o, int x, int y) {
149149
pdlua_gfx_mouse_event(o, x, y, 3);
150150
}
151151

152+
void pdlua_gfx_mouse_enter(t_pdlua *x, int xpos, int ypos) {
153+
pdlua_gfx_mouse_event(x, xpos, ypos, 4);
154+
}
155+
156+
void pdlua_gfx_mouse_exit(t_pdlua *x, int xpos, int ypos) {
157+
pdlua_gfx_mouse_event(x, xpos, ypos, 5);
158+
}
159+
152160
// Represents a path object, created with path.new(x, y)
153161
// for pd-vanilla, this contains all the points that the path contains. bezier curves are flattened out to points before being added
154162
// for plugdata, it only contains a unique ID to the juce::Path that this is mapped to
@@ -793,6 +801,7 @@ static int gfx_initialize(t_pdlua *obj)
793801
gfx->num_transforms = 0;
794802
gfx->num_layers = 0;
795803
gfx->layer_tags = NULL;
804+
gfx->mouse_inside = 0;
796805

797806
pdlua_gfx_repaint(obj, 0);
798807
return 0;

0 commit comments

Comments
 (0)