Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add mouse_enter() and mouse_exit() for Pd vanilla #70

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
204 changes: 171 additions & 33 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,21 @@ 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)
{
#if !PLUGDATA
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);
#endif
}

/** Dump an array of atoms into a Lua table. */
static void pdlua_pushatomtable
(
Expand Down Expand Up @@ -714,9 +813,24 @@ static t_pdlua *pdlua_new
if (lua_islightuserdata(__L(), -1))
{
object = lua_touserdata(__L(), -1);
#if !PLUGDATA
// 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;
}
}
#endif
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 +863,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,8 +881,9 @@ 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;
Expand All @@ -779,6 +893,14 @@ static void pdlua_delete(t_gobj *z, t_glist *glist){
}

canvas_deletelinesfor(glist, (t_text *)z);

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

#ifdef PURR_DATA // Purr Data uses an older version of this API
Expand All @@ -789,19 +911,35 @@ static void pdlua_motion(t_gobj *z, t_floatarg dx, t_floatarg dy,
#endif
{
#if !PLUGDATA
t_pdlua *x = (t_pdlua *)z;
if (!x->has_gui) return;
#ifndef PURR_DATA
if (!up)
#endif
{
t_pdlua *x = (t_pdlua *)z;
x->gfx.mouse_drag_x = x->gfx.mouse_drag_x + dx;
x->gfx.mouse_drag_y = x->gfx.mouse_drag_y + dy;
// Handle mouse up immediately
if (up && x->gfx.mouse_down) {
int zoom = glist_getzoom(glist_getcanvas(x->canvas));
int xpos = (x->gfx.mouse_drag_x - text_xpix(&x->pd, x->canvas)) / zoom;
int ypos = (x->gfx.mouse_drag_y - text_ypix(&x->pd, x->canvas)) / zoom;
pdlua_gfx_mouse_drag(x, xpos, ypos);
int xpos = (x->gfx.mouse_x - text_xpix(&x->pd, x->canvas)) / zoom;
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

x->gfx.mouse_x += dx;
x->gfx.mouse_y += dy;
int zoom = glist_getzoom(glist_getcanvas(x->canvas));
int xpos = (x->gfx.mouse_x - text_xpix(&x->pd, x->canvas)) / zoom;
int ypos = (x->gfx.mouse_y - text_ypix(&x->pd, x->canvas)) / zoom;
pdlua_gfx_mouse_drag(x, xpos, ypos);
#endif
}

static int pdlua_click(t_gobj *z, t_glist *gl, int xpos, int ypos, int shift, int alt, int dbl, int doit){
Expand All @@ -812,34 +950,32 @@ static int pdlua_click(t_gobj *z, t_glist *gl, int xpos, int ypos, int shift, in
int zoom = glist_getzoom(gl);
int xpix = (xpos - text_xpix(&x->pd, gl)) / zoom;
int ypix = (ypos - text_ypix(&x->pd, gl)) / zoom;

if(doit){
if(!x->gfx.mouse_down)
{
pdlua_gfx_mouse_down(x, xpix, ypix);
x->gfx.mouse_drag_x = xpos;
x->gfx.mouse_drag_y = ypos;
}

glist_grab(x->canvas, &x->pd.te_g, (t_glistmotionfn)pdlua_motion, NULL, xpos, ypos);
}
else {
pdlua_gfx_mouse_move(x, xpix, ypix);


if (doit) {
x->gfx.mouse_down = 1;
x->gfx.mouse_x = xpos;
x->gfx.mouse_y = ypos;
pdlua_gfx_mouse_down(x, xpix, ypix);
glist_grab(gl, &x->pd.te_g, (t_glistmotionfn)pdlua_motion, NULL, xpos, ypos);
} else {
// Handle mouse up here for Purr Data
// (Vanilla PD handles it in pdlua_motion with the 'up' flag)
if(x->gfx.mouse_down)
{
pdlua_gfx_mouse_up(x, xpix, ypix);
x->gfx.mouse_down = 0;
}
// no move events generated here
// Let the proxy handle all move events
x->gfx.mouse_x = xpos;
x->gfx.mouse_y = ypos;
}

x->gfx.mouse_down = doit;
return 1;
} else
#endif
return text_widgetbehavior.w_clickfn(z, gl, xpos, ypos, shift, alt, dbl, doit);
}


static void pdlua_displace(t_gobj *z, t_glist *glist, int dx, int dy){
t_pdlua *x = (t_pdlua *)z;

Expand Down Expand Up @@ -1451,8 +1587,8 @@ static int pdlua_object_new(lua_State *L)

#if !PLUGDATA
// Init graphics state for pd
o->gfx.mouse_drag_x = 0;
o->gfx.mouse_drag_y = 0;
o->gfx.mouse_x = 0;
o->gfx.mouse_y = 0;
o->gfx.mouse_down = 0;
#else
// NULL until plugdata overrides them with something useful
Expand Down Expand Up @@ -3065,7 +3201,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
6 changes: 3 additions & 3 deletions pdlua.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ typedef struct _pdlua_gfx
int num_transforms;
char current_color[10]; // Keep track of current color

// Variables to keep track of mouse button state and drag position
int mouse_drag_x, mouse_drag_y, mouse_down;
// Variables to keep track of mouse position, button state and whether the mouse is inside the object
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
Loading