[gtk+] wayland: add support for wl_pointer frame/axis_source/axis_discrete/axis_stop



commit 3fca36169a0fd5f19b1b2099547060214f40b904
Author: Peter Hutterer <peter hutterer who-t net>
Date:   Tue Oct 20 13:21:54 2015 +1000

    wayland: add support for wl_pointer frame/axis_source/axis_discrete/axis_stop
    
    This adds support for the new wl_pointer events available in v5.
    
    The wl_pointer.axis_source events can be ignored for the purposes here, the
    main reason they exist is so that the combination of axis_source=finger and
    axis_stop triggers kinetic scrolling. We don't need to care about the source,
    axis_stop is enough for us to tell us when we're scrolling.
    
    The wl_pointer.frame events group events together and is intended as a
    mechanism to coalesce events together. This for example allows us to now
    send a single GTK scroll event for a diagonal scroll. Previously, the two
    wl_pointer.axis events had to be handled separately.
    
    The wl_pointer.axis_discrete event sends mouse wheel clicks where
    appropriate, and is translated into up/down/left/right scroll events.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=756729

 gdk/wayland/gdkdevice-wayland.c  |  289 ++++++++++++++++++++++++++++++++------
 gdk/wayland/gdkdisplay-wayland.c |    4 +-
 gdk/wayland/gdkdisplay-wayland.h |    1 +
 gdk/wayland/gdkprivate-wayland.h |    1 +
 4 files changed, 252 insertions(+), 43 deletions(-)
---
diff --git a/gdk/wayland/gdkdevice-wayland.c b/gdk/wayland/gdkdevice-wayland.c
index a4edcbf..50d07bd 100644
--- a/gdk/wayland/gdkdevice-wayland.c
+++ b/gdk/wayland/gdkdevice-wayland.c
@@ -43,6 +43,7 @@
 #define BUTTON_BASE (BTN_LEFT - 1) /* Used to translate to 1-indexed buttons */
 
 typedef struct _GdkWaylandTouchData GdkWaylandTouchData;
+typedef struct _GdkWaylandPointerFrameData GdkWaylandPointerFrameData;
 
 struct _GdkWaylandTouchData
 {
@@ -54,6 +55,15 @@ struct _GdkWaylandTouchData
   guint initial_touch : 1;
 };
 
+struct _GdkWaylandPointerFrameData
+{
+  GdkEvent *event;
+
+  /* Specific to the scroll event */
+  gdouble delta_x, delta_y;
+  int32_t discrete_x, discrete_y;
+};
+
 struct _GdkWaylandSeat
 {
   GdkSeat parent_instance;
@@ -118,6 +128,9 @@ struct _GdkWaylandSeat
   gdouble gesture_scale;
 
   GdkCursor *grab_cursor;
+
+  /* Accumulated event data for a pointer frame */
+  GdkWaylandPointerFrameData pointer_frame;
 };
 
 G_DEFINE_TYPE (GdkWaylandSeat, gdk_wayland_seat, GDK_TYPE_SEAT)
@@ -918,6 +931,114 @@ static const struct wl_data_device_listener data_device_listener = {
   data_device_selection
 };
 
+static GdkEvent *
+create_scroll_event (GdkWaylandSeat *seat,
+                     gboolean        emulated)
+{
+  GdkWaylandDisplay *display = GDK_WAYLAND_DISPLAY (seat->display);
+  GdkEvent *event;
+
+  event = gdk_event_new (GDK_SCROLL);
+  event->scroll.window = g_object_ref (seat->pointer_focus);
+  gdk_event_set_device (event, seat->master_pointer);
+  gdk_event_set_source_device (event, seat->pointer);
+  event->scroll.time = seat->time;
+  event->scroll.state = seat->button_modifiers | seat->key_modifiers;
+  gdk_event_set_screen (event, display->screen);
+
+  _gdk_event_set_pointer_emulated (event, emulated);
+
+  get_coordinates (seat,
+                   &event->scroll.x,
+                   &event->scroll.y,
+                   &event->scroll.x_root,
+                   &event->scroll.y_root);
+
+  return event;
+}
+
+static void
+flush_discrete_scroll_event (GdkWaylandSeat     *seat,
+                             GdkScrollDirection  direction)
+{
+  GdkEvent *event;
+
+  event = create_scroll_event (seat, TRUE);
+  event->scroll.direction = direction;
+
+  _gdk_wayland_display_deliver_event (seat->display, event);
+}
+
+static void
+flush_smooth_scroll_event (GdkWaylandSeat *seat,
+                           gdouble         delta_x,
+                           gdouble         delta_y)
+{
+  GdkEvent *event;
+
+  event = create_scroll_event (seat, FALSE);
+  event->scroll.direction = GDK_SCROLL_SMOOTH;
+  event->scroll.delta_x = delta_x;
+  event->scroll.delta_y = delta_y;
+
+  _gdk_wayland_display_deliver_event (seat->display, event);
+}
+
+static void
+flush_scroll_event (GdkWaylandSeat             *seat,
+                    GdkWaylandPointerFrameData *pointer_frame)
+{
+  if (pointer_frame->discrete_x || pointer_frame->discrete_y)
+    {
+      GdkScrollDirection direction;
+
+      if (pointer_frame->discrete_x > 0)
+        direction = GDK_SCROLL_LEFT;
+      else if (pointer_frame->discrete_x < 0)
+        direction = GDK_SCROLL_RIGHT;
+      else if (pointer_frame->discrete_y > 0)
+        direction = GDK_SCROLL_UP;
+      else
+        direction = GDK_SCROLL_DOWN;
+
+      flush_discrete_scroll_event (seat, direction);
+    }
+
+  flush_smooth_scroll_event (seat,
+                             pointer_frame->delta_x,
+                             pointer_frame->delta_y);
+
+  pointer_frame->delta_x = 0;
+  pointer_frame->delta_y = 0;
+  pointer_frame->discrete_x = 0;
+  pointer_frame->discrete_y = 0;
+}
+
+static void
+gdk_wayland_seat_flush_frame_event (GdkWaylandSeat *seat)
+{
+  if (seat->pointer_frame.event)
+    {
+      _gdk_wayland_display_deliver_event (gdk_seat_get_display (GDK_SEAT (seat)),
+                                          seat->pointer_frame.event);
+      seat->pointer_frame.event = NULL;
+    }
+  else
+    flush_scroll_event (seat, &seat->pointer_frame);
+}
+
+static GdkEvent *
+gdk_wayland_seat_get_frame_event (GdkWaylandSeat *seat,
+                                  GdkEventType    evtype)
+{
+  if (seat->pointer_frame.event &&
+      seat->pointer_frame.event->type != evtype)
+    gdk_wayland_seat_flush_frame_event (seat);
+
+  seat->pointer_frame.event = gdk_event_new (evtype);
+  return seat->pointer_frame.event;
+}
+
 static void
 pointer_handle_enter (void              *data,
                       struct wl_pointer *pointer,
@@ -946,7 +1067,7 @@ pointer_handle_enter (void              *data,
   device->surface_y = wl_fixed_to_double (sy);
   device->enter_serial = serial;
 
-  event = gdk_event_new (GDK_ENTER_NOTIFY);
+  event = gdk_wayland_seat_get_frame_event (device, GDK_ENTER_NOTIFY);
   event->crossing.window = g_object_ref (device->pointer_focus);
   gdk_event_set_device (event, device->master_pointer);
   gdk_event_set_source_device (event, device->pointer);
@@ -966,11 +1087,12 @@ pointer_handle_enter (void              *data,
                    &event->crossing.x_root,
                    &event->crossing.y_root);
 
-  _gdk_wayland_display_deliver_event (device->display, event);
-
   GDK_NOTE (EVENTS,
             g_message ("enter, device %p surface %p",
                        device, device->pointer_focus));
+
+  if (wayland_display->seat_version < WL_POINTER_HAS_FRAME)
+    gdk_wayland_seat_flush_frame_event (device);
 }
 
 static void
@@ -994,7 +1116,7 @@ pointer_handle_leave (void              *data,
 
   _gdk_wayland_display_update_serial (wayland_display, serial);
 
-  event = gdk_event_new (GDK_LEAVE_NOTIFY);
+  event = gdk_wayland_seat_get_frame_event (device, GDK_LEAVE_NOTIFY);
   event->crossing.window = g_object_ref (device->pointer_focus);
   gdk_event_set_device (event, device->master_pointer);
   gdk_event_set_source_device (event, device->pointer);
@@ -1014,8 +1136,6 @@ pointer_handle_leave (void              *data,
                    &event->crossing.x_root,
                    &event->crossing.y_root);
 
-  _gdk_wayland_display_deliver_event (device->display, event);
-
   GDK_NOTE (EVENTS,
             g_message ("leave, device %p surface %p",
                        device, device->pointer_focus));
@@ -1025,6 +1145,9 @@ pointer_handle_leave (void              *data,
     gdk_wayland_device_stop_window_cursor_animation (device);
 
   device->pointer_focus = NULL;
+
+  if (wayland_display->seat_version < WL_POINTER_HAS_FRAME)
+    gdk_wayland_seat_flush_frame_event (device);
 }
 
 static void
@@ -1041,13 +1164,11 @@ pointer_handle_motion (void              *data,
   if (!device->pointer_focus)
     return;
 
-  event = gdk_event_new (GDK_NOTHING);
-
   device->time = time;
   device->surface_x = wl_fixed_to_double (sx);
   device->surface_y = wl_fixed_to_double (sy);
 
-  event->motion.type = GDK_MOTION_NOTIFY;
+  event = gdk_wayland_seat_get_frame_event (device, GDK_MOTION_NOTIFY);
   event->motion.window = g_object_ref (device->pointer_focus);
   gdk_event_set_device (event, device->master_pointer);
   gdk_event_set_source_device (event, device->pointer);
@@ -1067,9 +1188,10 @@ pointer_handle_motion (void              *data,
   GDK_NOTE (EVENTS,
             g_message ("motion %f %f, device %p state %d",
                        wl_fixed_to_double (sx), wl_fixed_to_double (sy),
-                      device, event->button.state));
+                      device, event->motion.state));
 
-  _gdk_wayland_display_deliver_event (device->display, event);
+  if (display->seat_version < WL_POINTER_HAS_FRAME)
+    gdk_wayland_seat_flush_frame_event (device);
 }
 
 static void
@@ -1112,7 +1234,9 @@ pointer_handle_button (void              *data,
   if (state)
     device->button_press_serial = serial;
 
-  event = gdk_event_new (state ? GDK_BUTTON_PRESS : GDK_BUTTON_RELEASE);
+  event = gdk_wayland_seat_get_frame_event (device,
+                                            state ? GDK_BUTTON_PRESS :
+                                            GDK_BUTTON_RELEASE);
   event->button.window = g_object_ref (device->pointer_focus);
   gdk_event_set_device (event, device->master_pointer);
   gdk_event_set_source_device (event, device->pointer);
@@ -1142,7 +1266,8 @@ pointer_handle_button (void              *data,
                        device,
                        event->button.state));
 
-  _gdk_wayland_display_deliver_event (device->display, event);
+  if (display->seat_version < WL_POINTER_HAS_FRAME)
+    gdk_wayland_seat_flush_frame_event (device);
 }
 
 static void
@@ -1152,53 +1277,129 @@ pointer_handle_axis (void              *data,
                      uint32_t           axis,
                      wl_fixed_t         value)
 {
-  GdkWaylandDeviceData *device = data;
-  GdkWaylandDisplay *display = GDK_WAYLAND_DISPLAY (device->display);
-  GdkEvent *event;
-  gdouble delta_x, delta_y;
+  GdkWaylandSeat *seat = data;
+  GdkWaylandPointerFrameData *pointer_frame = &seat->pointer_frame;
+  GdkWaylandDisplay *display = GDK_WAYLAND_DISPLAY (seat->display);
 
-  if (!device->pointer_focus)
+  if (!seat->pointer_focus)
     return;
 
   /* get the delta and convert it into the expected range */
   switch (axis)
     {
     case WL_POINTER_AXIS_VERTICAL_SCROLL:
-      delta_x = 0;
-      delta_y = wl_fixed_to_double (value) / 10.0;
+      pointer_frame->delta_y = wl_fixed_to_double (value) / 10.0;
       break;
     case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
-      delta_x = wl_fixed_to_double (value) / 10.0;
-      delta_y = 0;
+      pointer_frame->delta_x = wl_fixed_to_double (value) / 10.0;
       break;
     default:
       g_return_if_reached ();
     }
 
-  device->time = time;
-  event = gdk_event_new (GDK_SCROLL);
-  event->scroll.window = g_object_ref (device->pointer_focus);
-  gdk_event_set_device (event, device->master_pointer);
-  gdk_event_set_source_device (event, device->pointer);
-  gdk_event_set_seat (event, gdk_device_get_seat (device->master_pointer));
-  event->scroll.time = time;
-  event->scroll.direction = GDK_SCROLL_SMOOTH;
-  event->scroll.delta_x = delta_x;
-  event->scroll.delta_y = delta_y;
-  event->scroll.state = device->button_modifiers | device->key_modifiers;
-  gdk_event_set_screen (event, display->screen);
+  seat->time = time;
 
-  get_coordinates (device,
-                   &event->scroll.x,
-                   &event->scroll.y,
-                   &event->scroll.x_root,
-                   &event->scroll.y_root);
+  GDK_NOTE (EVENTS,
+            g_message ("scroll, axis %d, value %f, seat %p",
+                       axis, wl_fixed_to_double (value) / 10.0,
+                       seat));
+
+  if (display->seat_version < WL_POINTER_HAS_FRAME)
+    gdk_wayland_seat_flush_frame_event (seat);
+}
+
+static void
+pointer_handle_frame (void              *data,
+                      struct wl_pointer *pointer)
+{
+  GdkWaylandSeat *seat = data;
+
+  if (!seat->pointer_focus)
+    return;
 
   GDK_NOTE (EVENTS,
-            g_message ("scroll %f %f, device %p",
-                       event->scroll.delta_x, event->scroll.delta_y, device));
+            g_message ("frame, seat %p", seat));
 
-  _gdk_wayland_display_deliver_event (device->display, event);
+  gdk_wayland_seat_flush_frame_event (seat);
+}
+
+static void
+pointer_handle_axis_source (void                        *data,
+                            struct wl_pointer           *pointer,
+                            enum wl_pointer_axis_source  source)
+{
+  GdkWaylandSeat *seat = data;
+
+  if (!seat->pointer_focus)
+    return;
+
+  /* We don't need to handle the scroll source right now. It only has real
+   * meaning for 'finger' (to trigger kinetic scrolling). The axis_stop
+   * event will generate the zero delta required to trigger kinetic
+   * scrolling, so explicity handling the source is not required.
+   */
+
+  GDK_NOTE (EVENTS,
+            g_message ("axis source %d, seat %p", source, seat));
+}
+
+static void
+pointer_handle_axis_stop (void              *data,
+                          struct wl_pointer *pointer,
+                          uint32_t           time,
+                          uint32_t           axis)
+{
+  GdkWaylandSeat *seat = data;
+  GdkWaylandPointerFrameData *pointer_frame = &seat->pointer_frame;
+
+  if (!seat->pointer_focus)
+    return;
+
+  seat->time = time;
+
+  switch (axis)
+    {
+    case WL_POINTER_AXIS_VERTICAL_SCROLL:
+      pointer_frame->delta_y = 0;
+      break;
+    case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
+      pointer_frame->delta_x = 0;
+      break;
+    default:
+      g_return_if_reached ();
+    }
+
+  GDK_NOTE (EVENTS,
+            g_message ("axis stop, seat %p", seat));
+}
+
+static void
+pointer_handle_axis_discrete (void              *data,
+                              struct wl_pointer *pointer,
+                              uint32_t           axis,
+                              int32_t            value)
+{
+  GdkWaylandSeat *seat = data;
+  GdkWaylandPointerFrameData *pointer_frame = &seat->pointer_frame;
+
+  if (!seat->pointer_focus)
+    return;
+
+  switch (axis)
+    {
+    case WL_POINTER_AXIS_VERTICAL_SCROLL:
+      pointer_frame->discrete_y = value;
+      break;
+    case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
+      pointer_frame->discrete_x = value;
+      break;
+    default:
+      g_return_if_reached ();
+    }
+
+  GDK_NOTE (EVENTS,
+            g_message ("discrete scroll, axis %d, value %d, seat %p",
+                       axis, value, seat));
 }
 
 static void
@@ -2005,6 +2206,10 @@ static const struct wl_pointer_listener pointer_listener = {
   pointer_handle_motion,
   pointer_handle_button,
   pointer_handle_axis,
+  pointer_handle_frame,
+  pointer_handle_axis_source,
+  pointer_handle_axis_stop,
+  pointer_handle_axis_discrete,
 };
 
 static const struct wl_keyboard_listener keyboard_listener = {
diff --git a/gdk/wayland/gdkdisplay-wayland.c b/gdk/wayland/gdkdisplay-wayland.c
index 22791c1..53e26ae 100644
--- a/gdk/wayland/gdkdisplay-wayland.c
+++ b/gdk/wayland/gdkdisplay-wayland.c
@@ -212,8 +212,10 @@ _gdk_wayland_display_add_seat (GdkWaylandDisplay *display_wayland,
   GdkDisplay *gdk_display = GDK_DISPLAY_OBJECT (display_wayland);
   struct wl_seat *seat;
 
+  display_wayland->seat_version = MIN (version, 5);
   seat = wl_registry_bind (display_wayland->wl_registry,
-                           id, &wl_seat_interface, MIN (version, 4));
+                           id, &wl_seat_interface,
+                           display_wayland->seat_version);
   _gdk_wayland_device_manager_add_seat (gdk_display->device_manager,
                                         id, seat);
   _gdk_wayland_display_async_roundtrip (display_wayland);
diff --git a/gdk/wayland/gdkdisplay-wayland.h b/gdk/wayland/gdkdisplay-wayland.h
index 36c663b..1185cd5 100644
--- a/gdk/wayland/gdkdisplay-wayland.h
+++ b/gdk/wayland/gdkdisplay-wayland.h
@@ -92,6 +92,7 @@ struct _GdkWaylandDisplay
   GSource *event_source;
 
   int compositor_version;
+  int seat_version;
 
   struct xkb_context *xkb_context;
 
diff --git a/gdk/wayland/gdkprivate-wayland.h b/gdk/wayland/gdkprivate-wayland.h
index 9d3b817..cb8a66a 100644
--- a/gdk/wayland/gdkprivate-wayland.h
+++ b/gdk/wayland/gdkprivate-wayland.h
@@ -41,6 +41,7 @@
 #include "config.h"
 
 #define WL_SURFACE_HAS_BUFFER_SCALE 3
+#define WL_POINTER_HAS_FRAME 5
 
 #define SUPPORTED_GTK_SHELL_VERSION 2
 


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