evdev: if a device's rotation is around 180 degrees, flip the wheel
authorPeter Hutterer <peter.hutterer@who-t.net>
Tue, 18 Apr 2023 06:53:05 +0000 (16:53 +1000)
committerJihoon Kim <jihoon48.kim@samsung.com>
Mon, 4 Dec 2023 10:23:27 +0000 (19:23 +0900)
For a device used upside-down, make sure the wheels correspond to the
new physical directions. There's a grace range of 20 degrees either way
since that seems like it makes sense.

For 90 degree rotation (or 270 degree) the wheel is left as-is, the
heuristics to guess what angle we want in this case is not clear enough.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
doc/user/configuration.rst
src/evdev-wheel.c
test/test-pointer.c

index f895d602f96792f3265b85b72ca64a9d3b54a639..1620728588a164ab4481a49f541d95b8473936d5 100644 (file)
@@ -173,3 +173,7 @@ allowing the device to be used e.g. sideways or upside-down. For example, a
 trackball may be used in a 90° rotated position for accessibility reasons -
 such a rotated position allows triggering the buttons with the thumb or
 the non-dominant hand.
+
+Note that where a device rotation is higher than 160 but less than 200 degrees,
+the direction of wheels is also inverted. For all other angles, the wheel
+direction is left as-is.
index 2381cb772241346eacb79dc60680dfcb344291bd..13efece6307be7ec5a1160af4f6e8e608dcaafeb 100644 (file)
@@ -323,6 +323,21 @@ wheel_handle_direction_change(struct fallback_dispatch *dispatch,
        }
 }
 
+static void
+fallback_rotate_wheel(struct fallback_dispatch *dispatch,
+                     struct evdev_device *device,
+                     struct input_event *e)
+{
+       /* Special case: if we're upside down (-ish),
+        * swap the direction of the wheels so that user-down
+        * means scroll down. This isn't done for any other angle
+        * since it's not clear what the heuristics should be.*/
+       if (dispatch->rotation.angle >= 160.0 &&
+           dispatch->rotation.angle <= 220.0) {
+               e->value *= -1;
+       }
+}
+
 void
 fallback_wheel_process_relative(struct fallback_dispatch *dispatch,
                                struct evdev_device *device,
@@ -330,6 +345,7 @@ fallback_wheel_process_relative(struct fallback_dispatch *dispatch,
 {
        switch (e->code) {
        case REL_WHEEL:
+               fallback_rotate_wheel(dispatch, device, e);
                dispatch->wheel.lo_res.y += e->value;
                if (dispatch->wheel.emulate_hi_res_wheel)
                        dispatch->wheel.hi_res.y += e->value * 120;
@@ -337,6 +353,7 @@ fallback_wheel_process_relative(struct fallback_dispatch *dispatch,
                wheel_handle_event(dispatch, WHEEL_EVENT_SCROLL, time);
                break;
        case REL_HWHEEL:
+               fallback_rotate_wheel(dispatch, device, e);
                dispatch->wheel.lo_res.x += e->value;
                if (dispatch->wheel.emulate_hi_res_wheel)
                        dispatch->wheel.hi_res.x += e->value * 120;
@@ -344,6 +361,7 @@ fallback_wheel_process_relative(struct fallback_dispatch *dispatch,
                wheel_handle_event(dispatch, WHEEL_EVENT_SCROLL, time);
                break;
        case REL_WHEEL_HI_RES:
+               fallback_rotate_wheel(dispatch, device, e);
                dispatch->wheel.hi_res.y += e->value;
                dispatch->wheel.hi_res_event_received = true;
                dispatch->pending_event |= EVDEV_WHEEL;
@@ -351,6 +369,7 @@ fallback_wheel_process_relative(struct fallback_dispatch *dispatch,
                wheel_handle_event(dispatch, WHEEL_EVENT_SCROLL, time);
                break;
        case REL_HWHEEL_HI_RES:
+               fallback_rotate_wheel(dispatch, device, e);
                dispatch->wheel.hi_res.x += e->value;
                dispatch->wheel.hi_res_event_received = true;
                dispatch->pending_event |= EVDEV_WHEEL;
index ed93b7637a310975a92f18369cd1132dcff6d5d0..7a80c57b397ed72e157fffa6c7a02e35a2b8b51d 100644 (file)
@@ -628,6 +628,13 @@ test_high_and_low_wheel_events_value(struct litest_device *dev,
                v120 *= -1;
        }
 
+       double angle = libinput_device_config_rotation_get_angle(dev->libinput_device);
+       if (angle >= 160.0 && angle <= 220.0) {
+               expected *= -1;
+               discrete *= -1;
+               v120 *= -1;
+       }
+
        axis = (which == REL_WHEEL || which == REL_WHEEL_HI_RES) ?
                                LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL :
                                LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL;
@@ -1060,6 +1067,38 @@ START_TEST(pointer_scroll_has_axis_invalid)
 }
 END_TEST
 
+START_TEST(pointer_scroll_with_rotation)
+{
+       struct litest_device *dev = litest_current_device();
+       struct libinput *li = dev->libinput;
+       struct libinput_device *device = dev->libinput_device;
+       double angle = _i * 20; /* ranged test */
+
+       litest_drain_events(li);
+       libinput_device_config_rotation_set_angle(device, angle);
+
+       /* make sure we hit at least one of the below two conditions */
+       ck_assert(libevdev_has_event_code(dev->evdev, EV_REL, REL_WHEEL) ||
+                 libevdev_has_event_code(dev->evdev, EV_REL, REL_HWHEEL));
+
+       if (libevdev_has_event_code(dev->evdev, EV_REL, REL_WHEEL)) {
+               test_wheel_event(dev, REL_WHEEL, -1);
+               test_wheel_event(dev, REL_WHEEL, 1);
+
+               test_wheel_event(dev, REL_WHEEL, -5);
+               test_wheel_event(dev, REL_WHEEL, 6);
+       }
+
+       if (libevdev_has_event_code(dev->evdev, EV_REL, REL_HWHEEL)) {
+               test_wheel_event(dev, REL_HWHEEL, -1);
+               test_wheel_event(dev, REL_HWHEEL, 1);
+
+               test_wheel_event(dev, REL_HWHEEL, -5);
+               test_wheel_event(dev, REL_HWHEEL, 6);
+       }
+}
+END_TEST
+
 START_TEST(pointer_seat_button_count)
 {
        struct litest_device *devices[4];
@@ -3713,6 +3752,7 @@ TEST_COLLECTION(pointer)
        struct range buttonorder = {0, _MB_BUTTONORDER_COUNT};
        struct range scroll_directions = {LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
                                          LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL + 1};
+       struct range rotation_20deg = {0, 18}; /* steps of 20 degrees */
 
        litest_add(pointer_motion_relative, LITEST_RELATIVE, LITEST_POINTINGSTICK);
        litest_add_for_device(pointer_motion_relative_zero, LITEST_MOUSE);
@@ -3754,6 +3794,7 @@ TEST_COLLECTION(pointer)
        litest_add(pointer_scroll_natural_enable_config, LITEST_WHEEL, LITEST_TABLET);
        litest_add(pointer_scroll_natural_wheel, LITEST_WHEEL, LITEST_TABLET);
        litest_add(pointer_scroll_has_axis_invalid, LITEST_WHEEL, LITEST_TABLET);
+       litest_add_ranged(pointer_scroll_with_rotation, LITEST_WHEEL, LITEST_TABLET, &rotation_20deg);
 
        litest_add(pointer_no_calibration, LITEST_ANY, LITEST_TOUCH|LITEST_SINGLE_TOUCH|LITEST_ABSOLUTE|LITEST_PROTOCOL_A|LITEST_TABLET);