gdk: Implement motion history as motion event data
authorCarlos Garnacho <carlosg@gnome.org>
Tue, 31 Oct 2017 11:37:50 +0000 (12:37 +0100)
committerCarlos Garnacho <carlosg@gnome.org>
Thu, 14 Dec 2017 00:05:48 +0000 (01:05 +0100)
In the motion compression phase the coalesced events will be saved
as a GdkTimeCoord on the motion event that shall be delivered.

For simplicity (and because history doesn't make much sense otherwise)
event history is only recorded while there are buttons pressed, this
also tidily ensures that those coalesced events would have the same
target widget on the gtk side than the delivered one, because of
implicit grabs.

gdk/gdkevents.c
gdk/gdkevents.h
gdk/gdkeventsprivate.h

index 19bf7e6f53f721b3f73b4e535e6f1ba23f418531..8b3b98144a8d53b32f2349ffd39856ac08612162 100644 (file)
@@ -337,13 +337,37 @@ _gdk_event_unqueue (GdkDisplay *display)
   return event;
 }
 
+static void
+gdk_event_push_history (GdkEvent       *event,
+                        const GdkEvent *history_event)
+{
+  GdkTimeCoord *hist;
+  GdkDevice *device;
+  gint i, n_axes;
+
+  g_assert (event->any.type == GDK_MOTION_NOTIFY);
+  g_assert (history_event->any.type == GDK_MOTION_NOTIFY);
+
+  hist = g_new0 (GdkTimeCoord, 1);
+
+  device = gdk_event_get_device (history_event);
+  n_axes = gdk_device_get_n_axes (device);
+
+  for (i = 0; i <= MIN (n_axes, GDK_MAX_TIMECOORD_AXES); i++)
+    gdk_event_get_axis (history_event, i, &hist->axes[i]);
+
+  event->motion.history = g_list_prepend (event->motion.history, hist);
+}
+
 void
 _gdk_event_queue_handle_motion_compression (GdkDisplay *display)
 {
   GList *tmp_list;
   GList *pending_motions = NULL;
+  GList *history = NULL;
   GdkWindow *pending_motion_window = NULL;
   GdkDevice *pending_motion_device = NULL;
+  GdkEvent *last_motion = NULL;
 
   /* If the last N events in the event queue are motion notify
    * events for the same window, drop all but the last */
@@ -371,6 +395,9 @@ _gdk_event_queue_handle_motion_compression (GdkDisplay *display)
       if (!event->any.window->event_compression)
         break;
 
+      if (!last_motion)
+        last_motion = event;
+
       pending_motion_window = event->any.window;
       pending_motion_device = event->any.device;
       pending_motions = tmp_list;
@@ -381,6 +408,15 @@ _gdk_event_queue_handle_motion_compression (GdkDisplay *display)
   while (pending_motions && pending_motions->next != NULL)
     {
       GList *next = pending_motions->next;
+
+      history = g_list_prepend (history, pending_motions->data);
+
+      if (last_motion &&
+          (last_motion->motion.state &
+           (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK |
+            GDK_BUTTON4_MASK | GDK_BUTTON5_MASK)))
+        gdk_event_push_history (last_motion, pending_motions->data);
+
       gdk_event_free (pending_motions->data);
       display->queued_events = g_list_delete_link (display->queued_events,
                                                    pending_motions);
@@ -558,6 +594,12 @@ gdk_event_get_pointer_emulated (GdkEvent *event)
   return (event->any.flags & GDK_EVENT_POINTER_EMULATED) != 0;
 }
 
+static GdkTimeCoord *
+copy_time_coord (const GdkTimeCoord *coord)
+{
+  return g_memdup (coord, sizeof (GdkTimeCoord));
+}
+
 /**
  * gdk_event_copy:
  * @event: a #GdkEvent
@@ -642,6 +684,12 @@ gdk_event_copy (const GdkEvent *event)
                                            sizeof (gdouble) * gdk_device_get_n_axes (event->any.device));
       if (event->motion.tool)
         g_object_ref (new_event->motion.tool);
+
+      if (event->motion.history)
+        {
+          new_event->motion.history = g_list_copy_deep (event->motion.history,
+                                                        (GCopyFunc) copy_time_coord, NULL);
+        }
       break;
 
     default:
@@ -718,6 +766,7 @@ gdk_event_finalize (GObject *object)
     case GDK_MOTION_NOTIFY:
       g_clear_object (&event->motion.tool);
       g_free (event->motion.axes);
+      g_list_free_full (event->motion.history, g_free);
       break;
 
     default:
@@ -2623,3 +2672,10 @@ gdk_event_get_axes (GdkEvent  *event,
   return FALSE;
 }
 
+GList *
+gdk_event_get_history (const GdkEvent *event)
+{
+  if (event->any.type != GDK_MOTION_NOTIFY)
+    return NULL;
+  return g_list_reverse (g_list_copy (event->motion.history));
+}
index db4b779e34ab77f18d1e2909aed3e07e3db39fff..65a00cc4944e793e1203d97ccfdfee6bb9cf9e65 100644 (file)
@@ -716,6 +716,7 @@ GDK_AVAILABLE_IN_3_92
 gboolean       gdk_event_get_axes      (GdkEvent  *event,
                                         gdouble  **axes,
                                         guint     *n_axes);
+GList        * gdk_event_get_history   (const GdkEvent  *event);
 
 G_END_DECLS
 
index bb3bb473bee00069c6d7bdc3c3a7e84526667a0b..b9fc789b819f8ec21081dc748e1af013ef8e5dca 100644 (file)
@@ -121,6 +121,7 @@ struct _GdkEventMotion
   gint16 is_hint;
   GdkDeviceTool *tool;
   gdouble x_root, y_root;
+  GList *history;
 };
 
 /**