diff --git a/clutter/clutter/clutter-mutter.h b/clutter/clutter/clutter-mutter.h index 9c75ad6a2..6e1f43930 100644 --- a/clutter/clutter/clutter-mutter.h +++ b/clutter/clutter/clutter-mutter.h @@ -75,6 +75,12 @@ CLUTTER_EXPORT gboolean clutter_seat_handle_event_post (ClutterSeat *seat, const ClutterEvent *event); +CLUTTER_EXPORT +void clutter_stage_get_device_coords (ClutterStage *stage, + ClutterInputDevice *device, + ClutterEventSequence *sequence, + graphene_point_t *coords); + #undef __CLUTTER_H_INSIDE__ #endif /* __CLUTTER_MUTTER_H__ */ diff --git a/clutter/clutter/clutter-stage-private.h b/clutter/clutter/clutter-stage-private.h index 47fef4395..8f7ff3135 100644 --- a/clutter/clutter/clutter-stage-private.h +++ b/clutter/clutter/clutter-stage-private.h @@ -128,6 +128,16 @@ GList * clutter_stage_get_views_for_rect (ClutterStage *stage, void clutter_stage_set_actor_needs_immediate_relayout (ClutterStage *stage); +void clutter_stage_update_device_entry (ClutterStage *self, + ClutterInputDevice *device, + ClutterEventSequence *sequence, + graphene_point_t coords, + ClutterActor *actor); + +void clutter_stage_remove_device_entry (ClutterStage *self, + ClutterInputDevice *device, + ClutterEventSequence *sequence); + G_END_DECLS #endif /* __CLUTTER_STAGE_PRIVATE_H__ */ diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c index 626193e2a..4222d76aa 100644 --- a/clutter/clutter/clutter-stage.c +++ b/clutter/clutter/clutter-stage.c @@ -98,6 +98,15 @@ typedef struct _PickClipRecord graphene_point_t vertex[4]; } PickClipRecord; +typedef struct _PointerDeviceEntry +{ + ClutterStage *stage; + ClutterInputDevice *device; + ClutterEventSequence *sequence; + graphene_point_t coords; + ClutterActor *current_actor; +} PointerDeviceEntry; + struct _ClutterStagePrivate { /* the stage implementation */ @@ -126,6 +135,9 @@ struct _ClutterStagePrivate gboolean needs_update_devices; gboolean pending_finish_queue_redraws; + GHashTable *pointer_devices; + GHashTable *touch_sequences; + guint throttle_motion_events : 1; guint min_size_changed : 1; guint motion_events_enabled : 1; @@ -165,6 +177,7 @@ static guint stage_signals[LAST_SIGNAL] = { 0, }; static const ClutterColor default_stage_color = { 255, 255, 255, 255 }; static void free_queue_redraw_entry (QueueRedrawEntry *entry); +static void free_pointer_device_entry (PointerDeviceEntry *entry); static void capture_view_into (ClutterStage *stage, gboolean paint, ClutterStageView *view, @@ -1301,6 +1314,9 @@ clutter_stage_finalize (GObject *object) g_queue_foreach (priv->event_queue, (GFunc) clutter_event_free, NULL); g_queue_free (priv->event_queue); + g_hash_table_destroy (priv->pointer_devices); + g_hash_table_destroy (priv->touch_sequences); + g_free (priv->title); g_array_free (priv->paint_volume_stack, TRUE); @@ -1596,6 +1612,13 @@ clutter_stage_init (ClutterStage *self) priv->sync_delay = -1; priv->motion_events_enabled = TRUE; + priv->pointer_devices = + g_hash_table_new_full (NULL, NULL, + NULL, (GDestroyNotify) free_pointer_device_entry); + priv->touch_sequences = + g_hash_table_new_full (NULL, NULL, + NULL, (GDestroyNotify) free_pointer_device_entry); + clutter_actor_set_background_color (CLUTTER_ACTOR (self), &default_stage_color); @@ -3392,3 +3415,182 @@ clutter_stage_set_actor_needs_immediate_relayout (ClutterStage *stage) priv->actor_needs_immediate_relayout = TRUE; } + +static void +on_device_actor_reactive_changed (ClutterActor *actor, + GParamSpec *pspec, + PointerDeviceEntry *entry) +{ +} + +static void +on_device_actor_destroyed (ClutterActor *actor, + PointerDeviceEntry *entry) +{ + /* Simply unset the current_actor pointer here, there's no need to + * unset has_pointer or to disconnect any signals because the actor + * is gone anyway. + * Also, as soon as the next repaint happens, a repick should be triggered + * and the PointerDeviceEntry will get updated again, so no need to + * trigger a repick here. + */ + entry->current_actor = NULL; +} + +static void +free_pointer_device_entry (PointerDeviceEntry *entry) +{ + if (entry->current_actor) + { + ClutterActor *actor = entry->current_actor; + + g_signal_handlers_disconnect_by_func (actor, + G_CALLBACK (on_device_actor_reactive_changed), + entry); + g_signal_handlers_disconnect_by_func (actor, + G_CALLBACK (on_device_actor_destroyed), + entry); + + _clutter_actor_set_has_pointer (actor, FALSE); + } + + g_free (entry); +} + +void +clutter_stage_update_device_entry (ClutterStage *self, + ClutterInputDevice *device, + ClutterEventSequence *sequence, + graphene_point_t coords, + ClutterActor *actor) +{ + ClutterStagePrivate *priv = self->priv; + PointerDeviceEntry *entry = NULL; + + g_assert (device != NULL); + + if (sequence != NULL) + entry = g_hash_table_lookup (priv->touch_sequences, sequence); + else + entry = g_hash_table_lookup (priv->pointer_devices, device); + + if (!entry) + { + entry = g_new0 (PointerDeviceEntry, 1); + + if (sequence != NULL) + g_hash_table_insert (priv->touch_sequences, sequence, entry); + else + g_hash_table_insert (priv->pointer_devices, device, entry); + + entry->stage = self; + entry->device = device; + entry->sequence = sequence; + } + + entry->coords = coords; + + if (entry->current_actor != actor) + { + if (entry->current_actor) + { + ClutterActor *old_actor = entry->current_actor; + + g_signal_handlers_disconnect_by_func (old_actor, + G_CALLBACK (on_device_actor_reactive_changed), + entry); + g_signal_handlers_disconnect_by_func (old_actor, + G_CALLBACK (on_device_actor_destroyed), + entry); + + _clutter_actor_set_has_pointer (old_actor, FALSE); + } + + entry->current_actor = actor; + + if (actor) + { + g_signal_connect (actor, "notify::reactive", + G_CALLBACK (on_device_actor_reactive_changed), entry); + g_signal_connect (actor, "destroy", + G_CALLBACK (on_device_actor_destroyed), entry); + + _clutter_actor_set_has_pointer (actor, TRUE); + } + } +} + +void +clutter_stage_remove_device_entry (ClutterStage *self, + ClutterInputDevice *device, + ClutterEventSequence *sequence) +{ + ClutterStagePrivate *priv = self->priv; + gboolean removed; + + g_assert (device != NULL); + + if (sequence != NULL) + removed = g_hash_table_remove (priv->touch_sequences, sequence); + else + removed = g_hash_table_remove (priv->pointer_devices, device); + + g_assert (removed); +} + +/** + * clutter_stage_get_device_actor: + * @stage: a #ClutterStage + * @device: a #ClutterInputDevice + * @sequence: (allow-none): an optional #ClutterEventSequence + * + * Retrieves the #ClutterActor underneath the pointer or touch point + * of @device and @sequence. + * + * Return value: (transfer none): a pointer to the #ClutterActor or %NULL + */ +ClutterActor * +clutter_stage_get_device_actor (ClutterStage *stage, + ClutterInputDevice *device, + ClutterEventSequence *sequence) +{ + ClutterStagePrivate *priv = stage->priv; + PointerDeviceEntry *entry = NULL; + + g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL); + g_return_val_if_fail (device != NULL, NULL); + + if (sequence != NULL) + entry = g_hash_table_lookup (priv->touch_sequences, sequence); + else + entry = g_hash_table_lookup (priv->pointer_devices, device); + + if (entry) + return entry->current_actor; + + return NULL; +} + +/** + * clutter_stage_get_device_coords: (skip): + */ +void +clutter_stage_get_device_coords (ClutterStage *stage, + ClutterInputDevice *device, + ClutterEventSequence *sequence, + graphene_point_t *coords) +{ + ClutterStagePrivate *priv = stage->priv; + PointerDeviceEntry *entry = NULL; + + g_return_if_fail (CLUTTER_IS_STAGE (stage)); + g_return_if_fail (device != NULL); + + if (sequence != NULL) + entry = g_hash_table_lookup (priv->touch_sequences, sequence); + else + entry = g_hash_table_lookup (priv->pointer_devices, device); + + if (entry && coords) + *coords = entry->coords; +} diff --git a/clutter/clutter/clutter-stage.h b/clutter/clutter/clutter-stage.h index 59a4ad145..a3b72b46c 100644 --- a/clutter/clutter/clutter-stage.h +++ b/clutter/clutter/clutter-stage.h @@ -236,6 +236,11 @@ ClutterStageView * clutter_stage_get_view_at (ClutterStage *stage, float x, float y); +CLUTTER_EXPORT +ClutterActor * clutter_stage_get_device_actor (ClutterStage *stage, + ClutterInputDevice *device, + ClutterEventSequence *sequence); + G_END_DECLS #endif /* __CLUTTER_STAGE_H__ */