evdev: Add middle button scrolling for trackpoints
authorHans de Goede <hdegoede@redhat.com>
Tue, 16 Sep 2014 14:22:36 +0000 (16:22 +0200)
committerPeter Hutterer <peter.hutterer@who-t.net>
Thu, 18 Sep 2014 03:29:42 +0000 (13:29 +1000)
Most trackpoint users want to be able to scroll using the trackpoint with
the middle button pressed, add support for this.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
include/linux/input.h
src/evdev.c
src/evdev.h
test/litest-trackpoint.c
test/pointer.c

index aa98ce7..39b550b 100644 (file)
@@ -163,6 +163,7 @@ struct input_keymap_entry {
 #define INPUT_PROP_BUTTONPAD           0x02    /* has button(s) under pad */
 #define INPUT_PROP_SEMI_MT             0x03    /* touch rectangle only */
 #define INPUT_PROP_TOPBUTTONPAD                0x04    /* softbuttons at top of pad */
+#define INPUT_PROP_POINTING_STICK      0x05    /* is a pointing stick */
 
 #define INPUT_PROP_MAX                 0x1f
 #define INPUT_PROP_CNT                 (INPUT_PROP_MAX + 1)
index 45020ba..85e4d71 100644 (file)
@@ -41,6 +41,7 @@
 #include "libinput-private.h"
 
 #define DEFAULT_AXIS_STEP_DISTANCE 10
+#define DEFAULT_MIDDLE_BUTTON_SCROLL_TIMEOUT 200
 
 enum evdev_key_type {
        EVDEV_KEY_TYPE_NONE,
@@ -203,6 +204,15 @@ evdev_flush_pending_event(struct evdev_device *device, uint64_t time)
                device->rel.dx = 0;
                device->rel.dy = 0;
 
+               /* Use unaccelerated deltas for pointing stick scroll */
+               if (device->scroll.has_middle_button_scroll &&
+                   hw_is_key_down(device, BTN_MIDDLE)) {
+                       if (device->scroll.middle_button_scroll_active)
+                               evdev_post_scroll(device, time,
+                                                 motion.dx, motion.dy);
+                       break;
+               }
+
                /* Apply pointer acceleration. */
                filter_dispatch(device->pointer.filter, &motion, device, time);
 
@@ -346,6 +356,37 @@ get_key_type(uint16_t code)
 }
 
 static void
+evdev_middle_button_scroll_timeout(uint64_t time, void *data)
+{
+       struct evdev_device *device = data;
+
+       device->scroll.middle_button_scroll_active = true;
+}
+
+static void
+evdev_middle_button_scroll_button(struct evdev_device *device,
+                                uint64_t time, int is_press)
+{
+       if (is_press) {
+               libinput_timer_set(&device->scroll.timer,
+                               time + DEFAULT_MIDDLE_BUTTON_SCROLL_TIMEOUT);
+       } else {
+               libinput_timer_cancel(&device->scroll.timer);
+               if (device->scroll.middle_button_scroll_active) {
+                       evdev_stop_scroll(device, time);
+                       device->scroll.middle_button_scroll_active = false;
+               } else {
+                       /* If the button is released quickly enough emit the
+                        * button press/release events. */
+                       evdev_pointer_notify_button(device, time, BTN_MIDDLE,
+                                       LIBINPUT_BUTTON_STATE_PRESSED);
+                       evdev_pointer_notify_button(device, time, BTN_MIDDLE,
+                                       LIBINPUT_BUTTON_STATE_RELEASED);
+               }
+       }
+}
+
+static void
 evdev_process_touch_button(struct evdev_device *device,
                           uint64_t time, int value)
 {
@@ -405,6 +446,12 @@ evdev_process_key(struct evdev_device *device,
                                   LIBINPUT_KEY_STATE_RELEASED);
                break;
        case EVDEV_KEY_TYPE_BUTTON:
+               if (device->scroll.has_middle_button_scroll &&
+                   e->code == BTN_MIDDLE) {
+                       evdev_middle_button_scroll_button(device, time,
+                                                         e->value);
+                       break;
+               }
                evdev_pointer_notify_button(
                        device,
                        time,
@@ -946,6 +993,15 @@ evdev_configure_device(struct evdev_device *device)
                        device->mt.slot = active_slot;
                }
        }
+
+       if (libevdev_has_property(evdev, INPUT_PROP_POINTING_STICK)) {
+               libinput_timer_init(&device->scroll.timer,
+                                   device->base.seat->libinput,
+                                   evdev_middle_button_scroll_timeout,
+                                   device);
+               device->scroll.has_middle_button_scroll = true;
+       }
+
        if (libevdev_has_event_code(evdev, EV_REL, REL_X) ||
            libevdev_has_event_code(evdev, EV_REL, REL_Y))
                has_rel = 1;
index 311dddc..e1506d2 100644 (file)
 
 #include "config.h"
 
+#include <stdbool.h>
 #include "linux/input.h"
 #include <libevdev/libevdev.h>
 
 #include "libinput-private.h"
+#include "timer.h"
 
 enum evdev_event_type {
        EVDEV_NONE,
@@ -96,6 +98,9 @@ struct evdev_device {
        } rel;
 
        struct {
+               struct libinput_timer timer;
+               bool has_middle_button_scroll;
+               bool middle_button_scroll_active;
                double threshold;
                uint32_t direction;
        } scroll;
index 25a377c..40b9ed0 100644 (file)
@@ -49,6 +49,8 @@ static int events[] = {
        EV_KEY, BTN_MIDDLE,
        EV_REL, REL_X,
        EV_REL, REL_Y,
+       INPUT_PROP_MAX, INPUT_PROP_POINTER,
+       INPUT_PROP_MAX, INPUT_PROP_POINTING_STICK,
        -1, -1,
 };
 
index 82c5245..f704372 100644 (file)
@@ -132,7 +132,9 @@ START_TEST(pointer_button)
                test_button_event(dev, BTN_RIGHT, 0);
        }
 
-       if (libevdev_has_event_code(dev->evdev, EV_KEY, BTN_MIDDLE)) {
+       /* Skip middle button test on trackpoints (used for scrolling) */
+       if (!libevdev_has_property(dev->evdev, INPUT_PROP_POINTING_STICK) &&
+           libevdev_has_event_code(dev->evdev, EV_KEY, BTN_MIDDLE)) {
                test_button_event(dev, BTN_MIDDLE, 1);
                test_button_event(dev, BTN_MIDDLE, 0);
        }