Skip to content

Commit

Permalink
add mouse_enter() and mouse_exit() for Pd vanilla
Browse files Browse the repository at this point in the history
with proxy function for canvas messages and clock for cleanup
  • Loading branch information
ben-wes committed Jan 17, 2025
1 parent 84e2362 commit dd69ce6
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 11 deletions.
6 changes: 6 additions & 0 deletions pd.lua
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,12 @@ pd._mouseevent = function (object, x, y, event_type)
if event_type == 3 and type(obj.mouse_drag) == "function" then
obj:mouse_drag(x, y)
end
if event_type == 4 and type(obj.mouse_enter) == "function" then
obj:mouse_enter(x, y)
end
if event_type == 5 and type(obj.mouse_exit) == "function" then
obj:mouse_exit(x, y)
end
end
end

Expand Down
148 changes: 138 additions & 10 deletions pdlua.c
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,15 @@ typedef struct pdlua_proxyclock
struct pdlua *owner; /**< Object to forward messages to. */
t_clock *clock; /** Pd clock to use. */
} t_pdlua_proxyclock;

/** Proxy canvas object data. */
typedef struct pdlua_proxycanvas
{
t_pd pd; /**< Minimal Pd object. */
t_symbol *p_sym; /**< The name of the canvas. */
struct pdlua *p_parent; /**< The parent object. */
t_clock *p_clock; /**< clock for deferred cleanup */
} t_pdlua_proxycanvas;
/* prototypes*/

static const char *pdlua_reader (lua_State *L, void *rr, size_t *size);
Expand All @@ -263,6 +272,14 @@ static t_pdlua_proxyclock *pdlua_proxyclock_new (struct pdlua *owner);
static void pdlua_proxyclock_setup (void);
/** Dump an array of atoms into a Lua table. */
static void pdlua_pushatomtable (int argc, t_atom *argv);
/** Proxy canvas 'anything' method. */
static void pdlua_proxycanvas_anything(t_pdlua_proxycanvas *p, t_symbol *s, int argc, t_atom *argv);
/** Proxy canvas cleanup and deallocation. */
static void pdlua_proxycanvas_free(t_pdlua_proxycanvas *p);
/** Proxy canvas allocation and initialization. */
static t_pdlua_proxycanvas *pdlua_proxycanvas_new(struct pdlua *owner, t_symbol *name);
/** Register the proxy canvas class with Pd. */
static void pdlua_proxycanvas_setup(void);
/** Pd object constructor. */
static t_pdlua *pdlua_new (t_symbol *s, int argc, t_atom *argv);
/** Pd object destructor. */
Expand Down Expand Up @@ -346,12 +363,15 @@ void pdlua_setup (void);
struct pdlua_proxyinlet;
struct pdlua_proxyreceive;
struct pdlua_proxyclock;
struct pdlua_proxycanvas;
/** Proxy inlet class pointer. */
static t_class *pdlua_proxyinlet_class;
/** Proxy receive class pointer. */
static t_class *pdlua_proxyreceive_class;
/** Proxy clock class pointer. */
static t_class *pdlua_proxyclock_class;
/** Proxy canvas class pointer. */
static t_class *pdlua_proxycanvas_class;

/** Lua file reader callback. */
static const char *pdlua_reader
Expand All @@ -378,6 +398,70 @@ static const char *pdlua_reader
}
}

static void pdlua_proxycanvas_anything(t_pdlua_proxycanvas *p, t_symbol *s, int argc, t_atom *argv) {
#if !PLUGDATA
// Early returns for invalid conditions
if (!p->p_parent) return;
if (s != gensym("motion")) return;
if (argc != 3) return;

t_pdlua *x = p->p_parent;
if (!x->has_gui || x->gfx.mouse_down) return;

float new_x = atom_getfloat(argv);
float new_y = atom_getfloat(argv + 1);

int zoom = glist_getzoom(x->canvas);
int obj_x = text_xpix(&x->pd, x->canvas);
int obj_y = text_ypix(&x->pd, x->canvas);

int xpos = (new_x - obj_x) / zoom;
int ypos = (new_y - obj_y) / zoom;

int inside = (xpos >= 0 && xpos < x->gfx.width &&
ypos >= 0 && ypos < x->gfx.height);

// Handle state changes first
if (!inside && x->gfx.mouse_inside) {
pdlua_gfx_mouse_exit(x, xpos, ypos);
x->gfx.mouse_inside = 0;
} else if (inside && !x->gfx.mouse_inside) {
pdlua_gfx_mouse_enter(x, xpos, ypos);
x->gfx.mouse_inside = 1;
}

// Only then send move event if we're inside
if (inside) {
pdlua_gfx_mouse_move(x, xpos, ypos);
}

x->gfx.mouse_x = new_x;
x->gfx.mouse_y = new_y;
#endif
}

static t_pdlua_proxycanvas *pdlua_proxycanvas_new(struct pdlua *owner, t_symbol *s) {
if (!owner || !s || s == &s_) return NULL;

t_pdlua_proxycanvas *p = (t_pdlua_proxycanvas *)pd_new(pdlua_proxycanvas_class);
if (!p) return NULL;

memset(p, 0, sizeof(t_pdlua_proxycanvas));
p->pd = pdlua_proxycanvas_class;
p->p_sym = s;
p->p_parent = owner;
p->p_clock = clock_new(p, (t_method)pdlua_proxycanvas_free);
pd_bind(&p->pd, p->p_sym);

return p;
}

static void pdlua_proxycanvas_free(t_pdlua_proxycanvas *p) {
if (!p) return;
if (p->p_sym) pd_unbind(&p->pd, p->p_sym);
if (p->p_clock) clock_free(p->p_clock);
}

/** Proxy inlet 'anything' method. */
static void pdlua_proxyinlet_anything
(
Expand Down Expand Up @@ -494,6 +578,19 @@ static void pdlua_proxyclock_setup(void)
pdlua_proxyclock_class = class_new(gensym("pdlua proxy clock"), 0, 0, sizeof(t_pdlua_proxyclock), 0, 0);
}

/** Setup the proxy class for canvas events. */
static void pdlua_proxycanvas_setup(void)
{
pdlua_proxycanvas_class = class_new(
gensym("pdlua proxy canvas"), 0,
(t_method)pdlua_proxycanvas_free,
sizeof(t_pdlua_proxycanvas),
CLASS_NOINLET | CLASS_PD,
0);
if (pdlua_proxycanvas_class)
class_addanything(pdlua_proxycanvas_class, (t_method)pdlua_proxycanvas_anything);
}

/** Dump an array of atoms into a Lua table. */
static void pdlua_pushatomtable
(
Expand Down Expand Up @@ -714,9 +811,24 @@ static t_pdlua *pdlua_new
if (lua_islightuserdata(__L(), -1))
{
object = lua_touserdata(__L(), -1);

// Create canvas proxy if we have GUI
if (object->has_gui) {
t_canvas *parent_canvas = glist_getcanvas(object->canvas);
char buf[MAXPDSTRING];
snprintf(buf, MAXPDSTRING-1, ".x%lx", (unsigned long)parent_canvas);
object->gfx.proxycanvas = pdlua_proxycanvas_new(object, gensym(buf));
if (!object->gfx.proxycanvas) {
pd_error(NULL, "pdlua: failed to create canvas proxy");
pd_free((t_pd *)object);
lua_pop(__L(), 2);
return NULL;
}
}

lua_pop(__L(), 2);/* pop the userdata and the global "pd" */
PDLUA_DEBUG2("pdlua_new: before returning object %p stack top %d", object, lua_gettop(__L()));
return object;
return object;
}
else
{
Expand Down Expand Up @@ -749,7 +861,6 @@ static void pdlua_free( t_pdlua *o /**< The object to destruct. */)
}

void pdlua_vis(t_gobj *z, t_glist *glist, int vis){

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

static void pdlua_delete(t_gobj *z, t_glist *glist){
if(!((t_pdlua *)z)->has_gui)
static void pdlua_delete(t_gobj *z, t_glist *glist) {
t_pdlua *x = (t_pdlua *)z;
if(!x->has_gui)
{
text_widgetbehavior.w_deletefn(z, glist);
return;
}

if(glist_isvisible(glist) && gobj_shouldvis(z, glist)) {
pdlua_vis(z, glist, 0);
}

canvas_deletelinesfor(glist, (t_text *)z);

if (x->gfx.proxycanvas) {
// Schedule deferred cleanup (similar to receivecanvas external code)
clock_delay(x->gfx.proxycanvas->p_clock, 0);
x->gfx.proxycanvas = NULL;
}
}

#ifdef PURR_DATA // Purr Data uses an older version of this API
Expand All @@ -791,7 +910,6 @@ static void pdlua_motion(t_gobj *z, t_floatarg dx, t_floatarg dy,
#if !PLUGDATA
t_pdlua *x = (t_pdlua *)z;
if (!x->has_gui) return;

#ifndef PURR_DATA
// Handle mouse up immediately
if (up && x->gfx.mouse_down) {
Expand All @@ -800,6 +918,14 @@ static void pdlua_motion(t_gobj *z, t_floatarg dx, t_floatarg dy,
int ypos = (x->gfx.mouse_y - text_ypix(&x->pd, x->canvas)) / zoom;
pdlua_gfx_mouse_up(x, xpos, ypos);
x->gfx.mouse_down = 0;

// After mouse up, check if we need to send exit
int inside = (xpos >= 0 && xpos < x->gfx.width &&
ypos >= 0 && ypos < x->gfx.height);
if (!inside && x->gfx.mouse_inside) {
pdlua_gfx_mouse_exit(x, xpos, ypos);
x->gfx.mouse_inside = 0;
}
return;
}
#endif
Expand Down Expand Up @@ -835,11 +961,11 @@ static int pdlua_click(t_gobj *z, t_glist *gl, int xpos, int ypos, int shift, in
{
pdlua_gfx_mouse_up(x, xpix, ypix);
x->gfx.mouse_down = 0;
} else {
x->gfx.mouse_x = xpos;
x->gfx.mouse_y = ypos;
pdlua_gfx_mouse_move(x, xpix, ypix);
}
// no move events generated here
// Let the proxy handle all move events
x->gfx.mouse_x = xpos;
x->gfx.mouse_y = ypos;
}
return 1;
} else
Expand Down Expand Up @@ -3072,7 +3198,9 @@ void pdlua_setup(void)
PDLUA_DEBUG("pdlua pdlua_proxyreceive_setup done", 0);
pdlua_proxyclock_setup();
PDLUA_DEBUG("pdlua pdlua_proxyclock_setup done", 0);
if (! pdlua_proxyinlet_class || ! pdlua_proxyreceive_class || ! pdlua_proxyclock_class)
pdlua_proxycanvas_setup();
PDLUA_DEBUG("pdlua pdlua_proxycanvas_setup done", 0);
if (! pdlua_proxyinlet_class || ! pdlua_proxyreceive_class || ! pdlua_proxyclock_class || ! pdlua_proxycanvas_class)
{
pd_error(NULL, "lua: error creating proxy classes");
pd_error(NULL, "lua: loader will not be registered!");
Expand Down
3 changes: 2 additions & 1 deletion pdlua.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ typedef struct _pdlua_gfx
char current_color[10]; // Keep track of current color

// Variables to keep track of mouse button state and drag position
int mouse_x, mouse_y, mouse_down;
int mouse_x, mouse_y, mouse_down, mouse_inside;
int first_draw;
struct pdlua_proxycanvas *proxycanvas;
#else
int current_layer;
void(*plugdata_draw_callback)(void*, int, t_symbol*, int, t_atom*); // Callback to perform drawing in plugdata
Expand Down
9 changes: 9 additions & 0 deletions pdlua_gfx.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,14 @@ void pdlua_gfx_mouse_drag(t_pdlua *o, int x, int y) {
pdlua_gfx_mouse_event(o, x, y, 3);
}

void pdlua_gfx_mouse_enter(t_pdlua *x, int xpos, int ypos) {
pdlua_gfx_mouse_event(x, xpos, ypos, 4);
}

void pdlua_gfx_mouse_exit(t_pdlua *x, int xpos, int ypos) {
pdlua_gfx_mouse_event(x, xpos, ypos, 5);
}

// Represents a path object, created with path.new(x, y)
// for pd-vanilla, this contains all the points that the path contains. bezier curves are flattened out to points before being added
// for plugdata, it only contains a unique ID to the juce::Path that this is mapped to
Expand Down Expand Up @@ -793,6 +801,7 @@ static int gfx_initialize(t_pdlua *obj)
gfx->num_transforms = 0;
gfx->num_layers = 0;
gfx->layer_tags = NULL;
gfx->mouse_inside = 0;

pdlua_gfx_repaint(obj, 0);
return 0;
Expand Down

0 comments on commit dd69ce6

Please sign in to comment.