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.
This commit is contained in:
Neil Roberts 2009-02-12 17:21:18 +00:00
parent 0c7e4172ab
commit 5d6a11e1bf
4 changed files with 70 additions and 20 deletions

View File

@ -1853,6 +1853,37 @@ clutter_event_get_device (ClutterEvent *event)
return NULL; 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 static inline void
generate_enter_leave_events (ClutterEvent *event) generate_enter_leave_events (ClutterEvent *event)
{ {
@ -1905,25 +1936,7 @@ generate_enter_leave_events (ClutterEvent *event)
} }
} }
if (last_actor && last_actor != motion_current_actor) set_motion_last_actor (motion_current_actor, event->motion.device);
{
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;
} }
/** /**
@ -1965,8 +1978,23 @@ clutter_do_event (ClutterEvent *event)
event->any.source = stage; event->any.source = stage;
break; break;
case CLUTTER_ENTER:
case CLUTTER_LEAVE: 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); emit_pointer_event (event, event->crossing.device);
break; break;

View File

@ -185,6 +185,7 @@ clutter_stage_egl_realize (ClutterActor *actor)
StructureNotifyMask | StructureNotifyMask |
FocusChangeMask | FocusChangeMask |
ExposureMask | ExposureMask |
EnterWindowMask | LeaveWindowMask |
PropertyChangeMask); PropertyChangeMask);
#ifdef USE_XINPUT #ifdef USE_XINPUT
_clutter_x11_select_events (stage_x11->xwin); _clutter_x11_select_events (stage_x11->xwin);
@ -198,6 +199,7 @@ clutter_stage_egl_realize (ClutterActor *actor)
PointerMotionMask | PointerMotionMask |
KeyPressMask | KeyReleaseMask | KeyPressMask | KeyReleaseMask |
ButtonPressMask | ButtonReleaseMask | ButtonPressMask | ButtonReleaseMask |
EnterWindowMask | LeaveWindowMask |
PropertyChangeMask); PropertyChangeMask);
} }

View File

@ -189,6 +189,7 @@ clutter_stage_glx_realize (ClutterActor *actor)
FocusChangeMask | FocusChangeMask |
ExposureMask | ExposureMask |
KeyPressMask | KeyReleaseMask | KeyPressMask | KeyReleaseMask |
EnterWindowMask | LeaveWindowMask |
PropertyChangeMask); PropertyChangeMask);
#ifdef USE_XINPUT #ifdef USE_XINPUT
_clutter_x11_select_events (stage_x11->xwin); _clutter_x11_select_events (stage_x11->xwin);
@ -202,6 +203,7 @@ clutter_stage_glx_realize (ClutterActor *actor)
PointerMotionMask | PointerMotionMask |
KeyPressMask | KeyReleaseMask | KeyPressMask | KeyReleaseMask |
ButtonPressMask | ButtonReleaseMask | ButtonPressMask | ButtonReleaseMask |
EnterWindowMask | LeaveWindowMask |
PropertyChangeMask); PropertyChangeMask);
} }

View File

@ -666,6 +666,24 @@ event_translate (ClutterBackend *backend,
event->motion.y = xevent->xmotion.y; event->motion.y = xevent->xmotion.y;
event->motion.modifier_state = xevent->xmotion.state; event->motion.modifier_state = xevent->xmotion.state;
break; 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: default:
/* ignore every other event */ /* ignore every other event */
res = FALSE; res = FALSE;