From 5d6a11e1bfad43d20b3a2cc4d8dbda09200c6cb8 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Thu, 12 Feb 2009 17:21:18 +0000 Subject: [PATCH] Emit CLUTTER_LEAVE events when the pointer leaves the stage Bug 1178 - No enter / leave events on actors when pointer leaves the stage window The patch is mostly thanks to Johan Bilien with small modifications based on suggestions by Owen Taylor. The X11 backend now listens for enter and leave notifications. Leave notifications get translated directly to a CLUTTER_LEAVE event. Clutter can detect these special events because the source actor is NULL in which case it sets the source actor to the last known actor and then sets the last known actor to NULL. Enter notifications just get translated to CLUTTER_MOTION events which will cause Clutter to generate an enter event through the usual code path. --- clutter/clutter-main.c | 68 ++++++++++++++++++++++---------- clutter/eglx/clutter-stage-egl.c | 2 + clutter/glx/clutter-stage-glx.c | 2 + clutter/x11/clutter-event-x11.c | 18 +++++++++ 4 files changed, 70 insertions(+), 20 deletions(-) diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index ed99235e6..1094104bd 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -1853,6 +1853,37 @@ clutter_event_get_device (ClutterEvent *event) return NULL; } +static void +set_motion_last_actor (ClutterActor *motion_current_actor, + ClutterInputDevice *device) +{ + ClutterMainContext *context = ClutterCntx; + ClutterActor *last_actor = context->motion_last_actor; + + if (device != NULL) + last_actor = device->motion_last_actor; + + if (last_actor && last_actor != motion_current_actor) + { + g_signal_handlers_disconnect_by_func + (last_actor, + G_CALLBACK (unset_motion_last_actor), + device); + } + + if (motion_current_actor && last_actor != motion_current_actor) + { + g_signal_connect (motion_current_actor, "destroy", + G_CALLBACK (unset_motion_last_actor), + device); + } + + if (device != NULL) + device->motion_last_actor = motion_current_actor; + else + context->motion_last_actor = motion_current_actor; +} + static inline void generate_enter_leave_events (ClutterEvent *event) { @@ -1905,25 +1936,7 @@ generate_enter_leave_events (ClutterEvent *event) } } - if (last_actor && last_actor != motion_current_actor) - { - g_signal_handlers_disconnect_by_func - (last_actor, - G_CALLBACK (unset_motion_last_actor), - device); - } - - if (motion_current_actor && last_actor != motion_current_actor) - { - g_signal_connect (motion_current_actor, "destroy", - G_CALLBACK (unset_motion_last_actor), - device); - } - - if (device != NULL) - device->motion_last_actor = motion_current_actor; - else - context->motion_last_actor = motion_current_actor; + set_motion_last_actor (motion_current_actor, event->motion.device); } /** @@ -1965,8 +1978,23 @@ clutter_do_event (ClutterEvent *event) event->any.source = stage; break; - case CLUTTER_ENTER: case CLUTTER_LEAVE: + /* The source is set for generated events, not for events + * resulting from the cursor leaving the stage + */ + if (event->any.source == NULL) + { + ClutterActor *last_actor = context->motion_last_actor; + + if (event->crossing.device != NULL) + last_actor = event->crossing.device->motion_last_actor; + + event->any.source = last_actor; + + set_motion_last_actor (NULL, event->crossing.device); + } + /* flow through */ + case CLUTTER_ENTER: emit_pointer_event (event, event->crossing.device); break; diff --git a/clutter/eglx/clutter-stage-egl.c b/clutter/eglx/clutter-stage-egl.c index e3ceae98d..5add2a862 100644 --- a/clutter/eglx/clutter-stage-egl.c +++ b/clutter/eglx/clutter-stage-egl.c @@ -185,6 +185,7 @@ clutter_stage_egl_realize (ClutterActor *actor) StructureNotifyMask | FocusChangeMask | ExposureMask | + EnterWindowMask | LeaveWindowMask | PropertyChangeMask); #ifdef USE_XINPUT _clutter_x11_select_events (stage_x11->xwin); @@ -198,6 +199,7 @@ clutter_stage_egl_realize (ClutterActor *actor) PointerMotionMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | + EnterWindowMask | LeaveWindowMask | PropertyChangeMask); } diff --git a/clutter/glx/clutter-stage-glx.c b/clutter/glx/clutter-stage-glx.c index b5aa6db80..f50225a00 100644 --- a/clutter/glx/clutter-stage-glx.c +++ b/clutter/glx/clutter-stage-glx.c @@ -189,6 +189,7 @@ clutter_stage_glx_realize (ClutterActor *actor) FocusChangeMask | ExposureMask | KeyPressMask | KeyReleaseMask | + EnterWindowMask | LeaveWindowMask | PropertyChangeMask); #ifdef USE_XINPUT _clutter_x11_select_events (stage_x11->xwin); @@ -202,6 +203,7 @@ clutter_stage_glx_realize (ClutterActor *actor) PointerMotionMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | + EnterWindowMask | LeaveWindowMask | PropertyChangeMask); } diff --git a/clutter/x11/clutter-event-x11.c b/clutter/x11/clutter-event-x11.c index aed0446c6..634a3c71a 100644 --- a/clutter/x11/clutter-event-x11.c +++ b/clutter/x11/clutter-event-x11.c @@ -666,6 +666,24 @@ event_translate (ClutterBackend *backend, event->motion.y = xevent->xmotion.y; event->motion.modifier_state = xevent->xmotion.state; break; + + case EnterNotify: + /* Convert enter notifies to motion events because X + doesn't emit the corresponding motion notify */ + event->motion.type = event->type = CLUTTER_MOTION; + event->motion.time = xevent->xcrossing.time; + event->motion.x = xevent->xcrossing.x; + event->motion.y = xevent->xcrossing.y; + event->motion.modifier_state = xevent->xcrossing.state; + break; + + case LeaveNotify: + event->crossing.type = event->type = CLUTTER_LEAVE; + event->crossing.time = xevent->xcrossing.time; + event->crossing.x = xevent->xcrossing.x; + event->crossing.y = xevent->xcrossing.y; + break; + default: /* ignore every other event */ res = FALSE;