[clutter] evdev: Emulate discrete scroll events out of smooth scroll ones.



commit 1fe21a8fe174a4c98e8c694cff5bfc80ddb9efde
Author: Carlos Garnacho <carlosg gnome org>
Date:   Fri Oct 9 20:43:25 2015 +0200

    evdev: Emulate discrete scroll events out of smooth scroll ones.
    
    There's handlers around relying on up/down/left/right scroll events,
    which won't work as expected if only smooth scroll events are sent.
    In order to work properly there, we have to retrofit discrete scroll
    events on the evdev backend.
    
    Fix this by implementing emission (on devices with a wheel) and
    emulation (on anything else) of discrete scroll events. On the former
    both smooth and discrete events are set, for the latter we do accumulate
    the dx/dy of the latest scroll events, and emit discrete ones when we
    accumulated enough. The ending 0/0 event will reset the accumulators for
    the next scrolling batch.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=756284

 clutter/evdev/clutter-device-manager-evdev.c |  130 ++++++++++++++++++++++++--
 1 files changed, 122 insertions(+), 8 deletions(-)
---
diff --git a/clutter/evdev/clutter-device-manager-evdev.c b/clutter/evdev/clutter-device-manager-evdev.c
index aba31a4..b4f7d95 100644
--- a/clutter/evdev/clutter-device-manager-evdev.c
+++ b/clutter/evdev/clutter-device-manager-evdev.c
@@ -28,6 +28,7 @@
 #endif
 
 #include <math.h>
+#include <float.h>
 #include <linux/input.h>
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -55,6 +56,8 @@
 
 #include "clutter-device-manager-evdev.h"
 
+#define DISCRETE_SCROLL_STEP 10.0
+
 #define AUTOREPEAT_VALUE 2
 
 /* Try to keep the pointer inside the stage. Hopefully no one is using
@@ -100,6 +103,10 @@ struct _ClutterSeatEvdev
 
   gfloat pointer_x;
   gfloat pointer_y;
+
+  /* Emulation of discrete scroll events out of smooth ones */
+  gfloat accum_scroll_dx;
+  gfloat accum_scroll_dy;
 };
 
 struct _ClutterEventFilter
@@ -428,6 +435,61 @@ notify_relative_motion (ClutterInputDevice *input_device,
   notify_absolute_motion (input_device, time_, new_x, new_y);
 }
 
+static ClutterScrollDirection
+discrete_to_direction (gdouble discrete_x,
+                       gdouble discrete_y)
+{
+  if (discrete_x > 0)
+    return CLUTTER_SCROLL_RIGHT;
+  else if (discrete_x < 0)
+    return CLUTTER_SCROLL_LEFT;
+  else if (discrete_y > 0)
+    return CLUTTER_SCROLL_DOWN;
+  else if (discrete_y < 0)
+    return CLUTTER_SCROLL_UP;
+  else
+    return CLUTTER_SCROLL_SMOOTH;
+}
+
+static void
+notify_discrete_scroll (ClutterInputDevice     *input_device,
+                        guint32                 time_,
+                        ClutterScrollDirection  direction)
+{
+  ClutterInputDeviceEvdev *device_evdev;
+  ClutterSeatEvdev *seat;
+  ClutterStage *stage;
+  ClutterEvent *event = NULL;
+
+  if (direction == CLUTTER_SCROLL_SMOOTH)
+    return;
+
+  /* We can drop the event on the floor if no stage has been
+   * associated with the device yet. */
+  stage = _clutter_input_device_get_stage (input_device);
+  if (!stage)
+    return;
+
+  device_evdev = CLUTTER_INPUT_DEVICE_EVDEV (input_device);
+  seat = _clutter_input_device_evdev_get_seat (device_evdev);
+
+  event = clutter_event_new (CLUTTER_SCROLL);
+
+  event->scroll.time = time_;
+  event->scroll.stage = CLUTTER_STAGE (stage);
+  event->scroll.device = seat->core_pointer;
+  _clutter_xkb_translate_state (event, seat->xkb, seat->button_state);
+
+  event->scroll.direction = direction;
+
+  event->scroll.x = seat->pointer_x;
+  event->scroll.y = seat->pointer_y;
+  clutter_event_set_device (event, seat->core_pointer);
+  clutter_event_set_source_device (event, input_device);
+
+  queue_event (event);
+}
+
 static void
 notify_scroll (ClutterInputDevice *input_device,
                guint32             time_,
@@ -460,7 +522,7 @@ notify_scroll (ClutterInputDevice *input_device,
    * To convert to Xi2 discrete step coordinate space, multiply the factor
    * 1/10. */
   event->scroll.direction = CLUTTER_SCROLL_SMOOTH;
-  scroll_factor = 1.0 / 10.0;
+  scroll_factor = 1.0 / DISCRETE_SCROLL_STEP;
   clutter_event_set_scroll_delta (event,
                                   scroll_factor * dx,
                                   scroll_factor * dy);
@@ -1198,6 +1260,37 @@ _device_seat_get_touch (ClutterInputDevice *input_device,
   return g_hash_table_lookup (seat->touches, GUINT_TO_POINTER (id));
 }
 
+static void
+check_notify_discrete_scroll (ClutterDeviceManagerEvdev *manager_evdev,
+                              ClutterInputDevice        *device,
+                              guint32                    time_)
+{
+  ClutterInputDeviceEvdev *device_evdev =
+    CLUTTER_INPUT_DEVICE_EVDEV (device);
+  ClutterSeatEvdev *seat = _clutter_input_device_evdev_get_seat (device_evdev);
+  int i, n_xscrolls, n_yscrolls;
+
+  n_xscrolls = floor (fabs (seat->accum_scroll_dx) / DISCRETE_SCROLL_STEP);
+  n_yscrolls = floor (fabs (seat->accum_scroll_dy) / DISCRETE_SCROLL_STEP);
+
+  for (i = 0; i < n_xscrolls; i++)
+    {
+      notify_discrete_scroll (device, time_,
+                              seat->accum_scroll_dx > 0 ?
+                              CLUTTER_SCROLL_RIGHT : CLUTTER_SCROLL_LEFT);
+    }
+
+  for (i = 0; i < n_yscrolls; i++)
+    {
+      notify_discrete_scroll (device, time_,
+                              seat->accum_scroll_dy > 0 ?
+                              CLUTTER_SCROLL_DOWN : CLUTTER_SCROLL_UP);
+    }
+
+  seat->accum_scroll_dx = fmodf (seat->accum_scroll_dx, DISCRETE_SCROLL_STEP);
+  seat->accum_scroll_dy = fmodf (seat->accum_scroll_dy, DISCRETE_SCROLL_STEP);
+}
+
 static gboolean
 process_device_event (ClutterDeviceManagerEvdev *manager_evdev,
                       struct libinput_event *event)
@@ -1306,14 +1399,17 @@ process_device_event (ClutterDeviceManagerEvdev *manager_evdev,
     case LIBINPUT_EVENT_POINTER_AXIS:
       {
         gdouble dx = 0.0, dy = 0.0;
+        gdouble discrete_x = 0.0, discrete_y = 0.0;
         guint32 time;
         gboolean wheel = FALSE;
         enum libinput_pointer_axis axis;
         enum libinput_pointer_axis_source source;
         struct libinput_event_pointer *axis_event =
           libinput_event_get_pointer_event (event);
+        ClutterSeatEvdev *seat;
 
         device = libinput_device_get_user_data (libinput_device);
+        seat = _clutter_input_device_evdev_get_seat (CLUTTER_INPUT_DEVICE_EVDEV (device));
 
         time = libinput_event_pointer_get_time (axis_event);
         source = libinput_event_pointer_get_axis_source (axis_event);
@@ -1329,22 +1425,40 @@ process_device_event (ClutterDeviceManagerEvdev *manager_evdev,
         axis = LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL;
         if (libinput_event_pointer_has_axis (axis_event, axis))
           {
-            if (wheel)
-              dy = 10 * libinput_event_pointer_get_axis_value_discrete (axis_event, axis);
+            discrete_y = libinput_event_pointer_get_axis_value_discrete (axis_event, axis);
+            dy = libinput_event_pointer_get_axis_value (axis_event, axis);
+
+            if (wheel || fabs (dy) < DBL_EPSILON)
+              seat->accum_scroll_dy = 0;
             else
-              dy = libinput_event_pointer_get_axis_value (axis_event, axis);
+              seat->accum_scroll_dy += dy;
           }
 
         axis = LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL;
         if (libinput_event_pointer_has_axis (axis_event, axis))
           {
-            if (wheel)
-              dx = 10 * libinput_event_pointer_get_axis_value_discrete (axis_event, axis);
+            discrete_x = libinput_event_pointer_get_axis_value_discrete (axis_event, axis);
+            dx = libinput_event_pointer_get_axis_value (axis_event, axis);
+
+            if (wheel || fabs (dx) < DBL_EPSILON)
+              seat->accum_scroll_dx = 0;
             else
-              dx = libinput_event_pointer_get_axis_value (axis_event, axis);
+              seat->accum_scroll_dx += dx;
+          }
+
+        if (wheel)
+          {
+            notify_scroll (device, time,
+                           discrete_x * DISCRETE_SCROLL_STEP,
+                           discrete_y * DISCRETE_SCROLL_STEP);
+            notify_discrete_scroll (device, time, discrete_to_direction (discrete_x, discrete_y));
+          }
+        else
+          {
+            notify_scroll (device, time, dx, dy);
+            check_notify_discrete_scroll (manager_evdev, device, time);
           }
 
-        notify_scroll (device, time, dx, dy);
         break;
 
       }


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