diff --git a/clutter/clutter-device-manager-private.h b/clutter/clutter-device-manager-private.h index eca8d119e..223cdde0f 100644 --- a/clutter/clutter-device-manager-private.h +++ b/clutter/clutter-device-manager-private.h @@ -136,8 +136,10 @@ void _clutter_input_device_set_time (ClutterInputDev void _clutter_input_device_set_stage (ClutterInputDevice *device, ClutterStage *stage); void _clutter_input_device_set_actor (ClutterInputDevice *device, - ClutterActor *actor); -ClutterActor * _clutter_input_device_update (ClutterInputDevice *device); + ClutterActor *actor, + gboolean emit_crossing); +ClutterActor * _clutter_input_device_update (ClutterInputDevice *device, + gboolean emit_crossing); void _clutter_input_device_set_n_keys (ClutterInputDevice *device, guint n_keys); guint _clutter_input_device_add_axis (ClutterInputDevice *device, diff --git a/clutter/clutter-device-manager.c b/clutter/clutter-device-manager.c index da6798334..53e812c83 100644 --- a/clutter/clutter-device-manager.c +++ b/clutter/clutter-device-manager.c @@ -423,7 +423,7 @@ _clutter_device_manager_update_devices (ClutterDeviceManager *device_manager) if (device->stage == NULL) continue; - _clutter_input_device_update (device); + _clutter_input_device_update (device, TRUE); } } diff --git a/clutter/clutter-event.c b/clutter/clutter-event.c index d0364953f..e645910bf 100644 --- a/clutter/clutter-event.c +++ b/clutter/clutter-event.c @@ -543,6 +543,7 @@ clutter_event_set_device (ClutterEvent *event, ClutterInputDevice *device) { g_return_if_fail (event != NULL); + g_return_if_fail (device == NULL || CLUTTER_IS_INPUT_DEVICE (device)); if (is_event_allocated (event)) { diff --git a/clutter/clutter-input-device.c b/clutter/clutter-input-device.c index cc804bda5..133abc4ee 100644 --- a/clutter/clutter-input-device.c +++ b/clutter/clutter-input-device.c @@ -445,49 +445,23 @@ void _clutter_input_device_set_stage (ClutterInputDevice *device, ClutterStage *stage) { - ClutterStage *old_stage; + if (device->stage == stage) + return; - g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device)); - - old_stage = device->stage; device->stage = stage; - /* if we left the stage then we also need to unset the - * cursor actor (and update its :has-pointer property) + /* we leave the ->cursor_actor in place in order to check + * if we left the stage without crossing it again; this way + * we can emit a leave event on the cursor actor right before + * we emit the leave event on the stage. */ - if (device->stage == NULL && - device->cursor_actor != NULL && - device->cursor_actor != CLUTTER_ACTOR (old_stage)) - { - ClutterEvent cev; - - cev.crossing.type = CLUTTER_LEAVE; - cev.crossing.time = device->current_time; - cev.crossing.flags = 0; - cev.crossing.stage = old_stage; - cev.crossing.source = device->cursor_actor; - cev.crossing.x = device->current_x; - cev.crossing.y = device->current_y; - cev.crossing.device = device; - cev.crossing.related = device->stage != NULL - ? CLUTTER_ACTOR (device->stage) - : CLUTTER_ACTOR (old_stage); - - _clutter_stage_queue_event (old_stage, &cev); - - _clutter_actor_set_has_pointer (device->cursor_actor, FALSE); - g_object_weak_unref (G_OBJECT (device->cursor_actor), - cursor_weak_unref, - device); - } - - device->cursor_actor = NULL; } /*< private > * clutter_input_device_set_actor: * @device: a #ClutterInputDevice * @actor: a #ClutterActor + * @emit_crossing: %TRUE to emit crossing events * * Sets the actor under the pointer coordinates of @device * @@ -504,34 +478,41 @@ _clutter_input_device_set_stage (ClutterInputDevice *device, */ void _clutter_input_device_set_actor (ClutterInputDevice *device, - ClutterActor *actor) + ClutterActor *actor, + gboolean emit_crossing) { ClutterActor *old_actor; - ClutterEvent cev; - g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device)); - - if (actor == device->cursor_actor) + if (device->cursor_actor == actor) return; old_actor = device->cursor_actor; + if (old_actor != NULL) { - cev.crossing.type = CLUTTER_LEAVE; - cev.crossing.time = device->current_time; - cev.crossing.flags = 0; - cev.crossing.stage = device->stage; - cev.crossing.source = device->cursor_actor; - cev.crossing.x = device->current_x; - cev.crossing.y = device->current_y; - cev.crossing.device = device; - cev.crossing.related = actor; + if (emit_crossing) + { + ClutterEvent *event; - /* we need to make sure that this event is processed before - * any other event we might have queued up until now, so we - * go on and synthesize the event emission - */ - _clutter_process_event (&cev); + event = clutter_event_new (CLUTTER_LEAVE); + event->crossing.time = device->current_time; + event->crossing.flags = 0; + event->crossing.stage = device->stage; + event->crossing.source = device->cursor_actor; + event->crossing.x = device->current_x; + event->crossing.y = device->current_y; + event->crossing.related = actor; + clutter_event_set_device (event, device); + + /* we need to make sure that this event is processed + * before any other event we might have queued up until + * now, so we go on, and synthesize the event emission + * ourselves + */ + _clutter_process_event (event); + + clutter_event_free (event); + } /* processing the event might have destroyed the actor */ if (device->cursor_actor != NULL) @@ -547,54 +528,28 @@ _clutter_input_device_set_actor (ClutterInputDevice *device, if (actor != NULL) { - cev.crossing.type = CLUTTER_ENTER; - cev.crossing.time = device->current_time; - cev.crossing.flags = 0; - cev.crossing.stage = device->stage; - cev.crossing.x = device->current_x; - cev.crossing.y = device->current_y; - cev.crossing.device = device; - - CLUTTER_NOTE (EVENT, "Device '%s' entering '%s' at %d, %d", - device->device_name, - clutter_actor_get_name (actor) != NULL - ? clutter_actor_get_name (actor) - : G_OBJECT_TYPE_NAME (actor), - device->current_x, - device->current_y); - - /* if there is an actor overlapping the Stage boundary and we - * don't do this check then we'll emit an ENTER event only on - * the actor instead of emitting it on the Stage *and* the - * actor - */ - if (old_actor == NULL && actor != CLUTTER_ACTOR (device->stage)) + if (emit_crossing) { - cev.crossing.source = CLUTTER_ACTOR (device->stage); - cev.crossing.related = NULL; + ClutterEvent *event; - CLUTTER_NOTE (EVENT, "Adding Crossing[Enter] event for Stage"); + event = clutter_event_new (CLUTTER_ENTER); + event->crossing.time = device->current_time; + event->crossing.flags = 0; + event->crossing.stage = device->stage; + event->crossing.x = device->current_x; + event->crossing.y = device->current_y; + event->crossing.source = actor; + event->crossing.related = old_actor; + clutter_event_set_device (event, device); - _clutter_process_event (&cev); + /* see above */ + _clutter_process_event (event); - cev.crossing.source = actor; - cev.crossing.related = CLUTTER_ACTOR (device->stage); + clutter_event_free (event); } - else - { - cev.crossing.source = actor; - cev.crossing.related = old_actor; - } - - /* as above: we need to make sure that this event is processed - * before any other event we might have queued up until now, so - * we go on and synthesize the event emission - */ - _clutter_process_event (&cev); } device->cursor_actor = actor; - if (device->cursor_actor != NULL) { g_object_weak_ref (G_OBJECT (device->cursor_actor), @@ -730,7 +685,8 @@ clutter_input_device_get_device_coords (ClutterInputDevice *device, * Since: 1.2 */ ClutterActor * -_clutter_input_device_update (ClutterInputDevice *device) +_clutter_input_device_update (ClutterInputDevice *device, + gboolean emit_crossing) { ClutterStage *stage; ClutterActor *new_cursor_actor; @@ -773,7 +729,7 @@ _clutter_input_device_update (ClutterInputDevice *device) if (new_cursor_actor == old_cursor_actor) return old_cursor_actor; - _clutter_input_device_set_actor (device, new_cursor_actor); + _clutter_input_device_set_actor (device, new_cursor_actor, emit_crossing); return device->cursor_actor; } diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index e622d2c2a..f24f0b510 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -2154,7 +2154,7 @@ emit_event (ClutterEvent *event, /* reentrancy check */ if (lock != FALSE) { - g_warning ("Tried emitting event during event delivery, bailing out.n"); + g_warning ("Tried emitting event during event delivery, bailing out."); return; } @@ -2248,10 +2248,14 @@ is_off_stage (ClutterActor *stage, gfloat x, gfloat y) { + gfloat width, height; + + clutter_actor_get_size (stage, &width, &height); + return (x < 0 || y < 0 || - x >= clutter_actor_get_width (stage) || - y >= clutter_actor_get_height (stage)); + x >= width || + y >= height); } /** @@ -2293,18 +2297,6 @@ _clutter_process_event_details (ClutterActor *stage, event->any.source = stage; break; - case CLUTTER_LEAVE: - case CLUTTER_ENTER: - emit_pointer_event (event, device); - break; - - case CLUTTER_DESTROY_NOTIFY: - case CLUTTER_DELETE: - event->any.source = stage; - /* the stage did not handle the event, so we just quit */ - clutter_stage_event (CLUTTER_STAGE (stage), event); - break; - case CLUTTER_KEY_PRESS: case CLUTTER_KEY_RELEASE: { @@ -2326,6 +2318,65 @@ _clutter_process_event_details (ClutterActor *stage, } break; + case CLUTTER_ENTER: + /* if we're entering from outside the stage we need + * to check whether the pointer is actually on another + * actor, and emit an additional pointer event + */ + if (event->any.source == stage && + event->crossing.related == NULL) + { + ClutterActor *actor = NULL; + + emit_pointer_event (event, device); + + actor = _clutter_input_device_update (device, FALSE); + if (actor != stage) + { + ClutterEvent *crossing; + + /* we emit the exact same event on the actor */ + crossing = clutter_event_copy (event); + crossing->crossing.related = stage; + crossing->crossing.source = actor; + + emit_pointer_event (crossing, device); + clutter_event_free (crossing); + } + } + else + emit_pointer_event (event, device); + break; + + case CLUTTER_LEAVE: + /* same as CLUTTER_ENTER above: when leaving the stage + * we need to also emit a CLUTTER_LEAVE event on the + * actor currently underneath the device, unless it's the + * stage + */ + if (event->any.source == stage && + event->crossing.related == NULL && + device->cursor_actor != stage) + { + ClutterEvent *crossing; + + crossing = clutter_event_copy (event); + crossing->crossing.related = stage; + crossing->crossing.source = device->cursor_actor; + + emit_pointer_event (crossing, device); + clutter_event_free (crossing); + } + emit_pointer_event (event, device); + break; + + case CLUTTER_DESTROY_NOTIFY: + case CLUTTER_DELETE: + event->any.source = stage; + /* the stage did not handle the event, so we just quit */ + clutter_stage_event (CLUTTER_STAGE (stage), event); + break; + case CLUTTER_MOTION: /* Only stage gets motion events if clutter_set_motion_events is TRUE, * and the event is not a synthetic event with source set. @@ -2413,7 +2464,7 @@ _clutter_process_event_details (ClutterActor *stage, * get the actor underneath */ if (device != NULL) - actor = _clutter_input_device_update (device); + actor = _clutter_input_device_update (device, TRUE); else { CLUTTER_NOTE (EVENT, "No device found: picking"); @@ -2434,11 +2485,6 @@ _clutter_process_event_details (ClutterActor *stage, actor = event->any.source; } - /* FIXME: for an optimisation should check if there are - * actually any reactive actors and avoid the pick all together - * (signalling just the stage). Should be big help for gles. - */ - CLUTTER_NOTE (EVENT, "Reactive event received at %.2f, %.2f - actor: %p", x, y, @@ -2507,7 +2553,7 @@ _clutter_process_event (ClutterEvent *event) * * Since: 0.6 */ -ClutterActor* +ClutterActor * clutter_get_actor_by_gid (guint32 id) { ClutterMainContext *context; diff --git a/clutter/clutter-stage-private.h b/clutter/clutter-stage-private.h index 879e162ae..f61fc9d32 100644 --- a/clutter/clutter-stage-private.h +++ b/clutter/clutter-stage-private.h @@ -24,6 +24,7 @@ #include #include +#include G_BEGIN_DECLS @@ -82,6 +83,13 @@ ClutterStageQueueRedrawEntry *_clutter_stage_queue_actor_redraw (Clut ClutterPaintVolume *clip); void _clutter_stage_queue_redraw_entry_invalidate (ClutterStageQueueRedrawEntry *entry); +void _clutter_stage_add_device (ClutterStage *stage, + ClutterInputDevice *device); +void _clutter_stage_remove_device (ClutterStage *stage, + ClutterInputDevice *device); +gboolean _clutter_stage_has_device (ClutterStage *stage, + ClutterInputDevice *device); + G_END_DECLS #endif /* __CLUTTER_STAGE_PRIVATE_H__ */ diff --git a/clutter/clutter-stage.c b/clutter/clutter-stage.c index 48935cb48..624967198 100644 --- a/clutter/clutter-stage.c +++ b/clutter/clutter-stage.c @@ -135,6 +135,8 @@ struct _ClutterStagePrivate ClutterPickMode pick_buffer_mode; + GHashTable *devices; + guint relayout_pending : 1; guint redraw_pending : 1; guint is_fullscreen : 1; @@ -1202,10 +1204,13 @@ clutter_stage_finalize (GObject *object) g_queue_foreach (priv->event_queue, (GFunc)clutter_event_free, NULL); g_queue_free (priv->event_queue); - g_free (stage->priv->title); + g_free (priv->title); g_array_free (priv->paint_volume_stack, TRUE); + if (priv->devices != NULL) + g_hash_table_destroy (priv->devices); + G_OBJECT_CLASS (clutter_stage_parent_class)->finalize (object); } @@ -3375,3 +3380,44 @@ clutter_stage_get_accept_focus (ClutterStage *stage) return stage->priv->accept_focus; } + +void +_clutter_stage_add_device (ClutterStage *stage, + ClutterInputDevice *device) +{ + ClutterStagePrivate *priv = stage->priv; + + if (G_UNLIKELY (priv->devices == NULL)) + priv->devices = g_hash_table_new (NULL, NULL); + + if (g_hash_table_lookup (priv->devices, device) != NULL) + return; + + g_hash_table_insert (priv->devices, device, GINT_TO_POINTER (1)); + _clutter_input_device_set_stage (device, stage); +} + +void +_clutter_stage_remove_device (ClutterStage *stage, + ClutterInputDevice *device) +{ + ClutterStagePrivate *priv = stage->priv; + + if (G_UNLIKELY (priv->devices == NULL)) + return; + + _clutter_input_device_set_stage (device, NULL); + g_hash_table_remove (priv->devices, device); +} + +gboolean +_clutter_stage_has_device (ClutterStage *stage, + ClutterInputDevice *device) +{ + ClutterStagePrivate *priv = stage->priv; + + if (priv->devices == NULL) + return FALSE; + + return g_hash_table_lookup (priv->devices, device) != NULL; +} diff --git a/clutter/win32/clutter-event-win32.c b/clutter/win32/clutter-event-win32.c index 8429fdd11..996ce36ac 100644 --- a/clutter/win32/clutter-event-win32.c +++ b/clutter/win32/clutter-event-win32.c @@ -548,6 +548,7 @@ clutter_win32_handle_event (const MSG *msg) we're not already */ if (!stage_win32->tracking_mouse) { + ClutterEvent *crossing = clutter_event_new (CLUTTER_ENTER); TRACKMOUSEEVENT tmevent; tmevent.cbSize = sizeof (tmevent); @@ -555,8 +556,18 @@ clutter_win32_handle_event (const MSG *msg) tmevent.hwndTrack = stage_win32->hwnd; TrackMouseEvent (&tmevent); + event->crossing.time = msg->time; + event->crossing.x = event->motion.x; + event->crossing.y = event->motion.y; + event->crossing.device = event->motion.device; + event->crossing.stage = stage; + event->crossing.source = CLUTTER_ACTOR (stage); + event->crossing.related = NULL; + /* we entered the stage */ - _clutter_input_device_set_stage (event->motion.device, stage); + _clutter_stage_add_device (stage, event->crossing.device); + + take_and_queue_event (crossing); stage_win32->tracking_mouse = TRUE; } @@ -573,10 +584,12 @@ clutter_win32_handle_event (const MSG *msg) event->crossing.x = msg->pt.x; event->crossing.y = msg->pt.y; event->crossing.device = core_pointer; - event->any.stage = stage; + event->crossing.stage = stage; + event->crossing.source = CLUTTER_ACTOR (stage); + event->crossing.related = NULL; /* we left the stage */ - _clutter_input_device_set_stage (event->crossing.device, NULL); + _clutter_stage_remove_device (stage, event->crossing.device); /* When we get a leave message the mouse tracking is automatically cancelled so we'll need to start it again when diff --git a/clutter/x11/clutter-device-manager-core-x11.c b/clutter/x11/clutter-device-manager-core-x11.c index 4614a07a9..2b55f42aa 100644 --- a/clutter/x11/clutter-device-manager-core-x11.c +++ b/clutter/x11/clutter-device-manager-core-x11.c @@ -220,7 +220,8 @@ translate_key_event (ClutterBackendX11 *backend_x11, event->key.type = xevent->xany.type == KeyPress ? CLUTTER_KEY_PRESS : CLUTTER_KEY_RELEASE; event->key.time = xevent->xkey.time; - event->key.device = manager_x11->core_keyboard; + + clutter_event_set_device (event, manager_x11->core_keyboard); /* KeyEvents have platform specific data associated to them */ event_x11 = _clutter_event_x11_new (); @@ -406,7 +407,7 @@ clutter_device_manager_x11_translate_event (ClutterEventTranslator *translator, event->scroll.x = xevent->xbutton.x; event->scroll.y = xevent->xbutton.y; event->scroll.modifier_state = xevent->xbutton.state; - event->scroll.device = manager_x11->core_pointer; + event->scroll.axes = NULL; break; default: @@ -416,11 +417,12 @@ clutter_device_manager_x11_translate_event (ClutterEventTranslator *translator, event->button.y = xevent->xbutton.y; event->button.modifier_state = xevent->xbutton.state; event->button.button = xevent->xbutton.button; - event->button.device = manager_x11->core_pointer; event->button.axes = NULL; break; } + clutter_event_set_device (event, manager_x11->core_pointer); + _clutter_stage_x11_set_user_time (stage_x11, xevent->xbutton.time); res = CLUTTER_TRANSLATE_QUEUE; break; @@ -450,7 +452,7 @@ clutter_device_manager_x11_translate_event (ClutterEventTranslator *translator, event->button.modifier_state = xevent->xbutton.state; event->button.button = xevent->xbutton.button; event->button.axes = NULL; - event->button.device = manager_x11->core_pointer; + clutter_event_set_device (event, manager_x11->core_pointer); res = CLUTTER_TRANSLATE_QUEUE; break; @@ -466,25 +468,25 @@ clutter_device_manager_x11_translate_event (ClutterEventTranslator *translator, event->motion.x = xevent->xmotion.x; event->motion.y = xevent->xmotion.y; event->motion.modifier_state = xevent->xmotion.state; - event->motion.device = manager_x11->core_pointer; event->motion.axes = NULL; + clutter_event_set_device (event, manager_x11->core_pointer); res = CLUTTER_TRANSLATE_QUEUE; break; case EnterNotify: - /* we know that we are entering the stage here */ - _clutter_input_device_set_stage (manager_x11->core_pointer, stage); - CLUTTER_NOTE (EVENT, "Entering the stage"); + CLUTTER_NOTE (EVENT, "Entering the stage (time:%u)", + (unsigned int) xevent->xcrossing.time); + + event->crossing.type = CLUTTER_ENTER; + event->crossing.time = xevent->xcrossing.time; + event->crossing.x = xevent->xcrossing.x; + event->crossing.y = xevent->xcrossing.y; + event->crossing.source = CLUTTER_ACTOR (stage); + event->crossing.related = NULL; + clutter_event_set_device (event, manager_x11->core_pointer); + + _clutter_stage_add_device (stage, manager_x11->core_pointer); - /* 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; - event->motion.source = CLUTTER_ACTOR (stage); - event->motion.device = manager_x11->core_pointer; res = CLUTTER_TRANSLATE_QUEUE; break; @@ -498,16 +500,19 @@ clutter_device_manager_x11_translate_event (ClutterEventTranslator *translator, } /* we know that we are leaving the stage here */ - _clutter_input_device_set_stage (manager_x11->core_pointer, NULL); CLUTTER_NOTE (EVENT, "Leaving the stage (time:%u)", - event->crossing.time); + (unsigned int) xevent->xcrossing.time); event->crossing.type = CLUTTER_LEAVE; event->crossing.time = xevent->xcrossing.time; event->crossing.x = xevent->xcrossing.x; event->crossing.y = xevent->xcrossing.y; event->crossing.source = CLUTTER_ACTOR (stage); - event->crossing.device = manager_x11->core_pointer; + event->crossing.related = NULL; + clutter_event_set_device (event, manager_x11->core_pointer); + + _clutter_stage_remove_device (stage, manager_x11->core_pointer); + res = CLUTTER_TRANSLATE_QUEUE; break; diff --git a/clutter/x11/clutter-device-manager-xi2.c b/clutter/x11/clutter-device-manager-xi2.c index 8924cf609..aff55cf10 100644 --- a/clutter/x11/clutter-device-manager-xi2.c +++ b/clutter/x11/clutter-device-manager-xi2.c @@ -847,22 +847,22 @@ clutter_device_manager_xi2_translate_event (ClutterEventTranslator *translator, device = g_hash_table_lookup (manager_xi2->devices_by_id, GINT_TO_POINTER (xev->deviceid)); + source_device = g_hash_table_lookup (manager_xi2->devices_by_id, + GINT_TO_POINTER (xev->sourceid)); + if (xi_event->evtype == XI_Enter) { - _clutter_input_device_set_stage (device, stage); + event->crossing.type = event->type = CLUTTER_ENTER; - event->motion.type = event->type = CLUTTER_MOTION; + event->crossing.stage = stage; + event->crossing.source = CLUTTER_ACTOR (stage); + event->crossing.related = NULL; - event->motion.stage = stage; - event->motion.source = CLUTTER_ACTOR (stage); + event->crossing.time = xev->time; + event->crossing.x = xev->event_x; + event->crossing.y = xev->event_y; - event->motion.time = xev->time; - event->motion.x = xev->event_x; - event->motion.y = xev->event_y; - event->motion.device = device; - event->motion.modifier_state = - _clutter_input_device_xi2_translate_state (&xev->mods, - &xev->buttons); + _clutter_stage_add_device (stage, device); } else { @@ -876,18 +876,22 @@ clutter_device_manager_xi2_translate_event (ClutterEventTranslator *translator, break; } - _clutter_input_device_set_stage (device, NULL); - event->crossing.type = event->type = CLUTTER_LEAVE; + event->crossing.stage = stage; event->crossing.source = CLUTTER_ACTOR (stage); + event->crossing.related = NULL; event->crossing.time = xev->time; event->crossing.x = xev->event_x; event->crossing.y = xev->event_y; - event->crossing.device = device; + + _clutter_stage_remove_device (stage, device); } + clutter_event_set_device (event, device); + _clutter_event_set_source_device (event, source_device); + retval = CLUTTER_TRANSLATE_QUEUE; } break;