From 753cb25abb4c52135b88d2bf44d0dc9073552abf Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 25 May 2015 20:44:16 -0400 Subject: [PATCH 01/46] test: add an extra loop for slow udev initialization On slower machines, e.g. VMs, udev isn't fast enough to get the properties set up by the time we're trying to get the device going. This fails when we try to add the device with libinput_path_add_device(). We know that all litest devices will have ID_INPUT set, so check for that before we continue. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/litest.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test/litest.c b/test/litest.c index 8221a7a2..96ea4ec7 100644 --- a/test/litest.c +++ b/test/litest.c @@ -977,6 +977,32 @@ litest_restore_log_handler(struct libinput *libinput) libinput_log_set_handler(libinput, litest_log_handler); } +static inline void +litest_wait_for_udev(int fd) +{ + struct udev *udev; + struct udev_device *device; + struct stat st; + int loop_count = 0; + + litest_assert_int_ge(fstat(fd, &st), 0); + + udev = udev_new(); + device = udev_device_new_from_devnum(udev, 'c', st.st_rdev); + litest_assert_ptr_notnull(device); + while (device && !udev_device_get_property_value(device, "ID_INPUT")) { + loop_count++; + litest_assert_int_lt(loop_count, 300); + + udev_device_unref(device); + msleep(2); + device = udev_device_new_from_devnum(udev, 'c', st.st_rdev); + } + + udev_device_unref(device); + udev_unref(udev); +} + struct litest_device * litest_add_device_with_overrides(struct libinput *libinput, enum litest_device_type which, @@ -1004,6 +1030,8 @@ litest_add_device_with_overrides(struct libinput *libinput, rc = libevdev_new_from_fd(fd, &d->evdev); litest_assert_int_eq(rc, 0); + litest_wait_for_udev(fd); + d->libinput = libinput; d->libinput_device = libinput_path_add_device(d->libinput, path); litest_assert(d->libinput_device != NULL); From fbb37a3c5eefd02e36bc00a18feead5d7e1ef78e Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 25 May 2015 10:07:51 +1000 Subject: [PATCH 02/46] touchpad: move disable-while-typing into its own struct Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede Tested-by: Benjamin Tissoires --- src/evdev-mt-touchpad.c | 34 +++++++++++++++++----------------- src/evdev-mt-touchpad.h | 4 +++- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 409d81e9..2ae6f60a 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -489,7 +489,7 @@ tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) struct device_float_coords delta; int dirs; - if (tp->sendevents.keyboard_active && + if (tp->dwt.keyboard_active && t->state == TOUCH_BEGIN) { t->palm.state = PALM_TYPING; t->palm.time = time; @@ -717,7 +717,7 @@ tp_post_events(struct tp_dispatch *tp, uint64_t time) if (filter_motion || tp->sendevents.trackpoint_active || - tp->sendevents.keyboard_active) { + tp->dwt.keyboard_active) { tp_edge_scroll_stop_events(tp, time); tp_gesture_stop(tp, time); return; @@ -767,15 +767,15 @@ static void tp_remove_sendevents(struct tp_dispatch *tp) { libinput_timer_cancel(&tp->sendevents.trackpoint_timer); - libinput_timer_cancel(&tp->sendevents.keyboard_timer); + libinput_timer_cancel(&tp->dwt.keyboard_timer); if (tp->buttons.trackpoint) libinput_device_remove_event_listener( &tp->sendevents.trackpoint_listener); - if (tp->sendevents.keyboard) + if (tp->dwt.keyboard) libinput_device_remove_event_listener( - &tp->sendevents.keyboard_listener); + &tp->dwt.keyboard_listener); } static void @@ -912,7 +912,7 @@ tp_keyboard_timeout(uint64_t now, void *data) struct tp_dispatch *tp = data; tp_tap_resume(tp, now); - tp->sendevents.keyboard_active = false; + tp->dwt.keyboard_active = false; } static void @@ -947,17 +947,17 @@ tp_keyboard_event(uint64_t time, struct libinput_event *event, void *data) break; } - if (!tp->sendevents.keyboard_active) { + if (!tp->dwt.keyboard_active) { tp_edge_scroll_stop_events(tp, time); tp_gesture_stop(tp, time); tp_tap_suspend(tp, time); - tp->sendevents.keyboard_active = true; + tp->dwt.keyboard_active = true; timeout = DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_1; } else { timeout = DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_2; } - libinput_timer_set(&tp->sendevents.keyboard_timer, + libinput_timer_set(&tp->dwt.keyboard_timer, time + timeout); } @@ -989,12 +989,12 @@ tp_interface_device_added(struct evdev_device *device, kbd_is_internal = bus_tp != BUS_BLUETOOTH && bus_kbd == bus_tp; if (tp_is_internal && kbd_is_internal && - tp->sendevents.keyboard == NULL) { + tp->dwt.keyboard == NULL) { libinput_device_add_event_listener(&added_device->base, - &tp->sendevents.keyboard_listener, + &tp->dwt.keyboard_listener, tp_keyboard_event, tp); - tp->sendevents.keyboard = added_device; - tp->sendevents.keyboard_active = false; + tp->dwt.keyboard = added_device; + tp->dwt.keyboard_active = false; } if (tp->sendevents.current_mode != @@ -1023,10 +1023,10 @@ tp_interface_device_removed(struct evdev_device *device, tp->buttons.trackpoint = NULL; } - if (removed_device == tp->sendevents.keyboard) { + if (removed_device == tp->dwt.keyboard) { libinput_device_remove_event_listener( - &tp->sendevents.keyboard_listener); - tp->sendevents.keyboard = NULL; + &tp->dwt.keyboard_listener); + tp->dwt.keyboard = NULL; } if (tp->sendevents.current_mode != @@ -1334,7 +1334,7 @@ tp_init_sendevents(struct tp_dispatch *tp, tp->device->base.seat->libinput, tp_trackpoint_timeout, tp); - libinput_timer_init(&tp->sendevents.keyboard_timer, + libinput_timer_init(&tp->dwt.keyboard_timer, tp->device->base.seat->libinput, tp_keyboard_timeout, tp); return 0; diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 3d51a398..f6c5aff0 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -282,12 +282,14 @@ struct tp_dispatch { bool trackpoint_active; struct libinput_event_listener trackpoint_listener; struct libinput_timer trackpoint_timer; + } sendevents; + struct { bool keyboard_active; struct libinput_event_listener keyboard_listener; struct libinput_timer keyboard_timer; struct evdev_device *keyboard; - } sendevents; + } dwt; }; #define tp_for_each_touch(_tp, _t) \ From 7248ffbd6615ffd4f6fbb60c1fae9ff4ee126bb1 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 25 May 2015 08:48:25 +1000 Subject: [PATCH 03/46] touchpad: extend the key blacklist for disable-while-typing Alt-tab should not trigger the disable-while-typing timeout, likewise with the F-keys, multimedia keys, the windows and menu key, etc. https://bugs.freedesktop.org/show_bug.cgi?id=90613 Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede Tested-by: Benjamin Tissoires --- src/evdev-mt-touchpad.c | 45 ++++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 2ae6f60a..6bd8d1d9 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -915,6 +915,37 @@ tp_keyboard_timeout(uint64_t now, void *data) tp->dwt.keyboard_active = false; } +static inline bool +tp_key_ignore_for_dwt(unsigned int keycode) +{ + switch (keycode) { + /* Ignore modifiers to be responsive to ctrl-click, alt-tab, etc. */ + case KEY_LEFTCTRL: + case KEY_RIGHTCTRL: + case KEY_LEFTALT: + case KEY_RIGHTALT: + case KEY_LEFTSHIFT: + case KEY_RIGHTSHIFT: + case KEY_FN: + case KEY_CAPSLOCK: + case KEY_TAB: + case KEY_COMPOSE: + case KEY_RIGHTMETA: + case KEY_LEFTMETA: + return true; + default: + break; + } + + /* Ignore keys not part of the "typewriter set", i.e. F-keys, + * multimedia keys, numpad, etc. + */ + if (keycode >= KEY_F1) + return true; + + return false; +} + static void tp_keyboard_event(uint64_t time, struct libinput_event *event, void *data) { @@ -934,18 +965,8 @@ tp_keyboard_event(uint64_t time, struct libinput_event *event, void *data) /* modifier keys don't trigger disable-while-typing so things like * ctrl+zoom or ctrl+click are possible */ - switch (libinput_event_keyboard_get_key(kbdev)) { - case KEY_LEFTCTRL: - case KEY_RIGHTCTRL: - case KEY_LEFTALT: - case KEY_RIGHTALT: - case KEY_LEFTSHIFT: - case KEY_RIGHTSHIFT: - case KEY_FN: - return; - default: - break; - } + if (tp_key_ignore_for_dwt(libinput_event_keyboard_get_key(kbdev))) + return; if (!tp->dwt.keyboard_active) { tp_edge_scroll_stop_events(tp, time); From 08fbfe52d21e9b1f5de1880d696a83d81fd42cdd Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 22 May 2015 15:16:31 +1000 Subject: [PATCH 04/46] touchpad: add helper function to get from tp to the libinput context Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede Tested-by: Benjamin Tissoires --- src/evdev-mt-touchpad-buttons.c | 8 ++++---- src/evdev-mt-touchpad-edge-scroll.c | 12 ++++++------ src/evdev-mt-touchpad-tap.c | 10 +++++----- src/evdev-mt-touchpad.c | 8 ++++---- src/evdev-mt-touchpad.h | 6 ++++++ 5 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index 43e983b2..09ea6d7a 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -392,7 +392,7 @@ tp_button_handle_event(struct tp_dispatch *tp, enum button_event event, uint64_t time) { - struct libinput *libinput = tp->device->base.seat->libinput; + struct libinput *libinput = tp_libinput_context(tp); enum button_state current = t->button.state; switch(t->button.state) { @@ -478,7 +478,7 @@ tp_process_button(struct tp_dispatch *tp, const struct input_event *e, uint64_t time) { - struct libinput *libinput = tp->device->base.seat->libinput; + struct libinput *libinput = tp_libinput_context(tp); uint32_t mask = 1 << (e->code - BTN_LEFT); /* Ignore other buttons on clickpads */ @@ -680,7 +680,7 @@ int tp_init_buttons(struct tp_dispatch *tp, struct evdev_device *device) { - struct libinput *libinput = tp->device->base.seat->libinput; + struct libinput *libinput = tp_libinput_context(tp); struct tp_touch *t; int width, height; double diagonal; @@ -731,7 +731,7 @@ tp_init_buttons(struct tp_dispatch *tp, tp_for_each_touch(tp, t) { t->button.state = BUTTON_STATE_NONE; libinput_timer_init(&t->button.timer, - tp->device->base.seat->libinput, + tp_libinput_context(tp), tp_button_handle_timeout, t); } diff --git a/src/evdev-mt-touchpad-edge-scroll.c b/src/evdev-mt-touchpad-edge-scroll.c index 369fdedc..9bf3f0c0 100644 --- a/src/evdev-mt-touchpad-edge-scroll.c +++ b/src/evdev-mt-touchpad-edge-scroll.c @@ -121,7 +121,7 @@ tp_edge_scroll_handle_none(struct tp_dispatch *tp, struct tp_touch *t, enum scroll_event event) { - struct libinput *libinput = tp->device->base.seat->libinput; + struct libinput *libinput = tp_libinput_context(tp); switch (event) { case SCROLL_EVENT_TOUCH: @@ -149,7 +149,7 @@ tp_edge_scroll_handle_edge_new(struct tp_dispatch *tp, struct tp_touch *t, enum scroll_event event) { - struct libinput *libinput = tp->device->base.seat->libinput; + struct libinput *libinput = tp_libinput_context(tp); switch (event) { case SCROLL_EVENT_TOUCH: @@ -178,7 +178,7 @@ tp_edge_scroll_handle_edge(struct tp_dispatch *tp, struct tp_touch *t, enum scroll_event event) { - struct libinput *libinput = tp->device->base.seat->libinput; + struct libinput *libinput = tp_libinput_context(tp); switch (event) { case SCROLL_EVENT_TOUCH: @@ -209,7 +209,7 @@ tp_edge_scroll_handle_area(struct tp_dispatch *tp, struct tp_touch *t, enum scroll_event event) { - struct libinput *libinput = tp->device->base.seat->libinput; + struct libinput *libinput = tp_libinput_context(tp); switch (event) { case SCROLL_EVENT_TOUCH: @@ -232,7 +232,7 @@ tp_edge_scroll_handle_event(struct tp_dispatch *tp, struct tp_touch *t, enum scroll_event event) { - struct libinput *libinput = tp->device->base.seat->libinput; + struct libinput *libinput = tp_libinput_context(tp); enum tp_edge_scroll_touch_state current = t->scroll.edge_state; switch (current) { @@ -301,7 +301,7 @@ tp_edge_scroll_init(struct tp_dispatch *tp, struct evdev_device *device) tp_for_each_touch(tp, t) { t->scroll.direction = -1; libinput_timer_init(&t->scroll.timer, - device->base.seat->libinput, + tp_libinput_context(tp), tp_edge_scroll_handle_timeout, t); } diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index fb8c9e43..bb7c8945 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -147,7 +147,7 @@ tp_tap_idle_handle_event(struct tp_dispatch *tp, struct tp_touch *t, enum tap_event event, uint64_t time) { - struct libinput *libinput = tp->device->base.seat->libinput; + struct libinput *libinput = tp_libinput_context(tp); switch (event) { case TAP_EVENT_TOUCH: @@ -223,7 +223,7 @@ tp_tap_tapped_handle_event(struct tp_dispatch *tp, struct tp_touch *t, enum tap_event event, uint64_t time) { - struct libinput *libinput = tp->device->base.seat->libinput; + struct libinput *libinput = tp_libinput_context(tp); switch (event) { case TAP_EVENT_MOTION: @@ -483,7 +483,7 @@ tp_tap_multitap_handle_event(struct tp_dispatch *tp, struct tp_touch *t, enum tap_event event, uint64_t time) { - struct libinput *libinput = tp->device->base.seat->libinput; + struct libinput *libinput = tp_libinput_context(tp); switch (event) { case TAP_EVENT_RELEASE: @@ -576,7 +576,7 @@ tp_tap_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time) { - struct libinput *libinput = tp->device->base.seat->libinput; + struct libinput *libinput = tp_libinput_context(tp); enum tp_tap_state current; current = tp->tap.state; @@ -857,7 +857,7 @@ tp_init_tap(struct tp_dispatch *tp) tp->tap.enabled = tp_tap_default(tp->device); libinput_timer_init(&tp->tap.timer, - tp->device->base.seat->libinput, + tp_libinput_context(tp), tp_tap_handle_timeout, tp); return 0; diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 6bd8d1d9..1ea3bcec 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -810,7 +810,7 @@ tp_release_fake_touches(struct tp_dispatch *tp) static void tp_clear_state(struct tp_dispatch *tp) { - uint64_t now = libinput_now(tp->device->base.seat->libinput); + uint64_t now = libinput_now(tp_libinput_context(tp)); struct tp_touch *t; /* Unroll the touchpad state. @@ -1251,7 +1251,7 @@ tp_scroll_config_scroll_method_set_method(struct libinput_device *device, { struct evdev_device *evdev = (struct evdev_device*)device; struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch; - uint64_t time = libinput_now(device->seat->libinput); + uint64_t time = libinput_now(tp_libinput_context(tp)); if (method == tp->scroll.method) return LIBINPUT_CONFIG_STATUS_SUCCESS; @@ -1352,11 +1352,11 @@ tp_init_sendevents(struct tp_dispatch *tp, struct evdev_device *device) { libinput_timer_init(&tp->sendevents.trackpoint_timer, - tp->device->base.seat->libinput, + tp_libinput_context(tp), tp_trackpoint_timeout, tp); libinput_timer_init(&tp->dwt.keyboard_timer, - tp->device->base.seat->libinput, + tp_libinput_context(tp), tp_keyboard_timeout, tp); return 0; } diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index f6c5aff0..1b644e0b 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -295,6 +295,12 @@ struct tp_dispatch { #define tp_for_each_touch(_tp, _t) \ for (unsigned int _i = 0; _i < (_tp)->ntouches && (_t = &(_tp)->touches[_i]); _i++) +static inline struct libinput* +tp_libinput_context(struct tp_dispatch *tp) +{ + return tp->device->base.seat->libinput; +} + static inline struct normalized_coords tp_normalize_delta(struct tp_dispatch *tp, struct device_float_coords delta) { From cfdb83b30529e622af9dd7522edc701ee0a585ee Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 22 May 2015 15:51:18 +1000 Subject: [PATCH 05/46] touchpad: only check keyboards for disable-while-typing The keyboard test is a simple one, if we have the first row of alphabetic keys, we assume it's a full keyboard. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede Tested-by: Benjamin Tissoires --- src/evdev-mt-touchpad.c | 22 ++++++++++++---------- src/evdev.c | 20 ++++++++++++++++++++ src/evdev.h | 1 + 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 1ea3bcec..7a5c3638 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -1006,16 +1006,18 @@ tp_interface_device_added(struct evdev_device *device, tp_trackpoint_event, tp); } - /* FIXME: detect external keyboard better */ - kbd_is_internal = bus_tp != BUS_BLUETOOTH && - bus_kbd == bus_tp; - if (tp_is_internal && kbd_is_internal && - tp->dwt.keyboard == NULL) { - libinput_device_add_event_listener(&added_device->base, - &tp->dwt.keyboard_listener, - tp_keyboard_event, tp); - tp->dwt.keyboard = added_device; - tp->dwt.keyboard_active = false; + if (added_device->tags & EVDEV_TAG_KEYBOARD) { + /* FIXME: detect external keyboard better */ + kbd_is_internal = bus_tp != BUS_BLUETOOTH && + bus_kbd == bus_tp; + if (tp_is_internal && kbd_is_internal && + tp->dwt.keyboard == NULL) { + libinput_device_add_event_listener(&added_device->base, + &tp->dwt.keyboard_listener, + tp_keyboard_event, tp); + tp->dwt.keyboard = added_device; + tp->dwt.keyboard_active = false; + } } if (tp->sendevents.current_mode != diff --git a/src/evdev.c b/src/evdev.c index c6c8102b..c552cb04 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -733,6 +733,25 @@ evdev_tag_trackpoint(struct evdev_device *device, device->tags |= EVDEV_TAG_TRACKPOINT; } +static void +evdev_tag_keyboard(struct evdev_device *device, + struct udev_device *udev_device) +{ + int code; + + if (!libevdev_has_event_type(device->evdev, EV_KEY)) + return; + + for (code = KEY_Q; code <= KEY_P; code++) { + if (!libevdev_has_event_code(device->evdev, + EV_KEY, + code)) + return; + } + + device->tags |= EVDEV_TAG_KEYBOARD; +} + static void fallback_process(struct evdev_dispatch *dispatch, struct evdev_device *device, @@ -831,6 +850,7 @@ fallback_tag_device(struct evdev_device *device, { evdev_tag_external_mouse(device, udev_device); evdev_tag_trackpoint(device, udev_device); + evdev_tag_keyboard(device, udev_device); } static int diff --git a/src/evdev.h b/src/evdev.h index 22e6b019..337097b5 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -68,6 +68,7 @@ enum evdev_device_tags { EVDEV_TAG_INTERNAL_TOUCHPAD = (1 << 1), EVDEV_TAG_TRACKPOINT = (1 << 2), EVDEV_TAG_TOUCHPAD_TRACKPOINT = (1 << 3), + EVDEV_TAG_KEYBOARD = (1 << 4), }; enum evdev_middlebutton_state { From 3a7264a03e0d1be400e9fa081aa86731977a6c31 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 22 May 2015 16:07:10 +1000 Subject: [PATCH 06/46] touchpad: be finer-grained about when to pair touchpads/keyboard for DWT Check a couple of easy yes/no definitives that cover most Lenovo laptops, and avoid false positives on Wacoms. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede Tested-by: Benjamin Tissoires --- src/evdev-mt-touchpad.c | 54 +++++++++++++++++++++++++++++------------ src/evdev-mt-touchpad.h | 2 -- src/libinput-util.h | 3 +++ test/device.c | 2 +- test/touchpad.c | 2 +- 5 files changed, 44 insertions(+), 19 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 7a5c3638..c908ead3 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -982,15 +982,43 @@ tp_keyboard_event(uint64_t time, struct libinput_event *event, void *data) time + timeout); } +static bool +tp_want_dwt(struct evdev_device *touchpad, + struct evdev_device *keyboard) +{ + unsigned int bus_tp = libevdev_get_id_bustype(touchpad->evdev), + bus_kbd = libevdev_get_id_bustype(keyboard->evdev); + + if (bus_tp == BUS_BLUETOOTH || bus_kbd == BUS_BLUETOOTH) + return false; + + /* evemu will set the right bus type */ + if (bus_tp == BUS_VIRTUAL || bus_kbd == BUS_VIRTUAL) + return false; + + /* If the touchpad is on serio, the keyboard is too, so ignore any + other devices */ + if (bus_tp == BUS_I8042 && bus_kbd != bus_tp) + return false; + + /* Wacom makes touchpads, but not internal ones */ + if (libevdev_get_id_vendor(touchpad->evdev) == VENDOR_ID_WACOM) + return false; + + /* everything else we don't really know, so we have to assume + they go together */ + + return true; +} + static void tp_interface_device_added(struct evdev_device *device, struct evdev_device *added_device) { struct tp_dispatch *tp = (struct tp_dispatch*)device->dispatch; unsigned int bus_tp = libevdev_get_id_bustype(device->evdev), - bus_trp = libevdev_get_id_bustype(added_device->evdev), - bus_kbd = libevdev_get_id_bustype(added_device->evdev); - bool tp_is_internal, trp_is_internal, kbd_is_internal; + bus_trp = libevdev_get_id_bustype(added_device->evdev); + bool tp_is_internal, trp_is_internal; tp_is_internal = bus_tp != BUS_USB && bus_tp != BUS_BLUETOOTH; trp_is_internal = bus_trp != BUS_USB && bus_trp != BUS_BLUETOOTH; @@ -1006,18 +1034,14 @@ tp_interface_device_added(struct evdev_device *device, tp_trackpoint_event, tp); } - if (added_device->tags & EVDEV_TAG_KEYBOARD) { - /* FIXME: detect external keyboard better */ - kbd_is_internal = bus_tp != BUS_BLUETOOTH && - bus_kbd == bus_tp; - if (tp_is_internal && kbd_is_internal && - tp->dwt.keyboard == NULL) { - libinput_device_add_event_listener(&added_device->base, - &tp->dwt.keyboard_listener, - tp_keyboard_event, tp); - tp->dwt.keyboard = added_device; - tp->dwt.keyboard_active = false; - } + if (added_device->tags & EVDEV_TAG_KEYBOARD && + tp->dwt.keyboard == NULL && + tp_want_dwt(device, added_device)) { + libinput_device_add_event_listener(&added_device->base, + &tp->dwt.keyboard_listener, + tp_keyboard_event, tp); + tp->dwt.keyboard = added_device; + tp->dwt.keyboard_active = false; } if (tp->sendevents.current_mode != diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 1b644e0b..9eeb5fbe 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -32,8 +32,6 @@ #define TOUCHPAD_HISTORY_LENGTH 4 #define TOUCHPAD_MIN_SAMPLES 4 -#define VENDOR_ID_APPLE 0x5ac - /* Convert mm to a distance normalized to DEFAULT_MOUSE_DPI */ #define TP_MM_TO_DPI_NORMALIZED(mm) (DEFAULT_MOUSE_DPI/25.4 * mm) diff --git a/src/libinput-util.h b/src/libinput-util.h index 1673551e..e2ea4ffc 100644 --- a/src/libinput-util.h +++ b/src/libinput-util.h @@ -30,6 +30,9 @@ #include "libinput.h" +#define VENDOR_ID_APPLE 0x5ac +#define VENDOR_ID_WACOM 0x56a + void set_logging_enabled(int enabled); diff --git a/test/device.c b/test/device.c index 0b589cc9..5156a3e8 100644 --- a/test/device.c +++ b/test/device.c @@ -69,7 +69,7 @@ START_TEST(device_sendevents_config_touchpad) expected = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED; /* The wacom devices in the test suite are external */ - if (libevdev_get_id_vendor(dev->evdev) != 0x56a) /* wacom */ + if (libevdev_get_id_vendor(dev->evdev) != VENDOR_ID_WACOM) expected |= LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE; diff --git a/test/touchpad.c b/test/touchpad.c index c8ecb322..7acea403 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -3200,7 +3200,7 @@ touchpad_has_palm_detect_size(struct litest_device *dev) double width, height; int rc; - if (libinput_device_get_id_vendor(dev->libinput_device) == 0x5ac) /* Apple */ + if (libinput_device_get_id_vendor(dev->libinput_device) == ID_VENDOR_APPLE) return 1; rc = libinput_device_get_size(dev->libinput_device, &width, &height); From 00c75a266751b7c65fcdd458954b63411c40fa41 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 21 May 2015 16:58:27 +1000 Subject: [PATCH 07/46] test: add disable-while-typing tests Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede Tested-by: Benjamin Tissoires --- test/litest.c | 12 ++ test/litest.h | 2 + test/touchpad.c | 302 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 316 insertions(+) diff --git a/test/litest.c b/test/litest.c index 96ea4ec7..1d3f0ce2 100644 --- a/test/litest.c +++ b/test/litest.c @@ -2016,6 +2016,18 @@ litest_timeout_middlebutton(void) msleep(70); } +void +litest_timeout_dwt_short(void) +{ + msleep(220); +} + +void +litest_timeout_dwt_long(void) +{ + msleep(520); +} + void litest_push_event_frame(struct litest_device *dev) { diff --git a/test/litest.h b/test/litest.h index 89f7677f..1476f4a8 100644 --- a/test/litest.h +++ b/test/litest.h @@ -375,6 +375,8 @@ void litest_timeout_buttonscroll(void); void litest_timeout_edgescroll(void); void litest_timeout_finger_switch(void); void litest_timeout_middlebutton(void); +void litest_timeout_dwt_short(void); +void litest_timeout_dwt_long(void); void litest_push_event_frame(struct litest_device *dev); void litest_pop_event_frame(struct litest_device *dev); diff --git a/test/touchpad.c b/test/touchpad.c index 7acea403..f8ee6e19 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -4590,6 +4590,299 @@ START_TEST(touchpad_initial_state) } END_TEST +static inline bool +has_disable_while_typing(struct litest_device *device) +{ + if (libevdev_get_id_vendor(device->evdev) == VENDOR_ID_WACOM) + return false; + + return true; +} + +START_TEST(touchpad_dwt) +{ + struct litest_device *touchpad = litest_current_device(); + struct litest_device *keyboard; + struct libinput *li = touchpad->libinput; + + if (!has_disable_while_typing(touchpad)) + return; + + keyboard = litest_add_device(li, LITEST_KEYBOARD); + libinput_device_config_tap_set_enabled(touchpad->libinput_device, + LIBINPUT_CONFIG_TAP_DISABLED); + litest_drain_events(li); + + litest_keyboard_key(keyboard, KEY_A, true); + litest_keyboard_key(keyboard, KEY_A, false); + libinput_dispatch(li); + litest_touch_down(touchpad, 0, 50, 50); + litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 10, 1); + litest_touch_up(touchpad, 0); + + litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY); + + /* within timeout - no events */ + litest_touch_down(touchpad, 0, 50, 50); + litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 10, 1); + litest_touch_up(touchpad, 0); + litest_assert_empty_queue(li); + + litest_timeout_dwt_short(); + libinput_dispatch(li); + + /* after timeout - motion events*/ + litest_touch_down(touchpad, 0, 50, 50); + litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 10, 1); + litest_touch_up(touchpad, 0); + + litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); + + litest_delete_device(keyboard); +} +END_TEST + +START_TEST(touchpad_dwt_touch_hold) +{ + struct litest_device *touchpad = litest_current_device(); + struct litest_device *keyboard; + struct libinput *li = touchpad->libinput; + + if (!has_disable_while_typing(touchpad)) + return; + + keyboard = litest_add_device(li, LITEST_KEYBOARD); + libinput_device_config_tap_set_enabled(touchpad->libinput_device, + LIBINPUT_CONFIG_TAP_DISABLED); + litest_drain_events(li); + + litest_keyboard_key(keyboard, KEY_A, true); + libinput_dispatch(li); + litest_touch_down(touchpad, 0, 50, 50); + litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 5, 1); + + litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY); + + /* touch still down - no events */ + litest_keyboard_key(keyboard, KEY_A, false); + libinput_dispatch(li); + litest_touch_move_to(touchpad, 0, 70, 50, 30, 50, 5, 1); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY); + + /* touch still down - no events */ + litest_timeout_dwt_short(); + libinput_dispatch(li); + litest_touch_move_to(touchpad, 0, 30, 50, 50, 50, 5, 1); + litest_touch_up(touchpad, 0); + litest_assert_empty_queue(li); + + litest_delete_device(keyboard); +} +END_TEST + +START_TEST(touchpad_dwt_key_hold) +{ + struct litest_device *touchpad = litest_current_device(); + struct litest_device *keyboard; + struct libinput *li = touchpad->libinput; + + if (!has_disable_while_typing(touchpad)) + return; + + keyboard = litest_add_device(li, LITEST_KEYBOARD); + libinput_device_config_tap_set_enabled(touchpad->libinput_device, + LIBINPUT_CONFIG_TAP_DISABLED); + litest_drain_events(li); + + litest_keyboard_key(keyboard, KEY_A, true); + libinput_dispatch(li); + litest_touch_down(touchpad, 0, 50, 50); + litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 5, 1); + litest_touch_up(touchpad, 0); + + litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY); + litest_keyboard_key(keyboard, KEY_A, false); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY); + + litest_delete_device(keyboard); +} +END_TEST + +START_TEST(touchpad_dwt_type) +{ + struct litest_device *touchpad = litest_current_device(); + struct litest_device *keyboard; + struct libinput *li = touchpad->libinput; + int i; + + if (!has_disable_while_typing(touchpad)) + return; + + keyboard = litest_add_device(li, LITEST_KEYBOARD); + libinput_device_config_tap_set_enabled(touchpad->libinput_device, + LIBINPUT_CONFIG_TAP_DISABLED); + litest_drain_events(li); + + for (i = 0; i < 5; i++) { + litest_keyboard_key(keyboard, KEY_A, true); + litest_keyboard_key(keyboard, KEY_A, false); + libinput_dispatch(li); + } + + litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY); + + litest_touch_down(touchpad, 0, 50, 50); + litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 5, 1); + litest_touch_up(touchpad, 0); + litest_assert_empty_queue(li); + + litest_timeout_dwt_long(); + libinput_dispatch(li); + litest_touch_down(touchpad, 0, 50, 50); + litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 5, 1); + litest_touch_up(touchpad, 0); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); + + litest_delete_device(keyboard); +} +END_TEST + +START_TEST(touchpad_dwt_type_short_timeout) +{ + struct litest_device *touchpad = litest_current_device(); + struct litest_device *keyboard; + struct libinput *li = touchpad->libinput; + int i; + + if (!has_disable_while_typing(touchpad)) + return; + + keyboard = litest_add_device(li, LITEST_KEYBOARD); + libinput_device_config_tap_set_enabled(touchpad->libinput_device, + LIBINPUT_CONFIG_TAP_DISABLED); + litest_drain_events(li); + + for (i = 0; i < 5; i++) { + litest_keyboard_key(keyboard, KEY_A, true); + litest_keyboard_key(keyboard, KEY_A, false); + libinput_dispatch(li); + } + + litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY); + + litest_touch_down(touchpad, 0, 50, 50); + litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 5, 1); + litest_touch_up(touchpad, 0); + litest_assert_empty_queue(li); + + litest_timeout_dwt_short(); + libinput_dispatch(li); + litest_touch_down(touchpad, 0, 50, 50); + litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 5, 1); + litest_touch_up(touchpad, 0); + litest_assert_empty_queue(li); + + litest_delete_device(keyboard); +} +END_TEST + +START_TEST(touchpad_dwt_tap) +{ + struct litest_device *touchpad = litest_current_device(); + struct litest_device *keyboard; + struct libinput *li = touchpad->libinput; + + if (!has_disable_while_typing(touchpad)) + return; + + keyboard = litest_add_device(li, LITEST_KEYBOARD); + libinput_device_config_tap_set_enabled(touchpad->libinput_device, + LIBINPUT_CONFIG_TAP_ENABLED); + litest_drain_events(li); + + litest_keyboard_key(keyboard, KEY_A, true); + libinput_dispatch(li); + litest_touch_down(touchpad, 0, 50, 50); + litest_touch_up(touchpad, 0); + + litest_keyboard_key(keyboard, KEY_A, false); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY); + + litest_timeout_dwt_short(); + litest_touch_down(touchpad, 0, 50, 50); + litest_touch_up(touchpad, 0); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_BUTTON); + + litest_delete_device(keyboard); +} +END_TEST + +START_TEST(touchpad_dwt_tap_drag) +{ + struct litest_device *touchpad = litest_current_device(); + struct litest_device *keyboard; + struct libinput *li = touchpad->libinput; + + if (!has_disable_while_typing(touchpad)) + return; + + keyboard = litest_add_device(li, LITEST_KEYBOARD); + libinput_device_config_tap_set_enabled(touchpad->libinput_device, + LIBINPUT_CONFIG_TAP_ENABLED); + litest_drain_events(li); + + litest_keyboard_key(keyboard, KEY_A, true); + libinput_dispatch(li); + litest_touch_down(touchpad, 0, 50, 50); + litest_touch_up(touchpad, 0); + litest_touch_down(touchpad, 0, 50, 50); + litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 5, 1); + + litest_keyboard_key(keyboard, KEY_A, false); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY); + + litest_timeout_dwt_short(); + libinput_dispatch(li); + litest_touch_move_to(touchpad, 0, 70, 50, 50, 50, 5, 1); + litest_touch_up(touchpad, 0); + litest_assert_empty_queue(li); + + litest_delete_device(keyboard); +} +END_TEST + +START_TEST(touchpad_dwt_click) +{ + struct litest_device *touchpad = litest_current_device(); + struct litest_device *keyboard; + struct libinput *li = touchpad->libinput; + + if (!has_disable_while_typing(touchpad)) + return; + + keyboard = litest_add_device(li, LITEST_KEYBOARD); + libinput_device_config_tap_set_enabled(touchpad->libinput_device, + LIBINPUT_CONFIG_TAP_DISABLED); + litest_drain_events(li); + + litest_keyboard_key(keyboard, KEY_A, true); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY); + + litest_touch_down(touchpad, 0, 50, 50); + litest_button_click(touchpad, BTN_LEFT, true); + litest_button_click(touchpad, BTN_LEFT, false); + libinput_dispatch(li); + litest_touch_up(touchpad, 0); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_BUTTON); + + litest_keyboard_key(keyboard, KEY_A, false); + + litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY); + + litest_delete_device(keyboard); +} +END_TEST + void litest_setup_tests(void) { @@ -4738,4 +5031,13 @@ litest_setup_tests(void) litest_add_for_device("touchpad:trackpoint", touchpad_trackpoint_no_trackpoint, LITEST_SYNAPTICS_TRACKPOINT_BUTTONS); litest_add_ranged("touchpad:state", touchpad_initial_state, LITEST_TOUCHPAD, LITEST_ANY, &axis_range); + + litest_add("touchpad:dwt", touchpad_dwt, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("touchpad:dwt", touchpad_dwt_touch_hold, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("touchpad:dwt", touchpad_dwt_key_hold, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("touchpad:dwt", touchpad_dwt_type, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("touchpad:dwt", touchpad_dwt_type_short_timeout, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("touchpad:dwt", touchpad_dwt_tap, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("touchpad:dwt", touchpad_dwt_tap_drag, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("touchpad:dwt", touchpad_dwt_click, LITEST_TOUCHPAD, LITEST_ANY); } From ff8f44a0c634fd452e89876c178e366bfb91cba5 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 21 May 2015 13:30:24 +1000 Subject: [PATCH 08/46] touchpad: split disable-while-typing handling into a helper function Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede Tested-by: Benjamin Tissoires --- src/evdev-mt-touchpad.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index c908ead3..0b644d9a 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -481,6 +481,22 @@ tp_palm_tap_is_palm(struct tp_dispatch *tp, struct tp_touch *t) return false; } +static int +tp_palm_detect_dwt(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) +{ + if (!tp->dwt.keyboard_active) + return 0; + + if (t->state == TOUCH_BEGIN) { + t->palm.state = PALM_TYPING; + t->palm.time = time; + t->palm.first = t->point; + return 1; + } + + return 0; +} + static void tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) { @@ -489,13 +505,8 @@ tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) struct device_float_coords delta; int dirs; - if (tp->dwt.keyboard_active && - t->state == TOUCH_BEGIN) { - t->palm.state = PALM_TYPING; - t->palm.time = time; - t->palm.first = t->point; - return; - } + if (tp_palm_detect_dwt(tp, t, time)) + return; /* If labelled a touch as palm, we unlabel as palm when we move out of the palm edge zone within the timeout, provided From 17d3cf414a85ed4b1803913c345bfddc138043e9 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 22 May 2015 15:14:04 +1000 Subject: [PATCH 09/46] touchpad: add palm state debugging Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede Tested-by: Benjamin Tissoires --- src/evdev-mt-touchpad.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 0b644d9a..994effec 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -475,8 +475,11 @@ tp_palm_tap_is_palm(struct tp_dispatch *tp, struct tp_touch *t) /* We're inside the left/right palm edge and in the northern half of * the touchpad - this tap is a palm */ - if (t->point.y < tp->palm.vert_center) + if (t->point.y < tp->palm.vert_center) { + log_debug(tp_libinput_context(tp), + "palm: palm-tap detected\n"); return true; + } return false; } @@ -506,7 +509,7 @@ tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) int dirs; if (tp_palm_detect_dwt(tp, t, time)) - return; + goto out; /* If labelled a touch as palm, we unlabel as palm when we move out of the palm edge zone within the timeout, provided @@ -520,6 +523,8 @@ tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) tp_normalize_delta(tp, delta)); if ((dirs & DIRECTIONS) && !(dirs & ~DIRECTIONS)) { t->palm.state = PALM_NONE; + log_debug(tp_libinput_context(tp), + "palm: touch released, out of edge zone\n"); } } return; @@ -541,6 +546,11 @@ tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) t->palm.state = PALM_EDGE; t->palm.time = time; t->palm.first = t->point; + +out: + log_debug(tp_libinput_context(tp), + "palm: palm detected (%s)\n", + t->palm.state == PALM_EDGE ? "edge" : "typing"); } static void @@ -923,7 +933,10 @@ tp_keyboard_timeout(uint64_t now, void *data) struct tp_dispatch *tp = data; tp_tap_resume(tp, now); + tp->dwt.keyboard_active = false; + + log_debug(tp_libinput_context(tp), "palm: keyboard timeout\n"); } static inline bool @@ -1048,6 +1061,11 @@ tp_interface_device_added(struct evdev_device *device, if (added_device->tags & EVDEV_TAG_KEYBOARD && tp->dwt.keyboard == NULL && tp_want_dwt(device, added_device)) { + log_debug(tp_libinput_context(tp), + "palm: dwt activated with %s<->%s\n", + device->devname, + added_device->devname); + libinput_device_add_event_listener(&added_device->base, &tp->dwt.keyboard_listener, tp_keyboard_event, tp); From 6d0d36fd473ff410f41b61f7145b38c651163c82 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 25 May 2015 11:36:34 +1000 Subject: [PATCH 10/46] touchpad: reset the touch state when edge scrolling is stopped Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede Tested-by: Benjamin Tissoires --- src/evdev-mt-touchpad-edge-scroll.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/evdev-mt-touchpad-edge-scroll.c b/src/evdev-mt-touchpad-edge-scroll.c index 9bf3f0c0..f7eae9e9 100644 --- a/src/evdev-mt-touchpad-edge-scroll.c +++ b/src/evdev-mt-touchpad-edge-scroll.c @@ -438,6 +438,10 @@ tp_edge_scroll_stop_events(struct tp_dispatch *tp, uint64_t time) &zero, &zero_discrete); t->scroll.direction = -1; + /* reset touch to area state, avoids loading the + * state machine with special case handling */ + t->scroll.edge = EDGE_NONE; + t->scroll.edge_state = EDGE_SCROLL_TOUCH_STATE_AREA; } } } From f85a46b661096852c8d4b04aa0df0b7356e58648 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 25 May 2015 16:02:56 +1000 Subject: [PATCH 11/46] touchpad: don't enable edge palm detection on Wacom touchpads Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede Tested-by: Benjamin Tissoires --- src/evdev-mt-touchpad.c | 10 ++++++++-- test/touchpad.c | 6 +++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 994effec..db330ec0 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -1372,6 +1372,7 @@ tp_init_palmdetect(struct tp_dispatch *tp, struct evdev_device *device) { int width, height; + unsigned int vendor_id; tp->palm.right_edge = INT_MAX; tp->palm.left_edge = INT_MIN; @@ -1382,8 +1383,13 @@ tp_init_palmdetect(struct tp_dispatch *tp, height = abs(device->abs.absinfo_y->maximum - device->abs.absinfo_y->minimum); - /* Apple touchpads are always big enough to warrant palm detection */ - if (evdev_device_get_id_vendor(device) != VENDOR_ID_APPLE) { + vendor_id = evdev_device_get_id_vendor(device); + + /* Wacom doesn't have internal touchpads, + * Apple touchpads are always big enough to warrant palm detection */ + if (vendor_id == VENDOR_ID_WACOM) { + return 0; + } else if (vendor_id != VENDOR_ID_APPLE) { /* We don't know how big the touchpad is */ if (device->abs.absinfo_x->resolution == 1) return 0; diff --git a/test/touchpad.c b/test/touchpad.c index f8ee6e19..fc774396 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -3198,9 +3198,13 @@ static int touchpad_has_palm_detect_size(struct litest_device *dev) { double width, height; + unsigned int vendor; int rc; - if (libinput_device_get_id_vendor(dev->libinput_device) == ID_VENDOR_APPLE) + vendor = libinput_device_get_id_vendor(dev->libinput_device); + if (vendor == VENDOR_ID_WACOM) + return 0; + if (vendor == VENDOR_ID_APPLE) return 1; rc = libinput_device_get_size(dev->libinput_device, &width, &height); From 0dc058a0c754e61a6c1dd8edf5c11c7ca312e124 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 21 May 2015 16:32:42 +1000 Subject: [PATCH 12/46] touchpad: touches after the last key press can be released The current code labels a touch as palm if it started within the typing timeouts. To move the pointer even after the timeout expires, a user has to lift the finger which is quite annoying and different to the old synaptics driver behaviour (which had a simple on/off toggle on whether to let events through or not). Be smarter about this: if a touch starts _after_ the last key press event, release it for pointer motion once the timeout expires. Touches started before the last key press remain labelled as palms. This makes it possible to rest the palm on the touchpad while typing without getting interference but also provides a more responsive UI when moving from typing to using the touchpad normally. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede Tested-by: Benjamin Tissoires --- doc/palm-detection.dox | 5 +- src/evdev-mt-touchpad-edge-scroll.c | 3 + src/evdev-mt-touchpad.c | 24 ++++- src/evdev-mt-touchpad.h | 2 + test/touchpad.c | 144 +++++++++++++++++++++++++++- 5 files changed, 169 insertions(+), 9 deletions(-) diff --git a/doc/palm-detection.dox b/doc/palm-detection.dox index a03f9c14..d787455e 100644 --- a/doc/palm-detection.dox +++ b/doc/palm-detection.dox @@ -74,8 +74,9 @@ Notable behaviors of libinput's disable-while-typing feature: - Some keys do not trigger the timeout, specifically some modifier keys (Ctrl, Alt, Shift, and Fn). Actions such as Ctrl + click thus stay responsive. -- Touches started while the touchpad is disabled do not control the cursor, - it is thus possible to rest the palm on the touchpad while typing. +- Touches started while typing do not control the cursor even after typing + has stopped, it is thus possible to rest the palm on the touchpad while + typing. - Physical buttons work even while the touchpad is disabled. This includes software-emulated buttons. diff --git a/src/evdev-mt-touchpad-edge-scroll.c b/src/evdev-mt-touchpad-edge-scroll.c index f7eae9e9..8a4d892f 100644 --- a/src/evdev-mt-touchpad-edge-scroll.c +++ b/src/evdev-mt-touchpad-edge-scroll.c @@ -361,6 +361,9 @@ tp_edge_scroll_post_events(struct tp_dispatch *tp, uint64_t time) if (!t->dirty) continue; + if (t->palm.state != PALM_NONE) + continue; + switch (t->scroll.edge) { case EDGE_NONE: if (t->scroll.direction != -1) { diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index db330ec0..e1303637 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -237,6 +237,7 @@ tp_end_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) t->state = TOUCH_END; t->pinned.is_pinned = false; t->millis = time; + t->palm.time = 0; assert(tp->nfingers_down >= 1); tp->nfingers_down--; tp->queued |= TOUCHPAD_EVENT_MOTION; @@ -487,14 +488,28 @@ tp_palm_tap_is_palm(struct tp_dispatch *tp, struct tp_touch *t) static int tp_palm_detect_dwt(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) { - if (!tp->dwt.keyboard_active) - return 0; - - if (t->state == TOUCH_BEGIN) { + if (tp->dwt.keyboard_active && + t->state == TOUCH_BEGIN) { t->palm.state = PALM_TYPING; t->palm.time = time; t->palm.first = t->point; return 1; + } else if (!tp->dwt.keyboard_active && + t->state == TOUCH_UPDATE && + t->palm.state == PALM_TYPING) + { + /* If a touch has started before the first or after the last + key press, release it on timeout. Benefit: a palm rested + while typing on the touchpad will be ignored, but a touch + started once we stop typing will be able to control the + pointer (alas not tap, etc.). + */ + if (t->palm.time == 0 || + t->palm.time > tp->dwt.keyboard_last_press_time) { + t->palm.state = PALM_NONE; + log_debug(tp_libinput_context(tp), + "palm: touch released, timeout after typing\n"); + } } return 0; @@ -1002,6 +1017,7 @@ tp_keyboard_event(uint64_t time, struct libinput_event *event, void *data) timeout = DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_2; } + tp->dwt.keyboard_last_press_time = time; libinput_timer_set(&tp->dwt.keyboard_timer, time + timeout); } diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 9eeb5fbe..bced2b16 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -287,6 +287,8 @@ struct tp_dispatch { struct libinput_event_listener keyboard_listener; struct libinput_timer keyboard_timer; struct evdev_device *keyboard; + + uint64_t keyboard_last_press_time; } dwt; }; diff --git a/test/touchpad.c b/test/touchpad.c index fc774396..5579c043 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -4646,6 +4646,45 @@ START_TEST(touchpad_dwt) } END_TEST +START_TEST(touchpad_dwt_enable_touch) +{ + struct litest_device *touchpad = litest_current_device(); + struct litest_device *keyboard; + struct libinput *li = touchpad->libinput; + + if (!has_disable_while_typing(touchpad)) + return; + + keyboard = litest_add_device(li, LITEST_KEYBOARD); + libinput_device_config_tap_set_enabled(touchpad->libinput_device, + LIBINPUT_CONFIG_TAP_DISABLED); + litest_drain_events(li); + + litest_keyboard_key(keyboard, KEY_A, true); + litest_keyboard_key(keyboard, KEY_A, false); + libinput_dispatch(li); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY); + + /* finger down after last key event, but + we're still within timeout - no events */ + msleep(10); + litest_touch_down(touchpad, 0, 50, 50); + litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 10, 1); + litest_assert_empty_queue(li); + + litest_timeout_dwt_short(); + libinput_dispatch(li); + + /* same touch after timeout - motion events*/ + litest_touch_move_to(touchpad, 0, 70, 50, 50, 50, 10, 1); + litest_touch_up(touchpad, 0); + + litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); + + litest_delete_device(keyboard); +} +END_TEST + START_TEST(touchpad_dwt_touch_hold) { struct litest_device *touchpad = litest_current_device(); @@ -4661,7 +4700,7 @@ START_TEST(touchpad_dwt_touch_hold) litest_drain_events(li); litest_keyboard_key(keyboard, KEY_A, true); - libinput_dispatch(li); + msleep(1); /* make sure touch starts after key press */ litest_touch_down(touchpad, 0, 50, 50); litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 5, 1); @@ -4678,7 +4717,7 @@ START_TEST(touchpad_dwt_touch_hold) libinput_dispatch(li); litest_touch_move_to(touchpad, 0, 30, 50, 50, 50, 5, 1); litest_touch_up(touchpad, 0); - litest_assert_empty_queue(li); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); litest_delete_device(keyboard); } @@ -4837,6 +4876,7 @@ START_TEST(touchpad_dwt_tap_drag) litest_keyboard_key(keyboard, KEY_A, true); libinput_dispatch(li); + msleep(1); /* make sure touch starts after key press */ litest_touch_down(touchpad, 0, 50, 50); litest_touch_up(touchpad, 0); litest_touch_down(touchpad, 0, 50, 50); @@ -4849,7 +4889,7 @@ START_TEST(touchpad_dwt_tap_drag) libinput_dispatch(li); litest_touch_move_to(touchpad, 0, 70, 50, 50, 50, 5, 1); litest_touch_up(touchpad, 0); - litest_assert_empty_queue(li); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); litest_delete_device(keyboard); } @@ -4887,6 +4927,101 @@ START_TEST(touchpad_dwt_click) } END_TEST +START_TEST(touchpad_dwt_edge_scroll) +{ + struct litest_device *touchpad = litest_current_device(); + struct litest_device *keyboard; + struct libinput *li = touchpad->libinput; + + if (!has_disable_while_typing(touchpad)) + return; + + enable_edge_scroll(touchpad); + + keyboard = litest_add_device(li, LITEST_KEYBOARD); + litest_drain_events(li); + + litest_keyboard_key(keyboard, KEY_A, true); + litest_keyboard_key(keyboard, KEY_A, false); + litest_keyboard_key(keyboard, KEY_A, true); + litest_keyboard_key(keyboard, KEY_A, false); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY); + + litest_touch_down(touchpad, 0, 99, 20); + libinput_dispatch(li); + litest_timeout_edgescroll(); + libinput_dispatch(li); + litest_assert_empty_queue(li); + + /* edge scroll timeout is 300ms atm, make sure we don't accidentally + exit the DWT timeout */ + litest_keyboard_key(keyboard, KEY_A, true); + litest_keyboard_key(keyboard, KEY_A, false); + libinput_dispatch(li); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY); + + litest_touch_move_to(touchpad, 0, 99, 20, 99, 80, 60, 10); + libinput_dispatch(li); + litest_assert_empty_queue(li); + + litest_touch_move_to(touchpad, 0, 99, 80, 99, 20, 60, 10); + litest_touch_up(touchpad, 0); + libinput_dispatch(li); + litest_assert_empty_queue(li); + + litest_delete_device(keyboard); +} +END_TEST + +START_TEST(touchpad_dwt_edge_scroll_interrupt) +{ + struct litest_device *touchpad = litest_current_device(); + struct litest_device *keyboard; + struct libinput *li = touchpad->libinput; + struct libinput_event_pointer *stop_event; + + if (!has_disable_while_typing(touchpad)) + return; + + enable_edge_scroll(touchpad); + + keyboard = litest_add_device(li, LITEST_KEYBOARD); + litest_drain_events(li); + + litest_touch_down(touchpad, 0, 99, 20); + libinput_dispatch(li); + litest_timeout_edgescroll(); + litest_touch_move_to(touchpad, 0, 99, 20, 99, 30, 10, 10); + libinput_dispatch(li); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_AXIS); + + litest_keyboard_key(keyboard, KEY_A, true); + litest_keyboard_key(keyboard, KEY_A, false); + litest_keyboard_key(keyboard, KEY_A, true); + litest_keyboard_key(keyboard, KEY_A, false); + + /* scroll stop event */ + litest_wait_for_event(li); + stop_event = litest_is_axis_event(libinput_get_event(li), + LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, + LIBINPUT_POINTER_AXIS_SOURCE_FINGER); + libinput_event_destroy(libinput_event_pointer_get_base_event(stop_event)); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY); + + litest_timeout_dwt_long(); + + /* Known bad behavior: a touch starting to edge-scroll before dwt + * kicks in will stop to scroll but be recognized as normal + * pointer-moving touch once the timeout expires. We'll fix that + * when we need to. + */ + litest_touch_move_to(touchpad, 0, 99, 30, 99, 80, 10, 5); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); + + litest_delete_device(keyboard); +} +END_TEST + void litest_setup_tests(void) { @@ -5037,6 +5172,7 @@ litest_setup_tests(void) litest_add_ranged("touchpad:state", touchpad_initial_state, LITEST_TOUCHPAD, LITEST_ANY, &axis_range); litest_add("touchpad:dwt", touchpad_dwt, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("touchpad:dwt", touchpad_dwt_enable_touch, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:dwt", touchpad_dwt_touch_hold, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:dwt", touchpad_dwt_key_hold, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:dwt", touchpad_dwt_type, LITEST_TOUCHPAD, LITEST_ANY); @@ -5044,4 +5180,6 @@ litest_setup_tests(void) litest_add("touchpad:dwt", touchpad_dwt_tap, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:dwt", touchpad_dwt_tap_drag, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:dwt", touchpad_dwt_click, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("touchpad:dwt", touchpad_dwt_edge_scroll, LITEST_TOUCHPAD, LITEST_CLICKPAD); + litest_add("touchpad:dwt", touchpad_dwt_edge_scroll_interrupt, LITEST_TOUCHPAD, LITEST_CLICKPAD); } From 9f584fcd5b0393b69fbb0e45b84f7e8d248f8b2e Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 27 May 2015 18:29:26 +1000 Subject: [PATCH 13/46] touchpad: add missing break statement No effect since it was the last case, but it's more correct. Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index e1303637..711e2930 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -311,6 +311,7 @@ tp_process_absolute(struct tp_dispatch *tp, tp_new_touch(tp, t, time); else tp_end_sequence(tp, t, time); + break; } } From 969d19dd223fb73489a635ba307da1aa1dee2c0d Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 28 May 2015 08:23:59 +1000 Subject: [PATCH 14/46] Update Red Hat's copyright Updated to 2015 where appropriate, added where missing. Signed-off-by: Peter Hutterer --- COPYING | 2 +- src/evdev-middle-button.c | 2 +- src/evdev-mt-touchpad-buttons.c | 2 +- src/evdev-mt-touchpad-edge-scroll.c | 2 +- src/evdev-mt-touchpad-tap.c | 2 +- src/evdev-mt-touchpad.c | 2 +- src/evdev-mt-touchpad.h | 2 +- src/evdev.c | 1 + src/evdev.h | 1 + src/filter.c | 1 + src/filter.h | 1 + src/libinput-private.h | 1 + src/libinput-util.c | 1 + src/libinput-util.h | 1 + src/libinput.c | 1 + src/libinput.h | 1 + src/path.c | 2 +- src/timer.c | 2 +- src/udev-seat.c | 1 + 19 files changed, 19 insertions(+), 9 deletions(-) diff --git a/COPYING b/COPYING index efc1a949..f6855731 100644 --- a/COPYING +++ b/COPYING @@ -4,7 +4,7 @@ Copyright © 2010-2012 Intel Corporation Copyright © 2010-2011 Benjamin Franzke Copyright © 2011-2012 Collabora, Ltd. Copyright © 2013-2014 Jonas Ådahl -Copyright © 2013-2014 Red Hat, Inc. +Copyright © 2013-2015 Red Hat, Inc. Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that diff --git a/src/evdev-middle-button.c b/src/evdev-middle-button.c index 328cf6c3..903fa4d0 100644 --- a/src/evdev-middle-button.c +++ b/src/evdev-middle-button.c @@ -1,5 +1,5 @@ /* - * Copyright © 2014 Red Hat, Inc. + * Copyright © 2014-2015 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index 09ea6d7a..64e9d289 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -1,5 +1,5 @@ /* - * Copyright © 2014 Red Hat, Inc. + * Copyright © 2014-2015 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided diff --git a/src/evdev-mt-touchpad-edge-scroll.c b/src/evdev-mt-touchpad-edge-scroll.c index 8a4d892f..585c764c 100644 --- a/src/evdev-mt-touchpad-edge-scroll.c +++ b/src/evdev-mt-touchpad-edge-scroll.c @@ -1,5 +1,5 @@ /* - * Copyright © 2014 Red Hat, Inc. + * Copyright © 2014-2015 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index bb7c8945..55b79160 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -1,5 +1,5 @@ /* - * Copyright © 2013 Red Hat, Inc. + * Copyright © 2013-2015 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software * and its documentation for any purpose is hereby granted without diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 711e2930..b18f74e9 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -1,5 +1,5 @@ /* - * Copyright © 2014 Red Hat, Inc. + * Copyright © 2014-2015 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index bced2b16..fef5cb32 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -1,5 +1,5 @@ /* - * Copyright © 2014 Red Hat, Inc. + * Copyright © 2014-2015 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided diff --git a/src/evdev.c b/src/evdev.c index c552cb04..4384177f 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1,6 +1,7 @@ /* * Copyright © 2010 Intel Corporation * Copyright © 2013 Jonas Ådahl + * Copyright © 2013-2015 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided diff --git a/src/evdev.h b/src/evdev.h index 337097b5..69279527 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -1,6 +1,7 @@ /* * Copyright © 2011, 2012 Intel Corporation * Copyright © 2013 Jonas Ådahl + * Copyright © 2013-2015 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided diff --git a/src/filter.c b/src/filter.c index 626cb0aa..0cdcb63e 100644 --- a/src/filter.c +++ b/src/filter.c @@ -1,6 +1,7 @@ /* * Copyright © 2006-2009 Simon Thum * Copyright © 2012 Jonas Ådahl + * Copyright © 2014-2015 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided diff --git a/src/filter.h b/src/filter.h index a0538601..16896a4c 100644 --- a/src/filter.h +++ b/src/filter.h @@ -1,5 +1,6 @@ /* * Copyright © 2012 Jonas Ådahl + * Copyright © 2014-2015 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided diff --git a/src/libinput-private.h b/src/libinput-private.h index cec529c8..889ede91 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -1,5 +1,6 @@ /* * Copyright © 2013 Jonas Ådahl + * Copyright © 2013-2015 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided diff --git a/src/libinput-util.c b/src/libinput-util.c index 4857435b..ceea054b 100644 --- a/src/libinput-util.c +++ b/src/libinput-util.c @@ -1,6 +1,7 @@ /* * Copyright © 2008-2011 Kristian Høgsberg * Copyright © 2011 Intel Corporation + * Copyright © 2013-2015 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that diff --git a/src/libinput-util.h b/src/libinput-util.h index e2ea4ffc..732f8130 100644 --- a/src/libinput-util.h +++ b/src/libinput-util.h @@ -1,5 +1,6 @@ /* * Copyright © 2008 Kristian Høgsberg + * Copyright © 2013-2015 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that diff --git a/src/libinput.c b/src/libinput.c index 444e9ce9..72d27e4e 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -1,5 +1,6 @@ /* * Copyright © 2013 Jonas Ådahl + * Copyright © 2013-2015 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided diff --git a/src/libinput.h b/src/libinput.h index 9ef8b8f4..845e1c53 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -1,5 +1,6 @@ /* * Copyright © 2013 Jonas Ådahl + * Copyright © 2013-2015 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided diff --git a/src/path.c b/src/path.c index ab5587bf..5ec8bf51 100644 --- a/src/path.c +++ b/src/path.c @@ -1,5 +1,5 @@ /* - * Copyright © 2013 Red Hat, Inc. + * Copyright © 2013-2015 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided diff --git a/src/timer.c b/src/timer.c index d1d3c108..dbd38940 100644 --- a/src/timer.c +++ b/src/timer.c @@ -1,5 +1,5 @@ /* - * Copyright © 2014 Red Hat, Inc. + * Copyright © 2014-2015 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided diff --git a/src/udev-seat.c b/src/udev-seat.c index 8dc0c236..588e1b40 100644 --- a/src/udev-seat.c +++ b/src/udev-seat.c @@ -1,5 +1,6 @@ /* * Copyright © 2013 Intel Corporation + * Copyright © 2013-2015 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided From 2af226f8930865a5e4294091fd63e6b3b89f8eea Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 28 May 2015 08:53:00 +1000 Subject: [PATCH 15/46] doc: add a blurb about scroll sources to the scrolling docs Signed-off-by: Peter Hutterer --- doc/scrolling.dox | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/doc/scrolling.dox b/doc/scrolling.dox index b5a01cf8..94aa8158 100644 --- a/doc/scrolling.dox +++ b/doc/scrolling.dox @@ -63,4 +63,23 @@ the motion events. Cross-device scrolling is not supported but for one exception: libinput's @ref t440_support enables the use of the middle button for button scrolling (even when the touchpad is disabled). +@section scroll_sources Scroll sources + +libinput provides a pointer axis *source* for each scroll event. The +source can be obtained with the libinput_event_pointer_get_axis_source() +function and is one of **wheel**, **finger**, or **continuous**. The source +information lets a caller decide when to implement kinetic scrolling. +Usually, a caller will process events of source wheel as they come in. +For events of source finger a caller should calculate the velocity of the +scroll motion and upon finger release start a kinetic scrolling motion (i.e. +continue executing a scroll according to some friction factor). +libinput expects the caller to be in charge of widget handling, the source +information is thus enough to provide kinetic scrolling on a per-widget +basis. A caller should cancel kinetic scrolling when the pointer leaves the +current widget or when a key is pressed. + +See the libinput_event_pointer_get_axis_source() for details on the +behavior of each scroll source. + +See also http://who-t.blogspot.com.au/2015/03/libinput-scroll-sources.html */ From 182290c1548cf2f7d4bb7c26d19ed2ff8fd2af4d Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 28 May 2015 10:46:02 +1000 Subject: [PATCH 16/46] doc: add illustrations for clickfinger and software button behavior Signed-off-by: Peter Hutterer --- doc/Makefile.am | 3 + doc/clickpad-softbuttons.dox | 13 +- doc/svg/clickfinger.svg | 207 ++++++++++++++++++++++++++++++ doc/svg/software-buttons.svg | 175 +++++++++++++++++++++++++ doc/svg/top-software-buttons.svg | 213 +++++++++++++++++++++++++++++++ doc/t440-support.dox | 17 +-- 6 files changed, 605 insertions(+), 23 deletions(-) create mode 100644 doc/svg/clickfinger.svg create mode 100644 doc/svg/software-buttons.svg create mode 100644 doc/svg/top-software-buttons.svg diff --git a/doc/Makefile.am b/doc/Makefile.am index 3d81d7ce..ed2f72fd 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -24,9 +24,12 @@ header_files = \ diagram_files = \ $(srcdir)/dot/seats-sketch.gv \ $(srcdir)/dot/seats-sketch-libinput.gv \ + $(srcdir)/svg/software-buttons.svg \ + $(srcdir)/svg/clickfinger.svg \ $(srcdir)/svg/button-scrolling.svg \ $(srcdir)/svg/edge-scrolling.svg \ $(srcdir)/svg/palm-detection.svg \ + $(srcdir)/svg/top-software-buttons.svg \ $(srcdir)/svg/twofinger-scrolling.svg html/index.html: libinput.doxygen $(header_files) $(diagram_files) diff --git a/doc/clickpad-softbuttons.dox b/doc/clickpad-softbuttons.dox index 8d919369..e7c4e543 100644 --- a/doc/clickpad-softbuttons.dox +++ b/doc/clickpad-softbuttons.dox @@ -24,15 +24,6 @@ is split in the middle to generate left or right button events on click. The height of the button area depends on the hardware but is usually around 10mm. -@dot -digraph G { - clickpad [ - shape = "record"; - label = "{\nMain\nArea\n\n|{LEFT|RIGHT}}"; - ] -} -@enddot - Left, right and middle button events can be triggered as follows: - if a finger is in the main area or the left button area, a click generates left button events. @@ -40,6 +31,8 @@ Left, right and middle button events can be triggered as follows: - if there is a finger in both the left and right button area, a click generates middle button events. +@image html software-buttons.svg "Left, right and middle-button click with software button areas" + If fingers are down in the main area in addition to fingers in the left or right button area, those fingers are are ignored. A release event always releases the buttons logically down, regardless of @@ -69,6 +62,8 @@ three fingers are held down on the touchpad when a physical click is generated. The location of the fingers does not matter and there are no software-defined button areas. +@image html clickfinger.svg "One, two and three-finger click with Clickfinger behavior" + The Xorg synaptics driver uses 30% of the touchpad dimensions as threshold, libinput does not have this restriction. If two fingers are on the pad while clicking, that is a two-finger click. diff --git a/doc/svg/clickfinger.svg b/doc/svg/clickfinger.svg new file mode 100644 index 00000000..b92dcb44 --- /dev/null +++ b/doc/svg/clickfinger.svg @@ -0,0 +1,207 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/svg/software-buttons.svg b/doc/svg/software-buttons.svg new file mode 100644 index 00000000..903535c8 --- /dev/null +++ b/doc/svg/software-buttons.svg @@ -0,0 +1,175 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/svg/top-software-buttons.svg b/doc/svg/top-software-buttons.svg new file mode 100644 index 00000000..ab311240 --- /dev/null +++ b/doc/svg/top-software-buttons.svg @@ -0,0 +1,213 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/t440-support.dox b/doc/t440-support.dox index 277dbe8e..8e2f9c23 100644 --- a/doc/t440-support.dox +++ b/doc/t440-support.dox @@ -11,21 +11,10 @@ laptops had a separate set of physical buttons for the trackstick. This series removed these buttons, relying on a software emulation of the top section of the touchpad. This is visually marked on the trackpad itself, -approximately like this: +and clicks can be triggered by pressing the touchpad down with a finger in +the respective area: -@dot -digraph G { - subgraph cluster_0 { - margin="0"; - - clickpad [ - shape = "record"; - color = "none"; - label = "{{LLLLLLLLLL|MMMMM|RRRRRRRRR}|\n\n\n\n\n\n\n\n|{LLLLLLLL| |RRRRRRRR}}"; - ] - } -} -@enddot +@image html top-software-buttons.svg "Left, right and middle-button click with top software button areas" This page only covers the top software buttons, the bottom button behavior is covered in @ref clickpad_softbuttons "Clickpad software buttons". From e3673cc346b34825b191be92f3eea87491a9b066 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 28 May 2015 11:54:34 +1000 Subject: [PATCH 17/46] doc: improve the T440 documentation a bit Signed-off-by: Peter Hutterer --- doc/t440-support.dox | 75 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 58 insertions(+), 17 deletions(-) diff --git a/doc/t440-support.dox b/doc/t440-support.dox index 8e2f9c23..652a6d0b 100644 --- a/doc/t440-support.dox +++ b/doc/t440-support.dox @@ -25,36 +25,77 @@ property. @section t440_support_btn_size Size of the buttons +The size of the buttons matches the visual markings on this touchpad. +The width of the left and right buttons is approximately 42% of the +touchpad's width, the middle button is centered and assigned 16% of the +touchpad width. + The line of the buttons is 5mm from the top edge of the touchpad, measurements of button presses showed that the size of the buttons needs to be approximately 10mm high to work reliable (especially when using the thumb to press the button). -The width of the left and right buttons is approximately 42% of the -touchpad's width, the middle button is centered and should be assigned -approximately 16% of the touchpad width. - @section t440_support_btn_behavior Button behavior Movement in the top button area does not generate pointer movement. These buttons are not replacement buttons for the bottom button area but have -their own behavior. -Semantically attached to the trackstick device, libinput re-routes events -from these buttons to appear through the trackstick device. The top button -areas work even if the touchpad is disabled but will be disabled when the -trackstick device is disabled. +their own behavior. Semantically attached to the trackstick device, libinput +re-routes events from these buttons to appear through the trackstick device. -If the finger starts inside the top area and moves outside the button area -the finger is treated as dead and must be lifted to generate future buttons. -Likewise, movement into the top button area does not trigger button events, a click -has to start inside this area to take effect. +@dot +digraph top_button_routing +{ + rankdir="LR"; + node [shape="box";] + + trackstick [label="trackstick kernel device"]; + touchpad [label="touchpad kernel device"]; + + subgraph cluster0 { + bgcolor = floralwhite + label = "libinput" + + libinput_ts [label="trackstick libinput_device" + style=filled + fillcolor=white]; + libinput_tp [label="touchpad libinput_device" + style=filled + fillcolor=white]; + + libinput_tp -> libinput_ts [constraint=false + color="red4"]; + } + + trackstick -> libinput_ts [arrowhead="none"] + touchpad -> libinput_tp [color="red4"] + + events_tp [label="other touchpad events"]; + events_topbutton [label="top sofware button events"]; + + libinput_tp -> events_tp [arrowhead="none"] + libinput_ts -> events_topbutton [color="red4"] +} +@enddot + + +The top button areas work even if the touchpad is disabled but will be +disabled when the trackstick device is disabled. If the finger starts inside +the top area and moves outside the button area the finger is treated as dead +and must be lifted to generate future buttons. Likewise, movement into the +top button area does not trigger button events, a click has to start inside +this area to take effect. @section t440_support_identification Kernel support -The firmware on touchpads providing top software buttons is buggy and -announces wrong ranges. Kernel -patches are required; these fixes are available in kernels -3.14.1, 3.15 and later but each touchpad needs a separate fix. +The firmware on the first generation of touchpads providing top software +buttons is buggy and announces wrong ranges. +Kernel patches are required; +these fixes are available in kernels 3.14.1, 3.15 and later but each +touchpad needs a separate fix. + +The October 2014 refresh of these laptops do not have this firmware bug +anymore and should work without per-device patches, though +this kernel commit is required. For a complete list of supported touchpads check the From 767c1691cd520d873bea8175e22fa68a6027c29f Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 28 May 2015 08:04:42 +1000 Subject: [PATCH 18/46] README: spice up the readme a bit Signed-off-by: Peter Hutterer --- README.txt | 43 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/README.txt b/README.txt index c5dc61e5..3c7a5d54 100644 --- a/README.txt +++ b/README.txt @@ -9,22 +9,57 @@ applications that need to directly deal with input devices. It provides device detection, device handling, input device event processing and abstraction so minimize the amount of custom input code the user of libinput need to provide the common set of functionality that users expect. - Input event processing includes scaling touch coordinates, generating pointer events from touchpads, pointer acceleration, etc. -libinput originates from weston, the Wayland reference compositor. +libinput originates from +[weston](http://cgit.freedesktop.org/wayland/weston/), the Wayland reference +compositor. + +Source code +----------- The source code of libinput can be found at: http://cgit.freedesktop.org/wayland/libinput -For more information, visit: +For a list of current and past releases visit: http://www.freedesktop.org/wiki/Software/libinput/ +Reporting Bugs +-------------- + Bugs can be filed in the libinput component of Wayland: https://bugs.freedesktop.org/enter_bug.cgi?product=Wayland&component=libinput -Online API documentation: +Where possible, please provide an +[evemu](http://www.freedesktop.org/wiki/Evemu/) recording of the input +device and/or the event sequence in question. + +Documentation +------------- + +Developer API documentation: http://wayland.freedesktop.org/libinput/doc/latest/modules.html +High-level documentation about libinput's features: +http://wayland.freedesktop.org/libinput/doc/latest/pages.html + +License +------- + +libinput is licensed under the MIT license. + +> Permission to use, copy, modify, distribute, and sell this software and its +> documentation for any purpose is hereby granted without fee, provided that +> the above copyright notice appear in all copies and that both that copyright +> notice and this permission notice appear in supporting documentation, and +> that the name of the copyright holders not be used in advertising or +> publicity pertaining to distribution of the software without specific, +> written prior permission. The copyright holders make no representations +> about the suitability of this software for any purpose. It is provided "as +> is" without express or implied warranty. + +See the [COPYING](http://cgit.freedesktop.org/wayland/libinput/tree/COPYING) +file for the full license information. + */ From 887895c1c13f948714e710ed612cb5b6e0b09fd6 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 28 May 2015 13:33:54 +1000 Subject: [PATCH 19/46] test: don't abort if the device filter filtered all devices Signed-off-by: Peter Hutterer --- test/litest.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/test/litest.c b/test/litest.c index 1d3f0ce2..53c441b5 100644 --- a/test/litest.c +++ b/test/litest.c @@ -640,6 +640,7 @@ _litest_add_ranged_for_device(const char *name, { struct suite *s; struct litest_test_device **dev = devices; + bool device_filtered = false; assert(type < LITEST_NO_DEVICE); @@ -654,8 +655,10 @@ _litest_add_ranged_for_device(const char *name, s = get_suite(name); for (; *dev; dev++) { if (filter_device && - fnmatch(filter_device, (*dev)->shortname, 0) != 0) + fnmatch(filter_device, (*dev)->shortname, 0) != 0) { + device_filtered = true; continue; + } if ((*dev)->type == type) { litest_add_tcase_for_device(s, @@ -667,7 +670,9 @@ _litest_add_ranged_for_device(const char *name, } } - litest_abort_msg("Invalid test device type"); + /* only abort if no filter was set, that's a bug */ + if (!device_filtered) + litest_abort_msg("Invalid test device type"); } static int From d8208940e02de3d9ea84ac5e0a6c740d9dfa1fff Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 29 May 2015 12:13:02 +1000 Subject: [PATCH 20/46] tools: print symbolic key names too from event-debug Signed-off-by: Peter Hutterer --- tools/Makefile.am | 4 ++-- tools/event-debug.c | 11 +++++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/tools/Makefile.am b/tools/Makefile.am index b24c560d..68e60cbb 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -13,9 +13,9 @@ libshared_la_CFLAGS = $(LIBEVDEV_CFLAGS) libshared_la_LIBADD = $(LIBEVDEV_LIBS) event_debug_SOURCES = event-debug.c -event_debug_LDADD = ../src/libinput.la libshared.la $(LIBUDEV_LIBS) +event_debug_LDADD = ../src/libinput.la libshared.la $(LIBUDEV_LIBS) $(LIBEVDEV_LIBS) event_debug_LDFLAGS = -no-install -event_debug_CFLAGS = $(LIBUDEV_CFLAGS) +event_debug_CFLAGS = $(LIBUDEV_CFLAGS) $(LIBEVDEV_CFLAGS) ptraccel_debug_SOURCES = ptraccel-debug.c ptraccel_debug_LDADD = ../src/libfilter.la diff --git a/tools/event-debug.c b/tools/event-debug.c index 3629e745..cbbd978f 100644 --- a/tools/event-debug.c +++ b/tools/event-debug.c @@ -34,6 +34,7 @@ #include #include +#include #include "shared.h" @@ -193,11 +194,17 @@ print_key_event(struct libinput_event *ev) { struct libinput_event_keyboard *k = libinput_event_get_keyboard_event(ev); enum libinput_key_state state; + uint32_t key; + const char *keyname; print_event_time(libinput_event_keyboard_get_time(k)); state = libinput_event_keyboard_get_key_state(k); - printf("%d %s\n", - libinput_event_keyboard_get_key(k), + + key = libinput_event_keyboard_get_key(k); + keyname = libevdev_event_code_get_name(EV_KEY, key); + printf("%s (%d) %s\n", + keyname ? keyname : "???", + key, state == LIBINPUT_KEY_STATE_PRESSED ? "pressed" : "released"); } From a4313f13e33fdad3b30cc9219391da48aeec340f Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 28 May 2015 13:41:58 +1000 Subject: [PATCH 21/46] touchpad: check touchpad for basic features we expect If a relative device is tagged by udev as ID_INPUT_TOUCHPAD we need to catch this before we try to dereference device->abs.absinfo_x. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index b18f74e9..eacec6e3 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -1439,6 +1439,30 @@ tp_init_sendevents(struct tp_dispatch *tp, return 0; } +static int +tp_sanity_check(struct tp_dispatch *tp, + struct evdev_device *device) +{ + struct libevdev *evdev = device->evdev; + struct libinput *libinput = tp_libinput_context(tp); + + if (!libevdev_has_event_code(evdev, EV_ABS, ABS_X)) + goto error; + + if (!libevdev_has_event_code(evdev, EV_KEY, BTN_TOUCH)) + goto error; + + if (!libevdev_has_event_code(evdev, EV_KEY, BTN_TOOL_FINGER)) + goto error; + + return 0; + +error: + log_bug_kernel(libinput, + "device %s failed touchpad sanity checks\n"); + return -1; +} + static int tp_init(struct tp_dispatch *tp, struct evdev_device *device) @@ -1449,6 +1473,9 @@ tp_init(struct tp_dispatch *tp, tp->base.interface = &tp_interface; tp->device = device; + if (tp_sanity_check(tp, device) != 0) + return -1; + if (tp_init_slots(tp, device) != 0) return -1; From 742b5ccbfb8f6a0cd557e7416b9cac952ef57ec8 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 29 May 2015 07:29:07 +1000 Subject: [PATCH 22/46] test: add another wait loop for udev Wait after deleting a device so udev can catch up with everything and the various hooks to make sure it's happy with any newly created devices after this. The sleep is in the delete path to also cover the tests where we manually create uinput devices rather than using the litest hooks. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/litest.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/litest.c b/test/litest.c index 53c441b5..349eca08 100644 --- a/test/litest.c +++ b/test/litest.c @@ -1122,6 +1122,11 @@ litest_delete_device(struct litest_device *d) free(d->private); memset(d,0, sizeof(*d)); free(d); + + /* zzz so udev can catch up with things, so we don't accidentally open + * an old device in the next test and then get all upset when things blow + * up */ + msleep(10); } void From ec468e8993aae038ba0b0e99cd162d99f7593b18 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 29 May 2015 11:11:56 +1000 Subject: [PATCH 23/46] evdev: use the udev ID_INPUT_POINTINGSTICK property Added in systemd 220, but note that for udev backwards compatibility, the ID_INPUT_POINTINGSTICK tag is set in addition to the ID_INPUT_MOUSE tag. And use that property to tag a device as trackpoint too, this allows temporary workarounds for kernel bugs where the input prop isn't set yet. https://bugzilla.redhat.com/show_bug.cgi?id=1225563 Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index 4384177f..642f441c 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -60,6 +60,7 @@ enum evdev_device_udev_tags { EVDEV_UDEV_TAG_JOYSTICK = (1 << 6), EVDEV_UDEV_TAG_ACCELEROMETER = (1 << 7), EVDEV_UDEV_TAG_BUTTONSET = (1 << 8), + EVDEV_UDEV_TAG_POINTINGSTICK = (1 << 9), }; struct evdev_udev_tag_match { @@ -78,6 +79,7 @@ static const struct evdev_udev_tag_match evdev_udev_tag_matches[] = { {"ID_INPUT_TABLET_PAD", EVDEV_UDEV_TAG_BUTTONSET}, {"ID_INPUT_JOYSTICK", EVDEV_UDEV_TAG_JOYSTICK}, {"ID_INPUT_ACCELEROMETER", EVDEV_UDEV_TAG_ACCELEROMETER}, + {"ID_INPUT_POINTINGSTICK", EVDEV_UDEV_TAG_POINTINGSTICK}, /* sentinel value */ { 0 }, @@ -730,7 +732,10 @@ static void evdev_tag_trackpoint(struct evdev_device *device, struct udev_device *udev_device) { - if (libevdev_has_property(device->evdev, INPUT_PROP_POINTING_STICK)) + if (libevdev_has_property(device->evdev, + INPUT_PROP_POINTING_STICK) || + udev_device_get_property_value(udev_device, + "ID_INPUT_POINTINGSTICK")) device->tags |= EVDEV_TAG_TRACKPOINT; } @@ -1839,13 +1844,14 @@ evdev_configure_device(struct evdev_device *device) } log_info(libinput, - "input device '%s', %s is tagged by udev as:%s%s%s%s%s%s%s%s\n", + "input device '%s', %s is tagged by udev as:%s%s%s%s%s%s%s%s%s\n", device->devname, devnode, udev_tags & EVDEV_UDEV_TAG_KEYBOARD ? " Keyboard" : "", udev_tags & EVDEV_UDEV_TAG_MOUSE ? " Mouse" : "", udev_tags & EVDEV_UDEV_TAG_TOUCHPAD ? " Touchpad" : "", udev_tags & EVDEV_UDEV_TAG_TOUCHSCREEN ? " Touchscreen" : "", udev_tags & EVDEV_UDEV_TAG_TABLET ? " Tablet" : "", + udev_tags & EVDEV_UDEV_TAG_POINTINGSTICK ? " Pointingstick" : "", udev_tags & EVDEV_UDEV_TAG_JOYSTICK ? " Joystick" : "", udev_tags & EVDEV_UDEV_TAG_ACCELEROMETER ? " Accelerometer" : "", udev_tags & EVDEV_UDEV_TAG_BUTTONSET ? " Buttonset" : ""); @@ -1904,7 +1910,8 @@ evdev_configure_device(struct evdev_device *device) return device->dispatch == NULL ? -1 : 0; } - if (udev_tags & EVDEV_UDEV_TAG_MOUSE) { + if (udev_tags & EVDEV_UDEV_TAG_MOUSE || + udev_tags & EVDEV_UDEV_TAG_POINTINGSTICK) { if (libevdev_has_event_code(evdev, EV_REL, REL_X) && libevdev_has_event_code(evdev, EV_REL, REL_Y) && evdev_device_init_pointer_acceleration( From 35269404363d069bb9f8c3b67eb950bc1e145f17 Mon Sep 17 00:00:00 2001 From: "Jon A. Cruz" Date: Mon, 1 Jun 2015 08:09:26 +1000 Subject: [PATCH 24/46] Add missing config.h includes Signed-off-by: Jon A. Cruz Signed-off-by: Peter Hutterer --- src/evdev-middle-button.c | 2 ++ src/evdev-mt-touchpad-buttons.c | 2 ++ src/evdev-mt-touchpad-edge-scroll.c | 2 ++ src/timer.c | 2 ++ 4 files changed, 8 insertions(+) diff --git a/src/evdev-middle-button.c b/src/evdev-middle-button.c index 903fa4d0..c9894865 100644 --- a/src/evdev-middle-button.c +++ b/src/evdev-middle-button.c @@ -20,6 +20,8 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "config.h" + #include #include "evdev.h" diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index 64e9d289..4b1d6ab5 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -20,6 +20,8 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "config.h" + #include #include #include diff --git a/src/evdev-mt-touchpad-edge-scroll.c b/src/evdev-mt-touchpad-edge-scroll.c index 585c764c..2302d2c0 100644 --- a/src/evdev-mt-touchpad-edge-scroll.c +++ b/src/evdev-mt-touchpad-edge-scroll.c @@ -20,6 +20,8 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "config.h" + #include #include #include diff --git a/src/timer.c b/src/timer.c index dbd38940..1e507df1 100644 --- a/src/timer.c +++ b/src/timer.c @@ -20,6 +20,8 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "config.h" + #include #include #include From 96d4fa03640559e18484e5c1a2ca4b19049bbe1f Mon Sep 17 00:00:00 2001 From: "Jon A. Cruz" Date: Thu, 28 May 2015 19:01:01 -0700 Subject: [PATCH 25/46] Add xasprintf to avoid use of undefined pointer values If asprintf fails for any reason, the contents of the pointer are undefined. While some platforms set it to NULL, there is no guarantee that all will. This change adds a simple wrapper to ensure proper NULL results on failure. Signed-off-by: Jon A. Cruz Reviewed-by: Peter Hutterer Added LIBINPUT_PRINTF attribute and the required declaration for it. Signed-off-by: Peter Hutterer --- src/libinput-util.h | 34 ++++++++++++++++++++++++++++++++++ test/litest.c | 2 +- tools/libinput-list-devices.c | 14 +++++++------- 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/libinput-util.h b/src/libinput-util.h index 732f8130..910406cf 100644 --- a/src/libinput-util.h +++ b/src/libinput-util.h @@ -26,6 +26,8 @@ #include #include +#include +#include #include #include @@ -230,6 +232,38 @@ matrix_to_farray6(const struct matrix *m, float out[6]) out[5] = m->val[1][2]; } +/** + * Simple wrapper for asprintf that ensures the passed in-pointer is set + * to NULL upon error. + * The standard asprintf() call does not guarantee the passed in pointer + * will be NULL'ed upon failure, whereas this wrapper does. + * + * @param strp pointer to set to newly allocated string. + * This pointer should be passed to free() to release when done. + * @param fmt the format string to use for printing. + * @return The number of bytes printed (excluding the null byte terminator) + * upon success or -1 upon failure. In the case of failure the pointer is set + * to NULL. + */ +static inline int +xasprintf(char **strp, const char *fmt, ...) + LIBINPUT_ATTRIBUTE_PRINTF(2, 3); + +static inline int +xasprintf(char **strp, const char *fmt, ...) +{ + int rc = 0; + va_list args; + + va_start(args, fmt); + rc = vasprintf(strp, fmt, args); + va_end(args); + if ((rc == -1) && strp) + *strp = NULL; + + return rc; +} + enum ratelimit_state { RATELIMIT_EXCEEDED, RATELIMIT_THRESHOLD, diff --git a/test/litest.c b/test/litest.c index 349eca08..30d30566 100644 --- a/test/litest.c +++ b/test/litest.c @@ -876,7 +876,7 @@ litest_init_udev_rules(struct litest_test_device *dev) ck_abort_msg("Failed to create udev rules directory (%s)\n", strerror(errno)); - rc = asprintf(&path, + rc = xasprintf(&path, "%s/%s%s.rules", UDEV_RULES_D, UDEV_RULE_PREFIX, diff --git a/tools/libinput-list-devices.c b/tools/libinput-list-devices.c index 66251731..68ddb612 100644 --- a/tools/libinput-list-devices.c +++ b/tools/libinput-list-devices.c @@ -120,17 +120,17 @@ calibration_default(struct libinput_device *device) float calibration[6]; if (!libinput_device_config_calibration_has_matrix(device)) { - asprintf(&str, "n/a"); + xasprintf(&str, "n/a"); return str; } if (libinput_device_config_calibration_get_default_matrix(device, calibration) == 0) { - asprintf(&str, "identity matrix"); + xasprintf(&str, "identity matrix"); return str; } - asprintf(&str, + xasprintf(&str, "%.2f %.2f %.2f %.2f %.2f %.2f", calibration[0], calibration[1], @@ -150,13 +150,13 @@ scroll_defaults(struct libinput_device *device) scroll_methods = libinput_device_config_scroll_get_methods(device); if (scroll_methods == LIBINPUT_CONFIG_SCROLL_NO_SCROLL) { - asprintf(&str, "none"); + xasprintf(&str, "none"); return str; } method = libinput_device_config_scroll_get_default_method(device); - asprintf(&str, + xasprintf(&str, "%s%s%s%s%s%s", (method == LIBINPUT_CONFIG_SCROLL_2FG) ? "*" : "", (scroll_methods & LIBINPUT_CONFIG_SCROLL_2FG) ? "two-finger " : "", @@ -176,12 +176,12 @@ click_defaults(struct libinput_device *device) click_methods = libinput_device_config_click_get_methods(device); if (click_methods == LIBINPUT_CONFIG_CLICK_METHOD_NONE) { - asprintf(&str, "none"); + xasprintf(&str, "none"); return str; } method = libinput_device_config_click_get_default_method(device); - asprintf(&str, + xasprintf(&str, "%s%s%s%s", (method == LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS) ? "*" : "", (click_methods & LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS) ? "button-areas " : "", From c388a7e2fe68226ec9b9cb59700e9845fd7501c9 Mon Sep 17 00:00:00 2001 From: "Jon A. Cruz" Date: Fri, 29 May 2015 18:40:25 -0700 Subject: [PATCH 26/46] test: address gcc warnings on potentially uninitialized variables. Signed-off-by: Jon A. Cruz Signed-off-by: Peter Hutterer --- test/touch.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/touch.c b/test/touch.c index 2c07e090..be2bea92 100644 --- a/test/touch.c +++ b/test/touch.c @@ -586,7 +586,8 @@ START_TEST(touch_initial_state) { struct litest_device *dev; struct libinput *libinput1, *libinput2; - struct libinput_event *ev1, *ev2; + struct libinput_event *ev1 = NULL; + struct libinput_event *ev2 = NULL; struct libinput_event_touch *t1, *t2; struct libinput_device *device1, *device2; int axis = _i; /* looped test */ From fbead87edb46dc9e6d0e6aab2ae51f6eef068260 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 28 May 2015 08:12:33 +1000 Subject: [PATCH 27/46] COPYING: note that having linux/input.h in the tree does not make libinput GPL Signed-off-by: Peter Hutterer --- COPYING | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/COPYING b/COPYING index f6855731..93ac3bca 100644 --- a/COPYING +++ b/COPYING @@ -23,3 +23,11 @@ CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +libinput ships a copy of the GPL-licensed Linux kernel's linux/input.h +header file. [1] This does not make libinput GPL. +This copy is provided to provide consistent behavior regardless which kernel +version libinput is compiled against. The header is used during compilation +only, libinput does not link against GPL libraries. + +[1] http://cgit.freedesktop.org/wayland/libinput/tree/include/linux/input.h From 9883735c1ce77a6b2061a39ded4cc3b94e019a73 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 28 May 2015 09:33:07 +1000 Subject: [PATCH 28/46] README: add two diagrams to outline the stack Signed-off-by: Peter Hutterer --- README.txt | 21 +++++++++++++++++++++ doc/Makefile.am | 2 ++ doc/dot/libinput-stack-wayland.gv | 17 +++++++++++++++++ doc/dot/libinput-stack-xorg.gv | 19 +++++++++++++++++++ 4 files changed, 59 insertions(+) create mode 100644 doc/dot/libinput-stack-wayland.gv create mode 100644 doc/dot/libinput-stack-xorg.gv diff --git a/README.txt b/README.txt index 3c7a5d54..07e8fa31 100644 --- a/README.txt +++ b/README.txt @@ -16,6 +16,27 @@ libinput originates from [weston](http://cgit.freedesktop.org/wayland/weston/), the Wayland reference compositor. +Architecture +------------ + +libinput is not used directly by applications, rather it is used by the +xf86-input-libinput X.Org driver or wayland compositors. The typical +software stack for a system running Wayland is: + +@dotfile libinput-stack-wayland.gv + +Where the Wayland compositor may be Weston, mutter, KWin, etc. Note that +Wayland encourages the use of toolkits, so the Wayland client (your +application) does not usually talk directly to the compositor but rather +employs a toolkit (e.g. GTK) to do so. + +The simplified software stack for a system running X.Org is: + +@dotfile libinput-stack-xorg.gv + +Again, on a modern system the application does not usually talk directly to +the X server using Xlib but rather employs a toolkit to do so. + Source code ----------- diff --git a/doc/Makefile.am b/doc/Makefile.am index ed2f72fd..181fe2e3 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -24,6 +24,8 @@ header_files = \ diagram_files = \ $(srcdir)/dot/seats-sketch.gv \ $(srcdir)/dot/seats-sketch-libinput.gv \ + $(srcdir)/dot/libinput-stack-wayland.gv \ + $(srcdir)/dot/libinput-stack-xorg.gv \ $(srcdir)/svg/software-buttons.svg \ $(srcdir)/svg/clickfinger.svg \ $(srcdir)/svg/button-scrolling.svg \ diff --git a/doc/dot/libinput-stack-wayland.gv b/doc/dot/libinput-stack-wayland.gv new file mode 100644 index 00000000..20b334ea --- /dev/null +++ b/doc/dot/libinput-stack-wayland.gv @@ -0,0 +1,17 @@ +digraph stack +{ + rankdir="LR"; + node [ + shape="box"; + ] + + kernel [label="Kernel"]; + + libinput; + compositor [label="Wayland Compositor"]; + client [label="Wayland Client"]; + + kernel -> libinput + libinput -> compositor + compositor -> client +} diff --git a/doc/dot/libinput-stack-xorg.gv b/doc/dot/libinput-stack-xorg.gv new file mode 100644 index 00000000..e50f2418 --- /dev/null +++ b/doc/dot/libinput-stack-xorg.gv @@ -0,0 +1,19 @@ +digraph stack +{ + rankdir="LR"; + node [ + shape="box"; + ] + + kernel [label="Kernel"]; + + libinput; + xf86libinput [label="xf86-input-libinput"]; + xserver [label="X Server"]; + client [label="X11 client"]; + + kernel -> libinput + libinput -> xf86libinput + xf86libinput -> xserver + xserver -> client +} From def69068a8ba47bfdc976d607a21e30f0e4a9c38 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 28 May 2015 08:57:18 +1000 Subject: [PATCH 29/46] doc: add a FAQ page Signed-off-by: Peter Hutterer --- doc/Makefile.am | 2 ++ doc/dot/libinput-stack-gnome.gv | 30 +++++++++++++++++++ doc/faqs.dox | 51 +++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 doc/dot/libinput-stack-gnome.gv create mode 100644 doc/faqs.dox diff --git a/doc/Makefile.am b/doc/Makefile.am index 181fe2e3..9407f133 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -13,6 +13,7 @@ header_files = \ $(srcdir)/absolute-axes.dox \ $(srcdir)/clickpad-softbuttons.dox \ $(srcdir)/device-configuration-via-udev.dox \ + $(srcdir)/faqs.dox \ $(srcdir)/normalization-of-relative-motion.dox \ $(srcdir)/palm-detection.dox \ $(srcdir)/scrolling.dox \ @@ -26,6 +27,7 @@ diagram_files = \ $(srcdir)/dot/seats-sketch-libinput.gv \ $(srcdir)/dot/libinput-stack-wayland.gv \ $(srcdir)/dot/libinput-stack-xorg.gv \ + $(srcdir)/dot/libinput-stack-gnome.gv \ $(srcdir)/svg/software-buttons.svg \ $(srcdir)/svg/clickfinger.svg \ $(srcdir)/svg/button-scrolling.svg \ diff --git a/doc/dot/libinput-stack-gnome.gv b/doc/dot/libinput-stack-gnome.gv new file mode 100644 index 00000000..4e0ccf43 --- /dev/null +++ b/doc/dot/libinput-stack-gnome.gv @@ -0,0 +1,30 @@ +digraph stack +{ + compound=true; + rankdir="LR"; + node [ + shape="box"; + ] + + gcc -> gsettings + + xf86libinput -> libinput + + subgraph cluster0 { + label="X.Org"; + xf86libinput [label="xf86-input-libinput"]; + xserver [label="X Server"]; + xserver -> xf86libinput; + } + + gcc [label="gnome-control-center"]; + + subgraph cluster3 { + gsettings + } + + gsd [label="gnome-settings-daemon"]; + + gsd -> gsettings + gsd -> xserver +} diff --git a/doc/faqs.dox b/doc/faqs.dox new file mode 100644 index 00000000..92a42285 --- /dev/null +++ b/doc/faqs.dox @@ -0,0 +1,51 @@ +/** +@page faq FAQs - Frequently Asked Questions + +Frequently asked questions about libinput. + +@section faq_kinetic_scrolling Kinetic scrolling does not work + +The X.Org synaptics driver implemented kinetic scrolling in the driver. It +measures the scroll speed and once the finger leaves the touchpad the driver +keeps sending scroll events for a predetermined time. This effectively +provides for kinetic scrolling without client support but triggers an +unfixable [bug](https://bugs.freedesktop.org/show_bug.cgi?id=38909): the +client cannot know that the events are from a kinetic scroll source. Scroll +events in X are always sent to the current cursor position, a movement of the +cursor after lifting the finger will send the kinetic scroll events to the +new client, something the user does not usually expect. A key event during +the kinetic scroll procedure causes side-effects such as triggering zoom. + +libinput does not implement kinetic scrolling for touchpads. Instead it +provides the libinput_event_pointer_get_axis_source() function that enables +callers to implement kinetic scrolling on a per-widget basis, see @ref +scroll_sources. + +@section faq_gpl Is libinput GPL-licensed? + +No, libinput is MIT licensed. The Linux kernel header file linux/input.h in +libinput's tree is provded to ensure the same behavior regardless of which +kernel version libinput is built on. It does not make libinput GPL-licensed. + +@section faq_config_options Where is the configuration stored? + +libinput does not store configuration options, it is up to the caller to +manage these and decide which configuration option to apply to each device. +This must be done at startup, after a resume and whenever a new device is +detected. + +In a GNOME X.Org stack a user would usually toggle an option in +the gnome-control-center which adjusts a gsettings entry. That change is +picked up by gnome-settings-daemon and applied to the device by adjusting +input device properties that the xf86-input-libinput driver provides. +The input device property changes map to the respective libinput +configuration options. + +@dotfile libinput-stack-gnome.gv + +This has an effect on the availability of configuration options: if an +option is not exposed by the intermediary, it cannot be configured by the +client. Also some configuration options that are provided by the +intermediary may not be libinput-specific configuration options. + +*/ From 078421808179baa22af5cd35ad5e0aee86dffb41 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 1 Jun 2015 16:58:37 +1000 Subject: [PATCH 30/46] configure.ac: libinput 0.16.0 Signed-off-by: Peter Hutterer --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index f4c2b7d9..61a41963 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ AC_PREREQ([2.64]) m4_define([libinput_major_version], [0]) -m4_define([libinput_minor_version], [15]) +m4_define([libinput_minor_version], [16]) m4_define([libinput_micro_version], [0]) m4_define([libinput_version], [libinput_major_version.libinput_minor_version.libinput_micro_version]) @@ -31,7 +31,7 @@ AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz]) # b) If interfaces have been changed or added, but binary compatibility has # been preserved, change to C+1:0:A+1 # c) If the interface is the same as the previous version, change to C:R+1:A -LIBINPUT_LT_VERSION=12:0:2 +LIBINPUT_LT_VERSION=12:1:2 AC_SUBST(LIBINPUT_LT_VERSION) AM_SILENT_RULES([yes]) From aaa7622933ba41c61b7f265679e355ade5dc9fe9 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 1 Jun 2015 14:38:29 +1000 Subject: [PATCH 31/46] evdev: use the button down time for no-scroll middle button press event When we get the release event within the timeout, we send a press + release event for the middle button. Rather than using the release event's timestamp for both, remember and use the button press timestamp. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev.c | 4 +++- src/evdev.h | 2 ++ test/trackpoint.c | 23 +++++++++++++++++++++-- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index 642f441c..ed1a9a30 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -442,6 +442,7 @@ evdev_button_scroll_button(struct evdev_device *device, if (is_press) { libinput_timer_set(&device->scroll.timer, time + DEFAULT_MIDDLE_BUTTON_SCROLL_TIMEOUT); + device->scroll.button_down_time = time; } else { libinput_timer_cancel(&device->scroll.timer); if (device->scroll.button_scroll_active) { @@ -451,7 +452,8 @@ evdev_button_scroll_button(struct evdev_device *device, } else { /* If the button is released quickly enough emit the * button press/release events. */ - evdev_pointer_notify_physical_button(device, time, + evdev_pointer_notify_physical_button(device, + device->scroll.button_down_time, device->scroll.button, LIBINPUT_BUTTON_STATE_PRESSED); evdev_pointer_notify_physical_button(device, time, diff --git a/src/evdev.h b/src/evdev.h index 69279527..3f63c57a 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -150,6 +150,8 @@ struct evdev_device { /* Currently enabled method, button */ enum libinput_config_scroll_method method; uint32_t button; + uint64_t button_down_time; + /* set during device init, used at runtime to delay changes * until all buttons are up */ enum libinput_config_scroll_method want_method; diff --git a/test/trackpoint.c b/test/trackpoint.c index 9fcce6f7..0a6f6b0f 100644 --- a/test/trackpoint.c +++ b/test/trackpoint.c @@ -35,15 +35,34 @@ START_TEST(trackpoint_middlebutton) { struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; + struct libinput_event *event; + struct libinput_event_pointer *ptrev; + uint64_t ptime, rtime; litest_drain_events(li); /* A quick middle button click should get reported normally */ litest_button_click(dev, BTN_MIDDLE, 1); + msleep(2); litest_button_click(dev, BTN_MIDDLE, 0); - litest_assert_button_event(li, BTN_MIDDLE, 1); - litest_assert_button_event(li, BTN_MIDDLE, 0); + litest_wait_for_event(li); + + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); + ptime = libinput_event_pointer_get_time(ptrev); + libinput_event_destroy(event); + + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_RELEASED); + rtime = libinput_event_pointer_get_time(ptrev); + libinput_event_destroy(event); + + ck_assert_int_lt(ptime, rtime); litest_assert_empty_queue(li); } From 578c4e81c2606abb969972186b013f67fb152040 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 30 Apr 2015 15:23:34 +1000 Subject: [PATCH 32/46] filter: pass last_velocity as argument Let the caller set the various fields, here we just calculate stuff. No functional changes. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/filter.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/filter.c b/src/filter.c index 0cdcb63e..fe862155 100644 --- a/src/filter.c +++ b/src/filter.c @@ -197,17 +197,20 @@ acceleration_profile(struct pointer_accelerator *accel, static double calculate_acceleration(struct pointer_accelerator *accel, - void *data, double velocity, uint64_t time) + void *data, + double velocity, + double last_velocity, + uint64_t time) { double factor; /* Use Simpson's rule to calculate the avarage acceleration between * the previous motion and the most recent. */ factor = acceleration_profile(accel, data, velocity, time); - factor += acceleration_profile(accel, data, accel->last_velocity, time); + factor += acceleration_profile(accel, data, last_velocity, time); factor += 4.0 * acceleration_profile(accel, data, - (accel->last_velocity + velocity) / 2, + (last_velocity + velocity) / 2, time); factor = factor / 6.0; @@ -228,7 +231,11 @@ accelerator_filter(struct motion_filter *filter, feed_trackers(accel, unaccelerated, time); velocity = calculate_velocity(accel, time); - accel_value = calculate_acceleration(accel, data, velocity, time); + accel_value = calculate_acceleration(accel, + data, + velocity, + accel->last_velocity, + time); accelerated.x = accel_value * unaccelerated->x; accelerated.y = accel_value * unaccelerated->y; From a81051e5136aeb23ce0ed85e387ae2d9b9447faa Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 22 Apr 2015 11:46:57 +1000 Subject: [PATCH 33/46] filter: up the motion timeout to 1 second This timeout defines how far back in the events we search for velocity calculations. For really slow movements, 300ms is not enough. It causes the velocity to be 0 -> accel factor of 0 -> no movement. As a result, really slow movement does not move the cursor. Up the timeout to 1 second instead. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/filter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/filter.c b/src/filter.c index fe862155..3845c7ff 100644 --- a/src/filter.c +++ b/src/filter.c @@ -78,7 +78,7 @@ filter_get_speed(struct motion_filter *filter) */ #define MAX_VELOCITY_DIFF 1.0 /* units/ms */ -#define MOTION_TIMEOUT 300 /* (ms) */ +#define MOTION_TIMEOUT 1000 /* (ms) */ #define NUM_POINTER_TRACKERS 16 struct pointer_tracker { From 289e4675c81d2fe32650295ce2b6a66a1ebb9f24 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 22 Apr 2015 12:25:13 +1000 Subject: [PATCH 34/46] filter: enforce minimum velocity In the current code, a timeout or direction change on the first tracker will result in a velocity of 0. Really slow movements will thus always be zero, and the first event after a direction is swallowed. Enforce a minimum velocity: In the case of a timeout, assume the current velocity is that of distance/timeout. In the case of a direction change, the velocity is simply that since the last tracker. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/filter.c | 34 ++++++++++++++++++++++++++++++---- test/touchpad.c | 10 +++++----- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/src/filter.c b/src/filter.c index 3845c7ff..c54d866c 100644 --- a/src/filter.c +++ b/src/filter.c @@ -144,6 +144,24 @@ calculate_tracker_velocity(struct pointer_tracker *tracker, uint64_t time) return normalized_length(tracker->delta) / tdelta; /* units/ms */ } +static inline double +calculate_velocity_after_timeout(struct pointer_tracker *tracker) +{ + /* First movement after timeout needs special handling. + * + * When we trigger the timeout, the last event is too far in the + * past to use it for velocity calculation across multiple tracker + * values. + * + * Use the motion timeout itself to calculate the speed rather than + * the last tracker time. This errs on the side of being too fast + * for really slow movements but provides much more useful initial + * movement in normal use-cases (pause, move, pause, move) + */ + return calculate_tracker_velocity(tracker, + tracker->time + MOTION_TIMEOUT); +} + static double calculate_velocity(struct pointer_accelerator *accel, uint64_t time) { @@ -163,15 +181,23 @@ calculate_velocity(struct pointer_accelerator *accel, uint64_t time) /* Stop if too far away in time */ if (time - tracker->time > MOTION_TIMEOUT || - tracker->time > time) + tracker->time > time) { + if (offset == 1) + result = calculate_velocity_after_timeout(tracker); break; + } + + velocity = calculate_tracker_velocity(tracker, time); /* Stop if direction changed */ dir &= tracker->dir; - if (dir == 0) + if (dir == 0) { + /* First movement after dirchange - velocity is that + * of the last movement */ + if (offset == 1) + result = velocity; break; - - velocity = calculate_tracker_velocity(tracker, time); + } if (initial_velocity == 0.0) { result = initial_velocity = velocity; diff --git a/test/touchpad.c b/test/touchpad.c index 5579c043..a7479100 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -2959,7 +2959,7 @@ START_TEST(touchpad_edge_scroll) litest_touch_up(dev, 0); libinput_dispatch(li); - litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, 10); + litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, 4); litest_assert_empty_queue(li); litest_touch_down(dev, 0, 99, 80); @@ -2967,7 +2967,7 @@ START_TEST(touchpad_edge_scroll) litest_touch_up(dev, 0); libinput_dispatch(li); - litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, -10); + litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, -4); litest_assert_empty_queue(li); litest_touch_down(dev, 0, 20, 99); @@ -2975,7 +2975,7 @@ START_TEST(touchpad_edge_scroll) litest_touch_up(dev, 0); libinput_dispatch(li); - litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, 10); + litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, 4); litest_assert_empty_queue(li); litest_touch_down(dev, 0, 70, 99); @@ -2983,7 +2983,7 @@ START_TEST(touchpad_edge_scroll) litest_touch_up(dev, 0); libinput_dispatch(li); - litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, -10); + litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, -4); litest_assert_empty_queue(li); } END_TEST @@ -3065,7 +3065,7 @@ START_TEST(touchpad_edge_scroll_no_motion) litest_touch_up(dev, 0); libinput_dispatch(li); - litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, 5); + litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, 4); litest_assert_empty_queue(li); } END_TEST From cd33b3611b5c71fb2ea69a7e5bdf164f9064c95a Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 2 Jun 2015 09:53:00 +1000 Subject: [PATCH 35/46] Add a CODING_STYLE document Signed-off-by: Peter Hutterer --- CODING_STYLE | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 CODING_STYLE diff --git a/CODING_STYLE b/CODING_STYLE new file mode 100644 index 00000000..1a64ffe6 --- /dev/null +++ b/CODING_STYLE @@ -0,0 +1,79 @@ +- Indentation in tabs, 8 characters wide, spaces after the tabs where + vertical alignment is required (see below) + +- Max line width 80ch, do not break up printed strings though + +- Break up long lines at logical groupings, one line for each logical group + + int a = somelongname() + + someotherlongname(); + + if (a < 0 && + (b > 20 & d < 10) && + d != 0.0) + + + somelongfunctioncall(arg1, + arg2, + arg3); + +- Function declarations: return type on separate line, {} on separate line, + arguments broken up as above. + + static inline int + foobar(int a, int b) + { + + } + + void + somenamethatiswaytoolong(int a, + int b, + int c) + { + } + +- /* comments only */, no // comments + +- variable_name, not VariableName or variableName. same for functions. + +- no typedefs of structs, enums, unions + +- if it generates a compiler warning, it needs to be fixed +- if it generates a static checker warning, it needs to be fixed or + commented + +- declare variables at the top, try to keep them as local as possible. + Exception: if the same variable is re-used in multiple blocks, declare it + at the top. + + int a; + int c; + + if (foo) { + int b; + + c = get_value(); + usevalue(c); + } + + if (bar) { + c = get_value(); + useit(c); + } + +- public functions MUST be doxygen-commented, use doxygen's @foo rather than + \foo notation + +- include "config.h" comes first, followed by system headers, followed by + external library headers, followed by internal headers. + sort alphabetically where it makes sense (specifically system headers) + + #include "config.h" + + #include + #include + + #include + + #include "libinput-private.h" From edb69067efba4626b31dc77958a45995886c6f42 Mon Sep 17 00:00:00 2001 From: "Jon A. Cruz" Date: Mon, 1 Jun 2015 18:04:59 -0700 Subject: [PATCH 36/46] test: check getcwd() and system() return values in litest Added code to check for errors in getcwd() and system() that were previously ignored and silently dropped. Signed-off-by: Jon A. Cruz Reviewed-by: Peter Hutterer Signed-off-by: Peter Hutterer --- test/litest.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/test/litest.c b/test/litest.c index 30d30566..d457a272 100644 --- a/test/litest.c +++ b/test/litest.c @@ -84,8 +84,10 @@ litest_backtrace_get_lineno(const char *executable, char *s; unsigned int i; - if (!cwd[0]) - getcwd(cwd, sizeof(cwd)); + if (!cwd[0]) { + if (getcwd(cwd, sizeof(cwd)) == NULL) + cwd[0] = 0; /* contents otherwise undefined. */ + } sprintf (buffer, ADDR2LINE " -C -e %s -i %lx", @@ -375,7 +377,17 @@ static struct list all_tests; static void litest_reload_udev_rules(void) { - system("udevadm control --reload-rules"); + int ret = system("udevadm control --reload-rules"); + if (ret == -1) { + litest_abort_msg("Failed to execute: udevadm"); + } else if (WIFEXITED(ret)) { + if (WEXITSTATUS(ret)) + litest_abort_msg("udevadm failed with %d", + WEXITSTATUS(ret)); + } else if (WIFSIGNALED(ret)) { + litest_abort_msg("udevadm terminated with signal %d", + WTERMSIG(ret)); + } } static int From b8518f8f7c1611c58badb9d73e66d9c722849b55 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 2 Jun 2015 13:04:44 +1000 Subject: [PATCH 37/46] touchpad: reduce tap-n-drag timeout to 300ms The current 500ms is too long, reduce it to 300ms instead. This is still long enough to get multiple movements but not that long that it feels like the button is stuck. https://bugs.freedesktop.org/show_bug.cgi?id=90613 Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad-tap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index 55b79160..b51f083a 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -37,7 +37,7 @@ #define CASE_RETURN_STRING(a) case a: return #a #define DEFAULT_TAP_TIMEOUT_PERIOD 180 -#define DEFAULT_DRAG_TIMEOUT_PERIOD 500 +#define DEFAULT_DRAG_TIMEOUT_PERIOD 300 #define DEFAULT_TAP_MOVE_THRESHOLD TP_MM_TO_DPI_NORMALIZED(3) enum tap_event { From 9d84dddf91dcbcbf8ed70f143520998d5f775814 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 2 Jun 2015 15:45:37 +1000 Subject: [PATCH 38/46] doc: add a graphic to explain tap-n-drag Signed-off-by: Peter Hutterer --- doc/Makefile.am | 1 + doc/svg/tap-n-drag.svg | 810 +++++++++++++++++++++++++++++++++++++++++ doc/tapping.dox | 23 +- 3 files changed, 826 insertions(+), 8 deletions(-) create mode 100644 doc/svg/tap-n-drag.svg diff --git a/doc/Makefile.am b/doc/Makefile.am index 9407f133..58ae2161 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -33,6 +33,7 @@ diagram_files = \ $(srcdir)/svg/button-scrolling.svg \ $(srcdir)/svg/edge-scrolling.svg \ $(srcdir)/svg/palm-detection.svg \ + $(srcdir)/svg/tap-n-drag.svg \ $(srcdir)/svg/top-software-buttons.svg \ $(srcdir)/svg/twofinger-scrolling.svg diff --git a/doc/svg/tap-n-drag.svg b/doc/svg/tap-n-drag.svg new file mode 100644 index 00000000..5c84beb7 --- /dev/null +++ b/doc/svg/tap-n-drag.svg @@ -0,0 +1,810 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + a) Tap + b) Hold down + c) Move + d) Reset finger, move (optional) + f) Tap to end (optional) + e) Release + diff --git a/doc/tapping.dox b/doc/tapping.dox index 7eb81e62..1337fc53 100644 --- a/doc/tapping.dox +++ b/doc/tapping.dox @@ -27,15 +27,22 @@ libinput_device_config_tap_set_enabled() for details. libinput also supports "tap-and-drag" where a tap immediately followed by a finger down and that finger being held down emulates a button press. Moving -the finger around can thus drag the selected item on the screen. Lifting the -finger and putting it back down immediately (i.e. within the timeout) will -continue the dragging process, so that multiple touchpad-widths of distance -can be covered easily. If two-fingers are supported by the hardware, a -second finger can be used to drag while the first is held in-place. +the finger around can thus drag the selected item on the screen. -An alternative method to end a drag process is to tap immediately after -lifting the finger. The full sequence is thus: tap, finger down, drag, -finger up, tap. +@image html tap-n-drag.svg "Tap-and-drag process" + +The above diagram explains the process, a tap (a) followed by a finger held +down (b) starts the drag process and logically holds the left mouse button +down. A movement of the finger (c) will drag the selected item until the +finger is relased (e). If needed, the finger's position can be reset by +lifting and quickly setting it down again on the touchpad (d). This will be +interpreted as continuing move and is especially useful on small touchpads +or with slow pointer acceleration. +The release of the mouse buttons after the finger release (e) is triggered +by a timeout. To release the button immediately, simply tap again (f). + +If two fingers are supported by the hardware, a second finger can be used to +drag while the first is held in-place. @section tap_constraints Constraints while tapping From 5b940e6a3f066ff25034bcf5a83278b695ad6836 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 2 Jun 2015 16:32:41 +1000 Subject: [PATCH 39/46] evdev: always default to the middle button for button-scrolling The current code only defaulted to the middle button for those devices that used button scrolling by default, requiring the user to enable button scrolling _and_ set the button before it is active. This causes some confusion. There is no real benefit to leaving the button at 0 when the scroll method isn't enabled anyway. So always default to the middle button (if available). https://bugzilla.redhat.com/show_bug.cgi?id=1227182 Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index ed1a9a30..8932b6c2 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1112,14 +1112,7 @@ evdev_scroll_get_default_button(struct libinput_device *device) { struct evdev_device *evdev = (struct evdev_device *)device; - if (libevdev_has_property(evdev->evdev, INPUT_PROP_POINTING_STICK)) - return BTN_MIDDLE; - - /* A device that defaults to button scrolling defaults - to BTN_MIDDLE */ - if (evdev_scroll_get_default_method(device) == - LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN && - libevdev_has_event_code(evdev->evdev, EV_KEY, BTN_MIDDLE)) + if( libevdev_has_event_code(evdev->evdev, EV_KEY, BTN_MIDDLE)) return BTN_MIDDLE; return 0; From a83fe757c35b279217b07b79441e7b77434602e6 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 2 Jun 2015 17:22:41 -0400 Subject: [PATCH 40/46] evdev: remove tag_device from evdev_dispatch_interface Tagging a device should occur only once during configure. We do not have devices that can be changed after they are configured, so there is no point in having the tagging part in a deferred struct. Plus, the note saying that we tag with only one of EVDEV_TAG was wrong. Now that we are chosing when we call each evdev_tag_*, we can also get rid of the device->seat_caps tests. Signed-off-by: Benjamin Tissoires Reviewed-by: Peter Hutterer Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad.c | 7 +++---- src/evdev.c | 32 +++++++++----------------------- src/evdev.h | 8 ++++---- 3 files changed, 16 insertions(+), 31 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index eacec6e3..aa9fa57a 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -1137,9 +1137,9 @@ tp_interface_device_removed(struct evdev_device *device, tp_resume(tp, device); } -static void -tp_interface_tag_device(struct evdev_device *device, - struct udev_device *udev_device) +void +evdev_tag_touchpad(struct evdev_device *device, + struct udev_device *udev_device) { int bustype; @@ -1169,7 +1169,6 @@ static struct evdev_dispatch_interface tp_interface = { tp_interface_device_removed, tp_interface_device_removed, /* device_suspended, treat as remove */ tp_interface_device_added, /* device_resumed, treat as add */ - tp_interface_tag_device, }; static void diff --git a/src/evdev.c b/src/evdev.c index 8932b6c2..df46bb92 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -724,10 +724,8 @@ evdev_tag_external_mouse(struct evdev_device *device, int bustype; bustype = libevdev_get_id_bustype(device->evdev); - if (bustype == BUS_USB || bustype == BUS_BLUETOOTH) { - if (device->seat_caps & EVDEV_DEVICE_POINTER) - device->tags |= EVDEV_TAG_EXTERNAL_MOUSE; - } + if (bustype == BUS_USB || bustype == BUS_BLUETOOTH) + device->tags |= EVDEV_TAG_EXTERNAL_MOUSE; } static void @@ -852,15 +850,6 @@ fallback_destroy(struct evdev_dispatch *dispatch) free(dispatch); } -static void -fallback_tag_device(struct evdev_device *device, - struct udev_device *udev_device) -{ - evdev_tag_external_mouse(device, udev_device); - evdev_tag_trackpoint(device, udev_device); - evdev_tag_keyboard(device, udev_device); -} - static int evdev_calibration_has_matrix(struct libinput_device *libinput_device) { @@ -911,7 +900,6 @@ struct evdev_dispatch_interface fallback_interface = { NULL, /* device_removed */ NULL, /* device_suspended */ NULL, /* device_resumed */ - fallback_tag_device, }; static uint32_t @@ -1432,14 +1420,6 @@ evdev_need_mtdev(struct evdev_device *device) !libevdev_has_event_code(evdev, EV_ABS, ABS_MT_SLOT)); } -static void -evdev_tag_device(struct evdev_device *device) -{ - if (device->dispatch->interface->tag_device) - device->dispatch->interface->tag_device(device, - device->udev_device); -} - static inline int evdev_read_wheel_click_prop(struct evdev_device *device) { @@ -1902,6 +1882,8 @@ evdev_configure_device(struct evdev_device *device) log_info(libinput, "input device '%s', %s is a touchpad\n", device->devname, devnode); + + evdev_tag_touchpad(device, device->udev_device); return device->dispatch == NULL ? -1 : 0; } @@ -1926,6 +1908,9 @@ evdev_configure_device(struct evdev_device *device) device->scroll.natural_scrolling_enabled = true; /* want button scrolling config option */ device->scroll.want_button = 1; + + evdev_tag_external_mouse(device, device->udev_device); + evdev_tag_trackpoint(device, device->udev_device); } if (udev_tags & EVDEV_UDEV_TAG_KEYBOARD) { @@ -1940,6 +1925,8 @@ evdev_configure_device(struct evdev_device *device) device->scroll.natural_scrolling_enabled = true; device->seat_caps |= EVDEV_DEVICE_POINTER; } + + evdev_tag_keyboard(device, device->udev_device); } if (udev_tags & EVDEV_UDEV_TAG_TOUCHSCREEN) { @@ -2124,7 +2111,6 @@ evdev_device_create(struct libinput_seat *seat, list_insert(seat->devices_list.prev, &device->base.link); - evdev_tag_device(device); evdev_notify_added_device(device); return device; diff --git a/src/evdev.h b/src/evdev.h index 3f63c57a..a8756635 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -256,10 +256,6 @@ struct evdev_dispatch_interface { /* A device was resumed */ void (*device_resumed)(struct evdev_device *device, struct evdev_device *resumed_device); - - /* Tag device with one of EVDEV_TAG */ - void (*tag_device)(struct evdev_device *device, - struct udev_device *udev_device); }; struct evdev_dispatch { @@ -293,6 +289,10 @@ evdev_touchpad_create(struct evdev_device *device); struct evdev_dispatch * evdev_mt_touchpad_create(struct evdev_device *device); +void +evdev_tag_touchpad(struct evdev_device *device, + struct udev_device *udev_device); + void evdev_device_led_update(struct evdev_device *device, enum libinput_led leds); From 363ff3a52b075f9a44210188aad1169a027e6a7c Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 2 Jun 2015 17:22:42 -0400 Subject: [PATCH 41/46] evdev: remove direct checks for INPUT_PROP_POINTING_STICK If we need to temporary override a device with ID_INPUT_POINTINGSTICK, evdev sets the tag EVDEV_TAG_TRACKPOINT to the device. Rely on the tag to behave properly for scroll emulation. The dpi information should be retrieved after the device has been configured or the tag EVDEV_TAG_TRACKPOINT was not set. Signed-off-by: Benjamin Tissoires Reviewed-by: Peter Hutterer Signed-off-by: Peter Hutterer --- src/evdev.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index df46bb92..d6a2fff5 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1060,7 +1060,7 @@ evdev_scroll_get_default_method(struct libinput_device *device) { struct evdev_device *evdev = (struct evdev_device *)device; - if (libevdev_has_property(evdev->evdev, INPUT_PROP_POINTING_STICK)) + if (evdev->tags & EVDEV_TAG_TRACKPOINT) return LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN; /* Mice without a scroll wheel but with middle button have on-button @@ -1480,7 +1480,7 @@ evdev_read_dpi_prop(struct evdev_device *device) * POINTINGSTICK_CONST_ACCEL value to compensate for sensitivity * differences between models, we translate this to a fake dpi. */ - if (libevdev_has_property(device->evdev, INPUT_PROP_POINTING_STICK)) + if (device->tags & EVDEV_TAG_TRACKPOINT) return evdev_get_trackpoint_dpi(device); mouse_dpi = udev_device_get_property_value(device->udev_device, @@ -2078,7 +2078,6 @@ evdev_device_create(struct libinput_seat *seat, device->scroll.direction = 0; device->scroll.wheel_click_angle = evdev_read_wheel_click_prop(device); - device->dpi = evdev_read_dpi_prop(device); device->model = evdev_read_model(device); /* at most 5 SYN_DROPPED log-messages per 30s */ ratelimit_init(&device->syn_drop_limit, 30ULL * 1000, 5); @@ -2090,6 +2089,8 @@ evdev_device_create(struct libinput_seat *seat, if (evdev_configure_device(device) == -1) goto err; + device->dpi = evdev_read_dpi_prop(device); + if (device->seat_caps == 0) { unhandled_device = 1; goto err; From 75762a7c74fd7526dc033e0ff9f95700546eaed2 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 3 Jun 2015 11:30:11 +1000 Subject: [PATCH 42/46] touchpad: replace hardcoded resolution > 1 Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index aa9fa57a..24f33f46 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -1273,7 +1273,7 @@ tp_init_accel(struct tp_dispatch *tp, double diagonal) * and y resolution, so that a circle on the * touchpad does not turn into an elipse on the screen. */ - if (res_x > 1 && res_y > 1) { + if (!tp->device->abs.fake_resolution) { tp->accel.x_scale_coeff = (DEFAULT_MOUSE_DPI/25.4) / res_x; tp->accel.y_scale_coeff = (DEFAULT_MOUSE_DPI/25.4) / res_y; } else { From b2a6ead9929f8cdf0778a942ffcfd6e75bc76294 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 3 Jun 2015 11:06:54 +1000 Subject: [PATCH 43/46] touchpad: move clickfinger finger decision into a helper function No functional changes. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad-buttons.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index 4b1d6ab5..f0c39b62 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -784,6 +784,24 @@ tp_post_physical_buttons(struct tp_dispatch *tp, uint64_t time) return 0; } +static uint32_t +tp_clickfinger_set_button(struct tp_dispatch *tp) +{ + uint32_t button; + + switch (tp->nfingers_down) { + case 0: + case 1: button = BTN_LEFT; break; + case 2: button = BTN_RIGHT; break; + case 3: button = BTN_MIDDLE; break; + default: + button = 0; + break; + } + + return button; +} + static int tp_notify_clickpadbutton(struct tp_dispatch *tp, uint64_t time, @@ -818,14 +836,7 @@ tp_notify_clickpadbutton(struct tp_dispatch *tp, */ if (tp->buttons.click_method == LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER && state == LIBINPUT_BUTTON_STATE_PRESSED) { - switch (tp->nfingers_down) { - case 0: - case 1: button = BTN_LEFT; break; - case 2: button = BTN_RIGHT; break; - case 3: button = BTN_MIDDLE; break; - default: - button = 0; - } + button = tp_clickfinger_set_button(tp); tp->buttons.active = button; if (!button) From df83144457176e454953d2a580a8f28e73f4a677 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 3 Jun 2015 12:15:51 +1000 Subject: [PATCH 44/46] touchpad: impose maximum distance limits on clickfingers A common use-case for clickfinger is to use the index finger for moving the pointer, then triggering the click with a thumb. If the index finger isn't lifted before the click this counted as two-finger click. To avoid this, check the distance between touches on the touchpad (on touchpads reporting resolution values anyway). If the touches are too far apart, don't count them together (or specifically only count those close enough together as multi-finger). The touch area is uneven, it's wider than high. Spreading fingers horizontally is more common and this also makes it easier to rule out thumbs which tend to be well below the fingers. http://bugs.freedesktop.org/show_bug.cgi?id=90526 Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- doc/clickpad-softbuttons.dox | 13 +++- doc/svg/clickfinger-distance.svg | 106 +++++++++++++++++++++++++++++++ src/evdev-mt-touchpad-buttons.c | 71 ++++++++++++++++++++- test/touchpad.c | 48 +++++++++++++- 4 files changed, 233 insertions(+), 5 deletions(-) create mode 100644 doc/svg/clickfinger-distance.svg diff --git a/doc/clickpad-softbuttons.dox b/doc/clickpad-softbuttons.dox index e7c4e543..a4f2e444 100644 --- a/doc/clickpad-softbuttons.dox +++ b/doc/clickpad-softbuttons.dox @@ -64,9 +64,16 @@ software-defined button areas. @image html clickfinger.svg "One, two and three-finger click with Clickfinger behavior" -The Xorg synaptics driver uses 30% of the touchpad dimensions as threshold, -libinput does not have this restriction. If two fingers are on the pad -while clicking, that is a two-finger click. +On some touchpads, libinput imposes a limit on how the fingers may be placed +on the touchpad. In the most common use-case this allows for a user to +trigger a click with the thumb while leaving the pointer-moving finger on +the touchpad. + +@image html clickfinger-distance.svg "Illustration of the distance detection algorithm" + +In the illustration above the red area marks the proximity area around the +first finger. Since the thumb is outside of that area libinput considers the +click a single-finger click rather than a two-finger click. Clickfinger configuration can be enabled through the libinput_device_config_click_set_method() call. If clickfingers are diff --git a/doc/svg/clickfinger-distance.svg b/doc/svg/clickfinger-distance.svg new file mode 100644 index 00000000..ac659cfe --- /dev/null +++ b/doc/svg/clickfinger-distance.svg @@ -0,0 +1,106 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index f0c39b62..469f0fa5 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -784,12 +784,81 @@ tp_post_physical_buttons(struct tp_dispatch *tp, uint64_t time) return 0; } +static inline int +tp_check_clickfinger_distance(struct tp_dispatch *tp, + struct tp_touch *t1, + struct tp_touch *t2) +{ + int res_x, res_y; + double x, y; + + if (!t1 || !t2) + return 0; + + /* no resolution, so let's assume they're close enough together */ + if (tp->device->abs.fake_resolution) + return 1; + + res_x = tp->device->abs.absinfo_x->resolution; + res_y = tp->device->abs.absinfo_y->resolution; + + x = abs(t1->point.x - t2->point.x)/res_x; + y = abs(t1->point.y - t2->point.y)/res_y; + + /* maximum spread is 40mm horiz, 20mm vert. Anything wider than that + * is probably a gesture. The y spread is small so we ignore clicks + * with thumbs at the bottom of the touchpad while the pointer + * moving finger is still on the pad */ + return (x < 40 && y < 20) ? 1 : 0; +} + static uint32_t tp_clickfinger_set_button(struct tp_dispatch *tp) { uint32_t button; + unsigned int nfingers = tp->nfingers_down; + struct tp_touch *t; + struct tp_touch *first = NULL, + *second = NULL, + *third = NULL; + uint32_t close_touches = 0; - switch (tp->nfingers_down) { + if (nfingers < 2 || nfingers > 3) + goto out; + + /* two or three fingers down on the touchpad. Check for distance + * between the fingers. */ + tp_for_each_touch(tp, t) { + if (t->state != TOUCH_BEGIN && t->state != TOUCH_UPDATE) + continue; + + if (!first) + first = t; + else if (!second) + second = t; + else if (!third) { + third = t; + break; + } + } + + if (!first || !second) { + nfingers = 1; + goto out; + } + + close_touches |= tp_check_clickfinger_distance(tp, first, second) << 0; + close_touches |= tp_check_clickfinger_distance(tp, second, third) << 1; + close_touches |= tp_check_clickfinger_distance(tp, first, third) << 2; + + switch(__builtin_popcount(close_touches)) { + case 0: nfingers = 1; break; + case 1: nfingers = 2; break; + default: nfingers = 3; break; + } + +out: + switch (nfingers) { case 0: case 1: button = BTN_LEFT; break; case 2: button = BTN_RIGHT; break; diff --git a/test/touchpad.c b/test/touchpad.c index a7479100..d9daf434 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -1808,6 +1808,51 @@ START_TEST(touchpad_2fg_clickfinger) } END_TEST +START_TEST(touchpad_2fg_clickfinger_distance) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + libinput_device_config_click_set_method(dev->libinput_device, + LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER); + litest_drain_events(li); + + litest_touch_down(dev, 0, 90, 50); + litest_touch_down(dev, 1, 10, 50); + litest_event(dev, EV_KEY, BTN_LEFT, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_event(dev, EV_KEY, BTN_LEFT, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_touch_up(dev, 0); + litest_touch_up(dev, 1); + + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + + litest_assert_empty_queue(li); + + litest_touch_down(dev, 0, 50, 5); + litest_touch_down(dev, 1, 50, 95); + litest_event(dev, EV_KEY, BTN_LEFT, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_event(dev, EV_KEY, BTN_LEFT, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_touch_up(dev, 0); + litest_touch_up(dev, 1); + + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); +} +END_TEST + START_TEST(touchpad_clickfinger_to_area_method) { struct litest_device *dev = litest_current_device(); @@ -2636,7 +2681,7 @@ START_TEST(clickpad_topsoftbuttons_clickfinger) litest_assert_empty_queue(li); litest_touch_down(dev, 0, 90, 5); - litest_touch_down(dev, 1, 10, 5); + litest_touch_down(dev, 1, 80, 5); litest_event(dev, EV_KEY, BTN_LEFT, 1); litest_event(dev, EV_SYN, SYN_REPORT, 0); litest_event(dev, EV_KEY, BTN_LEFT, 0); @@ -5084,6 +5129,7 @@ litest_setup_tests(void) litest_add("touchpad:clickfinger", touchpad_1fg_clickfinger, LITEST_CLICKPAD, LITEST_ANY); litest_add("touchpad:clickfinger", touchpad_1fg_clickfinger_no_touch, LITEST_CLICKPAD, LITEST_ANY); litest_add("touchpad:clickfinger", touchpad_2fg_clickfinger, LITEST_CLICKPAD, LITEST_ANY); + litest_add("touchpad:clickfinger", touchpad_2fg_clickfinger_distance, LITEST_CLICKPAD, LITEST_APPLE_CLICKPAD); litest_add("touchpad:clickfinger", touchpad_clickfinger_to_area_method, LITEST_CLICKPAD, LITEST_ANY); litest_add("touchpad:clickfinger", touchpad_clickfinger_to_area_method_while_down, LITEST_CLICKPAD, LITEST_ANY); From 8af1f4b08536ca2d74950026eb4d210142dbf6fc Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 3 Jun 2015 14:27:43 +1000 Subject: [PATCH 45/46] touchpad: on non-resolution touchpads, use 30% as maximum clickfinger spread Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad-buttons.c | 35 ++++++++++++++++++++++----------- test/touchpad.c | 2 +- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index 469f0fa5..5786ea8b 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -789,27 +789,38 @@ tp_check_clickfinger_distance(struct tp_dispatch *tp, struct tp_touch *t1, struct tp_touch *t2) { - int res_x, res_y; double x, y; if (!t1 || !t2) return 0; + x = abs(t1->point.x - t2->point.x); + y = abs(t1->point.y - t2->point.y); + /* no resolution, so let's assume they're close enough together */ - if (tp->device->abs.fake_resolution) - return 1; + if (tp->device->abs.fake_resolution) { + int w, h; - res_x = tp->device->abs.absinfo_x->resolution; - res_y = tp->device->abs.absinfo_y->resolution; + /* Use a maximum of 30% of the touchpad width or height if + * we dont' have resolution. */ + w = tp->device->abs.absinfo_x->maximum - + tp->device->abs.absinfo_x->minimum; + h = tp->device->abs.absinfo_y->maximum - + tp->device->abs.absinfo_y->minimum; - x = abs(t1->point.x - t2->point.x)/res_x; - y = abs(t1->point.y - t2->point.y)/res_y; + return (x < w * 0.3 && y < h * 0.3) ? 1 : 0; + } else { + /* maximum spread is 40mm horiz, 20mm vert. Anything wider than that + * is probably a gesture. The y spread is small so we ignore clicks + * with thumbs at the bottom of the touchpad while the pointer + * moving finger is still on the pad */ + + x /= tp->device->abs.absinfo_x->resolution; + y /= tp->device->abs.absinfo_y->resolution; + + return (x < 40 && y < 20) ? 1 : 0; + } - /* maximum spread is 40mm horiz, 20mm vert. Anything wider than that - * is probably a gesture. The y spread is small so we ignore clicks - * with thumbs at the bottom of the touchpad while the pointer - * moving finger is still on the pad */ - return (x < 40 && y < 20) ? 1 : 0; } static uint32_t diff --git a/test/touchpad.c b/test/touchpad.c index d9daf434..692698ce 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -5129,7 +5129,7 @@ litest_setup_tests(void) litest_add("touchpad:clickfinger", touchpad_1fg_clickfinger, LITEST_CLICKPAD, LITEST_ANY); litest_add("touchpad:clickfinger", touchpad_1fg_clickfinger_no_touch, LITEST_CLICKPAD, LITEST_ANY); litest_add("touchpad:clickfinger", touchpad_2fg_clickfinger, LITEST_CLICKPAD, LITEST_ANY); - litest_add("touchpad:clickfinger", touchpad_2fg_clickfinger_distance, LITEST_CLICKPAD, LITEST_APPLE_CLICKPAD); + litest_add("touchpad:clickfinger", touchpad_2fg_clickfinger_distance, LITEST_CLICKPAD, LITEST_ANY); litest_add("touchpad:clickfinger", touchpad_clickfinger_to_area_method, LITEST_CLICKPAD, LITEST_ANY); litest_add("touchpad:clickfinger", touchpad_clickfinger_to_area_method_while_down, LITEST_CLICKPAD, LITEST_ANY); From 3d2264ef28cbee51b3d7f45fc1304e7e98f19f68 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 4 Jun 2015 10:20:25 +1000 Subject: [PATCH 46/46] configure.ac: libinput 0.17.0 Signed-off-by: Peter Hutterer --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 61a41963..b380ef5c 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ AC_PREREQ([2.64]) m4_define([libinput_major_version], [0]) -m4_define([libinput_minor_version], [16]) +m4_define([libinput_minor_version], [17]) m4_define([libinput_micro_version], [0]) m4_define([libinput_version], [libinput_major_version.libinput_minor_version.libinput_micro_version]) @@ -31,7 +31,7 @@ AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz]) # b) If interfaces have been changed or added, but binary compatibility has # been preserved, change to C+1:0:A+1 # c) If the interface is the same as the previous version, change to C:R+1:A -LIBINPUT_LT_VERSION=12:1:2 +LIBINPUT_LT_VERSION=12:2:2 AC_SUBST(LIBINPUT_LT_VERSION) AM_SILENT_RULES([yes])