[mutter] clutter/stage: Add infrastructure to track devices and their actors



commit 98a5cb37d9159737f8f1af4196420db90bfcf879
Author: Jonas Dreßler <verdre v0yd nl>
Date:   Thu Oct 15 16:39:00 2020 +0200

    clutter/stage: Add infrastructure to track devices and their actors
    
    With the introduction of the input thread, we want to avoid modifying
    ClutterInputDevices from the main thread, since they're owned and
    updated by the thread.
    
    There's one part of ClutterInputDevice that's still modified from the
    main thread though, that is device-actors of pointer devices, and we're
    going to move that state-tracking into ClutterStage instead.
    
    So start that by adding the infrastructure to ClutterStage to keep track
    of those things. It consists of two hashtables which associate devices
    and touch sequences with actors, those hashtables get updated using
    clutter_stage_update_device_entry() and
    clutter_stage_remove_device_entry(), they can be queried by calling
    clutter_stage_get_device_actor(), which will replace
    clutter_input_device_get_actor().
    
    clutter_stage_get_device_coords() is added and made available in
    clutter-mutter.h because we need to get the coordinates when repicking
    in meta_wayland_pointer_repick().
    
    Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1633>

 clutter/clutter/clutter-mutter.h        |   6 +
 clutter/clutter/clutter-stage-private.h |  10 ++
 clutter/clutter/clutter-stage.c         | 202 ++++++++++++++++++++++++++++++++
 clutter/clutter/clutter-stage.h         |   5 +
 4 files changed, 223 insertions(+)
---
diff --git a/clutter/clutter/clutter-mutter.h b/clutter/clutter/clutter-mutter.h
index 9c75ad6a25..6e1f439300 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 47fef43959..8f7ff31355 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 626193e2a0..4222d76aa0 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 59a4ad145a..a3b72b46c2 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__ */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]