@@ -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
243252static 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);
263272static void pdlua_proxyclock_setup (void );
264273/** Dump an array of atoms into a Lua table. */
265274static 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. */
267284static t_pdlua * pdlua_new (t_symbol * s , int argc , t_atom * argv );
268285/** Pd object destructor. */
@@ -346,12 +363,15 @@ void pdlua_setup (void);
346363struct pdlua_proxyinlet ;
347364struct pdlua_proxyreceive ;
348365struct pdlua_proxyclock ;
366+ struct pdlua_proxycanvas ;
349367/** Proxy inlet class pointer. */
350368static t_class * pdlua_proxyinlet_class ;
351369/** Proxy receive class pointer. */
352370static t_class * pdlua_proxyreceive_class ;
353371/** Proxy clock class pointer. */
354372static t_class * pdlua_proxyclock_class ;
373+ /** Proxy canvas class pointer. */
374+ static t_class * pdlua_proxycanvas_class ;
355375
356376/** Lua file reader callback. */
357377static 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. */
382466static 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. */
498595static 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
751863void 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!" );
0 commit comments