From 1fe21a8fe174a4c98e8c694cff5bfc80ddb9efde Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Fri, 9 Oct 2015 20:43:25 +0200 Subject: [PATCH] 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 file changed, 122 insertions(+), 8 deletions(-) diff --git a/clutter/evdev/clutter-device-manager-evdev.c b/clutter/evdev/clutter-device-manager-evdev.c index aba31a4a5..b4f7d95df 100644 --- a/clutter/evdev/clutter-device-manager-evdev.c +++ b/clutter/evdev/clutter-device-manager-evdev.c @@ -28,6 +28,7 @@ #endif #include +#include #include #include #include @@ -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; }