From f179c70bb5b97185997d68ec373f74ded576f836 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 21 Jul 2014 14:35:42 +1000 Subject: [PATCH 01/91] test: touchpads are too small for palm if we can't get the dimensions Signed-off-by: Peter Hutterer --- test/touchpad.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/touchpad.c b/test/touchpad.c index c1bdbd53..9b232bf4 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -1245,10 +1245,11 @@ static int touchpad_has_palm_detect_size(struct litest_device *dev) { double width, height; + int rc; - libinput_device_get_size(dev->libinput_device, &width, &height); + rc = libinput_device_get_size(dev->libinput_device, &width, &height); - return width >= 80; + return rc == 0 && width >= 80; } START_TEST(touchpad_palm_detect_at_edge) From 0915c2c51a8b3c86c8a19abe9e572af5b58bd4ec Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 21 Jul 2014 14:20:35 +1000 Subject: [PATCH 02/91] touchpad: always enable palm detection on apple touchpads They don't set resolution so we can't calculate the size but we know they're big enough to need palm detection. And fix the descriptor for the bcm5974. For some reason this was advertising synaptics coordinates. Fix it to represent (one of) the apple touchpads. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad-buttons.c | 2 +- src/evdev-mt-touchpad.c | 19 +++++++++++-------- src/evdev-mt-touchpad.h | 2 ++ test/litest-bcm5974.c | 19 ++++++++++++------- test/touchpad.c | 3 +++ 5 files changed, 29 insertions(+), 16 deletions(-) diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index bd3c0e25..fe33d0b1 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -521,7 +521,7 @@ tp_init_buttons(struct tp_dispatch *tp, tp->buttons.motion_dist = diagonal * DEFAULT_BUTTON_MOTION_THRESHOLD; - if (libevdev_get_id_vendor(device->evdev) == 0x5ac) /* Apple */ + if (libevdev_get_id_vendor(device->evdev) == VENDOR_ID_APPLE) tp->buttons.use_clickfinger = true; if (tp->buttons.is_clickpad && !tp->buttons.use_clickfinger) { diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 5db204f6..0755f2cd 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -757,17 +757,20 @@ tp_init_palmdetect(struct tp_dispatch *tp, tp->palm.right_edge = INT_MAX; tp->palm.left_edge = INT_MIN; - /* We don't know how big the touchpad is */ - if (device->abs.absinfo_x->resolution == 1) - return 0; - width = abs(device->abs.absinfo_x->maximum - device->abs.absinfo_x->minimum); - /* Enable palm detection on touchpads >= 80 mm. Anything smaller - probably won't need it, until we find out it does */ - if (width/device->abs.absinfo_x->resolution < 80) - return 0; + /* Apple touchpads are always big enough to warrant palm detection */ + if (evdev_device_get_id_vendor(device) != VENDOR_ID_APPLE) { + /* We don't know how big the touchpad is */ + if (device->abs.absinfo_x->resolution == 1) + return 0; + + /* Enable palm detection on touchpads >= 80 mm. Anything smaller + probably won't need it, until we find out it does */ + if (width/device->abs.absinfo_x->resolution < 80) + return 0; + } /* palm edges are 5% of the width on each side */ tp->palm.right_edge = device->abs.absinfo_x->maximum - width * 0.05; diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 47791201..192c401a 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -33,6 +33,8 @@ #define TOUCHPAD_HISTORY_LENGTH 4 #define TOUCHPAD_MIN_SAMPLES 4 +#define VENDOR_ID_APPLE 0x5ac + enum touchpad_event { TOUCHPAD_EVENT_NONE = 0, TOUCHPAD_EVENT_MOTION = (1 << 0), diff --git a/test/litest-bcm5974.c b/test/litest-bcm5974.c index 10a9eb46..c7bd8573 100644 --- a/test/litest-bcm5974.c +++ b/test/litest-bcm5974.c @@ -65,13 +65,18 @@ static struct litest_device_interface interface = { }; static struct input_absinfo absinfo[] = { - { ABS_X, 1472, 5472, 0, 0, 75 }, - { ABS_Y, 1408, 4448, 0, 0, 129 }, - { ABS_PRESSURE, 0, 255, 0, 0, 0 }, - { ABS_TOOL_WIDTH, 0, 15, 0, 0, 0 }, - { ABS_MT_SLOT, 0, 1, 0, 0, 0 }, - { ABS_MT_POSITION_X, 1472, 5472, 0, 0, 75 }, - { ABS_MT_POSITION_Y, 1408, 4448, 0, 0, 129 }, + { ABS_X, -4824, 4824, 0, 0, 0 }, + { ABS_Y, -172, 4290, 0, 0, 0 }, + { ABS_PRESSURE, 0, 256, 5, 0, 0 }, + { ABS_TOOL_WIDTH, 0, 16, 0, 0, 0 }, + { ABS_MT_SLOT, 0, 15, 0, 0, 0 }, + { ABS_MT_POSITION_X, -4824, 4824, 17, 0, 0 }, + { ABS_MT_POSITION_Y, -172, 4290, 17, 0, 0 }, + { ABS_MT_ORIENTATION, -16384, 16384, 3276, 0, 0 }, + { ABS_MT_TOUCH_MAJOR, 0, 2048, 81, 0, 0 }, + { ABS_MT_TOUCH_MINOR, 0, 2048, 81, 0, 0 }, + { ABS_MT_WIDTH_MAJOR, 0, 2048, 81, 0, 0 }, + { ABS_MT_WIDTH_MINOR, 0, 2048, 81, 0, 0 }, { ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 }, { ABS_MT_PRESSURE, 0, 255, 0, 0, 0 }, { .value = -1 }, diff --git a/test/touchpad.c b/test/touchpad.c index 9b232bf4..0de3cbb2 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -1247,6 +1247,9 @@ 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 */ + return 1; + rc = libinput_device_get_size(dev->libinput_device, &width, &height); return rc == 0 && width >= 80; From b64fb73ace5a01576670571200f14cf1275a6bd7 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 18 Jul 2014 11:06:36 +0200 Subject: [PATCH 03/91] touchpad: Protect tp_begin_touch and tp_end_touch against being called twice They were already protected against being called twice between SYN_REPORT, but not for being called twice before a SYN_REPORT arrives. This allows simplifying tp_process_fake_touch a bit. Note while at it also also flip the if condition in tp_process_fake_touch so that the if is true when the finger is down, and remove the bogus t->state = TOUCH_NONE. Signed-off-by: Hans de Goede Reviewed-by: Peter Hutterer Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad.c | 47 ++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 0755f2cd..6466c1bd 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -128,23 +128,25 @@ tp_get_touch(struct tp_dispatch *tp, unsigned int slot) } static inline void -tp_begin_touch(struct tp_dispatch *tp, struct tp_touch *t) +tp_begin_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) { - if (t->state != TOUCH_UPDATE) { - tp_motion_history_reset(t); - t->dirty = true; - t->state = TOUCH_BEGIN; - t->pinned.is_pinned = false; - tp->nfingers_down++; - assert(tp->nfingers_down >= 1); - tp->queued |= TOUCHPAD_EVENT_MOTION; - } + if (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE) + return; + + tp_motion_history_reset(t); + t->dirty = true; + t->state = TOUCH_BEGIN; + t->pinned.is_pinned = false; + t->millis = time; + tp->nfingers_down++; + assert(tp->nfingers_down >= 1); + tp->queued |= TOUCHPAD_EVENT_MOTION; } static inline void -tp_end_touch(struct tp_dispatch *tp, struct tp_touch *t) +tp_end_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) { - if (t->state == TOUCH_NONE) + if (t->state == TOUCH_END || t->state == TOUCH_NONE) return; t->dirty = true; @@ -152,6 +154,7 @@ tp_end_touch(struct tp_dispatch *tp, struct tp_touch *t) t->palm.is_palm = false; t->state = TOUCH_END; t->pinned.is_pinned = false; + t->millis = time; assert(tp->nfingers_down >= 1); tp->nfingers_down--; tp->queued |= TOUCHPAD_EVENT_MOTION; @@ -206,11 +209,10 @@ tp_process_absolute(struct tp_dispatch *tp, tp->slot = e->value; break; case ABS_MT_TRACKING_ID: - t->millis = time; if (e->value != -1) - tp_begin_touch(tp, t); + tp_begin_touch(tp, t, time); else - tp_end_touch(tp, t); + tp_end_touch(tp, t, time); } } @@ -268,18 +270,11 @@ tp_process_fake_touch(struct tp_dispatch *tp, for (i = 0; i < tp->ntouches; i++) { t = tp_get_touch(tp, i); - if (i >= nfake_touches) { - if (t->state != TOUCH_NONE) { - tp_end_touch(tp, t); - t->millis = time; - } - } else if (t->state != TOUCH_UPDATE && - t->state != TOUCH_BEGIN) { - t->state = TOUCH_NONE; - tp_begin_touch(tp, t); - t->millis = time; + if (i < nfake_touches) { + tp_begin_touch(tp, t, time); t->fake =true; - } + } else + tp_end_touch(tp, t, time); } assert(tp->nfingers_down == nfake_touches); From 9720c16eccec922630cde4ebf20490a688b33f35 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 18 Jul 2014 11:06:37 +0200 Subject: [PATCH 04/91] touchpad: Don't process fake touches if they are not dirty Don't process fake touches, e.g. re-adding the same position to the motion history when they are not dirty. This could trigger for example on a button press. Signed-off-by: Hans de Goede Reviewed-by: Peter Hutterer Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 6466c1bd..efe24d1c 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -408,10 +408,11 @@ tp_process_state(struct tp_dispatch *tp, uint64_t time) t->y = first->y; if (!t->dirty) t->dirty = first->dirty; - } else if (!t->dirty) { - continue; } + if (!t->dirty) + continue; + tp_palm_detect(tp, t, time); tp_motion_hysteresis(tp, t); From 64f431c303fab787eaf8a7cb14752d908b3da8b2 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 18 Jul 2014 11:06:38 +0200 Subject: [PATCH 05/91] touchpad: Create fake touches for BTN_TOOL_FOO on multi-touch pads too Multi-touch pads may track less touches then they can report fingers being present through BTN_TOOL_FOO. So create fake touches for fingers reported by BTN_TOOL_FOO on multi-touch pads too (when necessary). This fixes e.g. 3 finger tap not working on the T440s. Signed-off-by: Hans de Goede Reviewed-by: Peter Hutterer Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad.c | 74 +++++++++++++++++++++-------------------- src/evdev-mt-touchpad.h | 4 +-- 2 files changed, 40 insertions(+), 38 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index efe24d1c..4d4856f6 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -247,7 +247,7 @@ tp_process_fake_touch(struct tp_dispatch *tp, struct tp_touch *t; unsigned int fake_touches; unsigned int nfake_touches; - unsigned int i; + unsigned int i, start; unsigned int shift; if (e->code != BTN_TOUCH && @@ -268,16 +268,18 @@ tp_process_fake_touch(struct tp_dispatch *tp, fake_touches >>= 1; } - for (i = 0; i < tp->ntouches; i++) { + /* For single touch tps we use BTN_TOUCH for begin / end of touch 0 */ + start = tp->has_mt ? tp->real_touches : 0; + for (i = start; i < tp->ntouches; i++) { t = tp_get_touch(tp, i); - if (i < nfake_touches) { + if (i < nfake_touches) tp_begin_touch(tp, t, time); - t->fake =true; - } else + else tp_end_touch(tp, t, time); } - assert(tp->nfingers_down == nfake_touches); + /* On mt the actual touch info may arrive after BTN_TOOL_FOO */ + assert(tp->has_mt || tp->nfingers_down == nfake_touches); } static void @@ -295,8 +297,7 @@ tp_process_key(struct tp_dispatch *tp, case BTN_TOOL_DOUBLETAP: case BTN_TOOL_TRIPLETAP: case BTN_TOOL_QUADTAP: - if (!tp->has_mt) - tp_process_fake_touch(tp, e, time); + tp_process_fake_touch(tp, e, time); break; } } @@ -401,9 +402,11 @@ tp_process_state(struct tp_dispatch *tp, uint64_t time) { struct tp_touch *t; struct tp_touch *first = tp_get_touch(tp, 0); + unsigned int i; - tp_for_each_touch(tp, t) { - if (!tp->has_mt && t != first && first->fake) { + for (i = 0; i < tp->ntouches; i++) { + t = tp_get_touch(tp, i); + if (i >= tp->real_touches && t->state != TOUCH_NONE) { t->x = first->x; t->y = first->y; if (!t->dirty) @@ -443,10 +446,9 @@ tp_post_process_state(struct tp_dispatch *tp, uint64_t time) if (!t->dirty) continue; - if (t->state == TOUCH_END) { + if (t->state == TOUCH_END) t->state = TOUCH_NONE; - t->fake = false; - } else if (t->state == TOUCH_BEGIN) + else if (t->state == TOUCH_BEGIN) t->state = TOUCH_UPDATE; t->dirty = false; @@ -642,41 +644,41 @@ static int tp_init_slots(struct tp_dispatch *tp, struct evdev_device *device) { - size_t i; const struct input_absinfo *absinfo; + struct map { + unsigned int code; + int ntouches; + } max_touches[] = { + { BTN_TOOL_QUINTTAP, 5 }, + { BTN_TOOL_QUADTAP, 4 }, + { BTN_TOOL_TRIPLETAP, 3 }, + { BTN_TOOL_DOUBLETAP, 2 }, + }; + struct map *m; + unsigned int i, n_btn_tool_touches = 1; absinfo = libevdev_get_abs_info(device->evdev, ABS_MT_SLOT); if (absinfo) { - tp->ntouches = absinfo->maximum + 1; + tp->real_touches = absinfo->maximum + 1; tp->slot = absinfo->value; tp->has_mt = true; } else { - struct map { - unsigned int code; - int ntouches; - } max_touches[] = { - { BTN_TOOL_QUINTTAP, 5 }, - { BTN_TOOL_QUADTAP, 4 }, - { BTN_TOOL_TRIPLETAP, 3 }, - { BTN_TOOL_DOUBLETAP, 2 }, - }; - struct map *m; - + tp->real_touches = 1; tp->slot = 0; tp->has_mt = false; - tp->ntouches = 1; + } - ARRAY_FOR_EACH(max_touches, m) { - if (libevdev_has_event_code(device->evdev, - EV_KEY, - m->code)) { - tp->ntouches = m->ntouches; - break; - } + ARRAY_FOR_EACH(max_touches, m) { + if (libevdev_has_event_code(device->evdev, + EV_KEY, + m->code)) { + n_btn_tool_touches = m->ntouches; + break; } } - tp->touches = calloc(tp->ntouches, - sizeof(struct tp_touch)); + + tp->ntouches = max(tp->real_touches, n_btn_tool_touches); + tp->touches = calloc(tp->ntouches, sizeof(struct tp_touch)); if (!tp->touches) return -1; diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 192c401a..af6a3a38 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -103,7 +103,6 @@ struct tp_touch { struct tp_dispatch *tp; enum touch_state state; bool dirty; - bool fake; /* a fake touch */ bool is_pointer; /* the pointer-controlling touch */ int32_t x; int32_t y; @@ -156,7 +155,8 @@ struct tp_dispatch { unsigned int slot; /* current slot */ bool has_mt; - unsigned int ntouches; /* number of slots */ + unsigned int real_touches; /* number of slots */ + unsigned int ntouches; /* no slots inc. fakes */ struct tp_touch *touches; /* len == ntouches */ unsigned int fake_touches; /* fake touch mask */ From aab902cc396f6f6e43cc2a90409992017e974c44 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 18 Jul 2014 11:06:39 +0200 Subject: [PATCH 06/91] touchpad: make tp_estimate_delta return fractions Force a cast of the input arguments to a double before the divide, rather than after the divide. Signed-off-by: Hans de Goede Reviewed-by: Peter Hutterer Signed-off-by: Peter Hutterer --- 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 4d4856f6..b0520c79 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -163,7 +163,7 @@ tp_end_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) static double tp_estimate_delta(int x0, int x1, int x2, int x3) { - return (x0 + x1 - x2 - x3) / 4; + return (x0 + x1 - x2 - x3) / 4.0; } void From cc36be302d8cb969a0a966748ebfa60df7f0a248 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 21 Jul 2014 12:29:19 +1000 Subject: [PATCH 07/91] test: add test for 3-finger tapping Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/touchpad.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/test/touchpad.c b/test/touchpad.c index 0de3cbb2..334a59c3 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -496,6 +496,111 @@ START_TEST(touchpad_1fg_tap_n_drag_click) } END_TEST +START_TEST(touchpad_3fg_tap) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_event *event; + int i; + + libinput_device_config_tap_set_enabled(dev->libinput_device, 1); + + for (i = 0; i < 3; i++) { + litest_drain_events(li); + + litest_touch_down(dev, 0, 50, 50); + litest_touch_down(dev, 1, 70, 50); + litest_touch_down(dev, 2, 80, 50); + + litest_touch_up(dev, (i + 2) % 3); + litest_touch_up(dev, (i + 1) % 3); + litest_touch_up(dev, (i + 0) % 3); + + libinput_dispatch(li); + + assert_button_event(li, BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); + msleep(300); /* tap-n-drag timeout */ + assert_button_event(li, BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_RELEASED); + + libinput_dispatch(li); + event = libinput_get_event(li); + ck_assert(event == NULL); + } +} +END_TEST + +START_TEST(touchpad_3fg_tap_btntool) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_event *event; + + libinput_device_config_tap_set_enabled(dev->libinput_device, 1); + + litest_drain_events(li); + + litest_touch_down(dev, 0, 50, 50); + litest_touch_down(dev, 1, 70, 50); + litest_event(dev, EV_KEY, BTN_TOOL_TRIPLETAP, 1); + litest_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_event(dev, EV_KEY, BTN_TOOL_TRIPLETAP, 0); + litest_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_touch_up(dev, 1); + litest_touch_up(dev, 0); + + libinput_dispatch(li); + + assert_button_event(li, BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); + msleep(300); /* tap-n-drag timeout */ + assert_button_event(li, BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_RELEASED); + + libinput_dispatch(li); + event = libinput_get_event(li); + ck_assert(event == NULL); +} +END_TEST + +START_TEST(touchpad_3fg_tap_btntool_inverted) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_event *event; + + libinput_device_config_tap_set_enabled(dev->libinput_device, 1); + + litest_drain_events(li); + + litest_touch_down(dev, 0, 50, 50); + litest_touch_down(dev, 1, 70, 50); + litest_event(dev, EV_KEY, BTN_TOOL_TRIPLETAP, 1); + litest_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_event(dev, EV_KEY, BTN_TOOL_TRIPLETAP, 0); + litest_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_touch_up(dev, 0); + litest_touch_up(dev, 1); + + libinput_dispatch(li); + + assert_button_event(li, BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); + msleep(300); /* tap-n-drag timeout */ + assert_button_event(li, BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_RELEASED); + + libinput_dispatch(li); + event = libinput_get_event(li); + ck_assert(event == NULL); +} +END_TEST + START_TEST(touchpad_1fg_clickfinger) { struct litest_device *dev = litest_create_device(LITEST_BCM5974); @@ -1428,6 +1533,10 @@ int main(int argc, char **argv) { litest_add("touchpad:tap", touchpad_no_2fg_tap_after_timeout, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); litest_add("touchpad:tap", touchpad_no_first_fg_tap_after_move, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); litest_add("touchpad:tap", touchpad_no_first_fg_tap_after_move, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); + /* apple is the only one with real 3-finger support */ + litest_add("touchpad:tap", touchpad_3fg_tap_btntool, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_APPLE_CLICKPAD); + litest_add("touchpad:tap", touchpad_3fg_tap_btntool_inverted, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_APPLE_CLICKPAD); + litest_add("touchpad:tap", touchpad_3fg_tap, LITEST_APPLE_CLICKPAD, LITEST_ANY); /* Real buttons don't interfere with tapping, so don't run those for pads with buttons */ From d9cf64919985e35550fc4586b55c66c2025e73c9 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 21 Jul 2014 11:07:25 +1000 Subject: [PATCH 08/91] Use an enum to enable/disable tapping configuration More expressive in the caller and less ambiguous about return values (is it 1? is it non-zero? can it be negative?) Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad-tap.c | 14 +++++---- src/libinput-private.h | 6 ++-- src/libinput.c | 14 +++++---- src/libinput.h | 25 ++++++++++++---- test/touchpad.c | 60 +++++++++++++++++++++++++++---------- 5 files changed, 83 insertions(+), 36 deletions(-) diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index 0f1f837d..60085074 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -625,7 +625,8 @@ tp_tap_config_count(struct libinput_device *device) } static enum libinput_config_status -tp_tap_config_set_enabled(struct libinput_device *device, int enabled) +tp_tap_config_set_enabled(struct libinput_device *device, + enum libinput_config_tap_state enabled) { struct evdev_dispatch *dispatch; struct tp_dispatch *tp; @@ -633,12 +634,12 @@ tp_tap_config_set_enabled(struct libinput_device *device, int enabled) dispatch = ((struct evdev_device *) device)->dispatch; tp = container_of(dispatch, tp, base); - tp->tap.enabled = enabled; + tp->tap.enabled = (enabled == LIBINPUT_CONFIG_TAP_ENABLED); return LIBINPUT_CONFIG_STATUS_SUCCESS; } -static int +static enum libinput_config_tap_state tp_tap_config_is_enabled(struct libinput_device *device) { struct evdev_dispatch *dispatch; @@ -647,10 +648,11 @@ tp_tap_config_is_enabled(struct libinput_device *device) dispatch = ((struct evdev_device *) device)->dispatch; tp = container_of(dispatch, tp, base); - return tp->tap.enabled; + return tp->tap.enabled ? LIBINPUT_CONFIG_TAP_ENABLED : + LIBINPUT_CONFIG_TAP_DISABLED; } -static int +static enum libinput_config_tap_state tp_tap_config_get_default(struct libinput_device *device) { /** @@ -662,7 +664,7 @@ tp_tap_config_get_default(struct libinput_device *device) * usually know where to enable it, or at least you can search for * it. */ - return false; + return LIBINPUT_CONFIG_TAP_DISABLED; } int diff --git a/src/libinput-private.h b/src/libinput-private.h index 23c51405..94a3e07c 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -84,9 +84,9 @@ struct libinput_seat { struct libinput_device_config_tap { int (*count)(struct libinput_device *device); enum libinput_config_status (*set_enabled)(struct libinput_device *device, - int enable); - int (*get_enabled)(struct libinput_device *device); - int (*get_default)(struct libinput_device *device); + enum libinput_config_tap_state enable); + enum libinput_config_tap_state (*get_enabled)(struct libinput_device *device); + enum libinput_config_tap_state (*get_default)(struct libinput_device *device); }; struct libinput_device_config { diff --git a/src/libinput.c b/src/libinput.c index ff4b01e1..a278ce49 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -1284,8 +1284,12 @@ libinput_device_config_tap_get_finger_count(struct libinput_device *device) LIBINPUT_EXPORT enum libinput_config_status libinput_device_config_tap_set_enabled(struct libinput_device *device, - int enable) + enum libinput_config_tap_state enable) { + if (enable != LIBINPUT_CONFIG_TAP_ENABLED && + enable != LIBINPUT_CONFIG_TAP_DISABLED) + return LIBINPUT_CONFIG_STATUS_INVALID; + if (enable && libinput_device_config_tap_get_finger_count(device) == 0) return LIBINPUT_CONFIG_STATUS_UNSUPPORTED; @@ -1293,20 +1297,20 @@ libinput_device_config_tap_set_enabled(struct libinput_device *device, return device->config.tap->set_enabled(device, enable); } -LIBINPUT_EXPORT int +LIBINPUT_EXPORT enum libinput_config_tap_state libinput_device_config_tap_get_enabled(struct libinput_device *device) { if (libinput_device_config_tap_get_finger_count(device) == 0) - return 0; + return LIBINPUT_CONFIG_TAP_DISABLED; return device->config.tap->get_enabled(device); } -LIBINPUT_EXPORT int +LIBINPUT_EXPORT enum libinput_config_tap_state libinput_device_config_tap_get_default_enabled(struct libinput_device *device) { if (libinput_device_config_tap_get_finger_count(device) == 0) - return 0; + return LIBINPUT_CONFIG_TAP_DISABLED; return device->config.tap->get_default(device); } diff --git a/src/libinput.h b/src/libinput.h index 7d33f2b2..3cd8e7b5 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -1441,6 +1441,16 @@ enum libinput_config_status { const char * libinput_config_status_to_str(enum libinput_config_status status); +/** + * @ingroup config + */ +enum libinput_config_tap_state { + LIBINPUT_CONFIG_TAP_DISABLED, /**< Tapping is to be disabled, or is + currently disabled */ + LIBINPUT_CONFIG_TAP_ENABLED, /**< Tapping is to be enabled, or is + currently enabled */ +}; + /** * @ingroup config * @@ -1468,7 +1478,8 @@ libinput_device_config_tap_get_finger_count(struct libinput_device *device); * libinput_device_config_tap_get_finger_count(). * * @param device The device to configure - * @param enable Non-zero to enable, zero to disable + * @param enable @ref LIBINPUT_CONFIG_TAP_ENABLED to enable tapping or @ref + * LIBINPUT_CONFIG_TAP_DISABLED to disable tapping * * @return A config status code. Disabling tapping on a device that does not * support tapping always succeeds. @@ -1479,7 +1490,7 @@ libinput_device_config_tap_get_finger_count(struct libinput_device *device); */ enum libinput_config_status libinput_device_config_tap_set_enabled(struct libinput_device *device, - int enable); + enum libinput_config_tap_state enable); /** * @ingroup config @@ -1489,13 +1500,14 @@ libinput_device_config_tap_set_enabled(struct libinput_device *device, * * @param device The device to configure * - * @return 1 if enabled, 0 otherwise. + * @return @ref LIBINPUT_CONFIG_TAP_ENABLED if tapping is currently enabled, + * or @ref LIBINPUT_CONFIG_TAP_DISABLED is currently disabled * * @see libinput_device_config_tap_get_finger_count * @see libinput_device_config_tap_set_enabled * @see libinput_device_config_tap_get_default_enabled */ -int +enum libinput_config_tap_state libinput_device_config_tap_get_enabled(struct libinput_device *device); /** @@ -1504,13 +1516,14 @@ libinput_device_config_tap_get_enabled(struct libinput_device *device); * Return the default setting for whether tapping is enabled on this device. * * @param device The device to configure - * @return 1 if tapping is enabled by default, or 0 otherwise + * @return @ref LIBINPUT_CONFIG_TAP_ENABLED if tapping is enabled by default, + * or @ref LIBINPUT_CONFIG_TAP_DISABLED is disabled by default * * @see libinput_device_config_tap_get_finger_count * @see libinput_device_config_tap_set_enabled * @see libinput_device_config_tap_get_enabled */ -int +enum libinput_config_tap_state libinput_device_config_tap_get_default_enabled(struct libinput_device *device); #ifdef __cplusplus diff --git a/test/touchpad.c b/test/touchpad.c index 334a59c3..86b2c91b 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -116,7 +116,8 @@ START_TEST(touchpad_1fg_tap) struct libinput *li = dev->libinput; struct libinput_event *event; - libinput_device_config_tap_set_enabled(dev->libinput_device, 1); + libinput_device_config_tap_set_enabled(dev->libinput_device, + LIBINPUT_CONFIG_TAP_ENABLED); litest_drain_events(li); @@ -143,7 +144,8 @@ START_TEST(touchpad_1fg_tap_n_drag) struct libinput *li = dev->libinput; struct libinput_event *event; - libinput_device_config_tap_set_enabled(dev->libinput_device, 1); + libinput_device_config_tap_set_enabled(dev->libinput_device, + LIBINPUT_CONFIG_TAP_ENABLED); litest_drain_events(li); @@ -195,7 +197,8 @@ START_TEST(touchpad_2fg_tap) struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; - libinput_device_config_tap_set_enabled(dev->libinput_device, 1); + libinput_device_config_tap_set_enabled(dev->libinput_device, + LIBINPUT_CONFIG_TAP_ENABLED); litest_drain_events(dev->libinput); @@ -221,7 +224,8 @@ START_TEST(touchpad_2fg_tap_inverted) struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; - libinput_device_config_tap_set_enabled(dev->libinput_device, 1); + libinput_device_config_tap_set_enabled(dev->libinput_device, + LIBINPUT_CONFIG_TAP_ENABLED); litest_drain_events(dev->libinput); @@ -247,7 +251,8 @@ START_TEST(touchpad_1fg_tap_click) struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; - libinput_device_config_tap_set_enabled(dev->libinput_device, 1); + libinput_device_config_tap_set_enabled(dev->libinput_device, + LIBINPUT_CONFIG_TAP_ENABLED); litest_drain_events(dev->libinput); @@ -276,7 +281,8 @@ START_TEST(touchpad_2fg_tap_click) struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; - libinput_device_config_tap_set_enabled(dev->libinput_device, 1); + libinput_device_config_tap_set_enabled(dev->libinput_device, + LIBINPUT_CONFIG_TAP_ENABLED); litest_drain_events(dev->libinput); @@ -307,7 +313,8 @@ START_TEST(touchpad_2fg_tap_click_apple) struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; - libinput_device_config_tap_set_enabled(dev->libinput_device, 1); + libinput_device_config_tap_set_enabled(dev->libinput_device, + LIBINPUT_CONFIG_TAP_ENABLED); litest_drain_events(dev->libinput); @@ -415,7 +422,8 @@ START_TEST(touchpad_1fg_double_tap_click) struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; - libinput_device_config_tap_set_enabled(dev->libinput_device, 1); + libinput_device_config_tap_set_enabled(dev->libinput_device, + LIBINPUT_CONFIG_TAP_ENABLED); litest_drain_events(dev->libinput); @@ -451,7 +459,8 @@ START_TEST(touchpad_1fg_tap_n_drag_click) struct libinput *li = dev->libinput; struct libinput_event *event; - libinput_device_config_tap_set_enabled(dev->libinput_device, 1); + libinput_device_config_tap_set_enabled(dev->libinput_device, + LIBINPUT_CONFIG_TAP_ENABLED); litest_drain_events(dev->libinput); @@ -503,7 +512,8 @@ START_TEST(touchpad_3fg_tap) struct libinput_event *event; int i; - libinput_device_config_tap_set_enabled(dev->libinput_device, 1); + libinput_device_config_tap_set_enabled(dev->libinput_device, + LIBINPUT_CONFIG_TAP_ENABLED); for (i = 0; i < 3; i++) { litest_drain_events(li); @@ -798,7 +808,8 @@ START_TEST(clickpad_softbutton_left_tap_n_drag) struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; - libinput_device_config_tap_set_enabled(dev->libinput_device, 1); + libinput_device_config_tap_set_enabled(dev->libinput_device, + LIBINPUT_CONFIG_TAP_ENABLED); litest_drain_events(li); @@ -840,7 +851,8 @@ START_TEST(clickpad_softbutton_right_tap_n_drag) struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; - libinput_device_config_tap_set_enabled(dev->libinput_device, 1); + libinput_device_config_tap_set_enabled(dev->libinput_device, + LIBINPUT_CONFIG_TAP_ENABLED); litest_drain_events(li); @@ -1323,7 +1335,8 @@ START_TEST(touchpad_tap_is_available) struct litest_device *dev = litest_current_device(); ck_assert_int_ge(libinput_device_config_tap_get_finger_count(dev->libinput_device), 1); - ck_assert_int_eq(libinput_device_config_tap_get_enabled(dev->libinput_device), 0); + ck_assert_int_eq(libinput_device_config_tap_get_enabled(dev->libinput_device), + LIBINPUT_CONFIG_TAP_DISABLED); } END_TEST @@ -1332,8 +1345,10 @@ START_TEST(touchpad_tap_is_not_available) struct litest_device *dev = litest_current_device(); ck_assert_int_eq(libinput_device_config_tap_get_finger_count(dev->libinput_device), 0); - ck_assert_int_eq(libinput_device_config_tap_get_enabled(dev->libinput_device), 0); - ck_assert_int_eq(libinput_device_config_tap_set_enabled(dev->libinput_device, 1), + ck_assert_int_eq(libinput_device_config_tap_get_enabled(dev->libinput_device), + LIBINPUT_CONFIG_TAP_DISABLED); + ck_assert_int_eq(libinput_device_config_tap_set_enabled(dev->libinput_device, + LIBINPUT_CONFIG_TAP_ENABLED), LIBINPUT_CONFIG_STATUS_UNSUPPORTED); } END_TEST @@ -1342,7 +1357,19 @@ START_TEST(touchpad_tap_default) { struct litest_device *dev = litest_current_device(); - ck_assert_int_eq(libinput_device_config_tap_get_default_enabled(dev->libinput_device), 0); + ck_assert_int_eq(libinput_device_config_tap_get_default_enabled(dev->libinput_device), + LIBINPUT_CONFIG_TAP_DISABLED); +} +END_TEST + +START_TEST(touchpad_tap_invalid) +{ + struct litest_device *dev = litest_current_device(); + + ck_assert_int_eq(libinput_device_config_tap_set_enabled(dev->libinput_device, 2), + LIBINPUT_CONFIG_STATUS_INVALID); + ck_assert_int_eq(libinput_device_config_tap_set_enabled(dev->libinput_device, -1), + LIBINPUT_CONFIG_STATUS_INVALID); } END_TEST @@ -1544,6 +1571,7 @@ int main(int argc, char **argv) { litest_add("touchpad:tap", touchpad_1fg_tap_n_drag_click, LITEST_CLICKPAD, LITEST_ANY); litest_add("touchpad:tap", touchpad_tap_default, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("touchpad:tap", touchpad_tap_invalid, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:tap", touchpad_tap_is_available, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:tap", touchpad_tap_is_not_available, LITEST_ANY, LITEST_TOUCHPAD); From 5019b25a6bf99f09e6315b3297bede79d5ad3ac2 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 21 Jul 2014 10:58:24 +1000 Subject: [PATCH 09/91] tools: always enable tapping in the event-gui program This is a debugging tool, so the features to debug should be enabled by default. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- tools/event-gui.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tools/event-gui.c b/tools/event-gui.c index b4a65066..544a6829 100644 --- a/tools/event-gui.c +++ b/tools/event-gui.c @@ -223,6 +223,15 @@ handle_event_device_notify(struct libinput_event *ev) type = "removed"; msg("%s %s\n", libinput_device_get_sysname(dev), type); + + if (libinput_device_config_tap_get_finger_count(dev) > 0) { + enum libinput_config_status status; + status = libinput_device_config_tap_set_enabled(dev, + LIBINPUT_CONFIG_TAP_ENABLED); + if (status != LIBINPUT_CONFIG_STATUS_SUCCESS) + error("%s: Failed to enable tapping\n", + libinput_device_get_sysname(dev)); + } } static void From 125e98a1f8909cafbb968ea0c2bd9b21349490bb Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 22 Jul 2014 09:00:27 +1000 Subject: [PATCH 10/91] style fix: Remove duplicate empty lines Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad.c | 1 - src/libinput.c | 1 - src/udev-seat.c | 1 - test/litest-keyboard.c | 1 - test/litest.c | 3 --- test/path.c | 4 ---- test/touch.c | 1 - test/udev.c | 1 - tools/event-gui.c | 1 - 9 files changed, 14 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index b0520c79..67828bda 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -777,7 +777,6 @@ tp_init_palmdetect(struct tp_dispatch *tp, return 0; } - static int tp_init(struct tp_dispatch *tp, struct evdev_device *device) diff --git a/src/libinput.c b/src/libinput.c index a278ce49..90b6a137 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -1052,7 +1052,6 @@ touch_notify_frame(struct libinput_device *device, &touch_event->base); } - static void libinput_post_event(struct libinput *libinput, struct libinput_event *event) diff --git a/src/udev-seat.c b/src/udev-seat.c index 635fabfd..8d198947 100644 --- a/src/udev-seat.c +++ b/src/udev-seat.c @@ -216,7 +216,6 @@ udev_input_remove_devices(struct udev_input *input) } } - static void udev_input_disable(struct libinput *libinput) { diff --git a/test/litest-keyboard.c b/test/litest-keyboard.c index c097315e..6a9accd6 100644 --- a/test/litest-keyboard.c +++ b/test/litest-keyboard.c @@ -195,7 +195,6 @@ static int events[] = { -1, -1, }; - struct litest_test_device litest_keyboard_device = { .type = LITEST_KEYBOARD, .features = LITEST_KEYS, diff --git a/test/litest.c b/test/litest.c index 1ffbd53d..4e79f7bc 100644 --- a/test/litest.c +++ b/test/litest.c @@ -97,7 +97,6 @@ struct litest_test_device* devices[] = { NULL, }; - static struct list all_tests; static void @@ -235,7 +234,6 @@ is_debugger_attached(void) return rc; } - static void litest_list_tests(struct list *tests) { @@ -421,7 +419,6 @@ merge_events(const int *orig, const int *override) return events; } - static struct litest_device * litest_create(enum litest_device_type which, const char *name_override, diff --git a/test/path.c b/test/path.c index 99b474eb..b5e222dd 100644 --- a/test/path.c +++ b/test/path.c @@ -53,7 +53,6 @@ const struct libinput_interface simple_interface = { .close_restricted = close_restricted, }; - START_TEST(path_create_NULL) { struct libinput *li; @@ -492,7 +491,6 @@ START_TEST(path_add_device_suspend_resume) ck_assert_int_eq(nevents, 2); - libinput_suspend(li); libinput_dispatch(li); @@ -576,7 +574,6 @@ START_TEST(path_add_device_suspend_resume_fail) ck_assert_int_eq(nevents, 2); - libinput_suspend(li); libinput_dispatch(li); @@ -667,7 +664,6 @@ START_TEST(path_add_device_suspend_resume_remove_device) ck_assert_int_eq(nevents, 2); - libinput_suspend(li); libinput_dispatch(li); diff --git a/test/touch.c b/test/touch.c index 6f6eec0a..fee05f1b 100644 --- a/test/touch.c +++ b/test/touch.c @@ -112,7 +112,6 @@ START_TEST(touch_abs_transform) } END_TEST - START_TEST(touch_many_slots) { struct libinput *libinput; diff --git a/test/udev.c b/test/udev.c index f07decb6..9a59eb58 100644 --- a/test/udev.c +++ b/test/udev.c @@ -47,7 +47,6 @@ const struct libinput_interface simple_interface = { .close_restricted = close_restricted, }; - START_TEST(udev_create_NULL) { struct libinput *li; diff --git a/tools/event-gui.c b/tools/event-gui.c index 544a6829..3ef30057 100644 --- a/tools/event-gui.c +++ b/tools/event-gui.c @@ -431,7 +431,6 @@ parse_opts(int argc, char *argv[]) return 0; } - static int open_restricted(const char *path, int flags, void *user_data) { From ea1d2d6348e791ef2d1a07c8a7567eb850334f44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 16 Jul 2014 21:14:51 +0200 Subject: [PATCH 11/91] test/path: Avoid creating ignored test devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some tests doesn't use or doesn't need to use the test device automatically created when adding a test case for certain types of devices. For these tests, to shorten test run time, don't create the test devices that would be ignored. Signed-off-by: Jonas Ådahl Reviewed-by: Peter Hutterer --- test/path.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/test/path.c b/test/path.c index b5e222dd..475e125f 100644 --- a/test/path.c +++ b/test/path.c @@ -243,12 +243,11 @@ END_TEST START_TEST(path_add_invalid_path) { - struct litest_device *dev = litest_current_device(); - struct libinput *li = dev->libinput; + struct libinput *li; struct libinput_event *event; struct libinput_device *device; - litest_drain_events(li); + li = litest_create_context(); device = libinput_path_add_device(li, "/tmp/"); ck_assert(device == NULL); @@ -257,6 +256,8 @@ START_TEST(path_add_invalid_path) while ((event = libinput_get_event(li))) ck_abort(); + + libinput_unref(li); } END_TEST @@ -794,24 +795,23 @@ END_TEST int main (int argc, char **argv) { - litest_add("path:create", path_create_NULL, LITEST_ANY, LITEST_ANY); - litest_add("path:create", path_create_invalid, LITEST_ANY, LITEST_ANY); - litest_add("path:create", path_create_destroy, LITEST_ANY, LITEST_ANY); - litest_add("path:suspend", path_suspend, LITEST_ANY, LITEST_ANY); - litest_add("path:suspend", path_double_suspend, LITEST_ANY, LITEST_ANY); - litest_add("path:suspend", path_double_resume, LITEST_ANY, LITEST_ANY); - litest_add("path:suspend", path_add_device_suspend_resume, LITEST_ANY, LITEST_ANY); - litest_add("path:suspend", path_add_device_suspend_resume_fail, LITEST_ANY, LITEST_ANY); - litest_add("path:suspend", path_add_device_suspend_resume_remove_device, LITEST_ANY, LITEST_ANY); + litest_add_no_device("path:create", path_create_NULL); + litest_add_no_device("path:create", path_create_invalid); + litest_add_no_device("path:create", path_create_destroy); + litest_add_no_device("path:suspend", path_suspend); + litest_add_no_device("path:suspend", path_double_suspend); + litest_add_no_device("path:suspend", path_double_resume); + litest_add_no_device("path:suspend", path_add_device_suspend_resume); + litest_add_no_device("path:suspend", path_add_device_suspend_resume_fail); + litest_add_no_device("path:suspend", path_add_device_suspend_resume_remove_device); litest_add("path:seat events", path_added_seat, LITEST_ANY, LITEST_ANY); litest_add("path:device events", path_added_device, LITEST_ANY, LITEST_ANY); litest_add("path:device events", path_device_sysname, LITEST_ANY, LITEST_ANY); litest_add("path:device events", path_add_device, LITEST_ANY, LITEST_ANY); - litest_add("path:device events", path_add_invalid_path, LITEST_ANY, LITEST_ANY); + litest_add_no_device("path:device events", path_add_invalid_path); litest_add("path:device events", path_remove_device, LITEST_ANY, LITEST_ANY); litest_add("path:device events", path_double_remove_device, LITEST_ANY, LITEST_ANY); - litest_add("path:seat", path_seat_recycle, - LITEST_DISABLE_DEVICE, LITEST_DISABLE_DEVICE); + litest_add_no_device("path:seat", path_seat_recycle); return litest_run(argc, argv); } From bb10ec84d3704fc0fb40591bcbffe90f6c77966d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Tue, 22 Jul 2014 21:37:02 +0200 Subject: [PATCH 12/91] configure.ac: libinput 0.5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jonas Ådahl --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index fd402e20..bb702c0f 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], [4]) +m4_define([libinput_minor_version], [5]) m4_define([libinput_micro_version], [0]) m4_define([libinput_version], [libinput_major_version.libinput_minor_version.libinput_micro_version]) @@ -30,7 +30,7 @@ AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz subdir-objects]) # - If binary compatibility has been broken (eg removed or changed interfaces) # change to C+1:0:0 # - If the interface is the same as the previous version, change to C:R+1:A -LIBINPUT_LT_VERSION=3:0:0 +LIBINPUT_LT_VERSION=4:0:1 AC_SUBST(LIBINPUT_LT_VERSION) AM_SILENT_RULES([yes]) From 61995348d911d763960454725a91043989844e8f Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 21 Jul 2014 12:30:40 +1000 Subject: [PATCH 13/91] test: auto-update for BTN_TOOL_* when using litest_touch_ functions Set BTN_TOUCH, BTN_TOOL_DOUBLETAP automatically depending on the number of fingers down. This emulates real event sequences a bit better than the current approach, though it's not a 100% correct emulation: 1) On real devices, BTN_* are usually sent last before the SYN_REPORT - here they are sent first to slot in with the custom, device-specific event sequence. We should only ever look at the complete sequence anyway, so this shouldn't matter. 2) On real devices, the switch from BTN_TOOL_DOUBLETAP to TRIPLETAP and vice versa is not always toggled within the same SYN_REPORT 3) On synaptics devices, BTN_TOUCH is released in the frame where BTN_TOOL_DOUBLETAP is set. It is then immediately set again in the next frame. With the current litest framework this is hard to integrate, so we just leave BTN_TOUCH set the whole time, which is what MT devices do if they don't have BTN_TOOL_DOUBLETAP. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/litest-bcm5974.c | 4 ---- test/litest-synaptics-st.c | 3 --- test/litest-synaptics-t440.c | 4 ---- test/litest-synaptics.c | 4 ---- test/litest.c | 19 +++++++++++++++++++ test/litest.h | 2 ++ 6 files changed, 21 insertions(+), 15 deletions(-) diff --git a/test/litest-bcm5974.c b/test/litest-bcm5974.c index c7bd8573..035bed22 100644 --- a/test/litest-bcm5974.c +++ b/test/litest-bcm5974.c @@ -34,8 +34,6 @@ static void litest_bcm5974_setup(void) } struct input_event down[] = { - { .type = EV_KEY, .code = BTN_TOOL_FINGER, .value = 1 }, - { .type = EV_KEY, .code = BTN_TOUCH, .value = 1 }, { .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN }, { .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN }, { .type = EV_ABS, .code = ABS_PRESSURE, .value = 30 }, @@ -53,8 +51,6 @@ static struct input_event move[] = { { .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN }, { .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN }, { .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN }, - { .type = EV_KEY, .code = BTN_TOOL_FINGER, .value = 1 }, - { .type = EV_KEY, .code = BTN_TOUCH, .value = 1 }, { .type = EV_SYN, .code = SYN_REPORT, .value = 0 }, { .type = -1, .code = -1 }, }; diff --git a/test/litest-synaptics-st.c b/test/litest-synaptics-st.c index fe263986..9f69332b 100644 --- a/test/litest-synaptics-st.c +++ b/test/litest-synaptics-st.c @@ -35,7 +35,6 @@ litest_synaptics_touchpad_setup(void) } static struct input_event down[] = { - { .type = EV_KEY, .code = BTN_TOUCH, .value = 1 }, { .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN }, { .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN }, { .type = EV_ABS, .code = ABS_PRESSURE, .value = 30 }, @@ -47,13 +46,11 @@ static struct input_event down[] = { static struct input_event move[] = { { .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN }, { .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN }, - { .type = EV_KEY, .code = BTN_TOUCH, .value = 1 }, { .type = EV_SYN, .code = SYN_REPORT, .value = 0 }, { .type = -1, .code = -1 }, }; struct input_event up[] = { - { .type = EV_KEY, .code = BTN_TOUCH, .value = 0 }, { .type = EV_SYN, .code = SYN_REPORT, .value = 0 }, { .type = -1, .code = -1 }, }; diff --git a/test/litest-synaptics-t440.c b/test/litest-synaptics-t440.c index e3f14413..65a0ad4e 100644 --- a/test/litest-synaptics-t440.c +++ b/test/litest-synaptics-t440.c @@ -35,8 +35,6 @@ litest_synaptics_t440_setup(void) } static struct input_event down[] = { - { .type = EV_KEY, .code = BTN_TOOL_FINGER, .value = 1 }, - { .type = EV_KEY, .code = BTN_TOUCH, .value = 1 }, { .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN }, { .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN }, { .type = EV_ABS, .code = ABS_PRESSURE, .value = 30 }, @@ -54,8 +52,6 @@ static struct input_event move[] = { { .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN }, { .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN }, { .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN }, - { .type = EV_KEY, .code = BTN_TOOL_FINGER, .value = 1 }, - { .type = EV_KEY, .code = BTN_TOUCH, .value = 1 }, { .type = EV_SYN, .code = SYN_REPORT, .value = 0 }, { .type = -1, .code = -1 }, }; diff --git a/test/litest-synaptics.c b/test/litest-synaptics.c index d55b65c4..5565e635 100644 --- a/test/litest-synaptics.c +++ b/test/litest-synaptics.c @@ -35,8 +35,6 @@ litest_synaptics_clickpad_setup(void) } static struct input_event down[] = { - { .type = EV_KEY, .code = BTN_TOOL_FINGER, .value = 1 }, - { .type = EV_KEY, .code = BTN_TOUCH, .value = 1 }, { .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN }, { .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN }, { .type = EV_ABS, .code = ABS_PRESSURE, .value = 30 }, @@ -54,8 +52,6 @@ static struct input_event move[] = { { .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN }, { .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN }, { .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN }, - { .type = EV_KEY, .code = BTN_TOOL_FINGER, .value = 1 }, - { .type = EV_KEY, .code = BTN_TOUCH, .value = 1 }, { .type = EV_SYN, .code = SYN_REPORT, .value = 0 }, { .type = -1, .code = -1 }, }; diff --git a/test/litest.c b/test/litest.c index 4e79f7bc..deab0cff 100644 --- a/test/litest.c +++ b/test/litest.c @@ -24,6 +24,7 @@ #include "config.h" #endif +#include #include #include #include @@ -620,6 +621,16 @@ auto_assign_value(struct litest_device *d, return value; } +static void +send_btntool(struct litest_device *d) +{ + litest_event(d, EV_KEY, BTN_TOUCH, d->ntouches_down != 0); + litest_event(d, EV_KEY, BTN_TOOL_FINGER, d->ntouches_down == 1); + litest_event(d, EV_KEY, BTN_TOOL_DOUBLETAP, d->ntouches_down == 2); + litest_event(d, EV_KEY, BTN_TOOL_TRIPLETAP, d->ntouches_down == 3); + litest_event(d, EV_KEY, BTN_TOOL_QUADTAP, d->ntouches_down == 4); + litest_event(d, EV_KEY, BTN_TOOL_QUINTTAP, d->ntouches_down == 5); +} void litest_touch_down(struct litest_device *d, unsigned int slot, @@ -627,6 +638,10 @@ litest_touch_down(struct litest_device *d, unsigned int slot, { struct input_event *ev; + assert(++d->ntouches_down > 0); + + send_btntool(d); + if (d->interface->touch_down) { d->interface->touch_down(d, slot, x, y); return; @@ -651,6 +666,10 @@ litest_touch_up(struct litest_device *d, unsigned int slot) { .type = -1, .code = -1 } }; + assert(--d->ntouches_down >= 0); + + send_btntool(d); + if (d->interface->touch_up) { d->interface->touch_up(d, slot); return; diff --git a/test/litest.h b/test/litest.h index 2363c7f9..9a9d10a6 100644 --- a/test/litest.h +++ b/test/litest.h @@ -67,6 +67,8 @@ struct litest_device { bool owns_context; struct libinput_device *libinput_device; struct litest_device_interface *interface; + + int ntouches_down; }; struct libinput *litest_create_context(void); From d429da529ca96338a33b9b945cf3bb7bc8f7d7cc Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 23 Jul 2014 18:03:15 +1000 Subject: [PATCH 14/91] Document that the delta from pointer events is accelerated Signed-off-by: Peter Hutterer --- src/libinput.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libinput.h b/src/libinput.h index 3cd8e7b5..1e8ae679 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -427,6 +427,9 @@ libinput_event_pointer_get_time(struct libinput_event_pointer *event); * events that are not of type LIBINPUT_EVENT_POINTER_MOTION, this function * returns 0. * + * If a device employs pointer acceleration, the delta returned by this + * function is the accelerated delta. + * * @note It is an application bug to call this function for events other than * LIBINPUT_EVENT_POINTER_MOTION. * @@ -442,6 +445,9 @@ libinput_event_pointer_get_dx(struct libinput_event_pointer *event); * events that are not of type LIBINPUT_EVENT_POINTER_MOTION, this function * returns 0. * + * If a device employs pointer acceleration, the delta returned by this + * function is the accelerated delta. + * * @note It is an application bug to call this function for events other than * LIBINPUT_EVENT_POINTER_MOTION. * From 3264dde8c0e9d5238e66f038729b54e0b5a62dff Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 21 Jul 2014 15:25:47 +0200 Subject: [PATCH 15/91] touchpad: reset motion history when nfingers changes on semi-mt pads On semi-mt touchpads the reported position of the first finger down may jump when the pad switches from st to mt mode. When this happens a large delta gets seen on the first finger at the same time the second fingers is first seen down, causing a spurious 2 finger scroll event. Reset the motion history when nfingers changes on semi-mt pads to avoid this. Signed-off-by: Hans de Goede Reviewed-by: Peter Hutterer Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad.c | 8 ++++++++ src/evdev-mt-touchpad.h | 2 ++ 2 files changed, 10 insertions(+) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 67828bda..b58a6caf 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -406,6 +406,11 @@ tp_process_state(struct tp_dispatch *tp, uint64_t time) for (i = 0; i < tp->ntouches; i++) { t = tp_get_touch(tp, i); + + /* semi-mt finger postions may "jump" when nfingers changes */ + if (tp->semi_mt && tp->nfingers_down != tp->old_nfingers_down) + tp_motion_history_reset(t); + if (i >= tp->real_touches && t->state != TOUCH_NONE) { t->x = first->x; t->y = first->y; @@ -454,6 +459,7 @@ tp_post_process_state(struct tp_dispatch *tp, uint64_t time) t->dirty = false; } + tp->old_nfingers_down = tp->nfingers_down; tp->buttons.old_state = tp->buttons.state; tp->queued = TOUCHPAD_EVENT_NONE; @@ -668,6 +674,8 @@ tp_init_slots(struct tp_dispatch *tp, tp->has_mt = false; } + tp->semi_mt = libevdev_has_property(device->evdev, INPUT_PROP_SEMI_MT); + ARRAY_FOR_EACH(max_touches, m) { if (libevdev_has_event_code(device->evdev, EV_KEY, diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index af6a3a38..83edf4f2 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -152,8 +152,10 @@ struct tp_dispatch { struct evdev_dispatch base; struct evdev_device *device; unsigned int nfingers_down; /* number of fingers down */ + unsigned int old_nfingers_down; /* previous no fingers down */ unsigned int slot; /* current slot */ bool has_mt; + bool semi_mt; unsigned int real_touches; /* number of slots */ unsigned int ntouches; /* no slots inc. fakes */ From 22e86f932282ce623736bc59cc243f8a87759f68 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 24 Jul 2014 16:15:43 +1000 Subject: [PATCH 16/91] evdev: don't return a width/height if we faked the resolution Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev.c | 7 ++++++- src/evdev.h | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index f980812b..a1255100 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -620,6 +620,7 @@ evdev_configure_device(struct evdev_device *device) fixed = *absinfo; fixed.resolution = 1; libevdev_set_abs_info(evdev, ABS_X, &fixed); + device->abs.fake_resolution = 1; } device->abs.absinfo_x = absinfo; has_abs = 1; @@ -629,6 +630,7 @@ evdev_configure_device(struct evdev_device *device) fixed = *absinfo; fixed.resolution = 1; libevdev_set_abs_info(evdev, ABS_Y, &fixed); + device->abs.fake_resolution = 1; } device->abs.absinfo_y = absinfo; has_abs = 1; @@ -645,6 +647,7 @@ evdev_configure_device(struct evdev_device *device) libevdev_set_abs_info(evdev, ABS_MT_POSITION_X, &fixed); + device->abs.fake_resolution = 1; } device->abs.absinfo_x = absinfo; absinfo = libevdev_get_abs_info(evdev, ABS_MT_POSITION_Y); @@ -654,6 +657,7 @@ evdev_configure_device(struct evdev_device *device) libevdev_set_abs_info(evdev, ABS_MT_POSITION_Y, &fixed); + device->abs.fake_resolution = 1; } device->abs.absinfo_y = absinfo; device->is_mt = 1; @@ -908,7 +912,8 @@ evdev_device_get_size(struct evdev_device *device, x = libevdev_get_abs_info(device->evdev, ABS_X); y = libevdev_get_abs_info(device->evdev, ABS_Y); - if (!x || !y || !x->resolution || !y->resolution) + if (!x || !y || device->abs.fake_resolution || + !x->resolution || !y->resolution) return -1; *width = evdev_convert_to_mm(x, x->maximum); diff --git a/src/evdev.h b/src/evdev.h index fad1f84c..a21b03e5 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -67,8 +67,9 @@ struct evdev_device { int fd; struct { const struct input_absinfo *absinfo_x, *absinfo_y; - int32_t x, y; + int fake_resolution; + int32_t x, y; int32_t seat_slot; int apply_calibration; From d1cc84265b7132e7b4e836656e42051a712d6b14 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 24 Jul 2014 13:18:56 +1000 Subject: [PATCH 17/91] test: add a semi-mt Alps test device Provides the bounding box only, with slot 0 always being the upper/left, slot 1 being the lower-right touch. This needs to use the touch_down etc. litest interfaces, which are now widened to double (leftover from 489630f58) and a device-specific private pointer in the litest device. New device feature for litest: LITEST_SEMI_MT Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/Makefile.am | 1 + test/litest-alps-semi-mt.c | 260 +++++++++++++++++++++++++++++++++++++ test/litest-int.h | 4 +- test/litest.c | 17 ++- test/litest.h | 6 + 5 files changed, 279 insertions(+), 9 deletions(-) create mode 100644 test/litest-alps-semi-mt.c diff --git a/test/Makefile.am b/test/Makefile.am index c3c293a0..35c3bf84 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -15,6 +15,7 @@ liblitest_la_SOURCES = \ ../src/libinput-util.c \ litest.h \ litest-int.h \ + litest-alps-semi-mt.c \ litest-bcm5974.c \ litest-keyboard.c \ litest-mouse.c \ diff --git a/test/litest-alps-semi-mt.c b/test/litest-alps-semi-mt.c new file mode 100644 index 00000000..943ca2db --- /dev/null +++ b/test/litest-alps-semi-mt.c @@ -0,0 +1,260 @@ +/* + * Copyright © 2014 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 + * 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. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * 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. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "libinput-util.h" + +#include "litest.h" +#include "litest-int.h" + +static int tracking_id; + +/* this is a semi-mt device, so we keep track of the touches that the tests + * send and modify them so that the first touch is always slot 0 and sends + * the top-left of the bounding box, the second is always slot 1 and sends + * the bottom-right of the bounding box. + * Lifting any of two fingers terminates slot 1 + */ +struct alps { + /* The actual touches requested by the test for the two slots + * in the 0..100 range used by litest */ + struct { + double x, y; + } touches[2]; +}; + +static void alps_create(struct litest_device *d); + +static void +litest_alps_setup(void) +{ + struct litest_device *d = litest_create_device(LITEST_ALPS_SEMI_MT); + litest_set_current_device(d); +} + +static void +send_abs_xy(struct litest_device *d, double x, double y) +{ + struct input_event e; + int val; + + e.type = EV_ABS; + e.code = ABS_X; + e.value = LITEST_AUTO_ASSIGN; + val = litest_auto_assign_value(d, &e, 0, x, y); + litest_event(d, EV_ABS, ABS_X, val); + + e.code = ABS_Y; + val = litest_auto_assign_value(d, &e, 0, x, y); + litest_event(d, EV_ABS, ABS_Y, val); +} + +static void +send_abs_mt_xy(struct litest_device *d, double x, double y) +{ + struct input_event e; + int val; + + e.type = EV_ABS; + e.code = ABS_MT_POSITION_X; + e.value = LITEST_AUTO_ASSIGN; + val = litest_auto_assign_value(d, &e, 0, x, y); + litest_event(d, EV_ABS, ABS_MT_POSITION_X, val); + + e.code = ABS_MT_POSITION_Y; + e.value = LITEST_AUTO_ASSIGN; + val = litest_auto_assign_value(d, &e, 0, x, y); + litest_event(d, EV_ABS, ABS_MT_POSITION_Y, val); +} + +static void +alps_touch_down(struct litest_device *d, unsigned int slot, double x, double y) +{ + struct alps *alps = d->private; + double t, l, r, b; /* top, left, right, bottom */ + + if (d->ntouches_down > 2 || slot > 1) + return; + + if (d->ntouches_down == 1) { + l = x; + t = y; + } else { + int other = (slot + 1) % 2; + l = min(x, alps->touches[other].x); + t = min(y, alps->touches[other].y); + r = max(x, alps->touches[other].x); + b = max(y, alps->touches[other].y); + } + + send_abs_xy(d, l, t); + + litest_event(d, EV_ABS, ABS_MT_SLOT, 0); + + if (d->ntouches_down == 1) + litest_event(d, EV_ABS, ABS_MT_TRACKING_ID, ++tracking_id); + + send_abs_mt_xy(d, l, t); + + if (d->ntouches_down == 2) { + litest_event(d, EV_ABS, ABS_MT_SLOT, 1); + litest_event(d, EV_ABS, ABS_MT_TRACKING_ID, ++tracking_id); + + send_abs_mt_xy(d, r, b); + } + + litest_event(d, EV_SYN, SYN_REPORT, 0); + + alps->touches[slot].x = x; + alps->touches[slot].y = y; +} + +static void +alps_touch_move(struct litest_device *d, unsigned int slot, double x, double y) +{ + struct alps *alps = d->private; + double t, l, r, b; /* top, left, right, bottom */ + + if (d->ntouches_down > 2 || slot > 1) + return; + + if (d->ntouches_down == 1) { + l = x; + t = y; + } else { + int other = (slot + 1) % 2; + l = min(x, alps->touches[other].x); + t = min(y, alps->touches[other].y); + r = max(x, alps->touches[other].x); + b = max(y, alps->touches[other].y); + } + + send_abs_xy(d, l, t); + + litest_event(d, EV_ABS, ABS_MT_SLOT, 0); + send_abs_mt_xy(d, l, t); + + if (d->ntouches_down == 2) { + litest_event(d, EV_ABS, ABS_MT_SLOT, 1); + send_abs_mt_xy(d, r, b); + } + + litest_event(d, EV_SYN, SYN_REPORT, 0); + + alps->touches[slot].x = x; + alps->touches[slot].y = y; +} + +static void +alps_touch_up(struct litest_device *d, unsigned int slot) +{ + struct alps *alps = d->private; + + /* note: ntouches_down is decreased before we get here */ + if (d->ntouches_down >= 2 || slot > 1) + return; + + litest_event(d, EV_ABS, ABS_MT_SLOT, d->ntouches_down); + litest_event(d, EV_ABS, ABS_MT_TRACKING_ID, -1); + + /* if we have one finger left, send x/y coords for that finger left. + this is likely to happen with a real touchpad */ + if (d->ntouches_down == 1) { + int other = (slot + 1) % 2; + send_abs_xy(d, alps->touches[other].x, alps->touches[other].y); + litest_event(d, EV_ABS, ABS_MT_SLOT, 0); + send_abs_mt_xy(d, alps->touches[other].x, alps->touches[other].y); + } + + litest_event(d, EV_SYN, SYN_REPORT, 0); +} + +static struct litest_device_interface interface = { + .touch_down = alps_touch_down, + .touch_move = alps_touch_move, + .touch_up = alps_touch_up, +}; + +static struct input_id input_id = { + .bustype = 0x11, + .vendor = 0x2, + .product = 0x8, +}; + +static int events[] = { + EV_KEY, BTN_LEFT, + EV_KEY, BTN_RIGHT, + EV_KEY, BTN_MIDDLE, + EV_KEY, BTN_TOOL_FINGER, + EV_KEY, BTN_TOUCH, + EV_KEY, BTN_TOOL_DOUBLETAP, + EV_KEY, BTN_TOOL_TRIPLETAP, + EV_KEY, BTN_TOOL_QUADTAP, + INPUT_PROP_MAX, INPUT_PROP_POINTER, + INPUT_PROP_MAX, INPUT_PROP_SEMI_MT, + -1, -1, +}; + +static struct input_absinfo absinfo[] = { + { ABS_X, 0, 2000, 0, 0, 0 }, + { ABS_Y, 0, 1400, 0, 0, 0 }, + { ABS_PRESSURE, 0, 127, 0, 0, 0 }, + { ABS_MT_SLOT, 0, 1, 0, 0, 0 }, + { ABS_MT_POSITION_X, 0, 2000, 0, 0, 0 }, + { ABS_MT_POSITION_Y, 0, 1400, 0, 0, 0 }, + { ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 }, + { .value = -1 } +}; + +struct litest_test_device litest_alps_device = { + .type = LITEST_ALPS_SEMI_MT, + .features = LITEST_TOUCHPAD | LITEST_BUTTON | LITEST_SEMI_MT, + .shortname = "alps semi-mt", + .setup = litest_alps_setup, + .interface = &interface, + .create = alps_create, + + .name = "AlpsPS/2 ALPS GlidePoint", + .id = &input_id, + .events = events, + .absinfo = absinfo, +}; + +static void +alps_create(struct litest_device *d) +{ + struct alps *alps = zalloc(sizeof(*alps)); + assert(alps); + + d->private = alps; + + d->uinput = litest_create_uinput_device_from_description(litest_alps_device.name, + litest_alps_device.id, + absinfo, + events); + d->interface = &interface; +} diff --git a/test/litest-int.h b/test/litest-int.h index 581930b8..95bc2483 100644 --- a/test/litest-int.h +++ b/test/litest-int.h @@ -72,8 +72,8 @@ struct litest_test_device { }; struct litest_device_interface { - void (*touch_down)(struct litest_device *d, unsigned int slot, int x, int y); - void (*touch_move)(struct litest_device *d, unsigned int slot, int x, int y); + void (*touch_down)(struct litest_device *d, unsigned int slot, double x, double y); + void (*touch_move)(struct litest_device *d, unsigned int slot, double x, double y); void (*touch_up)(struct litest_device *d, unsigned int slot); /** diff --git a/test/litest.c b/test/litest.c index deab0cff..18e2e32d 100644 --- a/test/litest.c +++ b/test/litest.c @@ -85,6 +85,7 @@ extern struct litest_test_device litest_trackpoint_device; extern struct litest_test_device litest_bcm5974_device; extern struct litest_test_device litest_mouse_device; extern struct litest_test_device litest_wacom_touch_device; +extern struct litest_test_device litest_alps_device; struct litest_test_device* devices[] = { &litest_synaptics_clickpad_device, @@ -95,6 +96,7 @@ struct litest_test_device* devices[] = { &litest_bcm5974_device, &litest_mouse_device, &litest_wacom_touch_device, + &litest_alps_device, NULL, }; @@ -578,6 +580,7 @@ litest_delete_device(struct litest_device *d) libinput_unref(d->libinput); libevdev_free(d->evdev); libevdev_uinput_destroy(d->uinput); + free(d->private); memset(d,0, sizeof(*d)); free(d); } @@ -590,10 +593,10 @@ litest_event(struct litest_device *d, unsigned int type, ck_assert_int_eq(ret, 0); } -static int -auto_assign_value(struct litest_device *d, - const struct input_event *ev, - int slot, double x, double y) +int +litest_auto_assign_value(struct litest_device *d, + const struct input_event *ev, + int slot, double x, double y) { static int tracking_id; int value = ev->value; @@ -649,7 +652,7 @@ litest_touch_down(struct litest_device *d, unsigned int slot, ev = d->interface->touch_down_events; while (ev && (int16_t)ev->type != -1 && (int16_t)ev->code != -1) { - int value = auto_assign_value(d, ev, slot, x, y); + int value = litest_auto_assign_value(d, ev, slot, x, y); litest_event(d, ev->type, ev->code, value); ev++; } @@ -679,7 +682,7 @@ litest_touch_up(struct litest_device *d, unsigned int slot) ev = up; while (ev && (int16_t)ev->type != -1 && (int16_t)ev->code != -1) { - int value = auto_assign_value(d, ev, slot, 0, 0); + int value = litest_auto_assign_value(d, ev, slot, 0, 0); litest_event(d, ev->type, ev->code, value); ev++; } @@ -698,7 +701,7 @@ litest_touch_move(struct litest_device *d, unsigned int slot, ev = d->interface->touch_move_events; while (ev && (int16_t)ev->type != -1 && (int16_t)ev->code != -1) { - int value = auto_assign_value(d, ev, slot, x, y); + int value = litest_auto_assign_value(d, ev, slot, x, y); litest_event(d, ev->type, ev->code, value); ev++; } diff --git a/test/litest.h b/test/litest.h index 9a9d10a6..bb3fd7d1 100644 --- a/test/litest.h +++ b/test/litest.h @@ -43,6 +43,7 @@ enum litest_device_type { LITEST_TRACKPOINT, LITEST_MOUSE, LITEST_WACOM_TOUCH, + LITEST_ALPS_SEMI_MT, }; enum litest_device_feature { @@ -58,6 +59,7 @@ enum litest_device_feature { LITEST_SINGLE_TOUCH = 1 << 7, LITEST_APPLE_CLICKPAD = 1 << 8, LITEST_TOPBUTTONPAD = 1 << 9, + LITEST_SEMI_MT = 1 << 10, }; struct litest_device { @@ -69,6 +71,7 @@ struct litest_device { struct litest_device_interface *interface; int ntouches_down; + void *private; /* device-specific data */ }; struct libinput *litest_create_context(void); @@ -107,6 +110,9 @@ void litest_event(struct litest_device *t, unsigned int type, unsigned int code, int value); +int litest_auto_assign_value(struct litest_device *d, + const struct input_event *ev, + int slot, double x, double y); void litest_touch_up(struct litest_device *d, unsigned int slot); void litest_touch_move(struct litest_device *d, unsigned int slot, From 6595036d620148a806675a834e601f0121ece400 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 6 Aug 2014 12:22:38 +1000 Subject: [PATCH 18/91] touchpad: increase top software button area to 15% We had reports that the top software button area is hard to hit for those using the trackpoint and clicking the buttons with their thumb. Analysis of event recordings (3 different people) for left, right and middle clicks shows that there is a significant amount of events up to about 10mm (with outliers up to 12mm) from the top of the touchpad. That maps to 15%. Interestingly, the middle button is not affected by this, presumably the haptic feedback of the little dots sticking out from the surface make hitting the button easier. Signed-off-by: Peter Hutterer Acked-by: Hans de Goede --- src/evdev-mt-touchpad-buttons.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index fe33d0b1..92624666 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -542,12 +542,13 @@ tp_init_buttons(struct tp_dispatch *tp, if (tp->buttons.has_topbuttons) { /* T440s has the top button line 5mm from the top, - make the buttons 6mm high */ + event analysis has shown events to start down to ~10mm + from the top - which maps to 15% */ if (yres > 1) { tp->buttons.top_area.bottom_edge = - yoffset + 6 * yres; + yoffset + 10 * yres; } else { - tp->buttons.top_area.bottom_edge = height * .08 + yoffset; + tp->buttons.top_area.bottom_edge = height * .15 + yoffset; } tp->buttons.top_area.rightbutton_left_edge = width * .58 + xoffset; tp->buttons.top_area.leftbutton_right_edge = width * .42 + xoffset; From 4be59a972a89ea013b21ccdca19afe8456267520 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Mon, 14 Jul 2014 00:01:10 +0200 Subject: [PATCH 19/91] test: Use only one test device for some udev and path tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some tests in test/path.c and test/udev.c are not dependent on device behaviour but rather managing of device lifetime etc. Run those tests only once with only one device, resulting more or less the same code coverage but shorter run time. Signed-off-by: Jonas Ådahl Reviewed-by: Peter Hutterer --- test/litest.c | 45 +++++++++++++++++++++++++++++++++++---------- test/litest.h | 4 ++++ test/path.c | 13 +++++++------ test/udev.c | 15 ++++++++------- 4 files changed, 54 insertions(+), 23 deletions(-) diff --git a/test/litest.c b/test/litest.c index 18e2e32d..524b5456 100644 --- a/test/litest.c +++ b/test/litest.c @@ -181,11 +181,8 @@ litest_add_no_device(const char *name, void *func) litest_add(name, func, LITEST_DISABLE_DEVICE, LITEST_DISABLE_DEVICE); } -void -litest_add(const char *name, - void *func, - enum litest_device_feature required, - enum litest_device_feature excluded) +static struct suite * +get_suite(const char *name) { struct suite *s; @@ -193,10 +190,8 @@ litest_add(const char *name, list_init(&all_tests); list_for_each(s, &all_tests, node) { - if (strcmp(s->name, name) == 0) { - litest_add_tcase(s, func, required, excluded); - return; - } + if (strcmp(s->name, name) == 0) + return s; } s = zalloc(sizeof(*s)); @@ -205,7 +200,37 @@ litest_add(const char *name, list_init(&s->tests); list_insert(&all_tests, &s->node); - litest_add_tcase(s, func, required, excluded); + + return s; +} + +void +litest_add(const char *name, + void *func, + enum litest_device_feature required, + enum litest_device_feature excluded) +{ + litest_add_tcase(get_suite(name), func, required, excluded); +} + +void +litest_add_for_device(const char *name, + void *func, + enum litest_device_type type) +{ + struct suite *s; + struct litest_test_device **dev = devices; + + s = get_suite(name); + while (*dev) { + if ((*dev)->type == type) { + litest_add_tcase_for_device(s, func, *dev); + return; + } + dev++; + } + + ck_abort_msg("Invalid test device type"); } static int diff --git a/test/litest.h b/test/litest.h index bb3fd7d1..2dcb7b20 100644 --- a/test/litest.h +++ b/test/litest.h @@ -79,6 +79,10 @@ struct libinput *litest_create_context(void); void litest_add(const char *name, void *func, enum litest_device_feature required_feature, enum litest_device_feature excluded_feature); +void +litest_add_for_device(const char *name, + void *func, + enum litest_device_type type); void litest_add_no_device(const char *name, void *func); int litest_run(int argc, char **argv); diff --git a/test/path.c b/test/path.c index 475e125f..ecb78397 100644 --- a/test/path.c +++ b/test/path.c @@ -793,8 +793,9 @@ START_TEST(path_seat_recycle) } END_TEST -int main (int argc, char **argv) { - +int +main(int argc, char **argv) +{ litest_add_no_device("path:create", path_create_NULL); litest_add_no_device("path:create", path_create_invalid); litest_add_no_device("path:create", path_create_destroy); @@ -804,13 +805,13 @@ int main (int argc, char **argv) { litest_add_no_device("path:suspend", path_add_device_suspend_resume); litest_add_no_device("path:suspend", path_add_device_suspend_resume_fail); litest_add_no_device("path:suspend", path_add_device_suspend_resume_remove_device); - litest_add("path:seat events", path_added_seat, LITEST_ANY, LITEST_ANY); + litest_add_for_device("path:seat events", path_added_seat, LITEST_SYNAPTICS_CLICKPAD); litest_add("path:device events", path_added_device, LITEST_ANY, LITEST_ANY); litest_add("path:device events", path_device_sysname, LITEST_ANY, LITEST_ANY); - litest_add("path:device events", path_add_device, LITEST_ANY, LITEST_ANY); + litest_add_for_device("path:device events", path_add_device, LITEST_SYNAPTICS_CLICKPAD); litest_add_no_device("path:device events", path_add_invalid_path); - litest_add("path:device events", path_remove_device, LITEST_ANY, LITEST_ANY); - litest_add("path:device events", path_double_remove_device, LITEST_ANY, LITEST_ANY); + litest_add_for_device("path:device events", path_remove_device, LITEST_SYNAPTICS_CLICKPAD); + litest_add_for_device("path:device events", path_double_remove_device, LITEST_SYNAPTICS_CLICKPAD); litest_add_no_device("path:seat", path_seat_recycle); return litest_run(argc, argv); diff --git a/test/udev.c b/test/udev.c index 9a59eb58..95206635 100644 --- a/test/udev.c +++ b/test/udev.c @@ -407,19 +407,20 @@ START_TEST(udev_seat_recycle) } END_TEST -int main (int argc, char **argv) { - +int +main(int argc, char **argv) +{ litest_add_no_device("udev:create", udev_create_NULL); litest_add_no_device("udev:create", udev_create_seat0); litest_add_no_device("udev:create", udev_create_empty_seat); litest_add_no_device("udev:seat events", udev_added_seat_default); - litest_add("udev:suspend", udev_double_suspend, LITEST_ANY, LITEST_ANY); - litest_add("udev:suspend", udev_double_resume, LITEST_ANY, LITEST_ANY); - litest_add("udev:suspend", udev_suspend_resume, LITEST_ANY, LITEST_ANY); - litest_add("udev:device events", udev_device_sysname, LITEST_ANY, LITEST_ANY); - litest_add("udev:seat", udev_seat_recycle, LITEST_ANY, LITEST_ANY); + litest_add_for_device("udev:suspend", udev_double_suspend, LITEST_SYNAPTICS_CLICKPAD); + litest_add_for_device("udev:suspend", udev_double_resume, LITEST_SYNAPTICS_CLICKPAD); + litest_add_for_device("udev:suspend", udev_suspend_resume, LITEST_SYNAPTICS_CLICKPAD); + litest_add_for_device("udev:device events", udev_device_sysname, LITEST_SYNAPTICS_CLICKPAD); + litest_add_for_device("udev:seat", udev_seat_recycle, LITEST_SYNAPTICS_CLICKPAD); return litest_run(argc, argv); } From ec0161eda336439bcd502f00fb1e3481617c3f2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Mon, 14 Jul 2014 00:04:14 +0200 Subject: [PATCH 20/91] test: Remove test device from context when deleting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jonas Ådahl Reviewed-by: Peter Hutterer --- test/litest.c | 1 + 1 file changed, 1 insertion(+) diff --git a/test/litest.c b/test/litest.c index 524b5456..c058b6be 100644 --- a/test/litest.c +++ b/test/litest.c @@ -601,6 +601,7 @@ litest_delete_device(struct litest_device *d) return; libinput_device_unref(d->libinput_device); + libinput_path_remove_device(d->libinput_device); if (d->owns_context) libinput_unref(d->libinput); libevdev_free(d->evdev); From e3f39004a2e36461c508b76a645368d5613062a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Mon, 14 Jul 2014 23:34:25 +0200 Subject: [PATCH 21/91] test: Don't fail when events are enabled multiple times MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When overriding events of a test device, if one would enable an event that was already enabled by default for the overridden device, an assert checking if the event was already enabled would fail and cause the test to fail. Since the merging of the default and overriding event lists is implemented by simply concatinating them letting libevdev deal with ignoring superfluous event enabling, remove the assert to allow the implementation to work. Signed-off-by: Jonas Ådahl Reviewed-by: Peter Hutterer --- test/litest.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/litest.c b/test/litest.c index c058b6be..00db464b 100644 --- a/test/litest.c +++ b/test/litest.c @@ -900,8 +900,6 @@ litest_create_uinput_device_from_description(const char *name, if (type == INPUT_PROP_MAX) { rc = libevdev_enable_property(dev, code); } else { - if (type != EV_SYN) - ck_assert(!libevdev_has_event_code(dev, type, code)); rc = libevdev_enable_event_code(dev, type, code, type == EV_ABS ? &default_abs : NULL); } From c17282ffc49a279553961a964f126b9da22011c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Sat, 26 Jul 2014 21:34:47 +0200 Subject: [PATCH 22/91] evdev: Let dispatch instances set their own capabilities MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's up to a evdev device backend to configure seat capabilities it supports. Even though it may be possible for a touchpad to have extra keys, there is currently no support for sending keyboard events from the touchpad driver, and if that would be implemented, it'd be a detail of the touchpad driver, not the generic evdev device part. Signed-off-by: Jonas Ådahl Reviewed-by: Peter Hutterer --- src/evdev-mt-touchpad.c | 2 ++ src/evdev.c | 1 + 2 files changed, 3 insertions(+) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index b58a6caf..d831b836 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -824,6 +824,8 @@ tp_init(struct tp_dispatch *tp, if (tp_init_palmdetect(tp, device) != 0) return -1; + device->seat_caps |= EVDEV_DEVICE_POINTER; + return 0; } diff --git a/src/evdev.c b/src/evdev.c index a1255100..5a0ff909 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -707,6 +707,7 @@ evdev_configure_device(struct evdev_device *device) log_info(libinput, "input device '%s', %s is a touchpad\n", device->devname, device->devnode); + return device->dispatch == NULL ? -1 : 0; } for (i = KEY_ESC; i < KEY_MAX; i++) { if (i >= BTN_MISC && i < KEY_OK) From 100ba70e873b8ed7a6f0b90e9c63687f799664a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Sun, 13 Jul 2014 00:25:36 +0200 Subject: [PATCH 23/91] evdev: Use helper for separating buttons from keys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jonas Ådahl Reviewed-by: Peter Hutterer --- src/evdev.c | 83 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 47 insertions(+), 36 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index 5a0ff909..31ca620d 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -41,6 +41,12 @@ #define DEFAULT_AXIS_STEP_DISTANCE 10 +enum evdev_key_type { + EVDEV_KEY_TYPE_NONE, + EVDEV_KEY_TYPE_KEY, + EVDEV_KEY_TYPE_BUTTON, +}; + void evdev_device_led_update(struct evdev_device *device, enum libinput_led leds) { @@ -254,6 +260,23 @@ evdev_flush_pending_event(struct evdev_device *device, uint64_t time) device->pending_event = EVDEV_NONE; } +static enum evdev_key_type +get_key_type(uint16_t code) +{ + if (code == BTN_TOUCH) + return EVDEV_KEY_TYPE_NONE; + + if (code >= KEY_ESC && code <= KEY_MICMUTE) + return EVDEV_KEY_TYPE_KEY; + if (code >= BTN_MISC && code <= BTN_GEAR_UP) + return EVDEV_KEY_TYPE_BUTTON; + if (code >= KEY_OK && code <= KEY_LIGHTS_TOGGLE) + return EVDEV_KEY_TYPE_KEY; + if (code >= BTN_DPAD_UP && code <= BTN_TRIGGER_HAPPY40) + return EVDEV_KEY_TYPE_BUTTON; + return EVDEV_KEY_TYPE_NONE; +} + static void evdev_process_touch_button(struct evdev_device *device, uint64_t time, int value) @@ -275,9 +298,6 @@ evdev_process_key(struct evdev_device *device, if (e->value == 2) return; - if (e->code > KEY_MAX) - return; - if (e->code == BTN_TOUCH) { if (!device->is_mt) evdev_process_touch_button(device, time, e->value); @@ -286,29 +306,10 @@ evdev_process_key(struct evdev_device *device, evdev_flush_pending_event(device, time); - switch (e->code) { - case BTN_LEFT: - case BTN_RIGHT: - case BTN_MIDDLE: - case BTN_SIDE: - case BTN_EXTRA: - case BTN_FORWARD: - case BTN_BACK: - case BTN_TASK: - pointer_notify_button( - &device->base, - time, - e->code, - e->value ? LIBINPUT_BUTTON_STATE_PRESSED : - LIBINPUT_BUTTON_STATE_RELEASED); + switch (get_key_type(e->code)) { + case EVDEV_KEY_TYPE_NONE: break; - - default: - /* Only let KEY_* codes pass through. */ - if (!(e->code <= KEY_MICMUTE || - (e->code >= KEY_OK && e->code <= KEY_LIGHTS_TOGGLE))) - break; - + case EVDEV_KEY_TYPE_KEY: keyboard_notify_key( &device->base, time, @@ -316,6 +317,14 @@ evdev_process_key(struct evdev_device *device, e->value ? LIBINPUT_KEY_STATE_PRESSED : LIBINPUT_KEY_STATE_RELEASED); break; + case EVDEV_KEY_TYPE_BUTTON: + pointer_notify_button( + &device->base, + time, + e->code, + e->value ? LIBINPUT_BUTTON_STATE_PRESSED : + LIBINPUT_BUTTON_STATE_RELEASED); + break; } } @@ -709,22 +718,24 @@ evdev_configure_device(struct evdev_device *device) device->devname, device->devnode); return device->dispatch == NULL ? -1 : 0; } - for (i = KEY_ESC; i < KEY_MAX; i++) { - if (i >= BTN_MISC && i < KEY_OK) - continue; + + for (i = 0; i < KEY_MAX; i++) { if (libevdev_has_event_code(evdev, EV_KEY, i)) { - has_keyboard = 1; - break; + switch (get_key_type(i)) { + case EVDEV_KEY_TYPE_NONE: + break; + case EVDEV_KEY_TYPE_KEY: + has_keyboard = 1; + break; + case EVDEV_KEY_TYPE_BUTTON: + has_button = 1; + break; + } } } + if (libevdev_has_event_code(evdev, EV_KEY, BTN_TOUCH)) has_touch = 1; - for (i = BTN_MISC; i < BTN_JOYSTICK; i++) { - if (libevdev_has_event_code(evdev, EV_KEY, i)) { - has_button = 1; - break; - } - } } if (libevdev_has_event_type(evdev, EV_LED)) has_keyboard = 1; From c8017595fcb9fafd15c2c638c55f72f389da411a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Sun, 27 Jul 2014 15:43:59 +0200 Subject: [PATCH 24/91] evdev: Ignore key/button release events if key was never pressed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The kernel may send a 'release' event without ever having sent a key 'pressed' event in case the key was pressed before libinput was initiated. Ignore these events so that we always guarantee a release event always comes after a pressed event for any given key or button. Signed-off-by: Jonas Ådahl --- src/evdev.c | 39 +++++++++++++++++++++++----- src/evdev.h | 4 +++ src/libinput-util.h | 29 +++++++++++++++++++++ src/libinput.h | 3 ++- test/keyboard.c | 63 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 131 insertions(+), 7 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index 31ca620d..bc0237fb 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -47,6 +47,18 @@ enum evdev_key_type { EVDEV_KEY_TYPE_BUTTON, }; +static void +set_key_down(struct evdev_device *device, int code, int pressed) +{ + long_set_bit_state(device->key_mask, code, pressed); +} + +static int +is_key_down(struct evdev_device *device, int code) +{ + return long_bit_is_set(device->key_mask, code); +} + void evdev_device_led_update(struct evdev_device *device, enum libinput_led leds) { @@ -294,6 +306,8 @@ static inline void evdev_process_key(struct evdev_device *device, struct input_event *e, uint64_t time) { + enum evdev_key_type type; + /* ignore kernel key repeat */ if (e->value == 2) return; @@ -306,7 +320,24 @@ evdev_process_key(struct evdev_device *device, evdev_flush_pending_event(device, time); - switch (get_key_type(e->code)) { + type = get_key_type(e->code); + + /* Ignore key release events from the kernel for keys that libinput + * never got a pressed event for. */ + if (e->value == 0) { + switch (type) { + case EVDEV_KEY_TYPE_NONE: + break; + case EVDEV_KEY_TYPE_KEY: + case EVDEV_KEY_TYPE_BUTTON: + if (!is_key_down(device, e->code)) + return; + } + } + + set_key_down(device, e->code, e->value); + + switch (type) { case EVDEV_KEY_TYPE_NONE: break; case EVDEV_KEY_TYPE_KEY: @@ -853,12 +884,8 @@ err: int evdev_device_get_keys(struct evdev_device *device, char *keys, size_t size) { - int len; - memset(keys, 0, size); - len = ioctl(device->fd, EVIOCGKEY(size), keys); - - return (len == -1) ? -errno : len; + return 0; } const char * diff --git a/src/evdev.h b/src/evdev.h index a21b03e5..68c1234a 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -95,6 +95,10 @@ struct evdev_device { struct { struct motion_filter *filter; } pointer; + + /* Bitmask of pressed keys used to ignore initial release events from + * the kernel. */ + unsigned long key_mask[NLONGS(KEY_CNT)]; }; #define EVDEV_UNHANDLED_DEVICE ((struct evdev_device *) 1) diff --git a/src/libinput-util.h b/src/libinput-util.h index c0235ef3..2f1a1dbf 100644 --- a/src/libinput-util.h +++ b/src/libinput-util.h @@ -72,6 +72,8 @@ int list_empty(const struct list *list); pos = tmp, \ tmp = container_of(pos->member.next, tmp, member)) +#define LONG_BITS (sizeof(long) * 8) +#define NLONGS(x) (((x) + LONG_BITS - 1) / LONG_BITS) #define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0]) #define ARRAY_FOR_EACH(_arr, _elem) \ for (size_t _i = 0; (_elem = &_arr[_i]) && _i < ARRAY_LENGTH(_arr); _i++) @@ -150,4 +152,31 @@ vector_get_direction(int dx, int dy) return dir; } +static inline int +long_bit_is_set(const unsigned long *array, int bit) +{ + return !!(array[bit / LONG_BITS] & (1LL << (bit % LONG_BITS))); +} + +static inline void +long_set_bit(unsigned long *array, int bit) +{ + array[bit / LONG_BITS] |= (1LL << (bit % LONG_BITS)); +} + +static inline void +long_clear_bit(unsigned long *array, int bit) +{ + array[bit / LONG_BITS] &= ~(1LL << (bit % LONG_BITS)); +} + +static inline void +long_set_bit_state(unsigned long *array, int bit, int state) +{ + if (state) + long_set_bit(array, bit); + else + long_clear_bit(array, bit); +} + #endif /* LIBINPUT_UTIL_H */ diff --git a/src/libinput.h b/src/libinput.h index 1e8ae679..9296a353 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -1359,7 +1359,8 @@ libinput_device_led_update(struct libinput_device *device, */ int libinput_device_get_keys(struct libinput_device *device, - char *keys, size_t size); + char *keys, size_t size) + LIBINPUT_ATTRIBUTE_DEPRECATED; /** * @ingroup device diff --git a/test/keyboard.c b/test/keyboard.c index a55405cb..c2040ccd 100644 --- a/test/keyboard.c +++ b/test/keyboard.c @@ -112,10 +112,73 @@ START_TEST(keyboard_seat_key_count) } END_TEST +START_TEST(keyboard_ignore_no_pressed_release) +{ + struct litest_device *dev; + struct libinput *unused_libinput; + struct libinput *libinput; + struct libinput_event *event; + struct libinput_event_keyboard *kevent; + int events[] = { + EV_KEY, KEY_A, + -1, -1, + }; + enum libinput_key_state *state; + enum libinput_key_state expected_states[] = { + LIBINPUT_KEY_STATE_PRESSED, + LIBINPUT_KEY_STATE_RELEASED, + }; + + /* We can't send pressed -> released -> pressed events using uinput + * as such non-symmetric events are dropped. Work-around this by first + * adding the test device to the tested context after having sent an + * initial pressed event. */ + unused_libinput = litest_create_context(); + dev = litest_add_device_with_overrides(unused_libinput, + LITEST_KEYBOARD, + "Generic keyboard", + NULL, NULL, events); + + litest_keyboard_key(dev, KEY_A, true); + litest_drain_events(unused_libinput); + + libinput = litest_create_context(); + libinput_path_add_device(libinput, + libevdev_uinput_get_devnode(dev->uinput)); + litest_drain_events(libinput); + + litest_keyboard_key(dev, KEY_A, false); + litest_keyboard_key(dev, KEY_A, true); + litest_keyboard_key(dev, KEY_A, false); + + libinput_dispatch(libinput); + + ARRAY_FOR_EACH(expected_states, state) { + event = libinput_get_event(libinput); + ck_assert_notnull(event); + ck_assert_int_eq(libinput_event_get_type(event), + LIBINPUT_EVENT_KEYBOARD_KEY); + kevent = libinput_event_get_keyboard_event(event); + ck_assert_int_eq(libinput_event_keyboard_get_key(kevent), + KEY_A); + ck_assert_int_eq(libinput_event_keyboard_get_key_state(kevent), + *state); + libinput_event_destroy(event); + libinput_dispatch(libinput); + } + + litest_assert_empty_queue(libinput); + litest_delete_device(dev); + libinput_unref(libinput); + libinput_unref(unused_libinput); +} +END_TEST + int main(int argc, char **argv) { litest_add_no_device("keyboard:seat key count", keyboard_seat_key_count); + litest_add_no_device("keyboard:key counting", keyboard_ignore_no_pressed_release); return litest_run(argc, argv); } From 3a3d70a3a895140b91c58bc305c90ee3f9268346 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Sun, 27 Jul 2014 15:54:49 +0200 Subject: [PATCH 25/91] evdev: Keep track of button/key press count per device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keep track of the number of times a given button or key is pressed on a device. For regular mouse devices or keyboard devices, such a count will never exceed 1, but counting button presses could help when button presses with the same code can originate from different sources. One could for example implement overlapping tap-drags with button presses by having them deal with their own life-time independently, sorting out when the user should receive button presses or not depending on the pressed count. Signed-off-by: Jonas Ådahl Reviewed-by: Peter Hutterer --- src/evdev-mt-touchpad-buttons.c | 30 +++++++++-------- src/evdev-mt-touchpad-tap.c | 8 ++--- src/evdev.c | 60 ++++++++++++++++++++++++++++++--- src/evdev.h | 15 +++++++++ 4 files changed, 91 insertions(+), 22 deletions(-) diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index 92624666..f6235a0a 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -607,11 +607,12 @@ tp_post_clickfinger_buttons(struct tp_dispatch *tp, uint64_t time) state = LIBINPUT_BUTTON_STATE_RELEASED; } - if (button) - pointer_notify_button(&tp->device->base, - time, - button, - state); + if (button) { + evdev_pointer_notify_button(tp->device, + time, + button, + state); + } return 1; } @@ -633,10 +634,10 @@ tp_post_physical_buttons(struct tp_dispatch *tp, uint64_t time) else state = LIBINPUT_BUTTON_STATE_RELEASED; - pointer_notify_button(&tp->device->base, - time, - button, - state); + evdev_pointer_notify_button(tp->device, + time, + button, + state); } button++; @@ -708,11 +709,12 @@ tp_post_softbutton_buttons(struct tp_dispatch *tp, uint64_t time) tp->buttons.click_pending = false; - if (button) - pointer_notify_button(&tp->device->base, - time, - button, - state); + if (button) { + evdev_pointer_notify_button(tp->device, + time, + button, + state); + } return 1; } diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index 60085074..64801a81 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -113,10 +113,10 @@ tp_tap_notify(struct tp_dispatch *tp, return; } - pointer_notify_button(&tp->device->base, - time, - button, - state); + evdev_pointer_notify_button(tp->device, + time, + button, + state); } static void diff --git a/src/evdev.c b/src/evdev.c index bc0237fb..a605964b 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -59,6 +59,58 @@ is_key_down(struct evdev_device *device, int code) return long_bit_is_set(device->key_mask, code); } +static int +update_key_down_count(struct evdev_device *device, int code, int pressed) +{ + int key_count; + assert(code >= 0 && code < KEY_CNT); + + if (pressed) { + key_count = ++device->key_count[code]; + } else { + assert(device->key_count[code] > 0); + key_count = --device->key_count[code]; + } + + if (key_count > 32) { + log_bug_libinput(device->base.seat->libinput, + "Key count for %s reached abnormal values\n", + libevdev_event_code_get_name(EV_KEY, code)); + } + + return key_count; +} + +void +evdev_keyboard_notify_key(struct evdev_device *device, + uint32_t time, + int key, + enum libinput_key_state state) +{ + int down_count; + + down_count = update_key_down_count(device, key, state); + + if ((state == LIBINPUT_KEY_STATE_PRESSED && down_count == 1) || + (state == LIBINPUT_KEY_STATE_RELEASED && down_count == 0)) + keyboard_notify_key(&device->base, time, key, state); +} + +void +evdev_pointer_notify_button(struct evdev_device *device, + uint32_t time, + int button, + enum libinput_button_state state) +{ + int down_count; + + down_count = update_key_down_count(device, button, state); + + if ((state == LIBINPUT_BUTTON_STATE_PRESSED && down_count == 1) || + (state == LIBINPUT_BUTTON_STATE_RELEASED && down_count == 0)) + pointer_notify_button(&device->base, time, button, state); +} + void evdev_device_led_update(struct evdev_device *device, enum libinput_led leds) { @@ -341,16 +393,16 @@ evdev_process_key(struct evdev_device *device, case EVDEV_KEY_TYPE_NONE: break; case EVDEV_KEY_TYPE_KEY: - keyboard_notify_key( - &device->base, + evdev_keyboard_notify_key( + device, time, e->code, e->value ? LIBINPUT_KEY_STATE_PRESSED : LIBINPUT_KEY_STATE_RELEASED); break; case EVDEV_KEY_TYPE_BUTTON: - pointer_notify_button( - &device->base, + evdev_pointer_notify_button( + device, time, e->code, e->value ? LIBINPUT_BUTTON_STATE_PRESSED : diff --git a/src/evdev.h b/src/evdev.h index 68c1234a..f1ccdc29 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -99,6 +99,9 @@ struct evdev_device { /* Bitmask of pressed keys used to ignore initial release events from * the kernel. */ unsigned long key_mask[NLONGS(KEY_CNT)]; + /* Key counter used for multiplexing button events internally in + * libinput. */ + uint8_t key_count[KEY_CNT]; }; #define EVDEV_UNHANDLED_DEVICE ((struct evdev_device *) 1) @@ -177,6 +180,18 @@ evdev_device_transform_y(struct evdev_device *device, double y, uint32_t height); +void +evdev_keyboard_notify_key(struct evdev_device *device, + uint32_t time, + int key, + enum libinput_key_state state); + +void +evdev_pointer_notify_button(struct evdev_device *device, + uint32_t time, + int button, + enum libinput_button_state state); + void evdev_device_remove(struct evdev_device *device); From 8f846a41fa0566fbd72ece676656e20e56ce43e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Sun, 27 Jul 2014 16:02:46 +0200 Subject: [PATCH 26/91] evdev: Release still pressed keys/buttons when removing device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When removing a device, its not guaranteed that all button or key presses have been released, resulting in an invalid seat wide button count. Note that kernel devices normally will send release events when being unplugged, but this won't happen when removing a device from the path backend. Signed-off-by: Jonas Ådahl Reviewed-by: Peter Hutterer --- src/evdev.c | 47 +++++++++++++++++++ test/keyboard.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++++ test/pointer.c | 90 +++++++++++++++++++++++++++++++++++++ 3 files changed, 254 insertions(+) diff --git a/src/evdev.c b/src/evdev.c index a605964b..b9f635db 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -59,6 +59,12 @@ is_key_down(struct evdev_device *device, int code) return long_bit_is_set(device->key_mask, code); } +static int +get_key_down_count(struct evdev_device *device, int code) +{ + return device->key_count[code]; +} + static int update_key_down_count(struct evdev_device *device, int code, int pressed) { @@ -1013,6 +1019,45 @@ evdev_device_get_size(struct evdev_device *device, return 0; } +static void +release_pressed_keys(struct evdev_device *device) +{ + struct libinput *libinput = device->base.seat->libinput; + struct timespec ts; + uint64_t time; + int code; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { + log_bug_libinput(libinput, "clock_gettime: %s\n", strerror(errno)); + return; + } + + time = ts.tv_sec * 1000ULL + ts.tv_nsec / 1000000; + + for (code = 0; code < KEY_CNT; code++) { + if (get_key_down_count(device, code) > 0) { + switch (get_key_type(code)) { + case EVDEV_KEY_TYPE_NONE: + break; + case EVDEV_KEY_TYPE_KEY: + keyboard_notify_key( + &device->base, + time, + code, + LIBINPUT_KEY_STATE_RELEASED); + break; + case EVDEV_KEY_TYPE_BUTTON: + pointer_notify_button( + &device->base, + time, + code, + LIBINPUT_BUTTON_STATE_RELEASED); + break; + } + } + } +} + void evdev_device_remove(struct evdev_device *device) { @@ -1020,6 +1065,8 @@ evdev_device_remove(struct evdev_device *device) libinput_remove_source(device->base.seat->libinput, device->source); + release_pressed_keys(device); + if (device->mtdev) mtdev_close_delete(device->mtdev); close_restricted(device->base.seat->libinput, device->fd); diff --git a/test/keyboard.c b/test/keyboard.c index c2040ccd..4563ce6f 100644 --- a/test/keyboard.c +++ b/test/keyboard.c @@ -25,6 +25,7 @@ #include #include +#include "libinput-util.h" #include "litest.h" START_TEST(keyboard_seat_key_count) @@ -174,11 +175,127 @@ START_TEST(keyboard_ignore_no_pressed_release) } END_TEST +static void +test_key_event(struct litest_device *dev, unsigned int key, int state) +{ + struct libinput *li = dev->libinput; + struct libinput_event *event; + struct libinput_event_keyboard *kevent; + + litest_event(dev, EV_KEY, key, state); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + + libinput_dispatch(li); + + event = libinput_get_event(li); + ck_assert(event != NULL); + ck_assert_int_eq(libinput_event_get_type(event), LIBINPUT_EVENT_KEYBOARD_KEY); + + kevent = libinput_event_get_keyboard_event(event); + ck_assert(kevent != NULL); + ck_assert_int_eq(libinput_event_keyboard_get_key(kevent), key); + ck_assert_int_eq(libinput_event_keyboard_get_key_state(kevent), + state ? LIBINPUT_KEY_STATE_PRESSED : + LIBINPUT_KEY_STATE_RELEASED); + libinput_event_destroy(event); +} + +START_TEST(keyboard_key_auto_release) +{ + struct libinput *libinput; + struct litest_device *dev; + struct libinput_event *event; + enum libinput_event_type type; + struct libinput_event_keyboard *kevent; + struct { + int code; + int released; + } keys[] = { + { .code = KEY_A, }, + { .code = KEY_S, }, + { .code = KEY_D, }, + { .code = KEY_G, }, + { .code = KEY_Z, }, + { .code = KEY_DELETE, }, + { .code = KEY_F24, }, + }; + int events[2 * (ARRAY_LENGTH(keys) + 1)]; + unsigned i; + int key; + int valid_code; + + /* Enable all tested keys on the device */ + i = 0; + while (i < 2 * ARRAY_LENGTH(keys)) { + key = keys[i / 2].code; + events[i++] = EV_KEY; + events[i++] = key; + } + events[i++] = -1; + events[i++] = -1; + + libinput = litest_create_context(); + dev = litest_add_device_with_overrides(libinput, + LITEST_KEYBOARD, + "Generic keyboard", + NULL, NULL, events); + + litest_drain_events(libinput); + + /* Send pressed events, without releasing */ + for (i = 0; i < ARRAY_LENGTH(keys); ++i) { + test_key_event(dev, keys[i].code, 1); + } + + litest_drain_events(libinput); + + /* "Disconnect" device */ + litest_delete_device(dev); + + /* Mark all released keys until device is removed */ + while (1) { + event = libinput_get_event(libinput); + ck_assert_notnull(event); + type = libinput_event_get_type(event); + + if (type == LIBINPUT_EVENT_DEVICE_REMOVED) { + libinput_event_destroy(event); + break; + } + + ck_assert_int_eq(type, LIBINPUT_EVENT_KEYBOARD_KEY); + kevent = libinput_event_get_keyboard_event(event); + ck_assert_int_eq(libinput_event_keyboard_get_key_state(kevent), + LIBINPUT_KEY_STATE_RELEASED); + key = libinput_event_keyboard_get_key(kevent); + + valid_code = 0; + for (i = 0; i < ARRAY_LENGTH(keys); ++i) { + if (keys[i].code == key) { + ck_assert_int_eq(keys[i].released, 0); + keys[i].released = 1; + valid_code = 1; + } + } + ck_assert_int_eq(valid_code, 1); + libinput_event_destroy(event); + } + + /* Check that all pressed keys has been released. */ + for (i = 0; i < ARRAY_LENGTH(keys); ++i) { + ck_assert_int_eq(keys[i].released, 1); + } + + libinput_unref(libinput); +} +END_TEST + int main(int argc, char **argv) { litest_add_no_device("keyboard:seat key count", keyboard_seat_key_count); litest_add_no_device("keyboard:key counting", keyboard_ignore_no_pressed_release); + litest_add_no_device("keyboard:key counting", keyboard_key_auto_release); return litest_run(argc, argv); } diff --git a/test/pointer.c b/test/pointer.c index aa75274b..c0af460c 100644 --- a/test/pointer.c +++ b/test/pointer.c @@ -152,6 +152,95 @@ START_TEST(pointer_button) } END_TEST +START_TEST(pointer_button_auto_release) +{ + struct libinput *libinput; + struct litest_device *dev; + struct libinput_event *event; + enum libinput_event_type type; + struct libinput_event_pointer *pevent; + struct { + int code; + int released; + } buttons[] = { + { .code = BTN_LEFT, }, + { .code = BTN_MIDDLE, }, + { .code = BTN_EXTRA, }, + { .code = BTN_SIDE, }, + { .code = BTN_BACK, }, + { .code = BTN_FORWARD, }, + { .code = BTN_4, }, + }; + int events[2 * (ARRAY_LENGTH(buttons) + 1)]; + unsigned i; + int button; + int valid_code; + + /* Enable all tested buttons on the device */ + for (i = 0; i < 2 * ARRAY_LENGTH(buttons);) { + button = buttons[i / 2].code; + events[i++] = EV_KEY; + events[i++] = button; + } + events[i++] = -1; + events[i++] = -1; + + libinput = litest_create_context(); + dev = litest_add_device_with_overrides(libinput, + LITEST_MOUSE, + "Generic mouse", + NULL, NULL, events); + + litest_drain_events(libinput); + + /* Send pressed events, without releasing */ + for (i = 0; i < ARRAY_LENGTH(buttons); ++i) { + test_button_event(dev, buttons[i].code, 1); + } + + litest_drain_events(libinput); + + /* "Disconnect" device */ + litest_delete_device(dev); + + /* Mark all released buttons until device is removed */ + while (1) { + event = libinput_get_event(libinput); + ck_assert_notnull(event); + type = libinput_event_get_type(event); + + if (type == LIBINPUT_EVENT_DEVICE_REMOVED) { + libinput_event_destroy(event); + break; + } + + ck_assert_int_eq(type, LIBINPUT_EVENT_POINTER_BUTTON); + pevent = libinput_event_get_pointer_event(event); + ck_assert_int_eq(libinput_event_pointer_get_button_state(pevent), + LIBINPUT_BUTTON_STATE_RELEASED); + button = libinput_event_pointer_get_button(pevent); + + valid_code = 0; + for (i = 0; i < ARRAY_LENGTH(buttons); ++i) { + if (buttons[i].code == button) { + ck_assert_int_eq(buttons[i].released, 0); + buttons[i].released = 1; + valid_code = 1; + } + } + ck_assert_int_eq(valid_code, 1); + libinput_event_destroy(event); + } + + /* Check that all pressed buttons has been released. */ + for (i = 0; i < ARRAY_LENGTH(buttons); ++i) { + ck_assert_int_eq(buttons[i].released, 1); + } + + libinput_unref(libinput); +} +END_TEST + static void test_wheel_event(struct litest_device *dev, int which, int amount) { @@ -300,6 +389,7 @@ int main (int argc, char **argv) { litest_add("pointer:motion", pointer_motion_relative, LITEST_POINTER, LITEST_ANY); litest_add("pointer:button", pointer_button, LITEST_BUTTON, LITEST_CLICKPAD); + litest_add_no_device("pointer:button_auto_release", pointer_button_auto_release); litest_add("pointer:scroll", pointer_scroll_wheel, LITEST_WHEEL, LITEST_ANY); litest_add_no_device("pointer:seat button count", pointer_seat_button_count); From ad13bac1f4f491eaf86179c3e90e4ae2764803b7 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 19 Aug 2014 07:57:25 +1000 Subject: [PATCH 27/91] Use -no-install instead of -static for local noinst linking The goal of -static was to avoid the libtool wrappers for easier debugging. The -no-install flag does exactly that, without requiring static linking. Related to https://bugs.freedesktop.org/show_bug.cgi?id=82292 Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/Makefile.am | 16 ++++++++-------- tools/Makefile.am | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/test/Makefile.am b/test/Makefile.am index 35c3bf84..76d4e8cb 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -48,35 +48,35 @@ TESTS = $(run_tests) test_udev_SOURCES = udev.c test_udev_LDADD = $(TEST_LIBS) -test_udev_LDFLAGS = -static +test_udev_LDFLAGS = -no-install test_path_SOURCES = path.c test_path_LDADD = $(TEST_LIBS) -test_path_LDFLAGS = -static +test_path_LDFLAGS = -no-install test_pointer_SOURCES = pointer.c test_pointer_LDADD = $(TEST_LIBS) -test_pointer_LDFLAGS = -static +test_pointer_LDFLAGS = -no-install test_touch_SOURCES = touch.c test_touch_LDADD = $(TEST_LIBS) -test_touch_LDFLAGS = -static +test_touch_LDFLAGS = -no-install test_log_SOURCES = log.c test_log_LDADD = $(TEST_LIBS) -test_log_LDFLAGS = -static +test_log_LDFLAGS = -no-install test_touchpad_SOURCES = touchpad.c test_touchpad_LDADD = $(TEST_LIBS) -test_touchpad_LDFLAGS = -static +test_touchpad_LDFLAGS = -no-install test_misc_SOURCES = misc.c test_misc_LDADD = $(TEST_LIBS) -test_misc_LDFLAGS = -static +test_misc_LDFLAGS = -no-install test_keyboard_SOURCES = keyboard.c test_keyboard_LDADD = $(TEST_LIBS) -test_keyboard_LDFLAGS = -static +test_keyboard_LDFLAGS = -no-install # build-test only test_build_pedantic_c99_SOURCES = build-pedantic.c diff --git a/tools/Makefile.am b/tools/Makefile.am index f59068da..aadd874a 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -5,7 +5,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/include \ event_debug_SOURCES = event-debug.c event_debug_LDADD = ../src/libinput.la $(LIBUDEV_LIBS) -event_debug_LDFLAGS = -static +event_debug_LDFLAGS = -no-install event_debug_CFLAGS = $(LIBUDEV_CFLAGS) if BUILD_EVENTGUI @@ -14,5 +14,5 @@ noinst_PROGRAMS += event-gui event_gui_SOURCES = event-gui.c event_gui_LDADD = ../src/libinput.la $(CAIRO_LIBS) $(GTK_LIBS) $(LIBUDEV_LIBS) event_gui_CFLAGS = $(CAIRO_CFLAGS) $(GTK_CFLAGS) $(LIBUDEV_CFLAGS) -event_gui_LDFLAGS = -static +event_gui_LDFLAGS = -no-install endif From f5e014f8b0c1d8a4d7bc94fd18aeb415f103fd75 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 19 Aug 2014 07:59:10 +1000 Subject: [PATCH 28/91] Disable static libraries Our static library leaks symbols like crazy, some of which are likely conflicts with users of this library (log_msg, open_restricted, ...). Disale static linking by default so we don't have to spend time debugging this. Related to: https://bugs.freedesktop.org/show_bug.cgi?id=82292 https://bugs.freedesktop.org/show_bug.cgi?id=82785 Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile.am b/src/Makefile.am index 76fe4c94..44e73d3f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -34,7 +34,7 @@ libinput_la_CFLAGS = -I$(top_srcdir)/include \ $(LIBEVDEV_CFLAGS) \ $(GCC_CFLAGS) -libinput_la_LDFLAGS = -version-info $(LIBINPUT_LT_VERSION) +libinput_la_LDFLAGS = -version-info $(LIBINPUT_LT_VERSION) -shared pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libinput.pc From c6478bbeeb6ecc328710f5777e4c4d7596dd6904 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 22 Aug 2014 14:48:42 +1000 Subject: [PATCH 29/91] evdev: plug memory leak on libevdev_new_from_fd failure Found by coverity. Signed-off-by: Peter Hutterer --- src/evdev.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index b9f635db..b09bb989 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -885,10 +885,11 @@ evdev_device_create(struct libinput_seat *seat, return NULL; libinput_device_init(&device->base, seat); + libinput_seat_ref(seat); rc = libevdev_new_from_fd(fd, &device->evdev); if (rc != 0) - return NULL; + goto err; libevdev_set_clock_id(device->evdev, CLOCK_MONOTONIC); @@ -905,8 +906,6 @@ evdev_device_create(struct libinput_seat *seat, device->pending_event = EVDEV_NONE; device->devname = libevdev_get_name(device->evdev); - libinput_seat_ref(seat); - if (evdev_configure_device(device) == -1) goto err; From 0b26f2592cbe28f35b96a5422fda4bc6298930cb Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 22 Aug 2014 14:54:06 +1000 Subject: [PATCH 30/91] Don't close the fd if libinput_add_fd() fails Let the caller decide what to do with the fd. In the current code the caller can't know if the fd was closed on error since we return NULL on malloc failure as well as on epoll_ctl() failure. In the latter case the fd was closed, not in the former. The caller had to close the fd anyway (and all three callers do), so drop closing the fd from this function. Found by Coverity. Signed-off-by: Peter Hutterer --- src/libinput.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libinput.c b/src/libinput.c index 90b6a137..ed5eba17 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -457,7 +457,6 @@ libinput_add_fd(struct libinput *libinput, ep.data.ptr = source; if (epoll_ctl(libinput->epoll_fd, EPOLL_CTL_ADD, fd, &ep) < 0) { - close(source->fd); free(source); return NULL; } From 7b66f16c2a30deb9ab50b3111b9b2611ffab27d6 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 22 Aug 2014 14:58:48 +1000 Subject: [PATCH 31/91] touchpad: mark a intentional switch case fallthrough as such Both motion and timeout expiry transition into the TOUCH_2_HOLD state, but only for motion do we need to cancel the current timeout. Found by Coverity. Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad-tap.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index 64801a81..fae8f5e8 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -253,6 +253,7 @@ tp_tap_touch2_handle_event(struct tp_dispatch *tp, break; case TAP_EVENT_MOTION: tp_tap_clear_timer(tp); + /* fallthrough */ case TAP_EVENT_TIMEOUT: tp->tap.state = TAP_STATE_TOUCH_2_HOLD; break; From bd8e3086939230a3501a6ee6ea3b2cdb1b2203a2 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 22 Aug 2014 15:08:57 +1000 Subject: [PATCH 32/91] Swap conditions for ARRAY_FOR_EACH() The current conditions result in _elem being assigned _arr[i] before the condition is checked. This is fine since we then break from the loop and don't access it anyway, but it makes Coverity unhappy. Signed-off-by: Peter Hutterer --- src/libinput-util.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libinput-util.h b/src/libinput-util.h index 2f1a1dbf..5d366b02 100644 --- a/src/libinput-util.h +++ b/src/libinput-util.h @@ -76,7 +76,7 @@ int list_empty(const struct list *list); #define NLONGS(x) (((x) + LONG_BITS - 1) / LONG_BITS) #define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0]) #define ARRAY_FOR_EACH(_arr, _elem) \ - for (size_t _i = 0; (_elem = &_arr[_i]) && _i < ARRAY_LENGTH(_arr); _i++) + for (size_t _i = 0; _i < ARRAY_LENGTH(_arr) && (_elem = &_arr[_i]); _i++) #define min(a, b) (((a) < (b)) ? (a) : (b)) #define max(a, b) (((a) > (b)) ? (a) : (b)) From d635b1da2d4a89a98b41439dd564d75a57b680d1 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 22 Aug 2014 15:15:17 +1000 Subject: [PATCH 33/91] touchpad: silence Coverity warnings about uninitialized use container_of() accesses tp for offset calculation. Which is fine, but Coverity doesn't know that. Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad-tap.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index fae8f5e8..c05b45ce 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -617,7 +617,7 @@ static int tp_tap_config_count(struct libinput_device *device) { struct evdev_dispatch *dispatch; - struct tp_dispatch *tp; + struct tp_dispatch *tp = NULL; dispatch = ((struct evdev_device *) device)->dispatch; tp = container_of(dispatch, tp, base); @@ -630,7 +630,7 @@ tp_tap_config_set_enabled(struct libinput_device *device, enum libinput_config_tap_state enabled) { struct evdev_dispatch *dispatch; - struct tp_dispatch *tp; + struct tp_dispatch *tp = NULL; dispatch = ((struct evdev_device *) device)->dispatch; tp = container_of(dispatch, tp, base); @@ -644,7 +644,7 @@ static enum libinput_config_tap_state tp_tap_config_is_enabled(struct libinput_device *device) { struct evdev_dispatch *dispatch; - struct tp_dispatch *tp; + struct tp_dispatch *tp = NULL; dispatch = ((struct evdev_device *) device)->dispatch; tp = container_of(dispatch, tp, base); From d39e5ad898484ff13645771bc44820b009878d2c Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 26 Aug 2014 11:33:59 +1000 Subject: [PATCH 34/91] Fix a doxygen reference Signed-off-by: Peter Hutterer --- src/libinput.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libinput.h b/src/libinput.h index 9296a353..04645a7e 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -1470,7 +1470,7 @@ enum libinput_config_tap_state { * * @see libinput_device_config_tap_set_enabled * @see libinput_device_config_tap_get_enabled - * @see libinput_device_config_tap_set_enabled_get_default + * @see libinput_device_config_tap_get_default_enabled */ int libinput_device_config_tap_get_finger_count(struct libinput_device *device); From ca3f3ea9ce435eb0a53a4b8eaec5d0a6ddc3fbf1 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 20 Aug 2014 17:15:50 +1000 Subject: [PATCH 35/91] test: add helpers to wait for specific events litest_wait_for_event() returns if any event is available. litest_wait_for_event_of_type(... type, type, type, -1) returns if any of the given event types is availble. All other events are discarded. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/litest.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ test/litest.h | 2 ++ 2 files changed, 48 insertions(+) diff --git a/test/litest.c b/test/litest.c index 00db464b..fcc8de1c 100644 --- a/test/litest.c +++ b/test/litest.c @@ -780,6 +780,52 @@ litest_scale(const struct litest_device *d, unsigned int axis, double val) return (max - min) * val/100.0 + min; } +void +litest_wait_for_event(struct libinput *li) +{ + return litest_wait_for_event_of_type(li, -1); +} + +void +litest_wait_for_event_of_type(struct libinput *li, ...) +{ + va_list args; + enum libinput_event_type types[32] = {LIBINPUT_EVENT_NONE}; + size_t ntypes = 0; + enum libinput_event_type type; + + va_start(args, li); + type = va_arg(args, int); + while ((int)type != -1) { + assert(type > 0); + assert(ntypes < ARRAY_LENGTH(types)); + types[ntypes++] = type; + } + va_end(args); + + while (1) { + size_t i; + struct libinput_event *event; + + while ((type = libinput_next_event_type(li)) == LIBINPUT_EVENT_NONE) { + msleep(10); + libinput_dispatch(li); + } + + /* no event mask means wait for any event */ + if (ntypes == 0) + return; + + for (i = 0; i < ntypes; i++) { + if (type == types[i]) + return; + } + + event = libinput_get_event(li); + libinput_event_destroy(event); + } +} + void litest_drain_events(struct libinput *li) { diff --git a/test/litest.h b/test/litest.h index 2dcb7b20..815a571b 100644 --- a/test/litest.h +++ b/test/litest.h @@ -137,6 +137,8 @@ void litest_button_click(struct litest_device *d, void litest_keyboard_key(struct litest_device *d, unsigned int key, bool is_press); +void litest_wait_for_event(struct libinput *li); +void litest_wait_for_event_of_type(struct libinput *li, ...); void litest_drain_events(struct libinput *li); void litest_assert_empty_queue(struct libinput *li); From efb908145457b117985393e7b3803da7d80b3064 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 21 Aug 2014 11:08:29 +1000 Subject: [PATCH 36/91] test: add a generic single-touch device With a non-zero absmin for both axes and different ranges for x/y, just to detect those errors. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/Makefile.am | 1 + test/litest-generic-singletouch.c | 88 +++++++++++++++++++++++++++++++ test/litest.c | 2 + test/litest.h | 1 + 4 files changed, 92 insertions(+) create mode 100644 test/litest-generic-singletouch.c diff --git a/test/Makefile.am b/test/Makefile.am index 76d4e8cb..56ed9d17 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -17,6 +17,7 @@ liblitest_la_SOURCES = \ litest-int.h \ litest-alps-semi-mt.c \ litest-bcm5974.c \ + litest-generic-singletouch.c \ litest-keyboard.c \ litest-mouse.c \ litest-synaptics.c \ diff --git a/test/litest-generic-singletouch.c b/test/litest-generic-singletouch.c new file mode 100644 index 00000000..1a1a6c68 --- /dev/null +++ b/test/litest-generic-singletouch.c @@ -0,0 +1,88 @@ +/* + * Copyright © 2014 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 + * 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. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * 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. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include "litest.h" +#include "litest-int.h" + +static void +litest_generic_singletouch_touch_setup(void) +{ + struct litest_device *d = litest_create_device(LITEST_GENERIC_SINGLETOUCH); + litest_set_current_device(d); +} + +static struct input_event down[] = { + { .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN }, + { .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN }, + { .type = EV_KEY, .code = BTN_TOUCH, .value = 1 }, + { .type = EV_SYN, .code = SYN_REPORT, .value = 0 }, + { .type = -1, .code = -1 }, +}; + +static struct input_event move[] = { + { .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN }, + { .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN }, + { .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN }, + { .type = EV_KEY, .code = BTN_TOUCH, .value = 1 }, + { .type = EV_SYN, .code = SYN_REPORT, .value = 0 }, + { .type = -1, .code = -1 }, +}; + +static struct litest_device_interface interface = { + .touch_down_events = down, + .touch_move_events = move, +}; + +static struct input_absinfo absinfo[] = { + { ABS_X, 10000, 20000, 0, 0, 10 }, + { ABS_Y, -2000, 2000, 0, 0, 9 }, + { .value = -1 }, +}; + +static struct input_id input_id = { + .bustype = 0x01, + .vendor = 0x02, + .product = 0x03, +}; + +static int events[] = { + EV_KEY, BTN_TOUCH, + INPUT_PROP_MAX, INPUT_PROP_DIRECT, + -1, -1, +}; + +struct litest_test_device litest_generic_singletouch_device = { + .type = LITEST_GENERIC_SINGLETOUCH, + .features = LITEST_SINGLE_TOUCH, + .shortname = "generic-singletouch", + .setup = litest_generic_singletouch_touch_setup, + .interface = &interface, + + .name = "generic_singletouch", + .id = &input_id, + .events = events, + .absinfo = absinfo, +}; diff --git a/test/litest.c b/test/litest.c index fcc8de1c..47a7776a 100644 --- a/test/litest.c +++ b/test/litest.c @@ -86,6 +86,7 @@ extern struct litest_test_device litest_bcm5974_device; extern struct litest_test_device litest_mouse_device; extern struct litest_test_device litest_wacom_touch_device; extern struct litest_test_device litest_alps_device; +extern struct litest_test_device litest_generic_singletouch_device; struct litest_test_device* devices[] = { &litest_synaptics_clickpad_device, @@ -97,6 +98,7 @@ struct litest_test_device* devices[] = { &litest_mouse_device, &litest_wacom_touch_device, &litest_alps_device, + &litest_generic_singletouch_device, NULL, }; diff --git a/test/litest.h b/test/litest.h index 815a571b..aefde3fe 100644 --- a/test/litest.h +++ b/test/litest.h @@ -44,6 +44,7 @@ enum litest_device_type { LITEST_MOUSE, LITEST_WACOM_TOUCH, LITEST_ALPS_SEMI_MT, + LITEST_GENERIC_SINGLETOUCH, }; enum litest_device_feature { From fc6f2bb9d67fbaa4ee0018bab1f08cb51b50713a Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 26 Aug 2014 10:33:39 +1000 Subject: [PATCH 37/91] udev: use evdev_device_calibrate() instead of manually writing the matrix We have a wrapper, use it. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/udev-seat.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/udev-seat.c b/src/udev-seat.c index 8d198947..2947e712 100644 --- a/src/udev-seat.c +++ b/src/udev-seat.c @@ -49,6 +49,7 @@ device_added(struct udev_device *udev_device, struct udev_input *input) const char *sysname; const char *device_seat, *seat_name, *output_name; const char *calibration_values; + float calibration[6]; struct udev_seat *seat; device_seat = udev_device_get_property_value(udev_device, "ID_SEAT"); @@ -93,21 +94,21 @@ device_added(struct udev_device *udev_device, struct udev_input *input) if (calibration_values && sscanf(calibration_values, "%f %f %f %f %f %f", - &device->abs.calibration[0], - &device->abs.calibration[1], - &device->abs.calibration[2], - &device->abs.calibration[3], - &device->abs.calibration[4], - &device->abs.calibration[5]) == 6) { - device->abs.apply_calibration = 1; + &calibration[0], + &calibration[1], + &calibration[2], + &calibration[3], + &calibration[4], + &calibration[5]) == 6) { + evdev_device_calibrate(device, calibration); log_info(&input->base, "Applying calibration: %f %f %f %f %f %f\n", - device->abs.calibration[0], - device->abs.calibration[1], - device->abs.calibration[2], - device->abs.calibration[3], - device->abs.calibration[4], - device->abs.calibration[5]); + calibration[0], + calibration[1], + calibration[2], + calibration[3], + calibration[4], + calibration[5]); } output_name = udev_device_get_property_value(udev_device, "WL_OUTPUT"); From 6f5f83e7c3d43fa3c71aefccde4d389e18895e2a Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 26 Aug 2014 12:57:41 +1000 Subject: [PATCH 38/91] evdev: constify evdev_device_calibrate Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev.c | 3 ++- src/evdev.h | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index b09bb989..9c53e327 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -976,7 +976,8 @@ evdev_device_get_id_vendor(struct evdev_device *device) } void -evdev_device_calibrate(struct evdev_device *device, float calibration[6]) +evdev_device_calibrate(struct evdev_device *device, + const float calibration[6]) { device->abs.apply_calibration = 1; memcpy(device->abs.calibration, calibration, sizeof device->abs.calibration); diff --git a/src/evdev.h b/src/evdev.h index f1ccdc29..6aa98f54 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -159,7 +159,8 @@ unsigned int evdev_device_get_id_vendor(struct evdev_device *device); void -evdev_device_calibrate(struct evdev_device *device, float calibration[6]); +evdev_device_calibrate(struct evdev_device *device, + const float calibration[6]); int evdev_device_has_capability(struct evdev_device *device, From 24c03646a9498493a8240c5e2b96b098a6aefe24 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 20 Aug 2014 18:23:43 +1000 Subject: [PATCH 39/91] evdev: apply calibration to multitouch values as well We apply calibration to single-touch and absolute devices, but we might as well do so for multitouch events. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index 9c53e327..a0298872 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -150,19 +150,20 @@ evdev_device_led_update(struct evdev_device *device, enum libinput_led leds) static void transform_absolute(struct evdev_device *device, int32_t *x, int32_t *y) { - if (!device->abs.apply_calibration) { - *x = device->abs.x; - *y = device->abs.y; - return; - } else { - *x = device->abs.x * device->abs.calibration[0] + - device->abs.y * device->abs.calibration[1] + - device->abs.calibration[2]; + int32_t tx, ty; - *y = device->abs.x * device->abs.calibration[3] + - device->abs.y * device->abs.calibration[4] + - device->abs.calibration[5]; - } + if (!device->abs.apply_calibration) + return; + + tx = *x * device->abs.calibration[0] + + *y * device->abs.calibration[1] + + device->abs.calibration[2]; + + ty = *x * device->abs.calibration[3] + + *y * device->abs.calibration[4] + + device->abs.calibration[5]; + *x = tx; + *y = ty; } static inline double @@ -194,7 +195,7 @@ evdev_flush_pending_event(struct evdev_device *device, uint64_t time) struct libinput *libinput = device->base.seat->libinput; struct motion_params motion; int32_t cx, cy; - double x, y; + int32_t x, y; int slot; int seat_slot; struct libinput_device *base = &device->base; @@ -239,6 +240,7 @@ evdev_flush_pending_event(struct evdev_device *device, uint64_t time) seat->slot_map |= 1 << seat_slot; x = device->mt.slots[slot].x; y = device->mt.slots[slot].y; + transform_absolute(device, &x, &y); touch_notify_touch_down(base, time, slot, seat_slot, x, y); break; @@ -253,6 +255,7 @@ evdev_flush_pending_event(struct evdev_device *device, uint64_t time) if (seat_slot == -1) break; + transform_absolute(device, &x, &y); touch_notify_touch_motion(base, time, slot, seat_slot, x, y); break; case EVDEV_ABSOLUTE_MT_UP: @@ -288,11 +291,15 @@ evdev_flush_pending_event(struct evdev_device *device, uint64_t time) seat->slot_map |= 1 << seat_slot; + cx = device->abs.x; + cy = device->abs.y; transform_absolute(device, &cx, &cy); touch_notify_touch_down(base, time, -1, seat_slot, cx, cy); break; case EVDEV_ABSOLUTE_MOTION: + cx = device->abs.x; + cy = device->abs.y; transform_absolute(device, &cx, &cy); x = cx; y = cy; From 748d094c9cb6a5f77d9d02853deef49f976b4446 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 26 Aug 2014 09:37:29 +1000 Subject: [PATCH 40/91] util: add a couple of 3x3 matrix helper functions Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/libinput-util.h | 100 ++++++++++++++++++++++++++++++++++++++++++++ test/misc.c | 78 ++++++++++++++++++++++++++++++++++ 2 files changed, 178 insertions(+) diff --git a/src/libinput-util.h b/src/libinput-util.h index 5d366b02..f8072108 100644 --- a/src/libinput-util.h +++ b/src/libinput-util.h @@ -25,6 +25,7 @@ #include #include +#include #include "libinput.h" @@ -179,4 +180,103 @@ long_set_bit_state(unsigned long *array, int bit, int state) long_clear_bit(array, bit); } +struct matrix { + float val[3][3]; /* [row][col] */ +}; + +static inline void +matrix_init_identity(struct matrix *m) +{ + memset(m, 0, sizeof(*m)); + m->val[0][0] = 1; + m->val[1][1] = 1; + m->val[2][2] = 1; +} + +static inline void +matrix_from_farray6(struct matrix *m, const float values[6]) +{ + matrix_init_identity(m); + m->val[0][0] = values[0]; + m->val[0][1] = values[1]; + m->val[0][2] = values[2]; + m->val[1][0] = values[3]; + m->val[1][1] = values[4]; + m->val[1][2] = values[5]; +} + +static inline void +matrix_init_scale(struct matrix *m, float sx, float sy) +{ + matrix_init_identity(m); + m->val[0][0] = sx; + m->val[1][1] = sy; +} + +static inline void +matrix_init_translate(struct matrix *m, float x, float y) +{ + matrix_init_identity(m); + m->val[0][2] = x; + m->val[1][2] = y; +} + +static inline int +matrix_is_identity(struct matrix *m) +{ + return (m->val[0][0] == 1 && + m->val[0][1] == 0 && + m->val[0][2] == 0 && + m->val[1][0] == 0 && + m->val[1][1] == 1 && + m->val[1][2] == 0 && + m->val[2][0] == 0 && + m->val[2][1] == 0 && + m->val[2][2] == 1); +} + +static inline void +matrix_mult(struct matrix *dest, + const struct matrix *m1, + const struct matrix *m2) +{ + struct matrix m; /* allow for dest == m1 or dest == m2 */ + int row, col, i; + + for (row = 0; row < 3; row++) { + for (col = 0; col < 3; col++) { + double v = 0; + for (i = 0; i < 3; i++) { + v += m1->val[row][i] * m2->val[i][col]; + } + m.val[row][col] = v; + } + } + + memcpy(dest, &m, sizeof(m)); +} + +static inline void +matrix_mult_vec(struct matrix *m, int *x, int *y) +{ + int tx, ty; + + tx = *x * m->val[0][0] + *y * m->val[0][1] + m->val[0][2]; + ty = *x * m->val[1][0] + *y * m->val[1][1] + m->val[1][2]; + + *x = tx; + *y = ty; +} + +static inline void +matrix_to_farray6(const struct matrix *m, float out[6]) +{ + out[0] = m->val[0][0]; + out[1] = m->val[0][1]; + out[2] = m->val[0][2]; + out[3] = m->val[1][0]; + out[4] = m->val[1][1]; + out[5] = m->val[1][2]; +} + #endif /* LIBINPUT_UTIL_H */ diff --git a/test/misc.c b/test/misc.c index 983c3f9b..1512180e 100644 --- a/test/misc.c +++ b/test/misc.c @@ -431,6 +431,83 @@ START_TEST(config_status_string) } END_TEST +START_TEST(matrix_helpers) +{ + struct matrix m1, m2, m3; + float f[6] = { 1, 2, 3, 4, 5, 6 }; + int x, y; + int row, col; + + matrix_init_identity(&m1); + + for (row = 0; row < 3; row++) { + for (col = 0; col < 3; col++) { + ck_assert_int_eq(m1.val[row][col], + (row == col) ? 1 : 0); + } + } + ck_assert(matrix_is_identity(&m1)); + + matrix_from_farray6(&m2, f); + ck_assert_int_eq(m2.val[0][0], 1); + ck_assert_int_eq(m2.val[0][1], 2); + ck_assert_int_eq(m2.val[0][2], 3); + ck_assert_int_eq(m2.val[1][0], 4); + ck_assert_int_eq(m2.val[1][1], 5); + ck_assert_int_eq(m2.val[1][2], 6); + ck_assert_int_eq(m2.val[2][0], 0); + ck_assert_int_eq(m2.val[2][1], 0); + ck_assert_int_eq(m2.val[2][2], 1); + + x = 100; + y = 5; + matrix_mult_vec(&m1, &x, &y); + ck_assert_int_eq(x, 100); + ck_assert_int_eq(y, 5); + + matrix_mult(&m3, &m1, &m1); + ck_assert(matrix_is_identity(&m3)); + + matrix_init_scale(&m2, 2, 4); + ck_assert_int_eq(m2.val[0][0], 2); + ck_assert_int_eq(m2.val[0][1], 0); + ck_assert_int_eq(m2.val[0][2], 0); + ck_assert_int_eq(m2.val[1][0], 0); + ck_assert_int_eq(m2.val[1][1], 4); + ck_assert_int_eq(m2.val[1][2], 0); + ck_assert_int_eq(m2.val[2][0], 0); + ck_assert_int_eq(m2.val[2][1], 0); + ck_assert_int_eq(m2.val[2][2], 1); + + matrix_mult_vec(&m2, &x, &y); + ck_assert_int_eq(x, 200); + ck_assert_int_eq(y, 20); + + matrix_init_translate(&m2, 10, 100); + ck_assert_int_eq(m2.val[0][0], 1); + ck_assert_int_eq(m2.val[0][1], 0); + ck_assert_int_eq(m2.val[0][2], 10); + ck_assert_int_eq(m2.val[1][0], 0); + ck_assert_int_eq(m2.val[1][1], 1); + ck_assert_int_eq(m2.val[1][2], 100); + ck_assert_int_eq(m2.val[2][0], 0); + ck_assert_int_eq(m2.val[2][1], 0); + ck_assert_int_eq(m2.val[2][2], 1); + + matrix_mult_vec(&m2, &x, &y); + ck_assert_int_eq(x, 210); + ck_assert_int_eq(y, 120); + + matrix_to_farray6(&m2, f); + ck_assert_int_eq(f[0], 1); + ck_assert_int_eq(f[1], 0); + ck_assert_int_eq(f[2], 10); + ck_assert_int_eq(f[3], 0); + ck_assert_int_eq(f[4], 1); + ck_assert_int_eq(f[5], 100); +} +END_TEST + int main (int argc, char **argv) { litest_add_no_device("events:conversion", event_conversion_device_notify); litest_add_no_device("events:conversion", event_conversion_pointer); @@ -441,5 +518,6 @@ int main (int argc, char **argv) { litest_add("device:id", device_ids, LITEST_ANY, LITEST_ANY); litest_add_no_device("config:status string", config_status_string); + litest_add_no_device("misc:matrix", matrix_helpers); return litest_run(argc, argv); } From 13972efd82813bdb0089b0234fec9c3700638d0d Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 26 Aug 2014 10:34:05 +1000 Subject: [PATCH 41/91] evdev: switch to a normalized transformation matrix The big change here is the requirement to have the translation component in a device-normalized coordinate space. Without that, we cannot reliably rotate as the coordinate space is effectively unknown and may differ between the axes. This affects any rotation matrix or translation matrix, pure scale matrices were working just fine since they're unit-less. Requiring the matrix in device-normalized space makes it possible for libinput to rotate or otherwise handle the matrix independent of the screen resolution. The rotation matrix is documented in a bit more detail to make it easier for users to figure it out. This changes the definition of the WL_CALIBRATION property (which is currently broken). Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev.c | 71 +++++++++++++++++++++++++++++++++++++++++--------- src/evdev.h | 2 +- src/libinput.h | 26 ++++++++++++++++++ 3 files changed, 85 insertions(+), 14 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index a0298872..4cd3cfaa 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -150,20 +150,10 @@ evdev_device_led_update(struct evdev_device *device, enum libinput_led leds) static void transform_absolute(struct evdev_device *device, int32_t *x, int32_t *y) { - int32_t tx, ty; - if (!device->abs.apply_calibration) return; - tx = *x * device->abs.calibration[0] + - *y * device->abs.calibration[1] + - device->abs.calibration[2]; - - ty = *x * device->abs.calibration[3] + - *y * device->abs.calibration[4] + - device->abs.calibration[5]; - *x = tx; - *y = ty; + matrix_mult_vec(&device->abs.calibration, x, y); } static inline double @@ -913,6 +903,8 @@ evdev_device_create(struct libinput_seat *seat, device->pending_event = EVDEV_NONE; device->devname = libevdev_get_name(device->evdev); + matrix_init_identity(&device->abs.calibration); + if (evdev_configure_device(device) == -1) goto err; @@ -986,8 +978,61 @@ void evdev_device_calibrate(struct evdev_device *device, const float calibration[6]) { - device->abs.apply_calibration = 1; - memcpy(device->abs.calibration, calibration, sizeof device->abs.calibration); + struct matrix scale, + translate, + transform; + double sx, sy; + + matrix_from_farray6(&transform, calibration); + device->abs.apply_calibration = !matrix_is_identity(&transform); + + if (!device->abs.apply_calibration) { + matrix_init_identity(&device->abs.calibration); + return; + } + + sx = device->abs.absinfo_x->maximum - device->abs.absinfo_x->minimum + 1; + sy = device->abs.absinfo_y->maximum - device->abs.absinfo_y->minimum + 1; + + /* The transformation matrix is in the form: + * [ a b c ] + * [ d e f ] + * [ 0 0 1 ] + * Where a, e are the scale components, a, b, d, e are the rotation + * component (combined with scale) and c and f are the translation + * component. The translation component in the input matrix must be + * normalized to multiples of the device width and height, + * respectively. e.g. c == 1 shifts one device-width to the right. + * + * We pre-calculate a single matrix to apply to event coordinates: + * M = Un-Normalize * Calibration * Normalize + * + * Normalize: scales the device coordinates to [0,1] + * Calibration: user-supplied matrix + * Un-Normalize: scales back up to device coordinates + * Matrix maths requires the normalize/un-normalize in reverse + * order. + */ + + /* Un-Normalize */ + matrix_init_translate(&translate, + device->abs.absinfo_x->minimum, + device->abs.absinfo_y->minimum); + matrix_init_scale(&scale, sx, sy); + matrix_mult(&scale, &translate, &scale); + + /* Calibration */ + matrix_mult(&transform, &scale, &transform); + + /* Normalize */ + matrix_init_translate(&translate, + -device->abs.absinfo_x->minimum/sx, + -device->abs.absinfo_y->minimum/sy); + matrix_init_scale(&scale, 1.0/sx, 1.0/sy); + matrix_mult(&scale, &translate, &scale); + + /* store final matrix in device */ + matrix_mult(&device->abs.calibration, &transform, &scale); } int diff --git a/src/evdev.h b/src/evdev.h index 6aa98f54..9196bd20 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -73,7 +73,7 @@ struct evdev_device { int32_t seat_slot; int apply_calibration; - float calibration[6]; + struct matrix calibration; } abs; struct { diff --git a/src/libinput.h b/src/libinput.h index 04645a7e..82970e25 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -1374,6 +1374,32 @@ libinput_device_get_keys(struct libinput_device *device, * [ d e f ] * [ y ] * [ 0 0 1 ] [ 1 ] * @endcode + * + * The translation component (c, f) is expected to be normalized to the + * device coordinate range. For example, the matrix + * @code + * [ 1 0 1 ] + * [ 0 1 -1 ] + * [ 0 0 1 ] + * @endcode + * moves all coordinates by 1 device-width to the right and 1 device-height + * up. + * + * The rotation matrix for rotation around the origin is defined as + * @code + * [ cos(a) -sin(a) 0 ] + * [ sin(a) cos(a) 0 ] + * [ 0 0 1 ] + * @endcode + * Note that any rotation requires an additional translation component to + * translate the rotated coordinates back into the original device space. + * The rotation matrixes for 90, 180 and 270 degrees clockwise are: + * @code + * 90 deg cw: 180 deg cw: 270 deg cw: + * [ 0 -1 1] [ -1 0 1] [ 0 1 0 ] + * [ 1 0 0] [ 0 -1 1] [ -1 0 1 ] + * [ 0 0 1] [ 0 0 1] [ 0 0 1 ] + * @endcode */ void libinput_device_calibrate(struct libinput_device *device, From 25eb749163e892c6e6aa9f9be97282eca125fa57 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 26 Aug 2014 14:02:44 +1000 Subject: [PATCH 42/91] udev: drop WL_CALIBRATION, replace with LIBINPUT_CALIBRATION_MATRIX WL_CALIBRATION is a weston-specific property that required the translation component of the matrix to be in pixels. libinput can't provide calibration based on unknown outputs, so drop support for this property. The basic functionality is maintained, renaming just makes the change in behavior more explicit. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/udev-seat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/udev-seat.c b/src/udev-seat.c index 2947e712..d0324dd0 100644 --- a/src/udev-seat.c +++ b/src/udev-seat.c @@ -90,7 +90,7 @@ device_added(struct udev_device *udev_device, struct udev_input *input) calibration_values = udev_device_get_property_value(udev_device, - "WL_CALIBRATION"); + "LIBINPUT_CALIBRATION_MATRIX"); if (calibration_values && sscanf(calibration_values, "%f %f %f %f %f %f", From a888a7a61c0dfc1e6cf70747e05d343cddcb616a Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 20 Aug 2014 17:16:06 +1000 Subject: [PATCH 43/91] test: add tests for touch calibration Basic tests for rotation, translation and scaling events. Note that tests need to be added separately for single-touch and touch devices, this is a restriction of the litest framework. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/touch.c | 192 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) diff --git a/test/touch.c b/test/touch.c index fee05f1b..0aab5c8e 100644 --- a/test/touch.c +++ b/test/touch.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include "libinput-util.h" @@ -212,6 +213,191 @@ START_TEST(touch_double_touch_down_up) } END_TEST +START_TEST(touch_calibration_scale) +{ + struct libinput *li; + struct litest_device *dev; + struct libinput_event *ev; + struct libinput_event_touch *tev; + float matrix[6] = { + 1, 0, 0, + 0, 1, 0 + }; + + float calibration; + double x, y; + const int width = 640, height = 480; + + dev = litest_current_device(); + li = dev->libinput; + + for (calibration = 0.1; calibration < 1; calibration += 0.1) { + libinput_device_calibrate(dev->libinput_device, matrix); + litest_drain_events(li); + + litest_touch_down(dev, 0, 100, 100); + litest_touch_up(dev, 0); + + litest_wait_for_event(li); + ev = libinput_get_event(li); + ck_assert_int_eq(libinput_event_get_type(ev), + LIBINPUT_EVENT_TOUCH_DOWN); + tev = libinput_event_get_touch_event(ev); + + x = libinput_event_touch_get_x_transformed(tev, width); + y = libinput_event_touch_get_y_transformed(tev, height); + + ck_assert_int_eq(round(x), round(width * matrix[0])); + ck_assert_int_eq(round(y), round(height * matrix[4])); + + libinput_event_destroy(ev); + litest_drain_events(li); + + matrix[0] = calibration; + matrix[4] = 1 - calibration; + } +} +END_TEST + +START_TEST(touch_calibration_rotation) +{ + struct libinput *li; + struct litest_device *dev; + struct libinput_event *ev; + struct libinput_event_touch *tev; + float matrix[6]; + int i; + double x, y; + int width = 1024, height = 480; + + dev = litest_current_device(); + li = dev->libinput; + + for (i = 0; i < 4; i++) { + float angle = i * M_PI/2; + + /* [ cos -sin tx ] + [ sin cos ty ] + [ 0 0 1 ] */ + matrix[0] = cos(angle); + matrix[1] = -sin(angle); + matrix[3] = sin(angle); + matrix[4] = cos(angle); + + switch(i) { + case 0: /* 0 deg */ + matrix[2] = 0; + matrix[5] = 0; + break; + case 1: /* 90 deg cw */ + matrix[2] = 1; + matrix[5] = 0; + break; + case 2: /* 180 deg cw */ + matrix[2] = 1; + matrix[5] = 1; + break; + case 3: /* 270 deg cw */ + matrix[2] = 0; + matrix[5] = 1; + break; + } + + libinput_device_calibrate(dev->libinput_device, matrix); + litest_drain_events(li); + + litest_touch_down(dev, 0, 80, 20); + litest_touch_up(dev, 0); + litest_wait_for_event(li); + ev = libinput_get_event(li); + ck_assert_int_eq(libinput_event_get_type(ev), + LIBINPUT_EVENT_TOUCH_DOWN); + tev = libinput_event_get_touch_event(ev); + + x = libinput_event_touch_get_x_transformed(tev, width); + y = libinput_event_touch_get_y_transformed(tev, height); + + /* rounding errors... */ +#define almost_equal(a_, b_) \ + { ck_assert_int_ge((a_) + 0.5, (b_) - 1); \ + ck_assert_int_le((a_) + 0.5, (b_) + 1); } + switch(i) { + case 0: /* 0 deg */ + almost_equal(x, width * 0.8); + almost_equal(y, height * 0.2); + break; + case 1: /* 90 deg cw */ + almost_equal(x, width * 0.8); + almost_equal(y, height * 0.8); + break; + case 2: /* 180 deg cw */ + almost_equal(x, width * 0.2); + almost_equal(y, height * 0.8); + break; + case 3: /* 270 deg cw */ + almost_equal(x, width * 0.2); + almost_equal(y, height * 0.2); + break; + } +#undef almost_equal + + + libinput_event_destroy(ev); + litest_drain_events(li); + } +} +END_TEST + +START_TEST(touch_calibration_translation) +{ + struct libinput *li; + struct litest_device *dev; + struct libinput_event *ev; + struct libinput_event_touch *tev; + float matrix[6] = { + 1, 0, 0, + 0, 1, 0 + }; + + float translate; + double x, y; + const int width = 640, height = 480; + + dev = litest_current_device(); + li = dev->libinput; + + /* translating from 0 up to 1 device width/height */ + for (translate = 0.1; translate <= 1; translate += 0.1) { + libinput_device_calibrate(dev->libinput_device, matrix); + litest_drain_events(li); + + litest_touch_down(dev, 0, 100, 100); + litest_touch_up(dev, 0); + + litest_wait_for_event(li); + ev = libinput_get_event(li); + ck_assert_int_eq(libinput_event_get_type(ev), + LIBINPUT_EVENT_TOUCH_DOWN); + tev = libinput_event_get_touch_event(ev); + + x = libinput_event_touch_get_x_transformed(tev, width); + y = libinput_event_touch_get_y_transformed(tev, height); + + /* sigh. rounding errors */ + ck_assert_int_ge(round(x), width + round(width * matrix[2]) - 1); + ck_assert_int_ge(round(y), height + round(height * matrix[5]) - 1); + ck_assert_int_le(round(x), width + round(width * matrix[2]) + 1); + ck_assert_int_le(round(y), height + round(height * matrix[5]) + 1); + + libinput_event_destroy(ev); + litest_drain_events(li); + + matrix[2] = translate; + matrix[5] = 1 - translate; + } +} +END_TEST + int main(int argc, char **argv) { @@ -219,6 +405,12 @@ main(int argc, char **argv) litest_add_no_device("touch:abs-transform", touch_abs_transform); litest_add_no_device("touch:many-slots", touch_many_slots); litest_add("touch:double-touch-down-up", touch_double_touch_down_up, LITEST_TOUCH, LITEST_ANY); + litest_add("touch:calibration", touch_calibration_scale, LITEST_TOUCH, LITEST_TOUCHPAD); + litest_add("touch:calibration", touch_calibration_scale, LITEST_SINGLE_TOUCH, LITEST_TOUCHPAD); + litest_add("touch:calibration", touch_calibration_rotation, LITEST_TOUCH, LITEST_TOUCHPAD); + litest_add("touch:calibration", touch_calibration_rotation, LITEST_SINGLE_TOUCH, LITEST_TOUCHPAD); + litest_add("touch:calibration", touch_calibration_translation, LITEST_TOUCH, LITEST_TOUCHPAD); + litest_add("touch:calibration", touch_calibration_translation, LITEST_SINGLE_TOUCH, LITEST_TOUCHPAD); return litest_run(argc, argv); } From ea00ff9114c100e83689dfdc143ddb3891efc26d Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 26 Aug 2014 11:41:19 +1000 Subject: [PATCH 44/91] Change calibration into a configuration option New configuration API: libinput_device_config_calibration_has_matrix() libinput_device_config_calibration_set_matrix() libinput_device_config_calibration_get_matrix() libinput_device_config_calibration_get_default_matrix() Deprecates libinput_device_calibrate(). For coordinate transformation, we're using a precalculated matrix. Thus, to support ..._get_matrix() we need to store the original user-specified matrix separately, in an unmangled state. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev.c | 59 ++++++++++++++++- src/evdev.h | 2 + src/libinput-private.h | 11 +++ src/libinput.c | 37 +++++++++++ src/libinput.h | 147 +++++++++++++++++++++++++++++++---------- test/pointer.c | 24 +++++++ test/touch.c | 9 ++- 7 files changed, 248 insertions(+), 41 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index 4cd3cfaa..00594440 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -578,19 +578,70 @@ fallback_destroy(struct evdev_dispatch *dispatch) free(dispatch); } +static int +evdev_calibration_has_matrix(struct libinput_device *libinput_device) +{ + struct evdev_device *device = (struct evdev_device*)libinput_device; + + return device->abs.absinfo_x && device->abs.absinfo_y; +} + +static enum libinput_config_status +evdev_calibration_set_matrix(struct libinput_device *libinput_device, + const float matrix[6]) +{ + struct evdev_device *device = (struct evdev_device*)libinput_device; + + evdev_device_calibrate(device, matrix); + + return LIBINPUT_CONFIG_STATUS_SUCCESS; +} + +static int +evdev_calibration_get_matrix(struct libinput_device *libinput_device, + float matrix[6]) +{ + struct evdev_device *device = (struct evdev_device*)libinput_device; + + matrix_to_farray6(&device->abs.usermatrix, matrix); + + return !matrix_is_identity(&device->abs.usermatrix); +} + +static int +evdev_calibration_get_default_matrix(struct libinput_device *libinput_device, + float matrix[6]) +{ + struct matrix m; + + /* Always return the identity matrix for now. In the future, this + should return the WL_CALIBRATION matrix defined as default + matrix for this device */ + matrix_init_identity(&m); + matrix_to_farray6(&m, matrix); + + return !matrix_is_identity(&m); +} + struct evdev_dispatch_interface fallback_interface = { fallback_process, fallback_destroy }; static struct evdev_dispatch * -fallback_dispatch_create(void) +fallback_dispatch_create(struct libinput_device *device) { struct evdev_dispatch *dispatch = malloc(sizeof *dispatch); if (dispatch == NULL) return NULL; dispatch->interface = &fallback_interface; + device->config.calibration = &dispatch->calibration; + + dispatch->calibration.has_matrix = evdev_calibration_has_matrix; + dispatch->calibration.set_matrix = evdev_calibration_set_matrix; + dispatch->calibration.get_matrix = evdev_calibration_get_matrix; + dispatch->calibration.get_default_matrix = evdev_calibration_get_default_matrix; return dispatch; } @@ -904,6 +955,7 @@ evdev_device_create(struct libinput_seat *seat, device->devname = libevdev_get_name(device->evdev); matrix_init_identity(&device->abs.calibration); + matrix_init_identity(&device->abs.usermatrix); if (evdev_configure_device(device) == -1) goto err; @@ -915,7 +967,7 @@ evdev_device_create(struct libinput_seat *seat, /* If the dispatch was not set up use the fallback. */ if (device->dispatch == NULL) - device->dispatch = fallback_dispatch_create(); + device->dispatch = fallback_dispatch_create(&device->base); if (device->dispatch == NULL) goto err; @@ -1014,6 +1066,9 @@ evdev_device_calibrate(struct evdev_device *device, * order. */ + /* back up the user matrix so we can return it on request */ + matrix_from_farray6(&device->abs.usermatrix, calibration); + /* Un-Normalize */ matrix_init_translate(&translate, device->abs.absinfo_x->minimum, diff --git a/src/evdev.h b/src/evdev.h index 9196bd20..110ea745 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -74,6 +74,7 @@ struct evdev_device { int apply_calibration; struct matrix calibration; + struct matrix usermatrix; /* as supplied by the caller */ } abs; struct { @@ -121,6 +122,7 @@ struct evdev_dispatch_interface { struct evdev_dispatch { struct evdev_dispatch_interface *interface; + struct libinput_device_config_calibration calibration; }; struct evdev_device * diff --git a/src/libinput-private.h b/src/libinput-private.h index 94a3e07c..9e084dd7 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -89,8 +89,19 @@ struct libinput_device_config_tap { enum libinput_config_tap_state (*get_default)(struct libinput_device *device); }; +struct libinput_device_config_calibration { + int (*has_matrix)(struct libinput_device *device); + enum libinput_config_status (*set_matrix)(struct libinput_device *device, + const float matrix[6]); + int (*get_matrix)(struct libinput_device *device, + float matrix[6]); + int (*get_default_matrix)(struct libinput_device *device, + float matrix[6]); +}; + struct libinput_device_config { struct libinput_device_config_tap *tap; + struct libinput_device_config_calibration *calibration; }; struct libinput_device { diff --git a/src/libinput.c b/src/libinput.c index ed5eba17..20aa1cb3 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -1312,3 +1312,40 @@ libinput_device_config_tap_get_default_enabled(struct libinput_device *device) return device->config.tap->get_default(device); } + +LIBINPUT_EXPORT int +libinput_device_config_calibration_has_matrix(struct libinput_device *device) +{ + return device->config.calibration ? + device->config.calibration->has_matrix(device) : 0; +} + +LIBINPUT_EXPORT enum libinput_config_status +libinput_device_config_calibration_set_matrix(struct libinput_device *device, + const float matrix[6]) +{ + if (!libinput_device_config_calibration_has_matrix(device)) + return LIBINPUT_CONFIG_STATUS_UNSUPPORTED; + + return device->config.calibration->set_matrix(device, matrix); +} + +LIBINPUT_EXPORT int +libinput_device_config_calibration_get_matrix(struct libinput_device *device, + float matrix[6]) +{ + if (!libinput_device_config_calibration_has_matrix(device)) + return 0; + + return device->config.calibration->get_matrix(device, matrix); +} + +LIBINPUT_EXPORT int +libinput_device_config_calibration_get_default_matrix(struct libinput_device *device, + float matrix[6]) +{ + if (!libinput_device_config_calibration_has_matrix(device)) + return 0; + + return device->config.calibration->get_default_matrix(device, matrix); +} diff --git a/src/libinput.h b/src/libinput.h index 82970e25..5af0ddec 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -1365,45 +1365,12 @@ libinput_device_get_keys(struct libinput_device *device, /** * @ingroup device * - * Apply the 3x3 transformation matrix to absolute device coordinates. This - * matrix has no effect on relative events. - * - * Given a 6-element array [a, b, c, d, e, f], the matrix is applied as - * @code - * [ a b c ] [ x ] - * [ d e f ] * [ y ] - * [ 0 0 1 ] [ 1 ] - * @endcode - * - * The translation component (c, f) is expected to be normalized to the - * device coordinate range. For example, the matrix - * @code - * [ 1 0 1 ] - * [ 0 1 -1 ] - * [ 0 0 1 ] - * @endcode - * moves all coordinates by 1 device-width to the right and 1 device-height - * up. - * - * The rotation matrix for rotation around the origin is defined as - * @code - * [ cos(a) -sin(a) 0 ] - * [ sin(a) cos(a) 0 ] - * [ 0 0 1 ] - * @endcode - * Note that any rotation requires an additional translation component to - * translate the rotated coordinates back into the original device space. - * The rotation matrixes for 90, 180 and 270 degrees clockwise are: - * @code - * 90 deg cw: 180 deg cw: 270 deg cw: - * [ 0 -1 1] [ -1 0 1] [ 0 1 0 ] - * [ 1 0 0] [ 0 -1 1] [ -1 0 1 ] - * [ 0 0 1] [ 0 0 1] [ 0 0 1 ] - * @endcode + * @deprecated Use libinput_device_config_calibration_set_matrix() instead. */ void libinput_device_calibrate(struct libinput_device *device, - float calibration[6]); + float calibration[6]) + LIBINPUT_ATTRIBUTE_DEPRECATED; /** * @ingroup device @@ -1559,6 +1526,114 @@ libinput_device_config_tap_get_enabled(struct libinput_device *device); enum libinput_config_tap_state libinput_device_config_tap_get_default_enabled(struct libinput_device *device); +/** + * @ingroup config + * + * Check if the device can be calibrated via a calibration matrix. + * + * @param device The device to check + * @return non-zero if the device can be calibrated, zero otherwise. + * + * @see libinput_device_config_calibration_set_matrix + * @see libinput_device_config_calibration_get_matrix + * @see libinput_device_config_calibration_get_default_matrix + */ +int +libinput_device_config_calibration_has_matrix(struct libinput_device *device); + +/** + * @ingroup config + * + * Apply the 3x3 transformation matrix to absolute device coordinates. This + * matrix has no effect on relative events. + * + * Given a 6-element array [a, b, c, d, e, f], the matrix is applied as + * @code + * [ a b c ] [ x ] + * [ d e f ] * [ y ] + * [ 0 0 1 ] [ 1 ] + * @endcode + * + * The translation component (c, f) is expected to be normalized to the + * device coordinate range. For example, the matrix + * @code + * [ 1 0 1 ] + * [ 0 1 -1 ] + * [ 0 0 1 ] + * @endcode + * moves all coordinates by 1 device-width to the right and 1 device-height + * up. + * + * The rotation matrix for rotation around the origin is defined as + * @code + * [ cos(a) -sin(a) 0 ] + * [ sin(a) cos(a) 0 ] + * [ 0 0 1 ] + * @endcode + * Note that any rotation requires an additional translation component to + * translate the rotated coordinates back into the original device space. + * The rotation matrixes for 90, 180 and 270 degrees clockwise are: + * @code + * 90 deg cw: 180 deg cw: 270 deg cw: + * [ 0 -1 1] [ -1 0 1] [ 0 1 0 ] + * [ 1 0 0] [ 0 -1 1] [ -1 0 1 ] + * [ 0 0 1] [ 0 0 1] [ 0 0 1 ] + * @endcode + * + * @param device The device to configure + * @param matrix An array representing the first two rows of a 3x3 matrix as + * described above. + * + * @return A config status code. + * + * @see libinput_device_config_calibration_has_matrix + * @see libinput_device_config_calibration_get_matrix + * @see libinput_device_config_calibration_get_default_matrix + */ +enum libinput_config_status +libinput_device_config_calibration_set_matrix(struct libinput_device *device, + const float matrix[6]); + +/** + * @ingroup config + * + * Return the current calibration matrix for this device. + * + * @param device The device to configure + * @param matrix Set to the array representing the first two rows of a 3x3 matrix as + * described in libinput_device_config_calibration_set_matrix(). + * + * @return 0 if no calibration is set and the returned matrix is the + * identity matrix, 1 otherwise + * + * @see libinput_device_config_calibration_has_matrix + * @see libinput_device_config_calibration_set_matrix + * @see libinput_device_config_calibration_get_default_matrix + */ +int +libinput_device_config_calibration_get_matrix(struct libinput_device *device, + float matrix[6]); + +/** + * @ingroup config + * + * Return the default calibration matrix for this device. + * + * @param device The device to configure + * @param matrix Set to the array representing the first two rows of a 3x3 matrix as + * described in libinput_device_config_calibration_set_matrix(). + * + * @return 0 if no calibration is set and the returned matrix is the + * identity matrix, 1 otherwise + * + * @see libinput_device_config_calibration_has_matrix + * @see libinput_device_config_calibration_set_matrix + * @see libinput_device_config_calibration_get_default_matrix + */ +int +libinput_device_config_calibration_get_default_matrix(struct libinput_device *device, + float matrix[6]); + #ifdef __cplusplus } #endif diff --git a/test/pointer.c b/test/pointer.c index c0af460c..861ab741 100644 --- a/test/pointer.c +++ b/test/pointer.c @@ -385,6 +385,28 @@ START_TEST(pointer_seat_button_count) } END_TEST +START_TEST(pointer_no_calibration) +{ + struct litest_device *dev = litest_current_device(); + struct libinput_device *d = dev->libinput_device; + enum libinput_config_status status; + int rc; + float calibration[6] = {0}; + + rc = libinput_device_config_calibration_has_matrix(d); + ck_assert_int_eq(rc, 0); + rc = libinput_device_config_calibration_get_matrix(d, calibration); + ck_assert_int_eq(rc, 0); + rc = libinput_device_config_calibration_get_default_matrix(d, + calibration); + ck_assert_int_eq(rc, 0); + + status = libinput_device_config_calibration_set_matrix(d, + calibration); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED); +} +END_TEST + int main (int argc, char **argv) { litest_add("pointer:motion", pointer_motion_relative, LITEST_POINTER, LITEST_ANY); @@ -393,5 +415,7 @@ int main (int argc, char **argv) { litest_add("pointer:scroll", pointer_scroll_wheel, LITEST_WHEEL, LITEST_ANY); litest_add_no_device("pointer:seat button count", pointer_seat_button_count); + litest_add("pointer:calibration", pointer_no_calibration, LITEST_ANY, LITEST_TOUCH|LITEST_SINGLE_TOUCH); + return litest_run(argc, argv); } diff --git a/test/touch.c b/test/touch.c index 0aab5c8e..1c0a4b6e 100644 --- a/test/touch.c +++ b/test/touch.c @@ -232,7 +232,8 @@ START_TEST(touch_calibration_scale) li = dev->libinput; for (calibration = 0.1; calibration < 1; calibration += 0.1) { - libinput_device_calibrate(dev->libinput_device, matrix); + libinput_device_config_calibration_set_matrix(dev->libinput_device, + matrix); litest_drain_events(li); litest_touch_down(dev, 0, 100, 100); @@ -303,7 +304,8 @@ START_TEST(touch_calibration_rotation) break; } - libinput_device_calibrate(dev->libinput_device, matrix); + libinput_device_config_calibration_set_matrix(dev->libinput_device, + matrix); litest_drain_events(li); litest_touch_down(dev, 0, 80, 20); @@ -368,7 +370,8 @@ START_TEST(touch_calibration_translation) /* translating from 0 up to 1 device width/height */ for (translate = 0.1; translate <= 1; translate += 0.1) { - libinput_device_calibrate(dev->libinput_device, matrix); + libinput_device_config_calibration_set_matrix(dev->libinput_device, + matrix); litest_drain_events(li); litest_touch_down(dev, 0, 100, 100); From d5136c6cb9fbefa14f1e41685a658f2fa029d163 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 26 Aug 2014 13:44:03 +1000 Subject: [PATCH 45/91] evdev: load the LIBINPUT_CALIBRATION_MATRIX as default matrix Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev.c | 19 ++++++++++++------- src/evdev.h | 4 ++++ src/udev-seat.c | 2 +- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index 00594440..e24e2680 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -612,15 +612,11 @@ static int evdev_calibration_get_default_matrix(struct libinput_device *libinput_device, float matrix[6]) { - struct matrix m; + struct evdev_device *device = (struct evdev_device*)libinput_device; - /* Always return the identity matrix for now. In the future, this - should return the WL_CALIBRATION matrix defined as default - matrix for this device */ - matrix_init_identity(&m); - matrix_to_farray6(&m, matrix); + matrix_to_farray6(&device->abs.default_calibration, matrix); - return !matrix_is_identity(&m); + return !matrix_is_identity(&device->abs.default_calibration); } struct evdev_dispatch_interface fallback_interface = { @@ -956,6 +952,7 @@ evdev_device_create(struct libinput_seat *seat, matrix_init_identity(&device->abs.calibration); matrix_init_identity(&device->abs.usermatrix); + matrix_init_identity(&device->abs.default_calibration); if (evdev_configure_device(device) == -1) goto err; @@ -1026,6 +1023,14 @@ evdev_device_get_id_vendor(struct evdev_device *device) return libevdev_get_id_vendor(device->evdev); } +void +evdev_device_set_default_calibration(struct evdev_device *device, + const float calibration[6]) +{ + matrix_from_farray6(&device->abs.default_calibration, calibration); + evdev_device_calibrate(device, calibration); +} + void evdev_device_calibrate(struct evdev_device *device, const float calibration[6]) diff --git a/src/evdev.h b/src/evdev.h index 110ea745..50ca713d 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -74,6 +74,7 @@ struct evdev_device { int apply_calibration; struct matrix calibration; + struct matrix default_calibration; /* from LIBINPUT_CALIBRATION_MATRIX */ struct matrix usermatrix; /* as supplied by the caller */ } abs; @@ -160,6 +161,9 @@ evdev_device_get_id_product(struct evdev_device *device); unsigned int evdev_device_get_id_vendor(struct evdev_device *device); +void +evdev_device_set_default_calibration(struct evdev_device *device, + const float calibration[6]); void evdev_device_calibrate(struct evdev_device *device, const float calibration[6]); diff --git a/src/udev-seat.c b/src/udev-seat.c index d0324dd0..ccff35c5 100644 --- a/src/udev-seat.c +++ b/src/udev-seat.c @@ -100,7 +100,7 @@ device_added(struct udev_device *udev_device, struct udev_input *input) &calibration[3], &calibration[4], &calibration[5]) == 6) { - evdev_device_calibrate(device, calibration); + evdev_device_set_default_calibration(device, calibration); log_info(&input->base, "Applying calibration: %f %f %f %f %f %f\n", calibration[0], From 481430d8bb32e93c54a3ade4dd128506cfe831ae Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 1 Sep 2014 12:39:38 +1000 Subject: [PATCH 46/91] test: fix infinite loop in litest_wait_for_event_of_type() Signed-off-by: Peter Hutterer --- test/litest.c | 1 + 1 file changed, 1 insertion(+) diff --git a/test/litest.c b/test/litest.c index 47a7776a..7d064d7e 100644 --- a/test/litest.c +++ b/test/litest.c @@ -802,6 +802,7 @@ litest_wait_for_event_of_type(struct libinput *li, ...) assert(type > 0); assert(ntypes < ARRAY_LENGTH(types)); types[ntypes++] = type; + type = va_arg(args, int); } va_end(args); From 496cd6ab27da0b255111992946cb085f43ffce96 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 1 Sep 2014 12:40:59 +1000 Subject: [PATCH 47/91] test: use the evironment variable for check's verbosity Allows to set CK_VERBOSITY to be set to "silent", "minimal", "normal", or "verbose". Falls back to CK_NORMAL if unset. Signed-off-by: Peter Hutterer --- test/litest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/litest.c b/test/litest.c index 7d064d7e..dbbf0543 100644 --- a/test/litest.c +++ b/test/litest.c @@ -359,7 +359,7 @@ litest_run(int argc, char **argv) { } } - srunner_run_all(sr, CK_NORMAL); + srunner_run_all(sr, CK_ENV); failed = srunner_ntests_failed(sr); srunner_free(sr); From 94c59ef2014e9f81ed63d47367bae43221702253 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Sun, 27 Jul 2014 23:28:31 +0200 Subject: [PATCH 48/91] touchpad: Only break out of tap FSM for clickpad button presses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It should be possible to initiate a drag by tapping-drag, but continue it by pressing a physical button continuing to drag by subsequent finger motions. As the generic evdev layer helps us ignore multiple button presses we can have the tap machine run completely separate from and uneffected by regular physical button presses, making the tap FSM much simpler than adding new states for handling button presse life times from outside of the tap state machine. A touchpad test is updated to test click while tapping instead of tap FSM break out. The updated test is re-added but only for clickpads only. The tap FSM svg is updated to say "clickpad button press" instead of "phys button press". Signed-off-by: Jonas Ådahl Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- doc/touchpad-tap-state-machine.svg | 1064 ++++++++++++++-------------- src/evdev-mt-touchpad-tap.c | 5 +- test/touchpad.c | 85 ++- 3 files changed, 616 insertions(+), 538 deletions(-) diff --git a/doc/touchpad-tap-state-machine.svg b/doc/touchpad-tap-state-machine.svg index 10739c68..7aecefc1 100644 --- a/doc/touchpad-tap-state-machine.svg +++ b/doc/touchpad-tap-state-machine.svg @@ -2,755 +2,755 @@ - - - - - - - + + + + + + + - IDLE + IDLE - + - TOUCH + TOUCH - + - first - finger down + first + finger down - - - + + + - finger up + finger up - - - + + + - button 1 - press + button 1 + press - + - timeout + timeout - - - + + + - move > - threshold + move > + threshold - - - + + + - second - finger down + second + finger down - - - + + + - TOUCH_2 + TOUCH_2 - + - second - finger up + second + finger up - - - + + + - button 2 - press + button 2 + press - + - move > - threshold + move > + threshold - + - timeout + timeout - - - - - + + + + + - button 1 - release + button 1 + release - + - button 2 - release + button 2 + release - - - - - + + + + + - TAPPED + TAPPED - + - timeout + timeout - - - + + + - first - finger down + first + finger down - - - + + + - DRAGGING + DRAGGING - + - first - finger up + first + finger up - + - btn1 - release + btn1 + release - - - - - - - + + + + + + + - IDLE + IDLE - + - third - finger down + third + finger down - - - + + + - TOUCH_3 + TOUCH_3 - - - + + + - button 3 - press + button 3 + press - + - button 3 - release + button 3 + release - - - + + + - move > - threshold + move > + threshold - - - + + + - IDLE + IDLE - + - timeout + timeout - - - + + + - first - finger up + first + finger up - - - + + + - IDLE + IDLE - + - fourth - finger down + fourth + finger down - - - - - + + + + + - DRAGGING_OR_DOUBLETAP + DRAGGING_OR_DOUBLETAP - - - + + + - timeout + timeout - - - + + + - first - finger up + first + finger up - - - + + + - button 1 - release + button 1 + release - + - button 1 - press + button 1 + press - + - btn1 - release + btn1 + release - - - - - - - - - + + + + + + + + + - second - finger down + second + finger down - - - + + + - move > - threshold + move > + threshold - - - - - + + + + + - HOLD + HOLD - + - first - finger up + first + finger up - - - - - + + + + + - second - finger down + second + finger down - - - - - - - + + + + + + + - TOUCH_2_HOLD + TOUCH_2_HOLD - + - second - finger up + second + finger up - - - + + + - first - finger up + first + finger up - - - - - - - + + + + + + + - third - finger down + third + finger down - - - - - - - + + + + + + + - TOUCH_3_HOLD + TOUCH_3_HOLD - - - + + + - fourth - finger down + fourth + finger down - + - DEAD + DEAD - - - - - - - + + + + + + + - any finger up + any finger up - + - fourth - finger up + fourth + finger up - + - any finger up + any finger up - - - - + + + + - - yes + + yes - + - any finger up + any finger up - - - - - - - - - + + + + + + + + + - IDLE + IDLE - + - if finger - count == 0 + if finger + count == 0 - - - - - - - + + + + + + + - second - finger up + second + finger up - + - DRAGGING_2 + DRAGGING_2 - - - - - + + + + + - first - finger up + first + finger up - - - - - - - + + + + + + + - second - finger down + second + finger down - - - - - - - + + + + + + + - third - finger down + third + finger down - - - + + + - btn1 - release + btn1 + release - - - + + + - phys - button - press + clickpad + button + press - - - - - - - - - - - - - + + + + + + + + + + + + + - phys - button - press + clickpad + button + press - - - + + + - button 1 - release + button 1 + release - - - - - - - - - - - + + + + + + + + + + + - DRAGGING_WAIT + DRAGGING_WAIT - + - timeout + timeout - - - - - - - + + + + + + + - first - finger down + first + finger down - - - + + + - TOUCH_TOUCH + TOUCH_TOUCH - + - TOUCH_IDLE + TOUCH_IDLE - - - - - - - - - + + + + + + + + + - TOUCH_DEAD + TOUCH_DEAD - - - - - - - - - - + + + + + + + + + + - - yes + + yes - + - TOUCH_DEAD + TOUCH_DEAD - - - - - - - + + + + + + + - TOUCH_IDLE + TOUCH_IDLE - - - + + + - TOUCH_TOUCH + TOUCH_TOUCH - - - - - + + + + + - TOUCH_IDLE + TOUCH_IDLE - - - + + + - TOUCH_IDLE + TOUCH_IDLE - - - + + + - TOUCH_IDLE + TOUCH_IDLE - - - + + + - TOUCH_TOUCH + TOUCH_TOUCH - - - + + + - that finger - TOUCH_IDLE + that finger + TOUCH_IDLE - - - + + + - TOUCH_DEAD + TOUCH_DEAD - - - - - - - + + + + + + + - that finger - TOUCH_IDLE + that finger + TOUCH_IDLE - - - - + + + + - - no + + no - + - TOUCH_TOUCH + TOUCH_TOUCH - - - + + + - TOUCH_IDLE + TOUCH_IDLE - - - + + + - TOUCH_TOUCH + TOUCH_TOUCH - - - + + + - TOUCH_DEAD + TOUCH_DEAD - - - - - + + + + + - TOUCH_IDLE + TOUCH_IDLE - - - + + + - TOUCH_TOUCH + TOUCH_TOUCH - - - - - + + + + + - TOUCH_TOUCH + TOUCH_TOUCH - - - + + + - TOUCH_IDLE + TOUCH_IDLE - - - + + + - TOUCH_IDLE + TOUCH_IDLE - - - + + + - TOUCH_TOUCH + TOUCH_TOUCH - - - + + + - TOUCH_IDLE + TOUCH_IDLE - - - + + + - TOUCH_TOUCH + TOUCH_TOUCH - - - + + + - that finger - TOUCH_IDLE + that finger + TOUCH_IDLE - - - + + + - TOUCH_DEAD + TOUCH_DEAD - - - + + + - TOUCH_DEAD + TOUCH_DEAD - + - TOUCH_DEAD + TOUCH_DEAD - + - TOUCH_DEAD + TOUCH_DEAD - - - + + + - TOUCH_DEAD + TOUCH_DEAD - - - + + + - TOUCH_DEAD + TOUCH_DEAD - - - + + + - state == - TOUCH_TOUCH + state == + TOUCH_TOUCH - + - that finger state == - TOUCH_TOUCH + that finger state == + TOUCH_TOUCH - - + + - - no + + no - + - TOUCH_DEAD + TOUCH_DEAD - - - + + + - TOUCH_DEAD + TOUCH_DEAD - + - TOUCH_DEAD + TOUCH_DEAD diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index c05b45ce..357a50a6 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -542,7 +542,10 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time) struct tp_touch *t; int filter_motion = 0; - if (tp->queued & TOUCHPAD_EVENT_BUTTON_PRESS) + /* Handle queued button pressed events from clickpads. For touchpads + * with separate physical buttons, ignore button pressed events so they + * don't interfere with tapping. */ + if (tp->buttons.is_clickpad && tp->queued & TOUCHPAD_EVENT_BUTTON_PRESS) tp_tap_handle_event(tp, NULL, TAP_EVENT_BUTTON, time); tp_for_each_touch(tp, t) { diff --git a/test/touchpad.c b/test/touchpad.c index 86b2c91b..eae92025 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -256,14 +256,17 @@ START_TEST(touchpad_1fg_tap_click) litest_drain_events(dev->libinput); - /* finger down, button click, finger up - -> only one button left event pair */ + /* Finger down, finger up -> tap button press + * Physical button click -> no button press/release + * Tap timeout -> tap button release */ litest_touch_down(dev, 0, 50, 50); + litest_touch_up(dev, 0); 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); + libinput_dispatch(li); + msleep(200); libinput_dispatch(li); @@ -286,6 +289,42 @@ START_TEST(touchpad_2fg_tap_click) litest_drain_events(dev->libinput); + /* two fingers down, left button click, fingers up + -> one left button, one right button event pair */ + litest_touch_down(dev, 0, 50, 50); + litest_touch_down(dev, 1, 70, 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, 1); + litest_touch_up(dev, 0); + + libinput_dispatch(li); + + assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + assert_button_event(li, BTN_RIGHT, + LIBINPUT_BUTTON_STATE_PRESSED); + assert_button_event(li, BTN_RIGHT, + LIBINPUT_BUTTON_STATE_RELEASED); + + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(clickpad_2fg_tap_click) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + libinput_device_config_tap_set_enabled(dev->libinput_device, + LIBINPUT_CONFIG_TAP_ENABLED); + + litest_drain_events(dev->libinput); + /* two fingers down, button click, fingers up -> only one button left event pair */ litest_touch_down(dev, 0, 50, 50); @@ -684,6 +723,38 @@ START_TEST(touchpad_btn_left) } END_TEST +START_TEST(clickpad_1fg_tap_click) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + libinput_device_config_tap_set_enabled(dev->libinput_device, + LIBINPUT_CONFIG_TAP_ENABLED); + + litest_drain_events(dev->libinput); + + /* finger down, button click, finger up + -> only one button left event pair */ + litest_touch_down(dev, 0, 50, 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); + libinput_dispatch(li); + msleep(200); + + libinput_dispatch(li); + + assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + + litest_assert_empty_queue(li); +} +END_TEST + START_TEST(clickpad_btn_left) { struct litest_device *dev = litest_current_device(); @@ -1553,8 +1624,9 @@ int main(int argc, char **argv) { litest_add("touchpad:tap", touchpad_1fg_tap_n_drag, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:tap", touchpad_2fg_tap, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); litest_add("touchpad:tap", touchpad_2fg_tap_inverted, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); - litest_add("touchpad:tap", touchpad_1fg_tap_click, LITEST_TOUCHPAD, LITEST_ANY); - litest_add("touchpad:tap", touchpad_2fg_tap_click, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_APPLE_CLICKPAD); + litest_add("touchpad:tap", touchpad_1fg_tap_click, LITEST_TOUCHPAD, LITEST_CLICKPAD); + litest_add("touchpad:tap", touchpad_2fg_tap_click, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_CLICKPAD); + litest_add("touchpad:tap", touchpad_2fg_tap_click_apple, LITEST_APPLE_CLICKPAD, LITEST_ANY); litest_add("touchpad:tap", touchpad_no_2fg_tap_after_move, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); litest_add("touchpad:tap", touchpad_no_2fg_tap_after_timeout, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); @@ -1575,6 +1647,9 @@ int main(int argc, char **argv) { litest_add("touchpad:tap", touchpad_tap_is_available, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:tap", touchpad_tap_is_not_available, LITEST_ANY, LITEST_TOUCHPAD); + litest_add("touchpad:tap", clickpad_1fg_tap_click, LITEST_CLICKPAD, LITEST_ANY); + litest_add("touchpad:tap", clickpad_2fg_tap_click, LITEST_CLICKPAD, LITEST_SINGLE_TOUCH|LITEST_APPLE_CLICKPAD); + litest_add_no_device("touchpad:clickfinger", touchpad_1fg_clickfinger); litest_add_no_device("touchpad:clickfinger", touchpad_2fg_clickfinger); From 0647574c46e5e930063ace7b35385213dca33dc1 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 2 Sep 2014 16:34:48 +0200 Subject: [PATCH 49/91] litest-alps-semi-mt: Fix compiler warnings This fixes the following (false positive) compiler warnings: litest-alps-semi-mt.c: In function 'alps_touch_move': litest-alps-semi-mt.c:163:3: warning: 'b' may be used uninitialized in this function [-Wmaybe-uninitialized] send_abs_mt_xy(d, r, b); ^ litest-alps-semi-mt.c:163:3: warning: 'r' may be used uninitialized in this function [-Wmaybe-uninitialized] litest-alps-semi-mt.c: In function 'alps_touch_down': litest-alps-semi-mt.c:127:3: warning: 'b' may be used uninitialized in this function [-Wmaybe-uninitialized] send_abs_mt_xy(d, r, b); ^ litest-alps-semi-mt.c:127:3: warning: 'r' may be used uninitialized in this function [-Wmaybe-uninitialized] Signed-off-by: Hans de Goede Reviewed-by: Peter Hutterer Signed-off-by: Peter Hutterer --- test/litest-alps-semi-mt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/litest-alps-semi-mt.c b/test/litest-alps-semi-mt.c index 943ca2db..12b93b8c 100644 --- a/test/litest-alps-semi-mt.c +++ b/test/litest-alps-semi-mt.c @@ -95,7 +95,7 @@ static void alps_touch_down(struct litest_device *d, unsigned int slot, double x, double y) { struct alps *alps = d->private; - double t, l, r, b; /* top, left, right, bottom */ + double t, l, r = 0, b = 0; /* top, left, right, bottom */ if (d->ntouches_down > 2 || slot > 1) return; @@ -137,7 +137,7 @@ static void alps_touch_move(struct litest_device *d, unsigned int slot, double x, double y) { struct alps *alps = d->private; - double t, l, r, b; /* top, left, right, bottom */ + double t, l, r = 0, b = 0; /* top, left, right, bottom */ if (d->ntouches_down > 2 || slot > 1) return; From 47f03ff4fcfc59d6d1c9e2c7fb9e5220955ed7ab Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 10 Sep 2014 09:00:09 +1000 Subject: [PATCH 50/91] Document LIBINPUT_CALIBRATION_MATRIX properly Make this part of our API proper and outline the 4 most common examples. Signed-off-by: Peter Hutterer --- src/libinput.h | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/libinput.h b/src/libinput.h index 5af0ddec..b7f161f0 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -1617,7 +1617,24 @@ libinput_device_config_calibration_get_matrix(struct libinput_device *device, /** * @ingroup config * - * Return the default calibration matrix for this device. + * Return the default calibration matrix for this device. On most devices, + * this is the identity matrix. If the udev property + * LIBINPUT_CALIBRATION_MATRIX is set on the respective udev device, + * that property's value becomes the default matrix. + * + * The udev property is parsed as 6 floating point numbers separated by a + * single space each (scanf(3) format "%f %f %f %f %f %f"). + * The 6 values represent the first two rows of the calibration matrix as + * described in libinput_device_config_calibration_set_matrix(). + * + * Example values are: + * @code + * ENV{LIBINPUT_CALIBRATION_MATRIX}="1 0 0 0 1 0" # default + * ENV{LIBINPUT_CALIBRATION_MATRIX}="0 -1 1 1 0 0" # 90 degree clockwise + * ENV{LIBINPUT_CALIBRATION_MATRIX}="-1 0 1 0 -1 1" # 180 degree clockwise + * ENV{LIBINPUT_CALIBRATION_MATRIX}="0 1 0 -1 0 1" # 270 degree clockwise + * ENV{LIBINPUT_CALIBRATION_MATRIX}="-1 0 1 1 0 0" # reflect along y axis + * @endcode * * @param device The device to configure * @param matrix Set to the array representing the first two rows of a 3x3 matrix as From 6c33e3d379e268aa14cfea98c5cb030d5df34eec Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 10 Sep 2014 09:31:08 +1000 Subject: [PATCH 51/91] Document the static udev configuration options we support Signed-off-by: Peter Hutterer --- src/libinput.h | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/libinput.h b/src/libinput.h index b7f161f0..a8a19c54 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -103,6 +103,38 @@ extern "C" { * middle button click. */ +/** + * @page udev_config Static device configuration via udev + * + * libinput supports some static configuration through udev properties. + * These propertiesare read when the device is initially added + * to libinput's device list, i.e. before the @ref + * LIBINPUT_EVENT_DEVICE_ADDED event is generated. + * + * The following udev properties are supported: + *
+ *
LIBINPUT_CALIBRATION_MATRIX
+ *
Sets the calibration matrix, see + * libinput_device_config_calibration_get_default_matrix(). If unset, + * defaults to the identity matrix.
+ *
ID_SEAT
+ *
Assigns the physical seat for this device. See + * libinput_seat_get_physical_name(). Defaults to "seat0".
+ *
WL_SEAT
+ *
Assigns the logical seat for this device. See + * libinput_seat_get_logical_name() + * context. Defaults to "default".
+ *
+ * + * Below is an example udev rule to assign "seat1" to a device from vendor + * 0x012a with the model ID of 0x034b. + * @code + * ACTION=="add|change", KERNEL=="event[0-9]*", ENV{ID_VENDOR_ID}=="012a", \ + * ENV{ID_MODEL_ID}=="034b", ENV{ID_SEAT}="seat1" + * @endcode + * + */ + /** * Log priority for internal logging messages. */ From 99ccc0d47aada5b2437f61677dc3519937136938 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 10 Sep 2014 01:32:24 +0200 Subject: [PATCH 52/91] build: symbol ck_assert_ptr_ne requires check-0.9.10 openSUSE 12.3 ships with check-0.9.9 and subsequently fails to build the tests. Change the call to look for check >= 0.9.10 where that symbol is available. Signed-off-by: Jan Engelhardt --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index bb702c0f..b3d24465 100644 --- a/configure.ac +++ b/configure.ac @@ -96,7 +96,7 @@ AC_ARG_ENABLE(tests, [build_tests="$enableval"], [build_tests="auto"]) -PKG_CHECK_MODULES(CHECK, [check >= 0.9.9], [HAVE_CHECK="yes"], [HAVE_CHECK="no"]) +PKG_CHECK_MODULES(CHECK, [check >= 0.9.10], [HAVE_CHECK="yes"], [HAVE_CHECK="no"]) if test "x$build_tests" = "xauto"; then build_tests="$HAVE_CHECK" From 224f1d823b2d44aa5193d5b997d73e4322c50f41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Thu, 11 Sep 2014 22:32:51 +0200 Subject: [PATCH 53/91] configure.ac: libinput 0.6 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jonas Ådahl --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index b3d24465..8aa2a552 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], [5]) +m4_define([libinput_minor_version], [6]) m4_define([libinput_micro_version], [0]) m4_define([libinput_version], [libinput_major_version.libinput_minor_version.libinput_micro_version]) @@ -30,7 +30,7 @@ AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz subdir-objects]) # - If binary compatibility has been broken (eg removed or changed interfaces) # change to C+1:0:0 # - If the interface is the same as the previous version, change to C:R+1:A -LIBINPUT_LT_VERSION=4:0:1 +LIBINPUT_LT_VERSION=5:0:0 AC_SUBST(LIBINPUT_LT_VERSION) AM_SILENT_RULES([yes]) From b9093ca2b1d16b78d93fec9f50a8cde8aaba8415 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 16 Sep 2014 14:30:44 +1000 Subject: [PATCH 54/91] test: fix a jumping touch movement touch_move_to() should usually continue from the touch_down() location Signed-off-by: Peter Hutterer --- test/touchpad.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/touchpad.c b/test/touchpad.c index eae92025..95010192 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -1558,7 +1558,7 @@ START_TEST(touchpad_palm_detect_palm_becomes_pointer) litest_drain_events(li); litest_touch_down(dev, 0, 99, 50); - litest_touch_move_to(dev, 0, 99, 70, 0, 70, 5); + litest_touch_move_to(dev, 0, 99, 50, 0, 70, 5); litest_touch_up(dev, 0); libinput_dispatch(li); From 87c8d82ac5d5297a38af3dc0bc3f7af6dfcbec54 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 17 Sep 2014 10:30:45 +1000 Subject: [PATCH 55/91] test: print strerror() if uinput device creation fails The most common error running the test suite is not running as root, but the error message is hard to interpret. Make it more explicit when it failed, printing the strerror of the errno. Note that libevdev 1.3 is needed to get EACCES instead of EBADF http://cgit.freedesktop.org/libevdev/commit/?id=debe9b030c8069cdf78307888ef3b65830b25122 A workaround is put in place for now until libevdev 1.3 is commonplace. Signed-off-by: Peter Hutterer --- test/litest.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/litest.c b/test/litest.c index dbbf0543..f99d145a 100644 --- a/test/litest.c +++ b/test/litest.c @@ -958,7 +958,11 @@ litest_create_uinput_device_from_description(const char *name, rc = libevdev_uinput_create_from_device(dev, LIBEVDEV_UINPUT_OPEN_MANAGED, &uinput); - ck_assert_int_eq(rc, 0); + /* workaround for a bug in libevdev pre-1.3 + http://cgit.freedesktop.org/libevdev/commit/?id=debe9b030c8069cdf78307888ef3b65830b25122 */ + if (rc == -EBADF) + rc = -EACCES; + ck_assert_msg(rc == 0, "Failed to create uinput device: %s", strerror(-rc)); libevdev_free(dev); From e9239d81a95e7968a2a3bc929cde63a0cc64b8e3 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 1 Sep 2014 16:47:28 +1000 Subject: [PATCH 56/91] Add a helper function for clock_gettime Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev.c | 7 +------ src/libinput-private.h | 15 +++++++++++++++ src/libinput-util.h | 1 + src/timer.c | 29 ++++++++--------------------- 4 files changed, 25 insertions(+), 27 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index e24e2680..721238f8 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1135,16 +1135,11 @@ static void release_pressed_keys(struct evdev_device *device) { struct libinput *libinput = device->base.seat->libinput; - struct timespec ts; uint64_t time; int code; - if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { - log_bug_libinput(libinput, "clock_gettime: %s\n", strerror(errno)); + if ((time = libinput_now(libinput)) == 0) return; - } - - time = ts.tv_sec * 1000ULL + ts.tv_nsec / 1000000; for (code = 0; code < KEY_CNT; code++) { if (get_key_down_count(device, code) > 0) { diff --git a/src/libinput-private.h b/src/libinput-private.h index 9e084dd7..cb90a158 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -23,6 +23,8 @@ #ifndef LIBINPUT_PRIVATE_H #define LIBINPUT_PRIVATE_H +#include + #include "linux/input.h" #include "libinput.h" @@ -229,4 +231,17 @@ touch_notify_touch_up(struct libinput_device *device, void touch_notify_frame(struct libinput_device *device, uint32_t time); + +static inline uint64_t +libinput_now(struct libinput *libinput) +{ + struct timespec ts = { 0, 0 }; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { + log_error(libinput, "clock_gettime failed: %s\n", strerror(errno)); + return 0; + } + + return ts.tv_sec * 1000ULL + ts.tv_nsec / 1000000; +} #endif /* LIBINPUT_PRIVATE_H */ diff --git a/src/libinput-util.h b/src/libinput-util.h index f8072108..51759e87 100644 --- a/src/libinput-util.h +++ b/src/libinput-util.h @@ -26,6 +26,7 @@ #include #include #include +#include #include "libinput.h" diff --git a/src/timer.c b/src/timer.c index ad0fd7c8..f6c8e427 100644 --- a/src/timer.c +++ b/src/timer.c @@ -67,19 +67,12 @@ void libinput_timer_set(struct libinput_timer *timer, uint64_t expire) { #ifndef NDEBUG - struct timespec ts; - - if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { - uint64_t now = ts.tv_sec * 1000ULL + ts.tv_nsec / 1000000; - if (abs(expire - now) > 5000) - log_bug_libinput(timer->libinput, - "timer offset more than 5s, now %" - PRIu64 " expire %" PRIu64 "\n", - now, expire); - } else { - log_error(timer->libinput, - "clock_gettime error: %s\n", strerror(errno)); - } + uint64_t now = libinput_now(timer->libinput); + if (abs(expire - now) > 5000) + log_bug_libinput(timer->libinput, + "timer offset more than 5s, now %" + PRIu64 " expire %" PRIu64 "\n", + now, expire); #endif assert(expire); @@ -107,17 +100,11 @@ libinput_timer_handler(void *data) { struct libinput *libinput = data; struct libinput_timer *timer, *tmp; - struct timespec ts; uint64_t now; - int r; - r = clock_gettime(CLOCK_MONOTONIC, &ts); - if (r) { - log_error(libinput, "clock_gettime error: %s\n", strerror(errno)); + now = libinput_now(libinput); + if (now == 0) return; - } - - now = ts.tv_sec * 1000ULL + ts.tv_nsec / 1000000; list_for_each_safe(timer, tmp, &libinput->timer.list, link) { if (timer->expire <= now) { From 647b2ba18d0ebcacd70c3f808197b8c8d3c8ea07 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 4 Aug 2014 12:49:59 +1000 Subject: [PATCH 57/91] test: avoid erroneous devices to be passed into the test suites The litest features overlap with the litest device specifiers, so it's easy to pass in LITEST_MOUSE where LITEST_POINTER should be passed in, and vice versa. Lacking proper type checking the best we can do here is simply move the devices into the negative range and check for that. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/litest.c | 5 +++++ test/litest.h | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/test/litest.c b/test/litest.c index f99d145a..e5e7316c 100644 --- a/test/litest.c +++ b/test/litest.c @@ -159,6 +159,9 @@ litest_add_tcase(struct suite *suite, void *func, { struct litest_test_device **dev = devices; + assert(required >= LITEST_DISABLE_DEVICE); + assert(excluded >= LITEST_DISABLE_DEVICE); + if (required == LITEST_DISABLE_DEVICE && excluded == LITEST_DISABLE_DEVICE) { litest_add_tcase_no_device(suite, func); @@ -223,6 +226,8 @@ litest_add_for_device(const char *name, struct suite *s; struct litest_test_device **dev = devices; + assert(type < LITEST_NO_DEVICE); + s = get_suite(name); while (*dev) { if ((*dev)->type == type) { diff --git a/test/litest.h b/test/litest.h index aefde3fe..cc55c8f0 100644 --- a/test/litest.h +++ b/test/litest.h @@ -35,16 +35,16 @@ enum litest_device_type { LITEST_NO_DEVICE = -1, - LITEST_SYNAPTICS_CLICKPAD, - LITEST_SYNAPTICS_TOUCHPAD, - LITEST_SYNAPTICS_TOPBUTTONPAD, - LITEST_BCM5974, - LITEST_KEYBOARD, - LITEST_TRACKPOINT, - LITEST_MOUSE, - LITEST_WACOM_TOUCH, - LITEST_ALPS_SEMI_MT, - LITEST_GENERIC_SINGLETOUCH, + LITEST_SYNAPTICS_CLICKPAD = -2, + LITEST_SYNAPTICS_TOUCHPAD = -3, + LITEST_SYNAPTICS_TOPBUTTONPAD = -4, + LITEST_BCM5974 = -5, + LITEST_KEYBOARD = -6, + LITEST_TRACKPOINT = -7, + LITEST_MOUSE = -8, + LITEST_WACOM_TOUCH = -9, + LITEST_ALPS_SEMI_MT = -10, + LITEST_GENERIC_SINGLETOUCH = -11, }; enum litest_device_feature { From dc3ad5315f6343be6f6f88a0cba152d4d533ebb3 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 29 Aug 2014 14:08:46 +1000 Subject: [PATCH 58/91] test: add litest_add_device() For adding a litest device to an existing context. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/litest.c | 12 ++++++++++++ test/litest.h | 2 ++ 2 files changed, 14 insertions(+) diff --git a/test/litest.c b/test/litest.c index e5e7316c..ba0c9fca 100644 --- a/test/litest.c +++ b/test/litest.c @@ -563,6 +563,18 @@ litest_add_device_with_overrides(struct libinput *libinput, return d; } +struct litest_device * +litest_add_device(struct libinput *libinput, + enum litest_device_type which) +{ + return litest_add_device_with_overrides(libinput, + which, + NULL, + NULL, + NULL, + NULL); +} + struct litest_device * litest_create_device_with_overrides(enum litest_device_type which, const char *name_override, diff --git a/test/litest.h b/test/litest.h index cc55c8f0..11a230b2 100644 --- a/test/litest.h +++ b/test/litest.h @@ -88,6 +88,8 @@ void litest_add_no_device(const char *name, void *func); int litest_run(int argc, char **argv); struct litest_device * litest_create_device(enum litest_device_type which); +struct litest_device * litest_add_device(struct libinput *libinput, + enum litest_device_type which); struct libevdev_uinput * litest_create_uinput_device_from_description(const char *name, const struct input_id *id, From 84007034aa0f06f1b4e7232075b23c79d151a3e3 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 3 Sep 2014 10:53:00 +1000 Subject: [PATCH 59/91] test: move assert_button_event to litest proper Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/litest.c | 21 ++++ test/litest.h | 3 + test/pointer.c | 19 +-- test/touchpad.c | 329 +++++++++++++++++++++++------------------------- 4 files changed, 181 insertions(+), 191 deletions(-) diff --git a/test/litest.c b/test/litest.c index ba0c9fca..8c239f1b 100644 --- a/test/litest.c +++ b/test/litest.c @@ -1063,3 +1063,24 @@ litest_create_uinput_device(const char *name, struct input_id *id, ...) return uinput; } + +void +litest_assert_button_event(struct libinput *li, unsigned int button, + enum libinput_button_state state) +{ + struct libinput_event *event; + struct libinput_event_pointer *ptrev; + + libinput_dispatch(li); + event = libinput_get_event(li); + + ck_assert(event != NULL); + ck_assert_int_eq(libinput_event_get_type(event), + LIBINPUT_EVENT_POINTER_BUTTON); + ptrev = libinput_event_get_pointer_event(event); + ck_assert_int_eq(libinput_event_pointer_get_button(ptrev), + button); + ck_assert_int_eq(libinput_event_pointer_get_button_state(ptrev), + state); + libinput_event_destroy(event); +} diff --git a/test/litest.h b/test/litest.h index 11a230b2..dd1386c9 100644 --- a/test/litest.h +++ b/test/litest.h @@ -144,6 +144,9 @@ void litest_wait_for_event(struct libinput *li); void litest_wait_for_event_of_type(struct libinput *li, ...); void litest_drain_events(struct libinput *li); void litest_assert_empty_queue(struct libinput *li); +void litest_assert_button_event(struct libinput *li, + unsigned int button, + enum libinput_button_state state); struct libevdev_uinput * litest_create_uinput_device(const char *name, struct input_id *id, diff --git a/test/pointer.c b/test/pointer.c index 861ab741..82c52459 100644 --- a/test/pointer.c +++ b/test/pointer.c @@ -105,26 +105,13 @@ static void test_button_event(struct litest_device *dev, unsigned int button, int state) { struct libinput *li = dev->libinput; - struct libinput_event *event; - struct libinput_event_pointer *ptrev; litest_event(dev, EV_KEY, button, state); litest_event(dev, EV_SYN, SYN_REPORT, 0); - libinput_dispatch(li); - - event = libinput_get_event(li); - ck_assert(event != NULL); - ck_assert_int_eq(libinput_event_get_type(event), LIBINPUT_EVENT_POINTER_BUTTON); - - ptrev = libinput_event_get_pointer_event(event); - ck_assert(ptrev != NULL); - ck_assert_int_eq(libinput_event_pointer_get_button(ptrev), button); - ck_assert_int_eq(libinput_event_pointer_get_button_state(ptrev), - state ? - LIBINPUT_BUTTON_STATE_PRESSED : - LIBINPUT_BUTTON_STATE_RELEASED); - libinput_event_destroy(event); + litest_assert_button_event(li, button, + state ? LIBINPUT_BUTTON_STATE_PRESSED : + LIBINPUT_BUTTON_STATE_RELEASED); } START_TEST(pointer_button) diff --git a/test/touchpad.c b/test/touchpad.c index 95010192..eecbf20a 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -89,27 +89,6 @@ START_TEST(touchpad_2fg_no_motion) } END_TEST -static void -assert_button_event(struct libinput *li, unsigned int button, - enum libinput_button_state state) -{ - struct libinput_event *event; - struct libinput_event_pointer *ptrev; - - libinput_dispatch(li); - event = libinput_get_event(li); - - ck_assert(event != NULL); - ck_assert_int_eq(libinput_event_get_type(event), - LIBINPUT_EVENT_POINTER_BUTTON); - ptrev = libinput_event_get_pointer_event(event); - ck_assert_int_eq(libinput_event_pointer_get_button(ptrev), - button); - ck_assert_int_eq(libinput_event_pointer_get_button_state(ptrev), - state); - libinput_event_destroy(event); -} - START_TEST(touchpad_1fg_tap) { struct litest_device *dev = litest_current_device(); @@ -126,11 +105,11 @@ START_TEST(touchpad_1fg_tap) libinput_dispatch(li); - assert_button_event(li, BTN_LEFT, - LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); msleep(300); /* tap-n-drag timeout */ - assert_button_event(li, BTN_LEFT, - LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); libinput_dispatch(li); event = libinput_get_event(li); @@ -157,8 +136,8 @@ START_TEST(touchpad_1fg_tap_n_drag) libinput_dispatch(li); - assert_button_event(li, BTN_LEFT, - LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); libinput_dispatch(li); while (libinput_next_event_type(li) == LIBINPUT_EVENT_POINTER_MOTION) { @@ -185,8 +164,8 @@ START_TEST(touchpad_1fg_tap_n_drag) msleep(300); /* tap-n-drag timeout */ - assert_button_event(li, BTN_LEFT, - LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); litest_assert_empty_queue(li); } @@ -209,11 +188,11 @@ START_TEST(touchpad_2fg_tap) libinput_dispatch(li); - assert_button_event(li, BTN_RIGHT, - LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, BTN_RIGHT, + LIBINPUT_BUTTON_STATE_PRESSED); msleep(300); /* tap-n-drag timeout */ - assert_button_event(li, BTN_RIGHT, - LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_button_event(li, BTN_RIGHT, + LIBINPUT_BUTTON_STATE_RELEASED); litest_assert_empty_queue(li); } @@ -236,11 +215,11 @@ START_TEST(touchpad_2fg_tap_inverted) libinput_dispatch(li); - assert_button_event(li, BTN_RIGHT, - LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, BTN_RIGHT, + LIBINPUT_BUTTON_STATE_PRESSED); msleep(300); /* tap-n-drag timeout */ - assert_button_event(li, BTN_RIGHT, - LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_button_event(li, BTN_RIGHT, + LIBINPUT_BUTTON_STATE_RELEASED); litest_assert_empty_queue(li); } @@ -270,10 +249,10 @@ START_TEST(touchpad_1fg_tap_click) libinput_dispatch(li); - assert_button_event(li, BTN_LEFT, - LIBINPUT_BUTTON_STATE_PRESSED); - assert_button_event(li, BTN_LEFT, - LIBINPUT_BUTTON_STATE_RELEASED); + 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); } @@ -302,14 +281,14 @@ START_TEST(touchpad_2fg_tap_click) libinput_dispatch(li); - assert_button_event(li, BTN_LEFT, - LIBINPUT_BUTTON_STATE_PRESSED); - assert_button_event(li, BTN_LEFT, - LIBINPUT_BUTTON_STATE_RELEASED); - assert_button_event(li, BTN_RIGHT, - LIBINPUT_BUTTON_STATE_PRESSED); - assert_button_event(li, BTN_RIGHT, - LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_button_event(li, BTN_RIGHT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, BTN_RIGHT, + LIBINPUT_BUTTON_STATE_RELEASED); litest_assert_empty_queue(li); } @@ -338,10 +317,10 @@ START_TEST(clickpad_2fg_tap_click) libinput_dispatch(li); - assert_button_event(li, BTN_LEFT, - LIBINPUT_BUTTON_STATE_PRESSED); - assert_button_event(li, BTN_LEFT, - LIBINPUT_BUTTON_STATE_RELEASED); + 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); } @@ -371,10 +350,10 @@ START_TEST(touchpad_2fg_tap_click_apple) libinput_dispatch(li); - assert_button_event(li, BTN_RIGHT, - LIBINPUT_BUTTON_STATE_PRESSED); - assert_button_event(li, BTN_RIGHT, - LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_button_event(li, BTN_RIGHT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, BTN_RIGHT, + LIBINPUT_BUTTON_STATE_RELEASED); litest_assert_empty_queue(li); } @@ -479,14 +458,14 @@ START_TEST(touchpad_1fg_double_tap_click) libinput_dispatch(li); - assert_button_event(li, BTN_LEFT, - LIBINPUT_BUTTON_STATE_PRESSED); - assert_button_event(li, BTN_LEFT, - LIBINPUT_BUTTON_STATE_RELEASED); - assert_button_event(li, BTN_LEFT, - LIBINPUT_BUTTON_STATE_PRESSED); - assert_button_event(li, BTN_LEFT, - LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + 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); } @@ -510,8 +489,8 @@ START_TEST(touchpad_1fg_tap_n_drag_click) litest_touch_down(dev, 0, 50, 50); litest_touch_move_to(dev, 0, 50, 50, 80, 50, 10); - assert_button_event(li, BTN_LEFT, - LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); libinput_dispatch(li); @@ -526,10 +505,10 @@ START_TEST(touchpad_1fg_tap_n_drag_click) litest_event(dev, EV_KEY, BTN_LEFT, 1); litest_event(dev, EV_SYN, SYN_REPORT, 0); - assert_button_event(li, BTN_LEFT, - LIBINPUT_BUTTON_STATE_RELEASED); - assert_button_event(li, BTN_LEFT, - LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); litest_event(dev, EV_KEY, BTN_LEFT, 0); litest_event(dev, EV_SYN, SYN_REPORT, 0); @@ -537,8 +516,8 @@ START_TEST(touchpad_1fg_tap_n_drag_click) libinput_dispatch(li); - assert_button_event(li, BTN_LEFT, - LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); litest_assert_empty_queue(li); } @@ -567,11 +546,11 @@ START_TEST(touchpad_3fg_tap) libinput_dispatch(li); - assert_button_event(li, BTN_MIDDLE, - LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); msleep(300); /* tap-n-drag timeout */ - assert_button_event(li, BTN_MIDDLE, - LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_button_event(li, BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_RELEASED); libinput_dispatch(li); event = libinput_get_event(li); @@ -603,11 +582,11 @@ START_TEST(touchpad_3fg_tap_btntool) libinput_dispatch(li); - assert_button_event(li, BTN_MIDDLE, - LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); msleep(300); /* tap-n-drag timeout */ - assert_button_event(li, BTN_MIDDLE, - LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_button_event(li, BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_RELEASED); libinput_dispatch(li); event = libinput_get_event(li); @@ -638,11 +617,11 @@ START_TEST(touchpad_3fg_tap_btntool_inverted) libinput_dispatch(li); - assert_button_event(li, BTN_MIDDLE, - LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); msleep(300); /* tap-n-drag timeout */ - assert_button_event(li, BTN_MIDDLE, - LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_button_event(li, BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_RELEASED); libinput_dispatch(li); event = libinput_get_event(li); @@ -666,10 +645,10 @@ START_TEST(touchpad_1fg_clickfinger) libinput_dispatch(li); - assert_button_event(li, BTN_LEFT, - LIBINPUT_BUTTON_STATE_PRESSED); - assert_button_event(li, BTN_LEFT, - LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); litest_delete_device(dev); } @@ -693,10 +672,10 @@ START_TEST(touchpad_2fg_clickfinger) libinput_dispatch(li); - assert_button_event(li, BTN_RIGHT, - LIBINPUT_BUTTON_STATE_PRESSED); - assert_button_event(li, BTN_RIGHT, - LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_button_event(li, BTN_RIGHT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, BTN_RIGHT, + LIBINPUT_BUTTON_STATE_RELEASED); litest_delete_device(dev); } @@ -716,10 +695,10 @@ START_TEST(touchpad_btn_left) libinput_dispatch(li); - assert_button_event(li, BTN_LEFT, - LIBINPUT_BUTTON_STATE_PRESSED); - assert_button_event(li, BTN_LEFT, - LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); } END_TEST @@ -746,10 +725,10 @@ START_TEST(clickpad_1fg_tap_click) libinput_dispatch(li); - assert_button_event(li, BTN_LEFT, - LIBINPUT_BUTTON_STATE_PRESSED); - assert_button_event(li, BTN_LEFT, - LIBINPUT_BUTTON_STATE_RELEASED); + 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); } @@ -787,8 +766,8 @@ START_TEST(clickpad_click_n_drag) litest_event(dev, EV_SYN, SYN_REPORT, 0); libinput_dispatch(li); - assert_button_event(li, BTN_LEFT, - LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); libinput_dispatch(li); ck_assert_int_eq(libinput_next_event_type(li), LIBINPUT_EVENT_NONE); @@ -811,8 +790,8 @@ START_TEST(clickpad_click_n_drag) litest_event(dev, EV_SYN, SYN_REPORT, 0); litest_touch_up(dev, 0); - assert_button_event(li, BTN_LEFT, - LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); } END_TEST @@ -827,16 +806,16 @@ START_TEST(clickpad_softbutton_left) litest_event(dev, EV_KEY, BTN_LEFT, 1); litest_event(dev, EV_SYN, SYN_REPORT, 0); - assert_button_event(li, - BTN_LEFT, + litest_assert_button_event(li, + BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED); litest_event(dev, EV_KEY, BTN_LEFT, 0); litest_event(dev, EV_SYN, SYN_REPORT, 0); litest_touch_up(dev, 0); - assert_button_event(li, - BTN_LEFT, + litest_assert_button_event(li, + BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED); libinput_dispatch(li); @@ -856,16 +835,16 @@ START_TEST(clickpad_softbutton_right) litest_event(dev, EV_KEY, BTN_LEFT, 1); litest_event(dev, EV_SYN, SYN_REPORT, 0); - assert_button_event(li, - BTN_RIGHT, + litest_assert_button_event(li, + BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED); litest_event(dev, EV_KEY, BTN_LEFT, 0); litest_event(dev, EV_SYN, SYN_REPORT, 0); litest_touch_up(dev, 0); - assert_button_event(li, - BTN_RIGHT, + litest_assert_button_event(li, + BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED); libinput_dispatch(li); @@ -895,14 +874,14 @@ START_TEST(clickpad_softbutton_left_tap_n_drag) litest_event(dev, EV_KEY, BTN_LEFT, 1); litest_event(dev, EV_SYN, SYN_REPORT, 0); - assert_button_event(li, - BTN_LEFT, + litest_assert_button_event(li, + BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED); - assert_button_event(li, - BTN_LEFT, + litest_assert_button_event(li, + BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED); - assert_button_event(li, - BTN_LEFT, + litest_assert_button_event(li, + BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED); litest_assert_empty_queue(li); @@ -910,8 +889,8 @@ START_TEST(clickpad_softbutton_left_tap_n_drag) litest_event(dev, EV_SYN, SYN_REPORT, 0); litest_touch_up(dev, 0); - assert_button_event(li, - BTN_LEFT, + litest_assert_button_event(li, + BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED); litest_assert_empty_queue(li); } @@ -938,24 +917,24 @@ START_TEST(clickpad_softbutton_right_tap_n_drag) litest_event(dev, EV_KEY, BTN_LEFT, 1); litest_event(dev, EV_SYN, SYN_REPORT, 0); - assert_button_event(li, - BTN_LEFT, - LIBINPUT_BUTTON_STATE_PRESSED); - assert_button_event(li, - BTN_LEFT, - LIBINPUT_BUTTON_STATE_RELEASED); - assert_button_event(li, - BTN_RIGHT, - LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_button_event(li, + BTN_RIGHT, + LIBINPUT_BUTTON_STATE_PRESSED); litest_assert_empty_queue(li); litest_event(dev, EV_KEY, BTN_LEFT, 0); litest_event(dev, EV_SYN, SYN_REPORT, 0); litest_touch_up(dev, 0); - assert_button_event(li, - BTN_RIGHT, - LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_button_event(li, + BTN_RIGHT, + LIBINPUT_BUTTON_STATE_RELEASED); litest_assert_empty_queue(li); } END_TEST @@ -983,9 +962,9 @@ START_TEST(clickpad_softbutton_left_1st_fg_move) litest_event(dev, EV_KEY, BTN_LEFT, 1); litest_event(dev, EV_SYN, SYN_REPORT, 0); - assert_button_event(li, - BTN_LEFT, - LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); litest_assert_empty_queue(li); /* move out of the area, then wait for softbutton timer */ @@ -1028,9 +1007,9 @@ START_TEST(clickpad_softbutton_left_1st_fg_move) litest_event(dev, EV_SYN, SYN_REPORT, 0); litest_touch_up(dev, 0); - assert_button_event(li, - BTN_LEFT, - LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); litest_assert_empty_queue(li); } @@ -1056,9 +1035,9 @@ START_TEST(clickpad_softbutton_left_2nd_fg_move) litest_event(dev, EV_KEY, BTN_LEFT, 1); litest_event(dev, EV_SYN, SYN_REPORT, 0); - assert_button_event(li, - BTN_LEFT, - LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); litest_assert_empty_queue(li); litest_touch_down(dev, 1, 20, 20); @@ -1119,9 +1098,9 @@ START_TEST(clickpad_softbutton_left_2nd_fg_move) litest_event(dev, EV_SYN, SYN_REPORT, 0); litest_touch_up(dev, 0); - assert_button_event(li, - BTN_LEFT, - LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); litest_assert_empty_queue(li); } @@ -1144,18 +1123,18 @@ START_TEST(clickpad_softbutton_left_to_right) litest_event(dev, EV_KEY, BTN_LEFT, 1); litest_event(dev, EV_SYN, SYN_REPORT, 0); - assert_button_event(li, - BTN_RIGHT, - LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_RIGHT, + LIBINPUT_BUTTON_STATE_PRESSED); litest_assert_empty_queue(li); litest_event(dev, EV_KEY, BTN_LEFT, 0); litest_event(dev, EV_SYN, SYN_REPORT, 0); litest_touch_up(dev, 0); - assert_button_event(li, - BTN_RIGHT, - LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_button_event(li, + BTN_RIGHT, + LIBINPUT_BUTTON_STATE_RELEASED); litest_assert_empty_queue(li); } @@ -1178,18 +1157,18 @@ START_TEST(clickpad_softbutton_right_to_left) litest_event(dev, EV_KEY, BTN_LEFT, 1); litest_event(dev, EV_SYN, SYN_REPORT, 0); - assert_button_event(li, - BTN_LEFT, - LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); litest_assert_empty_queue(li); litest_event(dev, EV_KEY, BTN_LEFT, 0); litest_event(dev, EV_SYN, SYN_REPORT, 0); litest_touch_up(dev, 0); - assert_button_event(li, - BTN_LEFT, - LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); litest_assert_empty_queue(li); } @@ -1206,18 +1185,18 @@ START_TEST(clickpad_topsoftbuttons_left) litest_event(dev, EV_KEY, BTN_LEFT, 1); litest_event(dev, EV_SYN, SYN_REPORT, 0); - assert_button_event(li, - BTN_LEFT, - LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); litest_assert_empty_queue(li); litest_event(dev, EV_KEY, BTN_LEFT, 0); litest_event(dev, EV_SYN, SYN_REPORT, 0); litest_touch_up(dev, 0); - assert_button_event(li, - BTN_LEFT, - LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); litest_assert_empty_queue(li); } @@ -1234,18 +1213,18 @@ START_TEST(clickpad_topsoftbuttons_right) litest_event(dev, EV_KEY, BTN_LEFT, 1); litest_event(dev, EV_SYN, SYN_REPORT, 0); - assert_button_event(li, - BTN_RIGHT, - LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_RIGHT, + LIBINPUT_BUTTON_STATE_PRESSED); litest_assert_empty_queue(li); litest_event(dev, EV_KEY, BTN_LEFT, 0); litest_event(dev, EV_SYN, SYN_REPORT, 0); litest_touch_up(dev, 0); - assert_button_event(li, - BTN_RIGHT, - LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_button_event(li, + BTN_RIGHT, + LIBINPUT_BUTTON_STATE_RELEASED); litest_assert_empty_queue(li); } @@ -1262,18 +1241,18 @@ START_TEST(clickpad_topsoftbuttons_middle) litest_event(dev, EV_KEY, BTN_LEFT, 1); litest_event(dev, EV_SYN, SYN_REPORT, 0); - assert_button_event(li, - BTN_MIDDLE, - LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); litest_assert_empty_queue(li); litest_event(dev, EV_KEY, BTN_LEFT, 0); litest_event(dev, EV_SYN, SYN_REPORT, 0); litest_touch_up(dev, 0); - assert_button_event(li, - BTN_MIDDLE, - LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_button_event(li, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_RELEASED); litest_assert_empty_queue(li); } From fca1519395195cc767024513574fb27597fa0733 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 3 Sep 2014 10:57:25 +1000 Subject: [PATCH 60/91] test: wait for events in litest_assert_button_events Takes the onus of waiting from the caller. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/litest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/litest.c b/test/litest.c index 8c239f1b..cf7e6922 100644 --- a/test/litest.c +++ b/test/litest.c @@ -1071,7 +1071,7 @@ litest_assert_button_event(struct libinput *li, unsigned int button, struct libinput_event *event; struct libinput_event_pointer *ptrev; - libinput_dispatch(li); + litest_wait_for_event(li); event = libinput_get_event(li); ck_assert(event != NULL); From 504c7667e90454834d65a29cb592e36f4f51ae99 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 3 Sep 2014 11:16:20 +1000 Subject: [PATCH 61/91] touchpad: fix tap-and-drag handling for timeouts Doing a tap-and-drag gesture but just holding the finger instead of moving should trigger a timeout and still switchin into tap-and-drag. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad-tap.c | 1 - test/touchpad.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index 357a50a6..a19d51e2 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -221,7 +221,6 @@ tp_tap_tapped_handle_event(struct tp_dispatch *tp, break; case TAP_EVENT_TOUCH: tp->tap.state = TAP_STATE_DRAGGING_OR_DOUBLETAP; - tp_tap_clear_timer(tp); break; case TAP_EVENT_TIMEOUT: tp->tap.state = TAP_STATE_IDLE; diff --git a/test/touchpad.c b/test/touchpad.c index eecbf20a..7ff3d142 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -171,6 +171,35 @@ START_TEST(touchpad_1fg_tap_n_drag) } END_TEST +START_TEST(touchpad_1fg_tap_n_drag_timeout) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + libinput_device_config_tap_set_enabled(dev->libinput_device, + LIBINPUT_CONFIG_TAP_ENABLED); + + litest_drain_events(li); + + litest_touch_down(dev, 0, 50, 50); + litest_touch_up(dev, 0); + litest_touch_down(dev, 0, 50, 50); + libinput_dispatch(li); + msleep(300); + + litest_assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + + litest_assert_empty_queue(li); + litest_touch_up(dev, 0); + + litest_assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + + litest_assert_empty_queue(li); +} +END_TEST + START_TEST(touchpad_2fg_tap) { struct litest_device *dev = litest_current_device(); @@ -1601,6 +1630,7 @@ int main(int argc, char **argv) { litest_add("touchpad:tap", touchpad_1fg_tap, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:tap", touchpad_1fg_tap_n_drag, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("touchpad:tap", touchpad_1fg_tap_n_drag_timeout, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:tap", touchpad_2fg_tap, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); litest_add("touchpad:tap", touchpad_2fg_tap_inverted, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); litest_add("touchpad:tap", touchpad_1fg_tap_click, LITEST_TOUCHPAD, LITEST_CLICKPAD); From 9f2b05d5ac552d62127c4b22cd66ff4d0a24dd2b Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 22 Aug 2014 10:41:20 +1000 Subject: [PATCH 62/91] evdev: prefix the hw key/button bitmask with 'hw' This bitmask reflects the hw state, prefix it accordingly. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev.c | 12 ++++++------ src/evdev.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index 721238f8..71c52367 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -48,15 +48,15 @@ enum evdev_key_type { }; static void -set_key_down(struct evdev_device *device, int code, int pressed) +hw_set_key_down(struct evdev_device *device, int code, int pressed) { - long_set_bit_state(device->key_mask, code, pressed); + long_set_bit_state(device->hw_key_mask, code, pressed); } static int -is_key_down(struct evdev_device *device, int code) +hw_is_key_down(struct evdev_device *device, int code) { - return long_bit_is_set(device->key_mask, code); + return long_bit_is_set(device->hw_key_mask, code); } static int @@ -385,12 +385,12 @@ evdev_process_key(struct evdev_device *device, break; case EVDEV_KEY_TYPE_KEY: case EVDEV_KEY_TYPE_BUTTON: - if (!is_key_down(device, e->code)) + if (!hw_is_key_down(device, e->code)) return; } } - set_key_down(device, e->code, e->value); + hw_set_key_down(device, e->code, e->value); switch (type) { case EVDEV_KEY_TYPE_NONE: diff --git a/src/evdev.h b/src/evdev.h index 50ca713d..9de44bc8 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -100,7 +100,7 @@ struct evdev_device { /* Bitmask of pressed keys used to ignore initial release events from * the kernel. */ - unsigned long key_mask[NLONGS(KEY_CNT)]; + unsigned long hw_key_mask[NLONGS(KEY_CNT)]; /* Key counter used for multiplexing button events internally in * libinput. */ uint8_t key_count[KEY_CNT]; From 702e8db2cfd7700f0c5a95656831df0e38475fc3 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 30 Jan 2014 15:34:00 +1000 Subject: [PATCH 63/91] evdev: factor out closing a device into evdev_suspend() No functional changes, just prep work for an upcoming patch Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev.c | 13 +++++++++++-- src/evdev.h | 2 ++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index 71c52367..054beb63 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1165,8 +1165,8 @@ release_pressed_keys(struct evdev_device *device) } } -void -evdev_device_remove(struct evdev_device *device) +int +evdev_device_suspend(struct evdev_device *device) { if (device->source) libinput_remove_source(device->base.seat->libinput, @@ -1178,6 +1178,15 @@ evdev_device_remove(struct evdev_device *device) mtdev_close_delete(device->mtdev); close_restricted(device->base.seat->libinput, device->fd); device->fd = -1; + + return 0; +} + +void +evdev_device_remove(struct evdev_device *device) +{ + evdev_device_suspend(device); + list_remove(&device->base.link); notify_removed_device(&device->base); diff --git a/src/evdev.h b/src/evdev.h index 9de44bc8..ef02491c 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -186,6 +186,8 @@ double evdev_device_transform_y(struct evdev_device *device, double y, uint32_t height); +int +evdev_device_suspend(struct evdev_device *device); void evdev_keyboard_notify_key(struct evdev_device *device, From 1e37d3bc44294c01db3370a5fa4068211d4f53e3 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 30 Jan 2014 15:55:37 +1000 Subject: [PATCH 64/91] evdev: prevent double-suspending a device Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index 054beb63..9e91ee5b 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1168,16 +1168,23 @@ release_pressed_keys(struct evdev_device *device) int evdev_device_suspend(struct evdev_device *device) { - if (device->source) + if (device->source) { libinput_remove_source(device->base.seat->libinput, device->source); + device->source = NULL; + } release_pressed_keys(device); - if (device->mtdev) + if (device->mtdev) { mtdev_close_delete(device->mtdev); - close_restricted(device->base.seat->libinput, device->fd); - device->fd = -1; + device->mtdev = NULL; + } + + if (device->fd != -1) { + close_restricted(device->base.seat->libinput, device->fd); + device->fd = -1; + } return 0; } From 3b51ea5cce158e01524c5fd8745c6d84f7459f44 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 30 Jan 2014 16:18:55 +1000 Subject: [PATCH 65/91] Add a config interface for enabling/disabling event generation from a device Rather than adding a config interface to disable a device merely allow a caller to toggle the "send events" mode on the device. If off, the device won't send events (though further events may be received depending on the current state of the device). Default is enabled, i.e. the device sends events. A special mode is added to the obvious enable/disable: disable the device when an external mouse is connected. Once set, the device will be enabled when no mouse is present and stop sending events otherwise. This isn't hooked up to anything yet though. Built into the config API is the default option of "enabled". Any device supports this, for the obvious reason. Disabling or conditionally disabling is left to the implementation. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/libinput-private.h | 9 ++++ src/libinput.c | 39 ++++++++++++++++ src/libinput.h | 100 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 148 insertions(+) diff --git a/src/libinput-private.h b/src/libinput-private.h index cb90a158..cf03c032 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -101,9 +101,18 @@ struct libinput_device_config_calibration { float matrix[6]); }; +struct libinput_device_config_send_events { + uint32_t (*get_modes)(struct libinput_device *device); + enum libinput_config_status (*set_mode)(struct libinput_device *device, + enum libinput_config_send_events_mode mode); + enum libinput_config_send_events_mode (*get_mode)(struct libinput_device *device); + enum libinput_config_send_events_mode (*get_default_mode)(struct libinput_device *device); +}; + struct libinput_device_config { struct libinput_device_config_tap *tap; struct libinput_device_config_calibration *calibration; + struct libinput_device_config_send_events *sendevents; }; struct libinput_device { diff --git a/src/libinput.c b/src/libinput.c index 20aa1cb3..14f02574 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -1349,3 +1349,42 @@ libinput_device_config_calibration_get_default_matrix(struct libinput_device *de return device->config.calibration->get_default_matrix(device, matrix); } + +LIBINPUT_EXPORT uint32_t +libinput_device_config_send_events_get_modes(struct libinput_device *device) +{ + uint32_t modes = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED; + + if (device->config.sendevents) + modes |= device->config.sendevents->get_modes(device); + + return modes; +} + +LIBINPUT_EXPORT enum libinput_config_status +libinput_device_config_send_events_set_mode(struct libinput_device *device, + enum libinput_config_send_events_mode mode) +{ + if ((libinput_device_config_send_events_get_modes(device) & mode) == 0) + return LIBINPUT_CONFIG_STATUS_UNSUPPORTED; + + if (device->config.sendevents) + return device->config.sendevents->set_mode(device, mode); + else /* mode must be _ENABLED to get here */ + return LIBINPUT_CONFIG_STATUS_SUCCESS; +} + +LIBINPUT_EXPORT enum libinput_config_send_events_mode +libinput_device_config_send_events_get_mode(struct libinput_device *device) +{ + if (device->config.sendevents) + return device->config.sendevents->get_mode(device); + else + return LIBINPUT_CONFIG_SEND_EVENTS_ENABLED; +} + +LIBINPUT_EXPORT enum libinput_config_send_events_mode +libinput_device_config_send_events_get_default_mode(struct libinput_device *device) +{ + return LIBINPUT_CONFIG_SEND_EVENTS_ENABLED; +} diff --git a/src/libinput.h b/src/libinput.h index a8a19c54..806e1abb 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -1683,6 +1683,106 @@ int libinput_device_config_calibration_get_default_matrix(struct libinput_device *device, float matrix[6]); +/** + * The send-event mode of a device defines when a device may generate events + * and pass those events to the caller. + */ +enum libinput_config_send_events_mode { + /** + * Send events from this device normally. + */ + LIBINPUT_CONFIG_SEND_EVENTS_ENABLED = (1 << 0), + /** + * Do not send events through this device. Depending on the device, + * this may close all file descriptors on the device or it may leave + * the file descriptors open and route events through a different + * device. + */ + LIBINPUT_CONFIG_SEND_EVENTS_DISABLED = (1 << 1), + /** + * If an external pointer device is plugged in, do not send events + * from this device. This option may be available on built-in + * touchpads. + */ + LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE = (1 << 2), +}; + +/** + * @ingroup config + * + * Return the possible send-event modes for this device. These modes define + * when a device may process and send events. + * + * @param device The device to configure + * + * @return A bitmask of possible modes. + * + * @see libinput_device_config_send_events_set_mode + * @see libinput_device_config_send_events_get_mode + * @see libinput_device_config_send_events_get_default_mode + */ +uint32_t +libinput_device_config_send_events_get_modes(struct libinput_device *device); + +/** + * Set the send-event mode for this device. The mode defines when the device + * processes and sends events to the caller. + * + * The selected mode may not take effect immediately. Events already + * received and processed from this device are unaffected and will be passed + * to the caller on the next call to libinput_get_event(). + * + * If the mode is one of @ref LIBINPUT_CONFIG_SEND_EVENTS_DISABLED or + * @ref LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE, the device + * may wait for or generate events until it is in a neutral state. + * For example, this may include waiting for or generating button release + * events. + * + * If the device is already suspended, this function does nothing and + * returns success. Changing the send-event mode on a device that has been + * removed is permitted. + * + * @param device The device to configure + * @param mode The send-event mode for this device. + * + * @return A config status code. + * + * @see libinput_device_config_send_events_get_modes + * @see libinput_device_config_send_events_get_mode + * @see libinput_device_config_send_events_get_default_mode + */ +enum libinput_config_status +libinput_device_config_send_events_set_mode(struct libinput_device *device, + enum libinput_config_send_events_mode mode); + +/** + * Get the send-event mode for this device. The mode defines when the device + * processes and sends events to the caller. + * + * @param device The device to configure + * @return The current send-event mode for this device. + * + * @see libinput_device_config_send_events_get_modes + * @see libinput_device_config_send_events_set_mode + * @see libinput_device_config_send_events_get_default_mode + */ +enum libinput_config_send_events_mode +libinput_device_config_send_events_get_mode(struct libinput_device *device); + +/** + * Get the default send-event mode for this device. The mode defines when + * the device processes and sends events to the caller. + * + * @param device The device to configure + * @return The current send-event mode for this device. + * + * @see libinput_device_config_send_events_get_modes + * @see libinput_device_config_send_events_set_mode + * @see libinput_device_config_send_events_get_default_mode + */ +enum libinput_config_send_events_mode +libinput_device_config_send_events_get_default_mode(struct libinput_device *device); + #ifdef __cplusplus } #endif From 7bef1ecf4e6aceaa61e2db4bdb4d229b461aaa2d Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 15 Sep 2014 14:59:07 +1000 Subject: [PATCH 66/91] evdev: add helper function evdev_need_mtdev Prep work for upcoming patches Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index 9e91ee5b..f7f8f088 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -733,6 +733,16 @@ configure_pointer_acceleration(struct evdev_device *device) return 0; } +static inline int +evdev_need_mtdev(struct evdev_device *device) +{ + struct libevdev *evdev = device->evdev; + + return (libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_X) && + libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_Y) && + !libevdev_has_event_code(evdev, EV_ABS, ABS_MT_SLOT)); +} + static int evdev_configure_device(struct evdev_device *device) { @@ -806,8 +816,7 @@ evdev_configure_device(struct evdev_device *device) has_touch = 1; has_mt = 1; - if (!libevdev_has_event_code(evdev, - EV_ABS, ABS_MT_SLOT)) { + if (evdev_need_mtdev(device)) { device->mtdev = mtdev_new_open(device->fd); if (!device->mtdev) return -1; From 3e93d913bef339311976a927091674fb7f1f4987 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 22 Aug 2014 13:40:40 +1000 Subject: [PATCH 67/91] evdev: hook up a generic enable/disable interface for devices The evdev fallback dispatch supports enabling and disabling devices. That's fairly easy to support since we don't (yet) have extra event generation within the fallback backend. Thus, we can simply close the fd and re-open it again later. Touchpads are currently excluded here, they generate extra events on tapping, scrolling, and software buttons and need a more complex implementation. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++- src/evdev.h | 8 +++++ 2 files changed, 100 insertions(+), 1 deletion(-) diff --git a/src/evdev.c b/src/evdev.c index f7f8f088..996ce32e 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -624,14 +624,63 @@ struct evdev_dispatch_interface fallback_interface = { fallback_destroy }; +static uint32_t +evdev_sendevents_get_modes(struct libinput_device *device) +{ + return LIBINPUT_CONFIG_SEND_EVENTS_ENABLED | + LIBINPUT_CONFIG_SEND_EVENTS_DISABLED; +} + +static enum libinput_config_status +evdev_sendevents_set_mode(struct libinput_device *device, + enum libinput_config_send_events_mode mode) +{ + struct evdev_device *evdev = (struct evdev_device*)device; + struct evdev_dispatch *dispatch = evdev->dispatch; + + if (mode == dispatch->sendevents.current_mode) + return LIBINPUT_CONFIG_STATUS_SUCCESS; + + switch(mode) { + case LIBINPUT_CONFIG_SEND_EVENTS_ENABLED: + evdev_device_resume(evdev); + break; + case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED: + evdev_device_suspend(evdev); + break; + default: + return LIBINPUT_CONFIG_STATUS_UNSUPPORTED; + } + + dispatch->sendevents.current_mode = mode; + + return LIBINPUT_CONFIG_STATUS_SUCCESS; +} + +static enum libinput_config_send_events_mode +evdev_sendevents_get_mode(struct libinput_device *device) +{ + struct evdev_device *evdev = (struct evdev_device*)device; + struct evdev_dispatch *dispatch = evdev->dispatch; + + return dispatch->sendevents.current_mode; +} + +static enum libinput_config_send_events_mode +evdev_sendevents_get_default_mode(struct libinput_device *device) +{ + return LIBINPUT_CONFIG_SEND_EVENTS_ENABLED; +} + static struct evdev_dispatch * fallback_dispatch_create(struct libinput_device *device) { - struct evdev_dispatch *dispatch = malloc(sizeof *dispatch); + struct evdev_dispatch *dispatch = zalloc(sizeof *dispatch); if (dispatch == NULL) return NULL; dispatch->interface = &fallback_interface; + device->config.calibration = &dispatch->calibration; dispatch->calibration.has_matrix = evdev_calibration_has_matrix; @@ -639,6 +688,14 @@ fallback_dispatch_create(struct libinput_device *device) dispatch->calibration.get_matrix = evdev_calibration_get_matrix; dispatch->calibration.get_default_matrix = evdev_calibration_get_default_matrix; + device->config.sendevents = &dispatch->sendevents.config; + + dispatch->sendevents.current_mode = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED; + dispatch->sendevents.config.get_modes = evdev_sendevents_get_modes; + dispatch->sendevents.config.set_mode = evdev_sendevents_set_mode; + dispatch->sendevents.config.get_mode = evdev_sendevents_get_mode; + dispatch->sendevents.config.get_default_mode = evdev_sendevents_get_default_mode; + return dispatch; } @@ -1198,6 +1255,40 @@ evdev_device_suspend(struct evdev_device *device) return 0; } +int +evdev_device_resume(struct evdev_device *device) +{ + struct libinput *libinput = device->base.seat->libinput; + int fd; + + if (device->fd != -1) + return 0; + + fd = open_restricted(libinput, device->devnode, O_RDWR | O_NONBLOCK); + + if (fd < 0) + return -errno; + + device->fd = fd; + + if (evdev_need_mtdev(device)) { + device->mtdev = mtdev_new_open(device->fd); + if (!device->mtdev) + return -ENODEV; + } + + device->source = + libinput_add_fd(libinput, fd, evdev_device_dispatch, device); + if (!device->source) { + mtdev_close_delete(device->mtdev); + return -ENOMEM; + } + + memset(device->hw_key_mask, 0, sizeof(device->hw_key_mask)); + + return 0; +} + void evdev_device_remove(struct evdev_device *device) { diff --git a/src/evdev.h b/src/evdev.h index ef02491c..2af68289 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -124,6 +124,11 @@ struct evdev_dispatch_interface { struct evdev_dispatch { struct evdev_dispatch_interface *interface; struct libinput_device_config_calibration calibration; + + struct { + struct libinput_device_config_send_events config; + enum libinput_config_send_events_mode current_mode; + } sendevents; }; struct evdev_device * @@ -189,6 +194,9 @@ evdev_device_transform_y(struct evdev_device *device, int evdev_device_suspend(struct evdev_device *device); +int +evdev_device_resume(struct evdev_device *device); + void evdev_keyboard_notify_key(struct evdev_device *device, uint32_t time, From 8be1abf58ad855ba25f48db5a7196c85dda1ff25 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 30 Jan 2014 16:33:24 +1000 Subject: [PATCH 68/91] evdev: don't resume a removed device A device may disappear and a new device may re-appear with the same device node while the original device is suspended. Prevent a device resume to open the wrong device. In a path context, a changing syspath is the only indicator we get of the device changing. In a udev context, if the device was removed and libinput_dispatch() was called, we can short-cut the syspath comparison by setting it to NULL. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++- src/evdev.h | 4 +++- src/path.c | 9 ++++++--- src/udev-seat.c | 4 +++- 4 files changed, 59 insertions(+), 6 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index 996ce32e..3a5d8742 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -971,7 +971,8 @@ evdev_configure_device(struct evdev_device *device) struct evdev_device * evdev_device_create(struct libinput_seat *seat, const char *devnode, - const char *sysname) + const char *sysname, + const char *syspath) { struct libinput *libinput = seat->libinput; struct evdev_device *device; @@ -1008,6 +1009,7 @@ evdev_device_create(struct libinput_seat *seat, device->mtdev = NULL; device->devnode = strdup(devnode); device->sysname = strdup(sysname); + device->syspath = strdup(syspath); device->rel.dx = 0; device->rel.dy = 0; device->abs.seat_slot = -1; @@ -1255,6 +1257,36 @@ evdev_device_suspend(struct evdev_device *device) return 0; } +static int +evdev_device_compare_syspath(struct evdev_device *device, int fd) +{ + struct udev *udev = NULL; + struct udev_device *udev_device = NULL; + const char *syspath; + struct stat st; + int rc = 1; + + udev = udev_new(); + if (!udev) + goto out; + + if (fstat(fd, &st) < 0) + goto out; + + udev_device = udev_device_new_from_devnum(udev, 'c', st.st_rdev); + if (!device) + goto out; + + syspath = udev_device_get_syspath(udev_device); + rc = strcmp(syspath, device->syspath); +out: + if (udev_device) + udev_device_unref(udev_device); + if (udev) + udev_unref(udev); + return rc; +} + int evdev_device_resume(struct evdev_device *device) { @@ -1264,11 +1296,19 @@ evdev_device_resume(struct evdev_device *device) if (device->fd != -1) return 0; + if (device->syspath == NULL) + return -ENODEV; + fd = open_restricted(libinput, device->devnode, O_RDWR | O_NONBLOCK); if (fd < 0) return -errno; + if (evdev_device_compare_syspath(device, fd)) { + close_restricted(libinput, fd); + return -ENODEV; + } + device->fd = fd; if (evdev_need_mtdev(device)) { @@ -1294,6 +1334,11 @@ evdev_device_remove(struct evdev_device *device) { evdev_device_suspend(device); + /* A device may be removed while suspended. Free the syspath to + * skip re-opening a different device with the same node */ + free(device->syspath); + device->syspath = NULL; + list_remove(&device->base.link); notify_removed_device(&device->base); @@ -1315,5 +1360,6 @@ evdev_device_destroy(struct evdev_device *device) free(device->mt.slots); free(device->devnode); free(device->sysname); + free(device->syspath); free(device); } diff --git a/src/evdev.h b/src/evdev.h index 2af68289..bdd4a4e5 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -63,6 +63,7 @@ struct evdev_device { char *output_name; char *devnode; char *sysname; + char *syspath; const char *devname; int fd; struct { @@ -134,7 +135,8 @@ struct evdev_dispatch { struct evdev_device * evdev_device_create(struct libinput_seat *seat, const char *devnode, - const char *sysname); + const char *sysname, + const char *syspath); struct evdev_dispatch * evdev_touchpad_create(struct evdev_device *device); diff --git a/src/path.c b/src/path.c index e9c0ee8d..37527512 100644 --- a/src/path.c +++ b/src/path.c @@ -112,6 +112,7 @@ path_seat_get_named(struct path_input *input, static int path_get_udev_properties(const char *path, char **sysname, + char **syspath, char **seat_name, char **seat_logical_name) { @@ -133,6 +134,7 @@ path_get_udev_properties(const char *path, goto out; *sysname = strdup(udev_device_get_sysname(device)); + *syspath = strdup(udev_device_get_syspath(device)); seat = udev_device_get_property_value(device, "ID_SEAT"); *seat_name = strdup(seat ? seat : default_seat); @@ -155,10 +157,10 @@ path_device_enable(struct path_input *input, const char *devnode) { struct path_seat *seat; struct evdev_device *device = NULL; - char *sysname = NULL; + char *sysname = NULL, *syspath = NULL; char *seat_name = NULL, *seat_logical_name = NULL; - if (path_get_udev_properties(devnode, &sysname, + if (path_get_udev_properties(devnode, &sysname, &syspath, &seat_name, &seat_logical_name) == -1) { log_info(&input->base, "failed to obtain sysname for device '%s'.\n", @@ -180,7 +182,7 @@ path_device_enable(struct path_input *input, const char *devnode) } } - device = evdev_device_create(&seat->base, devnode, sysname); + device = evdev_device_create(&seat->base, devnode, sysname, syspath); libinput_seat_unref(&seat->base); if (device == EVDEV_UNHANDLED_DEVICE) { @@ -198,6 +200,7 @@ path_device_enable(struct path_input *input, const char *devnode) out: free(sysname); + free(syspath); free(seat_name); free(seat_logical_name); diff --git a/src/udev-seat.c b/src/udev-seat.c index ccff35c5..f2be66e3 100644 --- a/src/udev-seat.c +++ b/src/udev-seat.c @@ -47,6 +47,7 @@ device_added(struct udev_device *udev_device, struct udev_input *input) struct evdev_device *device; const char *devnode; const char *sysname; + const char *syspath; const char *device_seat, *seat_name, *output_name; const char *calibration_values; float calibration[6]; @@ -61,6 +62,7 @@ device_added(struct udev_device *udev_device, struct udev_input *input) devnode = udev_device_get_devnode(udev_device); sysname = udev_device_get_sysname(udev_device); + syspath = udev_device_get_syspath(udev_device); /* Search for matching logical seat */ seat_name = udev_device_get_property_value(udev_device, "WL_SEAT"); @@ -77,7 +79,7 @@ device_added(struct udev_device *udev_device, struct udev_input *input) return -1; } - device = evdev_device_create(&seat->base, devnode, sysname); + device = evdev_device_create(&seat->base, devnode, sysname, syspath); libinput_seat_unref(&seat->base); if (device == EVDEV_UNHANDLED_DEVICE) { From 20831877d001936e017f8d1f38c6cda49367d5dc Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 30 Jan 2014 15:40:35 +1000 Subject: [PATCH 69/91] test: add test for device suspend/resume Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/Makefile.am | 7 +- test/device.c | 275 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 281 insertions(+), 1 deletion(-) create mode 100644 test/device.c diff --git a/test/Makefile.am b/test/Makefile.am index 56ed9d17..86859d8c 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -35,7 +35,8 @@ run_tests = \ test-log \ test-touchpad \ test-misc \ - test-keyboard + test-keyboard \ + test-device build_tests = \ test-build-cxx \ test-build-linker \ @@ -79,6 +80,10 @@ test_keyboard_SOURCES = keyboard.c test_keyboard_LDADD = $(TEST_LIBS) test_keyboard_LDFLAGS = -no-install +test_device_SOURCES = device.c +test_device_LDADD = $(TEST_LIBS) +test_device_LDFLAGS = -no-install + # build-test only test_build_pedantic_c99_SOURCES = build-pedantic.c test_build_pedantic_c99_CFLAGS = -std=c99 -pedantic -Werror diff --git a/test/device.c b/test/device.c new file mode 100644 index 00000000..12947409 --- /dev/null +++ b/test/device.c @@ -0,0 +1,275 @@ +/* + * Copyright © 2014 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 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. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR 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. + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "litest.h" + +START_TEST(device_sendevents_config) +{ + struct litest_device *dev = litest_current_device(); + struct libinput_device *device; + uint32_t modes; + + device = dev->libinput_device; + + modes = libinput_device_config_send_events_get_modes(device); + ck_assert_int_eq(modes, + LIBINPUT_CONFIG_SEND_EVENTS_ENABLED| + LIBINPUT_CONFIG_SEND_EVENTS_DISABLED); +} +END_TEST + +START_TEST(device_sendevents_config_default) +{ + struct litest_device *dev = litest_current_device(); + struct libinput_device *device; + uint32_t mode; + + device = dev->libinput_device; + + mode = libinput_device_config_send_events_get_mode(device); + ck_assert_int_eq(mode, + LIBINPUT_CONFIG_SEND_EVENTS_ENABLED); + + mode = libinput_device_config_send_events_get_default_mode(device); + ck_assert_int_eq(mode, + LIBINPUT_CONFIG_SEND_EVENTS_ENABLED); +} +END_TEST + +START_TEST(device_disable) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_device *device; + enum libinput_config_status status; + + device = dev->libinput_device; + + litest_drain_events(li); + + status = libinput_device_config_send_events_set_mode(device, + LIBINPUT_CONFIG_SEND_EVENTS_DISABLED); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); + + /* no event from disabling */ + litest_assert_empty_queue(li); + + /* no event from disabled device */ + litest_event(dev, EV_REL, REL_X, 10); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_assert_empty_queue(li); + + /* no event from resuming */ + status = libinput_device_config_send_events_set_mode(device, + LIBINPUT_CONFIG_SEND_EVENTS_ENABLED); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(device_disable_events_pending) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_device *device; + enum libinput_config_status status; + struct libinput_event *event; + int i; + + device = dev->libinput_device; + + litest_drain_events(li); + + /* put a couple of events in the queue, enough to + feed the ptraccel trackers */ + for (i = 0; i < 10; i++) { + litest_event(dev, EV_REL, REL_X, 10); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + } + libinput_dispatch(li); + + status = libinput_device_config_send_events_set_mode(device, + LIBINPUT_CONFIG_SEND_EVENTS_DISABLED); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); + + /* expect above events */ + litest_wait_for_event(li); + while ((event = libinput_get_event(li)) != NULL) { + ck_assert_int_eq(libinput_event_get_type(event), + LIBINPUT_EVENT_POINTER_MOTION); + libinput_event_destroy(event); + } +} +END_TEST + +START_TEST(device_double_disable) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_device *device; + enum libinput_config_status status; + + device = dev->libinput_device; + + litest_drain_events(li); + + status = libinput_device_config_send_events_set_mode(device, + LIBINPUT_CONFIG_SEND_EVENTS_DISABLED); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); + + status = libinput_device_config_send_events_set_mode(device, + LIBINPUT_CONFIG_SEND_EVENTS_DISABLED); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); + + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(device_double_enable) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_device *device; + enum libinput_config_status status; + + device = dev->libinput_device; + + litest_drain_events(li); + + status = libinput_device_config_send_events_set_mode(device, + LIBINPUT_CONFIG_SEND_EVENTS_ENABLED); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); + + status = libinput_device_config_send_events_set_mode(device, + LIBINPUT_CONFIG_SEND_EVENTS_ENABLED); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); + + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(device_reenable_syspath_changed) +{ + struct libinput *li; + struct litest_device *litest_device; + struct libinput_device *device1, *device2; + enum libinput_config_status status; + struct libinput_event *event; + + li = litest_create_context(); + litest_device = litest_add_device(li, LITEST_MOUSE); + device1 = litest_device->libinput_device; + + libinput_device_ref(device1); + status = libinput_device_config_send_events_set_mode(device1, + LIBINPUT_CONFIG_SEND_EVENTS_DISABLED); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); + + litest_drain_events(li); + + litest_delete_device(litest_device); + litest_drain_events(li); + + litest_device = litest_add_device(li, LITEST_MOUSE); + device2 = litest_device->libinput_device; + ck_assert_str_eq(libinput_device_get_sysname(device1), + libinput_device_get_sysname(device2)); + + status = libinput_device_config_send_events_set_mode(device1, + LIBINPUT_CONFIG_SEND_EVENTS_ENABLED); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); + + /* can't really check for much here, other than that if we pump + events through libinput, none of them should be from the first + device */ + litest_event(litest_device, EV_REL, REL_X, 1); + litest_event(litest_device, EV_REL, REL_Y, 1); + litest_event(litest_device, EV_SYN, SYN_REPORT, 0); + + libinput_dispatch(li); + while ((event = libinput_get_event(li))) { + ck_assert(libinput_event_get_device(event) != device1); + libinput_event_destroy(event); + } + + litest_delete_device(litest_device); + libinput_device_unref(device1); + libinput_unref(li); +} +END_TEST + +START_TEST(device_reenable_device_removed) +{ + struct libinput *li; + struct litest_device *litest_device; + struct libinput_device *device; + enum libinput_config_status status; + + li = litest_create_context(); + litest_device = litest_add_device(li, LITEST_MOUSE); + device = litest_device->libinput_device; + + libinput_device_ref(device); + status = libinput_device_config_send_events_set_mode(device, + LIBINPUT_CONFIG_SEND_EVENTS_DISABLED); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); + + litest_drain_events(li); + + litest_delete_device(litest_device); + litest_drain_events(li); + + status = libinput_device_config_send_events_set_mode(device, + LIBINPUT_CONFIG_SEND_EVENTS_ENABLED); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); + + /* can't really check for much here, this really just exercises the + code path. */ + litest_assert_empty_queue(li); + + libinput_device_unref(device); + libinput_unref(li); +} +END_TEST + +int main (int argc, char **argv) +{ + litest_add("device:sendevents", device_sendevents_config, LITEST_ANY, LITEST_TOUCHPAD); + litest_add("device:sendevents", device_sendevents_config_default, LITEST_ANY, LITEST_TOUCHPAD); + litest_add("device:sendevents", device_disable, LITEST_POINTER, LITEST_TOUCHPAD); + litest_add("device:sendevents", device_disable_events_pending, LITEST_POINTER, LITEST_TOUCHPAD); + litest_add("device:sendevents", device_double_disable, LITEST_ANY, LITEST_TOUCHPAD); + litest_add("device:sendevents", device_double_enable, LITEST_ANY, LITEST_TOUCHPAD); + litest_add_no_device("device:sendevents", device_reenable_syspath_changed); + litest_add_no_device("device:sendevents", device_reenable_device_removed); + + return litest_run(argc, argv); +} From bd7f7e8a08777ae3f9b9097a38ca666b948c9b3a Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 20 Aug 2014 11:11:28 +1000 Subject: [PATCH 70/91] evdev: drop the button count when releasing keys on remove We previously called this function only before device removal, so failing to update the button state didn't matter. To make this function generic for the device suspend/resume, we need to keep track of the button/key count properly. If we have a key down multiple times on suspend though, log a bug. The dispatch should release the keys before we even get here (functionality added in a subsequent patch). Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index 3a5d8742..44849691 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1210,20 +1210,29 @@ release_pressed_keys(struct evdev_device *device) return; for (code = 0; code < KEY_CNT; code++) { - if (get_key_down_count(device, code) > 0) { + int count = get_key_down_count(device, code); + + if (count > 1) { + log_bug_libinput(libinput, + "Key %d is down %d times.\n", + code, + count); + } + + while (get_key_down_count(device, code) > 0) { switch (get_key_type(code)) { case EVDEV_KEY_TYPE_NONE: break; case EVDEV_KEY_TYPE_KEY: - keyboard_notify_key( - &device->base, + evdev_keyboard_notify_key( + device, time, code, LIBINPUT_KEY_STATE_RELEASED); break; case EVDEV_KEY_TYPE_BUTTON: - pointer_notify_button( - &device->base, + evdev_pointer_notify_button( + device, time, code, LIBINPUT_BUTTON_STATE_RELEASED); From 49e630376b024ca86214497f0ee0bdd4229ba0e1 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 1 Sep 2014 17:05:49 +1000 Subject: [PATCH 71/91] touchpad: split handling the state into a separate function No functional changes Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index d831b836..7202a663 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -593,6 +593,15 @@ tp_post_events(struct tp_dispatch *tp, uint64_t time) pointer_notify_motion(&tp->device->base, time, dx, dy); } +static void +tp_handle_state(struct tp_dispatch *tp, + uint64_t time) +{ + tp_process_state(tp, time); + tp_post_events(tp, time); + tp_post_process_state(tp, time); +} + static void tp_process(struct evdev_dispatch *dispatch, struct evdev_device *device, @@ -613,9 +622,7 @@ tp_process(struct evdev_dispatch *dispatch, tp_process_key(tp, e, time); break; case EV_SYN: - tp_process_state(tp, time); - tp_post_events(tp, time); - tp_post_process_state(tp, time); + tp_handle_state(tp, time); break; } } From 45a7edb3fb08f10652c9157243c9191a9b55c9b2 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 1 Sep 2014 17:17:18 +1000 Subject: [PATCH 72/91] touchpad: hook up sendevents configuration We may be in the middle of a software button click or a tap, so make sure we go back to the device-neutral state by unwinding. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad-buttons.c | 10 ++++ src/evdev-mt-touchpad-tap.c | 6 +++ src/evdev-mt-touchpad.c | 91 +++++++++++++++++++++++++++++++++ src/evdev-mt-touchpad.h | 13 +++++ test/device.c | 10 ++-- 5 files changed, 125 insertions(+), 5 deletions(-) diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index f6235a0a..1dd89136 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -484,6 +484,16 @@ tp_process_button(struct tp_dispatch *tp, return 0; } +void +tp_release_all_buttons(struct tp_dispatch *tp, + uint64_t time) +{ + if (tp->buttons.state) { + tp->buttons.state = 0; + tp->queued |= TOUCHPAD_EVENT_BUTTON_RELEASE; + } +} + int tp_init_buttons(struct tp_dispatch *tp, struct evdev_device *device) diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index a19d51e2..8f055fc1 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -693,3 +693,9 @@ tp_destroy_tap(struct tp_dispatch *tp) { libinput_timer_cancel(&tp->tap.timer); } + +void +tp_release_all_taps(struct tp_dispatch *tp, uint64_t now) +{ + tp_tap_handle_timeout(now, tp); +} diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 7202a663..cfc41ecb 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -641,6 +641,41 @@ tp_destroy(struct evdev_dispatch *dispatch) free(tp); } +static void +tp_suspend(struct tp_dispatch *tp, struct evdev_device *device) +{ + uint64_t now = libinput_now(tp->device->base.seat->libinput); + struct tp_touch *t; + + /* Unroll the touchpad state. + * Release buttons first. If tp is a clickpad, the button event + * must come before the touch up. If it isn't, the order doesn't + * matter anyway + * + * Then cancel all timeouts on the taps, triggering the last set + * of events. + * + * Then lift all touches so the touchpad is in a neutral state. + * + */ + tp_release_all_buttons(tp, now); + tp_release_all_taps(tp, now); + + tp_for_each_touch(tp, t) { + tp_end_touch(tp, t, now); + } + + tp_handle_state(tp, now); + + evdev_device_suspend(device); +} + +static void +tp_resume(struct tp_dispatch *tp, struct evdev_device *device) +{ + evdev_device_resume(device); +} + static struct evdev_dispatch_interface tp_interface = { tp_process, tp_destroy @@ -836,6 +871,54 @@ tp_init(struct tp_dispatch *tp, return 0; } +static uint32_t +tp_sendevents_get_modes(struct libinput_device *device) +{ + return LIBINPUT_CONFIG_SEND_EVENTS_ENABLED | + LIBINPUT_CONFIG_SEND_EVENTS_DISABLED; +} + +static enum libinput_config_status +tp_sendevents_set_mode(struct libinput_device *device, + enum libinput_config_send_events_mode mode) +{ + struct evdev_device *evdev = (struct evdev_device*)device; + struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch; + + if (mode == tp->sendevents.current_mode) + return LIBINPUT_CONFIG_STATUS_SUCCESS; + + switch(mode) { + case LIBINPUT_CONFIG_SEND_EVENTS_ENABLED: + tp_resume(tp, evdev); + break; + case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED: + tp_suspend(tp, evdev); + break; + default: + return LIBINPUT_CONFIG_STATUS_UNSUPPORTED; + } + + tp->sendevents.current_mode = mode; + + return LIBINPUT_CONFIG_STATUS_SUCCESS; +} + +static enum libinput_config_send_events_mode +tp_sendevents_get_mode(struct libinput_device *device) +{ + struct evdev_device *evdev = (struct evdev_device*)device; + struct tp_dispatch *dispatch = (struct tp_dispatch*)evdev->dispatch; + + return dispatch->sendevents.current_mode; +} + +static enum libinput_config_send_events_mode +tp_sendevents_get_default_mode(struct libinput_device *device) +{ + return LIBINPUT_CONFIG_SEND_EVENTS_ENABLED; +} + struct evdev_dispatch * evdev_mt_touchpad_create(struct evdev_device *device) { @@ -850,5 +933,13 @@ evdev_mt_touchpad_create(struct evdev_device *device) return NULL; } + device->base.config.sendevents = &tp->sendevents.config; + + tp->sendevents.current_mode = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED; + tp->sendevents.config.get_modes = tp_sendevents_get_modes; + tp->sendevents.config.set_mode = tp_sendevents_set_mode; + tp->sendevents.config.get_mode = tp_sendevents_get_mode; + tp->sendevents.config.get_default_mode = tp_sendevents_get_default_mode; + return &tp->base; } diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 83edf4f2..6988b790 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -217,6 +217,11 @@ struct tp_dispatch { int32_t right_edge; int32_t left_edge; } palm; + + struct { + struct libinput_device_config_send_events config; + enum libinput_config_send_events_mode current_mode; + } sendevents; }; #define tp_for_each_touch(_tp, _t) \ @@ -248,6 +253,10 @@ tp_process_button(struct tp_dispatch *tp, const struct input_event *e, uint64_t time); +void +tp_release_all_buttons(struct tp_dispatch *tp, + uint64_t time); + int tp_post_button_events(struct tp_dispatch *tp, uint64_t time); @@ -260,4 +269,8 @@ tp_button_touch_active(struct tp_dispatch *tp, struct tp_touch *t); bool tp_button_is_inside_softbutton_area(struct tp_dispatch *tp, struct tp_touch *t); +void +tp_release_all_taps(struct tp_dispatch *tp, + uint64_t time); + #endif diff --git a/test/device.c b/test/device.c index 12947409..8db69fdd 100644 --- a/test/device.c +++ b/test/device.c @@ -262,12 +262,12 @@ END_TEST int main (int argc, char **argv) { - litest_add("device:sendevents", device_sendevents_config, LITEST_ANY, LITEST_TOUCHPAD); - litest_add("device:sendevents", device_sendevents_config_default, LITEST_ANY, LITEST_TOUCHPAD); - litest_add("device:sendevents", device_disable, LITEST_POINTER, LITEST_TOUCHPAD); + litest_add("device:sendevents", device_sendevents_config, LITEST_ANY, LITEST_ANY); + litest_add("device:sendevents", device_sendevents_config_default, LITEST_ANY, LITEST_ANY); + litest_add("device:sendevents", device_disable, LITEST_POINTER, LITEST_ANY); litest_add("device:sendevents", device_disable_events_pending, LITEST_POINTER, LITEST_TOUCHPAD); - litest_add("device:sendevents", device_double_disable, LITEST_ANY, LITEST_TOUCHPAD); - litest_add("device:sendevents", device_double_enable, LITEST_ANY, LITEST_TOUCHPAD); + litest_add("device:sendevents", device_double_disable, LITEST_ANY, LITEST_ANY); + litest_add("device:sendevents", device_double_enable, LITEST_ANY, LITEST_ANY); litest_add_no_device("device:sendevents", device_reenable_syspath_changed); litest_add_no_device("device:sendevents", device_reenable_device_removed); From 195a02c7a409fa4b735102788d1c0a010d276356 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 20 Aug 2014 10:32:29 +1000 Subject: [PATCH 73/91] test: test for release events on device suspend Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/device.c | 239 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 239 insertions(+) diff --git a/test/device.c b/test/device.c index 8db69fdd..cf206329 100644 --- a/test/device.c +++ b/test/device.c @@ -30,6 +30,7 @@ #include #include "litest.h" +#include "libinput-util.h" START_TEST(device_sendevents_config) { @@ -95,6 +96,39 @@ START_TEST(device_disable) } END_TEST +START_TEST(device_disable_touchpad) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_device *device; + enum libinput_config_status status; + + device = dev->libinput_device; + + litest_drain_events(li); + + status = libinput_device_config_send_events_set_mode(device, + LIBINPUT_CONFIG_SEND_EVENTS_DISABLED); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); + + /* no event from disabling */ + litest_assert_empty_queue(li); + + litest_touch_down(dev, 0, 50, 50); + litest_touch_move_to(dev, 0, 50, 50, 90, 90, 10); + litest_touch_up(dev, 0); + + + litest_assert_empty_queue(li); + + /* no event from resuming */ + status = libinput_device_config_send_events_set_mode(device, + LIBINPUT_CONFIG_SEND_EVENTS_ENABLED); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); + litest_assert_empty_queue(li); +} +END_TEST + START_TEST(device_disable_events_pending) { struct litest_device *dev = litest_current_device(); @@ -260,16 +294,221 @@ START_TEST(device_reenable_device_removed) } END_TEST +START_TEST(device_disable_release_buttons) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_device *device; + struct libinput_event *event; + struct libinput_event_pointer *ptrevent; + enum libinput_config_status status; + + device = dev->libinput_device; + + litest_button_click(dev, BTN_LEFT, true); + litest_drain_events(li); + litest_assert_empty_queue(li); + + status = libinput_device_config_send_events_set_mode(device, + LIBINPUT_CONFIG_SEND_EVENTS_DISABLED); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); + + litest_wait_for_event(li); + event = libinput_get_event(li); + + ck_assert_int_eq(libinput_event_get_type(event), + LIBINPUT_EVENT_POINTER_BUTTON); + ptrevent = libinput_event_get_pointer_event(event); + ck_assert_int_eq(libinput_event_pointer_get_button(ptrevent), + BTN_LEFT); + ck_assert_int_eq(libinput_event_pointer_get_button_state(ptrevent), + LIBINPUT_BUTTON_STATE_RELEASED); + + libinput_event_destroy(event); + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(device_disable_release_keys) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_device *device; + struct libinput_event *event; + struct libinput_event_keyboard *kbdevent; + enum libinput_config_status status; + + device = dev->libinput_device; + + litest_button_click(dev, KEY_A, true); + litest_drain_events(li); + litest_assert_empty_queue(li); + + status = libinput_device_config_send_events_set_mode(device, + LIBINPUT_CONFIG_SEND_EVENTS_DISABLED); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); + + litest_wait_for_event(li); + event = libinput_get_event(li); + + ck_assert_int_eq(libinput_event_get_type(event), + LIBINPUT_EVENT_KEYBOARD_KEY); + kbdevent = libinput_event_get_keyboard_event(event); + ck_assert_int_eq(libinput_event_keyboard_get_key(kbdevent), + KEY_A); + ck_assert_int_eq(libinput_event_keyboard_get_key_state(kbdevent), + LIBINPUT_KEY_STATE_RELEASED); + + libinput_event_destroy(event); + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(device_disable_release_tap) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_device *device; + enum libinput_config_status status; + + device = dev->libinput_device; + + libinput_device_config_tap_set_enabled(device, + LIBINPUT_CONFIG_TAP_ENABLED); + + litest_drain_events(li); + + litest_touch_down(dev, 0, 50, 50); + litest_touch_up(dev, 0); + + libinput_dispatch(li); + + status = libinput_device_config_send_events_set_mode(device, + LIBINPUT_CONFIG_SEND_EVENTS_DISABLED); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); + /* tap happened before suspending, so we still expect the event */ + + msleep(300); /* tap-n-drag timeout */ + + 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); + + /* resume, make sure we don't get anything */ + status = libinput_device_config_send_events_set_mode(device, + LIBINPUT_CONFIG_SEND_EVENTS_ENABLED); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); + libinput_dispatch(li); + litest_assert_empty_queue(li); + +} +END_TEST + +START_TEST(device_disable_release_tap_n_drag) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_device *device; + enum libinput_config_status status; + + device = dev->libinput_device; + + libinput_device_config_tap_set_enabled(device, + LIBINPUT_CONFIG_TAP_ENABLED); + + litest_drain_events(li); + + litest_touch_down(dev, 0, 50, 50); + litest_touch_up(dev, 0); + litest_touch_down(dev, 0, 50, 50); + libinput_dispatch(li); + msleep(400); /* tap-n-drag timeout */ + libinput_dispatch(li); + + status = libinput_device_config_send_events_set_mode(device, + LIBINPUT_CONFIG_SEND_EVENTS_DISABLED); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); + + libinput_dispatch(li); + litest_touch_up(dev, 0); + + 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); +} +END_TEST + + +START_TEST(device_disable_release_softbutton) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_device *device; + enum libinput_config_status status; + + device = dev->libinput_device; + + litest_drain_events(li); + + litest_touch_down(dev, 0, 90, 90); + litest_button_click(dev, BTN_LEFT, true); + + /* make sure softbutton works */ + litest_assert_button_event(li, + BTN_RIGHT, + LIBINPUT_BUTTON_STATE_PRESSED); + /* disable */ + status = libinput_device_config_send_events_set_mode(device, + LIBINPUT_CONFIG_SEND_EVENTS_DISABLED); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); + + litest_assert_button_event(li, + BTN_RIGHT, + LIBINPUT_BUTTON_STATE_RELEASED); + + litest_assert_empty_queue(li); + + litest_button_click(dev, BTN_LEFT, false); + litest_touch_up(dev, 0); + + litest_assert_empty_queue(li); + + /* resume, make sure we don't get anything */ + status = libinput_device_config_send_events_set_mode(device, + LIBINPUT_CONFIG_SEND_EVENTS_ENABLED); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); + libinput_dispatch(li); + litest_assert_empty_queue(li); + +} +END_TEST + int main (int argc, char **argv) { litest_add("device:sendevents", device_sendevents_config, LITEST_ANY, LITEST_ANY); litest_add("device:sendevents", device_sendevents_config_default, LITEST_ANY, LITEST_ANY); litest_add("device:sendevents", device_disable, LITEST_POINTER, LITEST_ANY); + litest_add("device:sendevents", device_disable_touchpad, LITEST_TOUCHPAD, LITEST_ANY); litest_add("device:sendevents", device_disable_events_pending, LITEST_POINTER, LITEST_TOUCHPAD); litest_add("device:sendevents", device_double_disable, LITEST_ANY, LITEST_ANY); litest_add("device:sendevents", device_double_enable, LITEST_ANY, LITEST_ANY); litest_add_no_device("device:sendevents", device_reenable_syspath_changed); litest_add_no_device("device:sendevents", device_reenable_device_removed); + litest_add_for_device("device:sendevents", device_disable_release_buttons, LITEST_MOUSE); + litest_add_for_device("device:sendevents", device_disable_release_keys, LITEST_KEYBOARD); + litest_add("device:sendevents", device_disable_release_tap, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("device:sendevents", device_disable_release_tap_n_drag, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("device:sendevents", device_disable_release_softbutton, LITEST_CLICKPAD, LITEST_APPLE_CLICKPAD); return litest_run(argc, argv); } From 1c8a5ca65960da9d105cc06dd54a93790a907585 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 3 Sep 2014 14:34:52 +1000 Subject: [PATCH 74/91] evdev: add a internal device notification mechanism When a device is added or removed, notify all internal devices about the device change. This allows all devices to configure themselves depending on other devices in the system. Prime use-case here is an internal touchpad that wants to know if an external mouse is connected. On device added, notification goes both ways: existing devices are notified about the new device, and the new device is notified about existing devices. On device removed, notification only goes one way. In both cases, the internal notification is complete before the event is sent to the caller. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad.c | 4 +++- src/evdev.c | 37 +++++++++++++++++++++++++++++++++++-- src/evdev.h | 8 ++++++++ 3 files changed, 46 insertions(+), 3 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index cfc41ecb..62becbaa 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -678,7 +678,9 @@ tp_resume(struct tp_dispatch *tp, struct evdev_device *device) static struct evdev_dispatch_interface tp_interface = { tp_process, - tp_destroy + tp_destroy, + NULL, /* device_added */ + NULL, /* device_removed */ }; static void diff --git a/src/evdev.c b/src/evdev.c index 44849691..638493fb 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -621,7 +621,9 @@ evdev_calibration_get_default_matrix(struct libinput_device *libinput_device, struct evdev_dispatch_interface fallback_interface = { fallback_process, - fallback_destroy + fallback_destroy, + NULL, /* device_added */ + NULL, /* device_removed */ }; static uint32_t @@ -968,6 +970,26 @@ evdev_configure_device(struct evdev_device *device) return 0; } +static void +evdev_notify_added_device(struct evdev_device *device) +{ + struct libinput_device *dev; + + list_for_each(dev, &device->base.seat->devices_list, link) { + struct evdev_device *d = (struct evdev_device*)dev; + if (dev == &device->base) + continue; + + if (d->dispatch->interface->device_added) + d->dispatch->interface->device_added(d, device); + + if (device->dispatch->interface->device_added) + device->dispatch->interface->device_added(device, d); + } + + notify_added_device(&device->base); +} + struct evdev_device * evdev_device_create(struct libinput_seat *seat, const char *devnode, @@ -1042,7 +1064,7 @@ evdev_device_create(struct libinput_seat *seat, goto err; list_insert(seat->devices_list.prev, &device->base.link); - notify_added_device(&device->base); + evdev_notify_added_device(device); return device; @@ -1341,6 +1363,17 @@ evdev_device_resume(struct evdev_device *device) void evdev_device_remove(struct evdev_device *device) { + struct libinput_device *dev; + + list_for_each(dev, &device->base.seat->devices_list, link) { + struct evdev_device *d = (struct evdev_device*)dev;; + if (dev == &device->base) + continue; + + if (d->dispatch->interface->device_removed) + d->dispatch->interface->device_removed(d, device); + } + evdev_device_suspend(device); /* A device may be removed while suspended. Free the syspath to diff --git a/src/evdev.h b/src/evdev.h index bdd4a4e5..74053c5d 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -120,6 +120,14 @@ struct evdev_dispatch_interface { /* Destroy an event dispatch handler and free all its resources. */ void (*destroy)(struct evdev_dispatch *dispatch); + + /* A new device was added */ + void (*device_added)(struct evdev_device *device, + struct evdev_device *added_device); + + /* A device was removed */ + void (*device_removed)(struct evdev_device *device, + struct evdev_device *removed_device); }; struct evdev_dispatch { From cc44e747c7bd182c908e3c00b2dd0f32649d5761 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 3 Sep 2014 15:50:28 +1000 Subject: [PATCH 75/91] evdev: add internal tagging system For conditional touchpad disabling we need two pieces of knowledge: is the device an internal touchpad and is another device an external mouse-like device. For that use-case it's enough to tag any device that's on USB and Bluetooth with pointer capabilities as external mouse. A more complex can be done when needed. The tag function is part of the dispatch interface (to save on udev code) and called before the caller is notified about the new device, i.e. the device is fully configured by the time it needs to be tagged, and other devices can rely on the tags being assigned by the time they get notified about the new device. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad.c | 1 + src/evdev.c | 44 +++++++++++++++++++++++++++++++++++++++++ src/evdev.h | 10 ++++++++++ 3 files changed, 55 insertions(+) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 62becbaa..c6bfd1d9 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -681,6 +681,7 @@ static struct evdev_dispatch_interface tp_interface = { tp_destroy, NULL, /* device_added */ NULL, /* device_removed */ + NULL, /* tag_device */ }; static void diff --git a/src/evdev.c b/src/evdev.c index 638493fb..f7fd2b4f 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -24,6 +24,7 @@ #include "config.h" #include +#include #include #include #include "linux/input.h" @@ -545,6 +546,19 @@ evdev_need_touch_frame(struct evdev_device *device) return 0; } +static void +evdev_tag_external_mouse(struct evdev_device *device, + struct udev_device *udev_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; + } +} + static void fallback_process(struct evdev_dispatch *dispatch, struct evdev_device *device, @@ -578,6 +592,13 @@ 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); +} + static int evdev_calibration_has_matrix(struct libinput_device *libinput_device) { @@ -624,6 +645,7 @@ struct evdev_dispatch_interface fallback_interface = { fallback_destroy, NULL, /* device_added */ NULL, /* device_removed */ + fallback_tag_device, }; static uint32_t @@ -792,6 +814,7 @@ configure_pointer_acceleration(struct evdev_device *device) return 0; } + static inline int evdev_need_mtdev(struct evdev_device *device) { @@ -802,6 +825,25 @@ 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) +{ + struct udev *udev; + struct udev_device *udev_device = NULL; + + udev = udev_new(); + if (!udev) + return; + + udev_device = udev_device_new_from_syspath(udev, device->syspath); + if (udev_device) { + if (device->dispatch->interface->tag_device) + device->dispatch->interface->tag_device(device, udev_device); + udev_device_unref(udev_device); + } + udev_unref(udev); +} + static int evdev_configure_device(struct evdev_device *device) { @@ -1064,6 +1106,8 @@ evdev_device_create(struct libinput_seat *seat, goto err; 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 74053c5d..f5345fa3 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -48,6 +48,11 @@ enum evdev_device_seat_capability { EVDEV_DEVICE_TOUCH = (1 << 2) }; +enum evdev_device_tags { + EVDEV_TAG_EXTERNAL_MOUSE = (1 << 0), + EVDEV_TAG_INTERNAL_TOUCHPAD = (1 << 1), +}; + struct mt_slot { int32_t seat_slot; int32_t x, y; @@ -92,6 +97,7 @@ struct evdev_device { enum evdev_event_type pending_event; enum evdev_device_seat_capability seat_caps; + enum evdev_device_tags tags; int is_mt; @@ -128,6 +134,10 @@ struct evdev_dispatch_interface { /* A device was removed */ void (*device_removed)(struct evdev_device *device, struct evdev_device *removed_device); + + /* Tag device with one of EVDEV_TAG */ + void (*tag_device)(struct evdev_device *device, + struct udev_device *udev_device); }; struct evdev_dispatch { From 490ec84e73712f352ca3be983eb2f8ec7160c73d Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 3 Sep 2014 15:45:57 +1000 Subject: [PATCH 76/91] touchpad: implement conditional disabling Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad.c | 89 ++++++++++++++++++++++++++++++++++++++--- test/device.c | 19 ++++++++- 2 files changed, 102 insertions(+), 6 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index c6bfd1d9..d9f6fd7e 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -676,12 +676,67 @@ tp_resume(struct tp_dispatch *tp, struct evdev_device *device) evdev_device_resume(device); } +static void +tp_device_added(struct evdev_device *device, + struct evdev_device *added_device) +{ + struct tp_dispatch *tp = (struct tp_dispatch*)device->dispatch; + + if (tp->sendevents.current_mode != + LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE) + return; + + if (added_device->tags & EVDEV_TAG_EXTERNAL_MOUSE) + tp_suspend(tp, device); +} + +static void +tp_device_removed(struct evdev_device *device, + struct evdev_device *removed_device) +{ + struct tp_dispatch *tp = (struct tp_dispatch*)device->dispatch; + struct libinput_device *dev; + + if (tp->sendevents.current_mode != + LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE) + return; + + list_for_each(dev, &device->base.seat->devices_list, link) { + struct evdev_device *d = (struct evdev_device*)dev; + if (d != removed_device && + (d->tags & EVDEV_TAG_EXTERNAL_MOUSE)) { + return; + } + } + + tp_resume(tp, device); +} + +static void +tp_tag_device(struct evdev_device *device, + struct udev_device *udev_device) +{ + int bustype; + + /* simple approach: touchpads on USB or Bluetooth are considered + * external, anything else is internal. Exception is Apple - + * internal touchpads are connected over USB and it doesn't have + * external USB touchpads anyway. + */ + bustype = libevdev_get_id_bustype(device->evdev); + if (bustype == BUS_USB) { + if (libevdev_get_id_vendor(device->evdev) == VENDOR_ID_APPLE) + device->tags |= EVDEV_TAG_INTERNAL_TOUCHPAD; + } else if (bustype != BUS_BLUETOOTH) + device->tags |= EVDEV_TAG_INTERNAL_TOUCHPAD; +} + static struct evdev_dispatch_interface tp_interface = { tp_process, tp_destroy, - NULL, /* device_added */ - NULL, /* device_removed */ - NULL, /* tag_device */ + tp_device_added, + tp_device_removed, + tp_tag_device, }; static void @@ -877,8 +932,29 @@ tp_init(struct tp_dispatch *tp, static uint32_t tp_sendevents_get_modes(struct libinput_device *device) { - return LIBINPUT_CONFIG_SEND_EVENTS_ENABLED | - LIBINPUT_CONFIG_SEND_EVENTS_DISABLED; + struct evdev_device *evdev = (struct evdev_device*)device; + uint32_t modes = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED | + LIBINPUT_CONFIG_SEND_EVENTS_DISABLED; + + if (evdev->tags & EVDEV_TAG_INTERNAL_TOUCHPAD) + modes |= LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE; + + return modes; +} + +static void +tp_suspend_conditional(struct tp_dispatch *tp, + struct evdev_device *device) +{ + struct libinput_device *dev; + + list_for_each(dev, &device->base.seat->devices_list, link) { + struct evdev_device *d = (struct evdev_device*)dev; + if (d->tags & EVDEV_TAG_EXTERNAL_MOUSE) { + tp_suspend(tp, device); + return; + } + } } static enum libinput_config_status @@ -898,6 +974,9 @@ tp_sendevents_set_mode(struct libinput_device *device, case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED: tp_suspend(tp, evdev); break; + case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE: + tp_suspend_conditional(tp, evdev); + break; default: return LIBINPUT_CONFIG_STATUS_UNSUPPORTED; } diff --git a/test/device.c b/test/device.c index cf206329..3f7ec4cd 100644 --- a/test/device.c +++ b/test/device.c @@ -47,6 +47,22 @@ START_TEST(device_sendevents_config) } END_TEST +START_TEST(device_sendevents_config_touchpad) +{ + struct litest_device *dev = litest_current_device(); + struct libinput_device *device; + uint32_t modes; + + device = dev->libinput_device; + + modes = libinput_device_config_send_events_get_modes(device); + ck_assert_int_eq(modes, + LIBINPUT_CONFIG_SEND_EVENTS_ENABLED| + LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE| + LIBINPUT_CONFIG_SEND_EVENTS_DISABLED); +} +END_TEST + START_TEST(device_sendevents_config_default) { struct litest_device *dev = litest_current_device(); @@ -495,7 +511,8 @@ END_TEST int main (int argc, char **argv) { - litest_add("device:sendevents", device_sendevents_config, LITEST_ANY, LITEST_ANY); + litest_add("device:sendevents", device_sendevents_config, LITEST_ANY, LITEST_TOUCHPAD); + litest_add("device:sendevents", device_sendevents_config_touchpad, LITEST_TOUCHPAD, LITEST_ANY); litest_add("device:sendevents", device_sendevents_config_default, LITEST_ANY, LITEST_ANY); litest_add("device:sendevents", device_disable, LITEST_POINTER, LITEST_ANY); litest_add("device:sendevents", device_disable_touchpad, LITEST_TOUCHPAD, LITEST_ANY); From 19648e29aba9063301bca4dfb4202dfdee24392c Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 4 Sep 2014 12:55:02 +1000 Subject: [PATCH 77/91] touchpad: move softbutton initialization to separate function No functional changes Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad-buttons.c | 77 ++++++++++++++++++++------------- src/evdev-mt-touchpad.h | 3 ++ 2 files changed, 50 insertions(+), 30 deletions(-) diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index 1dd89136..02d3205a 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -494,6 +494,52 @@ tp_release_all_buttons(struct tp_dispatch *tp, } } +void +tp_init_softbuttons(struct tp_dispatch *tp, + struct evdev_device *device) +{ + int width, height; + const struct input_absinfo *absinfo_x, *absinfo_y; + int xoffset, yoffset; + int yres; + + absinfo_x = device->abs.absinfo_x; + absinfo_y = device->abs.absinfo_y; + + xoffset = absinfo_x->minimum, + yoffset = absinfo_y->minimum; + yres = absinfo_y->resolution; + width = abs(absinfo_x->maximum - absinfo_x->minimum); + height = abs(absinfo_y->maximum - absinfo_y->minimum); + + /* button height: 10mm or 15% of the touchpad height, + whichever is smaller */ + if (yres > 1 && (height * 0.15/yres) > 10) { + tp->buttons.bottom_area.top_edge = + absinfo_y->maximum - 10 * yres; + } else { + tp->buttons.bottom_area.top_edge = height * .85 + yoffset; + } + + tp->buttons.bottom_area.rightbutton_left_edge = width/2 + xoffset; + + if (tp->buttons.has_topbuttons) { + /* T440s has the top button line 5mm from the top, + event analysis has shown events to start down to ~10mm + from the top - which maps to 15% */ + if (yres > 1) { + tp->buttons.top_area.bottom_edge = + yoffset + 10 * yres; + } else { + tp->buttons.top_area.bottom_edge = height * .15 + yoffset; + } + tp->buttons.top_area.rightbutton_left_edge = width * .58 + xoffset; + tp->buttons.top_area.leftbutton_right_edge = width * .42 + xoffset; + } else { + tp->buttons.top_area.bottom_edge = INT_MIN; + } +} + int tp_init_buttons(struct tp_dispatch *tp, struct evdev_device *device) @@ -535,36 +581,7 @@ tp_init_buttons(struct tp_dispatch *tp, tp->buttons.use_clickfinger = true; if (tp->buttons.is_clickpad && !tp->buttons.use_clickfinger) { - int xoffset = absinfo_x->minimum, - yoffset = absinfo_y->minimum; - int yres = absinfo_y->resolution; - - /* button height: 10mm or 15% of the touchpad height, - whichever is smaller */ - if (yres > 1 && (height * 0.15/yres) > 10) { - tp->buttons.bottom_area.top_edge = - absinfo_y->maximum - 10 * yres; - } else { - tp->buttons.bottom_area.top_edge = height * .85 + yoffset; - } - - tp->buttons.bottom_area.rightbutton_left_edge = width/2 + xoffset; - - if (tp->buttons.has_topbuttons) { - /* T440s has the top button line 5mm from the top, - event analysis has shown events to start down to ~10mm - from the top - which maps to 15% */ - if (yres > 1) { - tp->buttons.top_area.bottom_edge = - yoffset + 10 * yres; - } else { - tp->buttons.top_area.bottom_edge = height * .15 + yoffset; - } - tp->buttons.top_area.rightbutton_left_edge = width * .58 + xoffset; - tp->buttons.top_area.leftbutton_right_edge = width * .42 + xoffset; - } else { - tp->buttons.top_area.bottom_edge = INT_MIN; - } + tp_init_softbuttons(tp, device); } else { tp->buttons.bottom_area.top_edge = INT_MAX; tp->buttons.top_area.bottom_edge = INT_MIN; diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 6988b790..b67b063e 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -245,6 +245,9 @@ tp_destroy_tap(struct tp_dispatch *tp); int tp_init_buttons(struct tp_dispatch *tp, struct evdev_device *device); +void +tp_init_softbuttons(struct tp_dispatch *tp, struct evdev_device *device); + void tp_destroy_buttons(struct tp_dispatch *tp); From 0f112646ceb24501058708489febed8854444349 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Sep 2014 16:22:35 +0200 Subject: [PATCH 78/91] evdev: Move generic scroll code from evdev-mt-touchpad.c to evdev.c So that it can be used for middle button trackpoint scrolling too. Signed-off-by: Hans de Goede Reviewed-by: Peter Hutterer Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad.c | 57 +++-------------------------------------- src/evdev-mt-touchpad.h | 4 --- src/evdev.c | 49 +++++++++++++++++++++++++++++++++++ src/evdev.h | 15 +++++++++++ 4 files changed, 67 insertions(+), 58 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index d9f6fd7e..d1e4acd4 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -492,47 +492,7 @@ tp_post_twofinger_scroll(struct tp_dispatch *tp, uint64_t time) dy /= nchanged; tp_filter_motion(tp, &dx, &dy, time); - - /* Require at least five px scrolling to start */ - if (dy <= -5.0 || dy >= 5.0) - tp->scroll.direction |= (1 << LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL); - - if (dx <= -5.0 || dx >= 5.0) - tp->scroll.direction |= (1 << LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL); - - if (dy != 0.0 && - (tp->scroll.direction & (1 << LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL))) { - pointer_notify_axis(&tp->device->base, - time, - LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, - dy); - } - - if (dx != 0.0 && - (tp->scroll.direction & (1 << LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL))) { - pointer_notify_axis(&tp->device->base, - time, - LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, - dx); - } -} - -static void -tp_stop_scroll_events(struct tp_dispatch *tp, uint64_t time) -{ - /* terminate scrolling with a zero scroll event */ - if (tp->scroll.direction & (1 << LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)) - pointer_notify_axis(&tp->device->base, - time, - LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, - 0); - if (tp->scroll.direction & (1 << LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL)) - pointer_notify_axis(&tp->device->base, - time, - LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, - 0); - - tp->scroll.direction = 0; + evdev_post_scroll(tp->device, time, dx, dy); } static int @@ -548,7 +508,7 @@ tp_post_scroll_events(struct tp_dispatch *tp, uint64_t time) } if (nfingers_down != 2) { - tp_stop_scroll_events(tp, time); + evdev_stop_scroll(tp->device, time); return 0; } @@ -567,7 +527,7 @@ tp_post_events(struct tp_dispatch *tp, uint64_t time) consumed |= tp_post_button_events(tp, time); if (consumed) { - tp_stop_scroll_events(tp, time); + evdev_stop_scroll(tp->device, time); return; } @@ -846,14 +806,6 @@ tp_init_accel(struct tp_dispatch *tp, double diagonal) return 0; } -static int -tp_init_scroll(struct tp_dispatch *tp) -{ - tp->scroll.direction = 0; - - return 0; -} - static int tp_init_palmdetect(struct tp_dispatch *tp, struct evdev_device *device) @@ -909,9 +861,6 @@ tp_init(struct tp_dispatch *tp, tp->hysteresis.margin_y = diagonal / DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR; - if (tp_init_scroll(tp) != 0) - return -1; - if (tp_init_accel(tp, diagonal) != 0) return -1; diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index b67b063e..8671f797 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -200,10 +200,6 @@ struct tp_dispatch { } top_area; } buttons; /* physical buttons */ - struct { - enum libinput_pointer_axis direction; - } scroll; - enum touchpad_event queued; struct { diff --git a/src/evdev.c b/src/evdev.c index f7fd2b4f..45020bad 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1081,6 +1081,8 @@ evdev_device_create(struct libinput_seat *seat, device->fd = fd; device->pending_event = EVDEV_NONE; device->devname = libevdev_get_name(device->evdev); + device->scroll.threshold = 5.0; /* Default may be overridden */ + device->scroll.direction = 0; matrix_init_identity(&device->abs.calibration); matrix_init_identity(&device->abs.usermatrix); @@ -1265,6 +1267,53 @@ evdev_device_get_size(struct evdev_device *device, return 0; } +void +evdev_post_scroll(struct evdev_device *device, + uint64_t time, + double dx, + double dy) +{ + if (dy <= -device->scroll.threshold || dy >= device->scroll.threshold) + device->scroll.direction |= (1 << LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL); + + if (dx <= -device->scroll.threshold || dx >= device->scroll.threshold) + device->scroll.direction |= (1 << LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL); + + if (dy != 0.0 && + (device->scroll.direction & (1 << LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL))) { + pointer_notify_axis(&device->base, + time, + LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, + dy); + } + + if (dx != 0.0 && + (device->scroll.direction & (1 << LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL))) { + pointer_notify_axis(&device->base, + time, + LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, + dx); + } +} + +void +evdev_stop_scroll(struct evdev_device *device, uint64_t time) +{ + /* terminate scrolling with a zero scroll event */ + if (device->scroll.direction & (1 << LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)) + pointer_notify_axis(&device->base, + time, + LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, + 0); + if (device->scroll.direction & (1 << LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL)) + pointer_notify_axis(&device->base, + time, + LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, + 0); + + device->scroll.direction = 0; +} + static void release_pressed_keys(struct evdev_device *device) { diff --git a/src/evdev.h b/src/evdev.h index f5345fa3..311dddc8 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -95,6 +95,11 @@ struct evdev_device { int dx, dy; } rel; + struct { + double threshold; + uint32_t direction; + } scroll; + enum evdev_event_type pending_event; enum evdev_device_seat_capability seat_caps; enum evdev_device_tags tags; @@ -229,6 +234,16 @@ evdev_pointer_notify_button(struct evdev_device *device, int button, enum libinput_button_state state); +void +evdev_post_scroll(struct evdev_device *device, + uint64_t time, + double dx, + double dy); + + +void +evdev_stop_scroll(struct evdev_device *device, uint64_t time); + void evdev_device_remove(struct evdev_device *device); From 7e932ed33ffe707fb1c3d4bf8543be6c53549f11 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Sep 2014 16:22:36 +0200 Subject: [PATCH 79/91] evdev: Add middle button scrolling for trackpoints Most trackpoint users want to be able to scroll using the trackpoint with the middle button pressed, add support for this. Signed-off-by: Hans de Goede Reviewed-by: Peter Hutterer Signed-off-by: Peter Hutterer --- include/linux/input.h | 1 + src/evdev.c | 56 ++++++++++++++++++++++++++++++++++++++++ src/evdev.h | 5 ++++ test/litest-trackpoint.c | 2 ++ test/pointer.c | 4 ++- 5 files changed, 67 insertions(+), 1 deletion(-) diff --git a/include/linux/input.h b/include/linux/input.h index aa98ce75..39b550b5 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -163,6 +163,7 @@ struct input_keymap_entry { #define INPUT_PROP_BUTTONPAD 0x02 /* has button(s) under pad */ #define INPUT_PROP_SEMI_MT 0x03 /* touch rectangle only */ #define INPUT_PROP_TOPBUTTONPAD 0x04 /* softbuttons at top of pad */ +#define INPUT_PROP_POINTING_STICK 0x05 /* is a pointing stick */ #define INPUT_PROP_MAX 0x1f #define INPUT_PROP_CNT (INPUT_PROP_MAX + 1) diff --git a/src/evdev.c b/src/evdev.c index 45020bad..85e4d719 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -41,6 +41,7 @@ #include "libinput-private.h" #define DEFAULT_AXIS_STEP_DISTANCE 10 +#define DEFAULT_MIDDLE_BUTTON_SCROLL_TIMEOUT 200 enum evdev_key_type { EVDEV_KEY_TYPE_NONE, @@ -203,6 +204,15 @@ evdev_flush_pending_event(struct evdev_device *device, uint64_t time) device->rel.dx = 0; device->rel.dy = 0; + /* Use unaccelerated deltas for pointing stick scroll */ + if (device->scroll.has_middle_button_scroll && + hw_is_key_down(device, BTN_MIDDLE)) { + if (device->scroll.middle_button_scroll_active) + evdev_post_scroll(device, time, + motion.dx, motion.dy); + break; + } + /* Apply pointer acceleration. */ filter_dispatch(device->pointer.filter, &motion, device, time); @@ -345,6 +355,37 @@ get_key_type(uint16_t code) return EVDEV_KEY_TYPE_NONE; } +static void +evdev_middle_button_scroll_timeout(uint64_t time, void *data) +{ + struct evdev_device *device = data; + + device->scroll.middle_button_scroll_active = true; +} + +static void +evdev_middle_button_scroll_button(struct evdev_device *device, + uint64_t time, int is_press) +{ + if (is_press) { + libinput_timer_set(&device->scroll.timer, + time + DEFAULT_MIDDLE_BUTTON_SCROLL_TIMEOUT); + } else { + libinput_timer_cancel(&device->scroll.timer); + if (device->scroll.middle_button_scroll_active) { + evdev_stop_scroll(device, time); + device->scroll.middle_button_scroll_active = false; + } else { + /* If the button is released quickly enough emit the + * button press/release events. */ + evdev_pointer_notify_button(device, time, BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); + evdev_pointer_notify_button(device, time, BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_RELEASED); + } + } +} + static void evdev_process_touch_button(struct evdev_device *device, uint64_t time, int value) @@ -405,6 +446,12 @@ evdev_process_key(struct evdev_device *device, LIBINPUT_KEY_STATE_RELEASED); break; case EVDEV_KEY_TYPE_BUTTON: + if (device->scroll.has_middle_button_scroll && + e->code == BTN_MIDDLE) { + evdev_middle_button_scroll_button(device, time, + e->value); + break; + } evdev_pointer_notify_button( device, time, @@ -946,6 +993,15 @@ evdev_configure_device(struct evdev_device *device) device->mt.slot = active_slot; } } + + if (libevdev_has_property(evdev, INPUT_PROP_POINTING_STICK)) { + libinput_timer_init(&device->scroll.timer, + device->base.seat->libinput, + evdev_middle_button_scroll_timeout, + device); + device->scroll.has_middle_button_scroll = true; + } + if (libevdev_has_event_code(evdev, EV_REL, REL_X) || libevdev_has_event_code(evdev, EV_REL, REL_Y)) has_rel = 1; diff --git a/src/evdev.h b/src/evdev.h index 311dddc8..e1506d22 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -26,10 +26,12 @@ #include "config.h" +#include #include "linux/input.h" #include #include "libinput-private.h" +#include "timer.h" enum evdev_event_type { EVDEV_NONE, @@ -96,6 +98,9 @@ struct evdev_device { } rel; struct { + struct libinput_timer timer; + bool has_middle_button_scroll; + bool middle_button_scroll_active; double threshold; uint32_t direction; } scroll; diff --git a/test/litest-trackpoint.c b/test/litest-trackpoint.c index 25a377c4..40b9ed07 100644 --- a/test/litest-trackpoint.c +++ b/test/litest-trackpoint.c @@ -49,6 +49,8 @@ static int events[] = { EV_KEY, BTN_MIDDLE, EV_REL, REL_X, EV_REL, REL_Y, + INPUT_PROP_MAX, INPUT_PROP_POINTER, + INPUT_PROP_MAX, INPUT_PROP_POINTING_STICK, -1, -1, }; diff --git a/test/pointer.c b/test/pointer.c index 82c52459..f704372e 100644 --- a/test/pointer.c +++ b/test/pointer.c @@ -132,7 +132,9 @@ START_TEST(pointer_button) test_button_event(dev, BTN_RIGHT, 0); } - if (libevdev_has_event_code(dev->evdev, EV_KEY, BTN_MIDDLE)) { + /* Skip middle button test on trackpoints (used for scrolling) */ + if (!libevdev_has_property(dev->evdev, INPUT_PROP_POINTING_STICK) && + libevdev_has_event_code(dev->evdev, EV_KEY, BTN_MIDDLE)) { test_button_event(dev, BTN_MIDDLE, 1); test_button_event(dev, BTN_MIDDLE, 0); } From 3bf9b02d81403c4ae373fa230b6049ed23ec1734 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Sep 2014 16:22:37 +0200 Subject: [PATCH 80/91] evdev: Add an internal device suspend / resume notification system We have the ability for a device to form a link to another device through the device_added / device_removed callbacks. A device having such a link to another device may also want to know when that other device is disabled / enabled (suspended / resumed). So add a notification mechanism for this too. Signed-off-by: Hans de Goede Reviewed-by: Peter Hutterer Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad.c | 2 ++ src/evdev.c | 52 +++++++++++++++++++++++++++++++++++++++++ src/evdev.h | 15 ++++++++++++ 3 files changed, 69 insertions(+) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index d1e4acd4..4161d9b7 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -696,6 +696,8 @@ static struct evdev_dispatch_interface tp_interface = { tp_destroy, tp_device_added, tp_device_removed, + NULL, /* device_suspended */ + NULL, /* device_resumed */ tp_tag_device, }; diff --git a/src/evdev.c b/src/evdev.c index 85e4d719..cb43c271 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -692,6 +692,8 @@ struct evdev_dispatch_interface fallback_interface = { fallback_destroy, NULL, /* device_added */ NULL, /* device_removed */ + NULL, /* device_suspended */ + NULL, /* device_resumed */ fallback_tag_device, }; @@ -1078,11 +1080,17 @@ evdev_notify_added_device(struct evdev_device *device) if (dev == &device->base) continue; + /* Notify existing device d about addition of device device */ if (d->dispatch->interface->device_added) d->dispatch->interface->device_added(d, device); + /* Notify new device device about existing device d */ if (device->dispatch->interface->device_added) device->dispatch->interface->device_added(device, d); + + /* Notify new device device if existing device d is suspended */ + if (d->suspended && device->dispatch->interface->device_suspended) + device->dispatch->interface->device_suspended(device, d); } notify_added_device(&device->base); @@ -1413,9 +1421,51 @@ release_pressed_keys(struct evdev_device *device) } } +void +evdev_notify_suspended_device(struct evdev_device *device) +{ + struct libinput_device *it; + + if (device->suspended) + return; + + list_for_each(it, &device->base.seat->devices_list, link) { + struct evdev_device *d = (struct evdev_device*)it; + if (it == &device->base) + continue; + + if (d->dispatch->interface->device_suspended) + d->dispatch->interface->device_suspended(d, device); + } + + device->suspended = 1; +} + +void +evdev_notify_resumed_device(struct evdev_device *device) +{ + struct libinput_device *it; + + if (!device->suspended) + return; + + list_for_each(it, &device->base.seat->devices_list, link) { + struct evdev_device *d = (struct evdev_device*)it; + if (it == &device->base) + continue; + + if (d->dispatch->interface->device_resumed) + d->dispatch->interface->device_resumed(d, device); + } + + device->suspended = 0; +} + int evdev_device_suspend(struct evdev_device *device) { + evdev_notify_suspended_device(device); + if (device->source) { libinput_remove_source(device->base.seat->libinput, device->source); @@ -1506,6 +1556,8 @@ evdev_device_resume(struct evdev_device *device) memset(device->hw_key_mask, 0, sizeof(device->hw_key_mask)); + evdev_notify_resumed_device(device); + return 0; } diff --git a/src/evdev.h b/src/evdev.h index e1506d22..4047a6d9 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -110,6 +110,7 @@ struct evdev_device { enum evdev_device_tags tags; int is_mt; + int suspended; struct { struct motion_filter *filter; @@ -145,6 +146,14 @@ struct evdev_dispatch_interface { void (*device_removed)(struct evdev_device *device, struct evdev_device *removed_device); + /* A device was suspended */ + void (*device_suspended)(struct evdev_device *device, + struct evdev_device *suspended_device); + + /* 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); @@ -227,6 +236,12 @@ evdev_device_suspend(struct evdev_device *device); int evdev_device_resume(struct evdev_device *device); +void +evdev_notify_suspended_device(struct evdev_device *device); + +void +evdev_notify_resumed_device(struct evdev_device *device); + void evdev_keyboard_notify_key(struct evdev_device *device, uint32_t time, From a8ffc096bed1b9ebed7f2d870ddf5d743e2c83ee Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 16 Sep 2014 16:22:38 +0200 Subject: [PATCH 81/91] touchpad: Keep track of associated trackpoint device The top soft buttons are intended for use with a trackpoint, and to e.g. make middle button scrolling work correctly, we must post the events for these "buttons" through the trackpoint device. This commit is a preparation patch for this, it adds a link to the trackpoint to the touchpad, but does not yet do anything with it. Signed-off-by: Peter Hutterer Signed-off-by: Hans de Goede Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad.c | 7 +++++++ src/evdev-mt-touchpad.h | 2 ++ src/evdev.c | 9 +++++++++ src/evdev.h | 1 + 4 files changed, 19 insertions(+) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 4161d9b7..4639231b 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -642,6 +642,10 @@ tp_device_added(struct evdev_device *device, { struct tp_dispatch *tp = (struct tp_dispatch*)device->dispatch; + if (tp->buttons.trackpoint == NULL && + (added_device->tags & EVDEV_TAG_TRACKPOINT)) + tp->buttons.trackpoint = added_device; + if (tp->sendevents.current_mode != LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE) return; @@ -657,6 +661,9 @@ tp_device_removed(struct evdev_device *device, struct tp_dispatch *tp = (struct tp_dispatch*)device->dispatch; struct libinput_device *dev; + if (removed_device == tp->buttons.trackpoint) + tp->buttons.trackpoint = NULL; + if (tp->sendevents.current_mode != LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE) return; diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 8671f797..4a16db9d 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -198,6 +198,8 @@ struct tp_dispatch { int32_t rightbutton_left_edge; int32_t leftbutton_right_edge; } top_area; + + struct evdev_device *trackpoint; } buttons; /* physical buttons */ enum touchpad_event queued; diff --git a/src/evdev.c b/src/evdev.c index cb43c271..4e9db2fb 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -606,6 +606,14 @@ evdev_tag_external_mouse(struct evdev_device *device, } } +static void +evdev_tag_trackpoint(struct evdev_device *device, + struct udev_device *udev_device) +{ + if (libevdev_has_property(device->evdev, INPUT_PROP_POINTING_STICK)) + device->tags |= EVDEV_TAG_TRACKPOINT; +} + static void fallback_process(struct evdev_dispatch *dispatch, struct evdev_device *device, @@ -644,6 +652,7 @@ 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); } static int diff --git a/src/evdev.h b/src/evdev.h index 4047a6d9..2da30f84 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -53,6 +53,7 @@ enum evdev_device_seat_capability { enum evdev_device_tags { EVDEV_TAG_EXTERNAL_MOUSE = (1 << 0), EVDEV_TAG_INTERNAL_TOUCHPAD = (1 << 1), + EVDEV_TAG_TRACKPOINT = (1 << 2), }; struct mt_slot { From 8f20c07a1f0c465317f77d21304d7afaadb1b4a8 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Sep 2014 16:22:39 +0200 Subject: [PATCH 82/91] touchpad: Route top softbuttons through the trackstick if we've one The touchpad top softbuttons such as found on the Lenove T440 are intended for use with the trackstick. Route their events through the trackstick, so that they can be used for e.g. middle button scrolling with the trackstick. Note that sending top button events to a disabled trackpoint makes no sense (and will mess up internal state). Likely a user with a disabled trackpoint will still expect the top buttons to work, so rather than not sending events in that case, simply treat a suspendeded trackpoint as not being there, and send the events directly from the touchpad device. Signed-off-by: Hans de Goede Reviewed-by: Peter Hutterer Signed-off-by: Peter Hutterer --- doc/touchpad-softbutton-state-machine.svg | 198 ++++++++++++---------- src/evdev-mt-touchpad-buttons.c | 47 ++++- src/evdev-mt-touchpad.c | 17 +- src/evdev-mt-touchpad.h | 1 + 4 files changed, 164 insertions(+), 99 deletions(-) diff --git a/doc/touchpad-softbutton-state-machine.svg b/doc/touchpad-softbutton-state-machine.svg index 1d569bf6..ffa17a29 100644 --- a/doc/touchpad-softbutton-state-machine.svg +++ b/doc/touchpad-softbutton-state-machine.svg @@ -1,5 +1,5 @@ - + @@ -68,13 +68,13 @@ - + - Check state of - all touches + Check state of + all touches - - + + @@ -82,88 +82,89 @@ tp_post_softbutton_buttons() - + - !buttons.click_pend - && current == old + !buttons.click_pend + && current == old - - + + - - yes + + yes - - + + - - no + + no - - - + + + - current = buttons.state & 0x01 - old = buttons.old_state & 0x01 - button = 0 + current = buttons.state & 0x01 + old = buttons.old_state & 0x01 + button = 0 + is_top = 0 - - - + + + - All touches are in state none + All touches are in state none - - + + - - no + + no - - + + - - yes + + yes - + - buttons.click_pend = 1 + buttons.click_pend = 1 - - - + + + - (Some touches are in middle) || - ((Some touches are in right) && - (Some touches are in left)) + (Some touches are in middle) || + ((Some touches are in right) && + (Some touches are in left)) - - + + - - yes + + yes - + - button = BTN_MIDDLE + button = BTN_MIDDLE - - - + + + - current + current - - + + - - no + + no - - + + - - yes + + yes @@ -172,13 +173,13 @@ - + yes - + - + no @@ -192,25 +193,28 @@ - + no - + - buttons.active = button - state = BUTTON_PRESSED + buttons.active = button + buttons.active_is_top = is_top + state = BUTTON_PRESSED - + - - - - - + + + + + - button = buttons.active - buttons.active = 0 - state = BUTTON_RELEASED + button = buttons.active + is_top = buttons.active_is_top + buttons.active = 0 + buttons.active_is_top = 0 + state = BUTTON_RELEASED @@ -218,7 +222,7 @@ - + @@ -227,26 +231,26 @@ - + no - + yes - pointer_notify_button( - button, state) + tp_notify_softbutton( + button, is_top, state) - - + + finger in @@ -261,8 +265,8 @@ curr = button start enter timeout - - + + @@ -356,5 +360,27 @@ + + + touches in top? + + + + + + yes + + + + is_top = 1 + + + + + + + + no + diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index 02d3205a..b6aee24d 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -675,16 +675,42 @@ tp_post_physical_buttons(struct tp_dispatch *tp, uint64_t time) return 0; } +static void +tp_notify_softbutton(struct tp_dispatch *tp, + uint64_t time, + uint32_t button, + uint32_t is_topbutton, + enum libinput_button_state state) +{ + /* If we've a trackpoint, send top buttons through the trackpoint */ + if (is_topbutton && tp->buttons.trackpoint) { + struct evdev_dispatch *dispatch = tp->buttons.trackpoint->dispatch; + struct input_event event; + + event.time.tv_sec = time/1000; + event.time.tv_usec = (time % 1000) * 1000; + event.type = EV_KEY; + event.code = button; + event.value = (state == LIBINPUT_BUTTON_STATE_PRESSED) ? 1 : 0; + dispatch->interface->process(dispatch, tp->buttons.trackpoint, + &event, time); + return; + } + + evdev_pointer_notify_button(tp->device, time, button, state); +} + static int tp_post_softbutton_buttons(struct tp_dispatch *tp, uint64_t time) { - uint32_t current, old, button; + uint32_t current, old, button, is_top; enum libinput_button_state state; enum { AREA = 0x01, LEFT = 0x02, MIDDLE = 0x04, RIGHT = 0x08 }; current = tp->buttons.state; old = tp->buttons.old_state; button = 0; + is_top = 0; if (!tp->buttons.click_pending && current == old) return 0; @@ -697,15 +723,18 @@ tp_post_softbutton_buttons(struct tp_dispatch *tp, uint64_t time) case BUTTON_EVENT_IN_AREA: button |= AREA; break; - case BUTTON_EVENT_IN_BOTTOM_L: case BUTTON_EVENT_IN_TOP_L: + is_top = 1; + case BUTTON_EVENT_IN_BOTTOM_L: button |= LEFT; break; case BUTTON_EVENT_IN_TOP_M: + is_top = 1; button |= MIDDLE; break; - case BUTTON_EVENT_IN_BOTTOM_R: case BUTTON_EVENT_IN_TOP_R: + is_top = 1; + case BUTTON_EVENT_IN_BOTTOM_R: button |= RIGHT; break; default: @@ -727,21 +756,21 @@ tp_post_softbutton_buttons(struct tp_dispatch *tp, uint64_t time) button = BTN_LEFT; tp->buttons.active = button; + tp->buttons.active_is_topbutton = is_top; state = LIBINPUT_BUTTON_STATE_PRESSED; } else { button = tp->buttons.active; + is_top = tp->buttons.active_is_topbutton; tp->buttons.active = 0; + tp->buttons.active_is_topbutton = 0; state = LIBINPUT_BUTTON_STATE_RELEASED; } tp->buttons.click_pending = false; - if (button) { - evdev_pointer_notify_button(tp->device, - time, - button, - state); - } + if (button) + tp_notify_softbutton(tp, time, button, is_top, state); + return 1; } diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 4639231b..bed62569 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -643,8 +643,11 @@ tp_device_added(struct evdev_device *device, struct tp_dispatch *tp = (struct tp_dispatch*)device->dispatch; if (tp->buttons.trackpoint == NULL && - (added_device->tags & EVDEV_TAG_TRACKPOINT)) + (added_device->tags & EVDEV_TAG_TRACKPOINT)) { + /* Don't send any pending releases to the new trackpoint */ + tp->buttons.active_is_topbutton = false; tp->buttons.trackpoint = added_device; + } if (tp->sendevents.current_mode != LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE) @@ -661,8 +664,14 @@ tp_device_removed(struct evdev_device *device, struct tp_dispatch *tp = (struct tp_dispatch*)device->dispatch; struct libinput_device *dev; - if (removed_device == tp->buttons.trackpoint) + if (removed_device == tp->buttons.trackpoint) { + /* Clear any pending releases for the trackpoint */ + if (tp->buttons.active && tp->buttons.active_is_topbutton) { + tp->buttons.active = 0; + tp->buttons.active_is_topbutton = false; + } tp->buttons.trackpoint = NULL; + } if (tp->sendevents.current_mode != LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE) @@ -703,8 +712,8 @@ static struct evdev_dispatch_interface tp_interface = { tp_destroy, tp_device_added, tp_device_removed, - NULL, /* device_suspended */ - NULL, /* device_resumed */ + tp_device_removed, /* device_suspended, treat as remove */ + tp_device_added, /* device_resumed, treat as add */ tp_tag_device, }; diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 4a16db9d..3d3932b5 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -183,6 +183,7 @@ struct tp_dispatch { uint32_t old_state; uint32_t motion_dist; /* for pinned touches */ unsigned int active; /* currently active button, for release event */ + bool active_is_topbutton; /* is active a top button? */ /* Only used for clickpads. The software button areas are * always 2 horizontal stripes across the touchpad. From 0f434cf73b13feda70b0741a653df6f0225fb904 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Sep 2014 16:22:40 +0200 Subject: [PATCH 83/91] touchpad: Put state unrolling code in a tp_clear_state() helper function For touchpads with top softbuttons we don't want to fully disable the touchpad on suspend, as we want to keep the top softbuttons working for the trackpoint. So in the suspended state some of the touchpad sub-statemachines will keep running (e.g. buttons) where others (e.g. tap) will not. This means that we will need to clear the touchpad state on resume too, to avoid things being in an inconsistent state after resume. This commit factors out the state clearing code into a helper functions, so that the same code can be used on resume. No functional changes. Signed-off-by: Hans de Goede Reviewed-by: Peter Hutterer Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index bed62569..522f1113 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -602,7 +602,7 @@ tp_destroy(struct evdev_dispatch *dispatch) } static void -tp_suspend(struct tp_dispatch *tp, struct evdev_device *device) +tp_clear_state(struct tp_dispatch *tp, struct evdev_device *device) { uint64_t now = libinput_now(tp->device->base.seat->libinput); struct tp_touch *t; @@ -626,7 +626,12 @@ tp_suspend(struct tp_dispatch *tp, struct evdev_device *device) } tp_handle_state(tp, now); +} +static void +tp_suspend(struct tp_dispatch *tp, struct evdev_device *device) +{ + tp_clear_state(tp, device); evdev_device_suspend(device); } From b264c52a8b69dc23774c37f7628eb8f94bc8ffed Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Sep 2014 16:22:41 +0200 Subject: [PATCH 84/91] touchpad: When disabling a TOPBUTTONPAD, leave the top buttons enabled On a TOPBUTTONPAD, we can't disable the touchpad altogether - the trackstick relies on the touchpad's top software buttons. Signed-off-by: Hans de Goede Reviewed-by: Peter Hutterer Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad-buttons.c | 4 ++++ src/evdev-mt-touchpad.c | 25 +++++++++++++++++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index b6aee24d..fae6a13e 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -697,6 +697,10 @@ tp_notify_softbutton(struct tp_dispatch *tp, return; } + /* Ignore button events not for the trackpoint while suspended */ + if (tp->device->suspended) + return; + evdev_pointer_notify_button(tp->device, time, button, state); } diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 522f1113..70a4e40f 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -523,6 +523,12 @@ tp_post_events(struct tp_dispatch *tp, uint64_t time) double dx, dy; int consumed = 0; + /* Only post (top) button events while suspended */ + if (tp->device->suspended) { + tp_post_button_events(tp, time); + return; + } + consumed |= tp_tap_handle_state(tp, time); consumed |= tp_post_button_events(tp, time); @@ -632,13 +638,28 @@ static void tp_suspend(struct tp_dispatch *tp, struct evdev_device *device) { tp_clear_state(tp, device); - evdev_device_suspend(device); + + /* On devices with top softwarebuttons we don't actually suspend the + * device, to keep the "trackpoint" buttons working. tp_post_events() + * will only send events for the trackpoint while suspended. + */ + if (tp->buttons.has_topbuttons) { + evdev_notify_suspended_device(device); + } else { + evdev_device_suspend(device); + } } static void tp_resume(struct tp_dispatch *tp, struct evdev_device *device) { - evdev_device_resume(device); + if (tp->buttons.has_topbuttons) { + /* tap state-machine is offline while suspended, reset state */ + tp_clear_state(tp, device); + evdev_notify_resumed_device(device); + } else { + evdev_device_resume(device); + } } static void From 45299593c060f51c6c5e44943bb575d936ddc665 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 16 Sep 2014 16:22:42 +0200 Subject: [PATCH 85/91] test: Test the topbuttons of a disabled touchpad still send events through the trackpoint Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- test/device.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/test/device.c b/test/device.c index 3f7ec4cd..33aae04e 100644 --- a/test/device.c +++ b/test/device.c @@ -509,6 +509,62 @@ START_TEST(device_disable_release_softbutton) } END_TEST +START_TEST(device_disable_topsoftbutton) +{ + struct litest_device *dev = litest_current_device(); + struct litest_device *trackpoint; + struct libinput *li = dev->libinput; + struct libinput_device *device; + enum libinput_config_status status; + + struct libinput_event *event; + struct libinput_event_pointer *ptrevent; + + device = dev->libinput_device; + + trackpoint = litest_add_device(li, LITEST_TRACKPOINT); + + status = libinput_device_config_send_events_set_mode(device, + LIBINPUT_CONFIG_SEND_EVENTS_DISABLED); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); + litest_drain_events(li); + + litest_touch_down(dev, 0, 90, 10); + litest_button_click(dev, BTN_LEFT, true); + litest_button_click(dev, BTN_LEFT, false); + litest_touch_up(dev, 0); + + litest_wait_for_event(li); + event = libinput_get_event(li); + ck_assert_int_eq(libinput_event_get_type(event), + LIBINPUT_EVENT_POINTER_BUTTON); + ck_assert_int_eq(libinput_event_get_device(event), + trackpoint->libinput_device); + ptrevent = libinput_event_get_pointer_event(event); + ck_assert_int_eq(libinput_event_pointer_get_button(ptrevent), + BTN_RIGHT); + ck_assert_int_eq(libinput_event_pointer_get_button_state(ptrevent), + LIBINPUT_BUTTON_STATE_PRESSED); + libinput_event_destroy(event); + + event = libinput_get_event(li); + ck_assert_int_eq(libinput_event_get_type(event), + LIBINPUT_EVENT_POINTER_BUTTON); + ck_assert_int_eq(libinput_event_get_device(event), + trackpoint->libinput_device); + ptrevent = libinput_event_get_pointer_event(event); + ck_assert_int_eq(libinput_event_pointer_get_button(ptrevent), + BTN_RIGHT); + ck_assert_int_eq(libinput_event_pointer_get_button_state(ptrevent), + LIBINPUT_BUTTON_STATE_RELEASED); + libinput_event_destroy(event); + + litest_assert_empty_queue(li); + + litest_delete_device(trackpoint); +} +END_TEST + int main (int argc, char **argv) { litest_add("device:sendevents", device_sendevents_config, LITEST_ANY, LITEST_TOUCHPAD); @@ -527,5 +583,7 @@ int main (int argc, char **argv) litest_add("device:sendevents", device_disable_release_tap_n_drag, LITEST_TOUCHPAD, LITEST_ANY); litest_add("device:sendevents", device_disable_release_softbutton, LITEST_CLICKPAD, LITEST_APPLE_CLICKPAD); + litest_add("device:sendevents", device_disable_topsoftbutton, LITEST_TOPBUTTONPAD, LITEST_ANY); + return litest_run(argc, argv); } From 5eafd375992db13581bc7376c02488f98198f980 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Thu, 18 Sep 2014 17:02:39 -0500 Subject: [PATCH 86/91] cosmetic: Remove prototype for nonexistent create_linear_acceleration_filter() Signed-off-by: Peter Hutterer --- src/filter.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/filter.h b/src/filter.h index 31ac7ebe..85534952 100644 --- a/src/filter.h +++ b/src/filter.h @@ -52,9 +52,6 @@ struct motion_filter { struct motion_filter_interface *interface; }; -struct motion_filter * -create_linear_acceleration_filter(double speed); - typedef double (*accel_profile_func_t)(struct motion_filter *filter, void *data, double velocity, From d3ad8cf888a453da68b4bf41804caefd5622528e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 17 Sep 2014 15:35:30 +0200 Subject: [PATCH 87/91] touchpad: Enlarge topbutton area a bit while the touchpad is disabled Make it easier to hit the topbutton area when the touchpad is disabled, normally we don't want to make the topbutton area too big, so as to not interfere with normal touchpad operation, but when disabled we have no such worries. Signed-off-by: Hans de Goede Reviewed-by: Peter Hutterer Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad-buttons.c | 19 ++++++++++++------- src/evdev-mt-touchpad.c | 4 ++++ src/evdev-mt-touchpad.h | 4 +++- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index fae6a13e..21417ab9 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -496,7 +496,8 @@ tp_release_all_buttons(struct tp_dispatch *tp, void tp_init_softbuttons(struct tp_dispatch *tp, - struct evdev_device *device) + struct evdev_device *device, + double topbutton_size_mult) { int width, height; const struct input_absinfo *absinfo_x, *absinfo_y; @@ -524,14 +525,18 @@ tp_init_softbuttons(struct tp_dispatch *tp, tp->buttons.bottom_area.rightbutton_left_edge = width/2 + xoffset; if (tp->buttons.has_topbuttons) { - /* T440s has the top button line 5mm from the top, - event analysis has shown events to start down to ~10mm - from the top - which maps to 15% */ + /* T440s has the top button line 5mm from the top, event + analysis has shown events to start down to ~10mm from the + top - which maps to 15%. We allow the caller to enlarge the + area using a multiplier for the touchpad disabled case. */ + double topsize_mm = 10 * topbutton_size_mult; + double topsize_pct = .15 * topbutton_size_mult; + if (yres > 1) { tp->buttons.top_area.bottom_edge = - yoffset + 10 * yres; + yoffset + topsize_mm * yres; } else { - tp->buttons.top_area.bottom_edge = height * .15 + yoffset; + tp->buttons.top_area.bottom_edge = height * topsize_pct + yoffset; } tp->buttons.top_area.rightbutton_left_edge = width * .58 + xoffset; tp->buttons.top_area.leftbutton_right_edge = width * .42 + xoffset; @@ -581,7 +586,7 @@ tp_init_buttons(struct tp_dispatch *tp, tp->buttons.use_clickfinger = true; if (tp->buttons.is_clickpad && !tp->buttons.use_clickfinger) { - tp_init_softbuttons(tp, device); + tp_init_softbuttons(tp, device, 1.0); } else { tp->buttons.bottom_area.top_edge = INT_MAX; tp->buttons.top_area.bottom_edge = INT_MIN; diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 70a4e40f..86923330 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -645,6 +645,8 @@ tp_suspend(struct tp_dispatch *tp, struct evdev_device *device) */ if (tp->buttons.has_topbuttons) { evdev_notify_suspended_device(device); + /* Enlarge topbutton area while suspended */ + tp_init_softbuttons(tp, device, 1.5); } else { evdev_device_suspend(device); } @@ -656,6 +658,8 @@ tp_resume(struct tp_dispatch *tp, struct evdev_device *device) if (tp->buttons.has_topbuttons) { /* tap state-machine is offline while suspended, reset state */ tp_clear_state(tp, device); + /* restore original topbutton area size */ + tp_init_softbuttons(tp, device, 1.0); evdev_notify_resumed_device(device); } else { evdev_device_resume(device); diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 3d3932b5..107195f3 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -245,7 +245,9 @@ int tp_init_buttons(struct tp_dispatch *tp, struct evdev_device *device); void -tp_init_softbuttons(struct tp_dispatch *tp, struct evdev_device *device); +tp_init_softbuttons(struct tp_dispatch *tp, + struct evdev_device *device, + double topbutton_size_mult); void tp_destroy_buttons(struct tp_dispatch *tp); From faa87649211b7ee133ded070b24fff897eb11e7f Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 17 Sep 2014 15:35:31 +0200 Subject: [PATCH 88/91] litest: Add litest_assert_scroll() helper function Make check_2fg_scroll functionality available outside of touchpad.c , no functional changes. Signed-off-by: Hans de Goede Reviewed-by: Peter Hutterer Signed-off-by: Peter Hutterer --- test/litest.c | 39 +++++++++++++++++++++++++++++++++++++++ test/litest.h | 1 + test/touchpad.c | 49 ++++--------------------------------------------- 3 files changed, 44 insertions(+), 45 deletions(-) diff --git a/test/litest.c b/test/litest.c index cf7e6922..e5092b8c 100644 --- a/test/litest.c +++ b/test/litest.c @@ -1084,3 +1084,42 @@ litest_assert_button_event(struct libinput *li, unsigned int button, state); libinput_event_destroy(event); } + +void litest_assert_scroll(struct libinput *li, unsigned int axis, int dir) +{ + struct libinput_event *event, *next_event; + struct libinput_event_pointer *ptrev; + + event = libinput_get_event(li); + next_event = libinput_get_event(li); + ck_assert(next_event != NULL); /* At least 1 scroll + stop scroll */ + + while (event) { + ck_assert_int_eq(libinput_event_get_type(event), + LIBINPUT_EVENT_POINTER_AXIS); + ptrev = libinput_event_get_pointer_event(event); + ck_assert(ptrev != NULL); + ck_assert_int_eq(libinput_event_pointer_get_axis(ptrev), axis); + + if (next_event) { + /* Normal scroll event, check dir */ + if (dir > 0) { + ck_assert_int_ge( + libinput_event_pointer_get_axis_value(ptrev), + dir); + } else { + ck_assert_int_le( + libinput_event_pointer_get_axis_value(ptrev), + dir); + } + } else { + /* Last scroll event, must be 0 */ + ck_assert_int_eq( + libinput_event_pointer_get_axis_value(ptrev), + 0); + } + libinput_event_destroy(event); + event = next_event; + next_event = libinput_get_event(li); + } +} diff --git a/test/litest.h b/test/litest.h index dd1386c9..fdf815f8 100644 --- a/test/litest.h +++ b/test/litest.h @@ -147,6 +147,7 @@ void litest_assert_empty_queue(struct libinput *li); void litest_assert_button_event(struct libinput *li, unsigned int button, enum libinput_button_state state); +void litest_assert_scroll(struct libinput *li, unsigned int axis, int dir); struct libevdev_uinput * litest_create_uinput_device(const char *name, struct input_id *id, diff --git a/test/touchpad.c b/test/touchpad.c index 7ff3d142..522cee65 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -1346,47 +1346,6 @@ test_2fg_scroll(struct litest_device *dev, double dx, double dy, int sleep) libinput_dispatch(li); } -static void -check_2fg_scroll(struct litest_device *dev, unsigned int axis, int dir) -{ - struct libinput *li = dev->libinput; - struct libinput_event *event, *next_event; - struct libinput_event_pointer *ptrev; - - event = libinput_get_event(li); - next_event = libinput_get_event(li); - ck_assert(next_event != NULL); /* At least 1 scroll + stop scroll */ - - while (event) { - ck_assert_int_eq(libinput_event_get_type(event), - LIBINPUT_EVENT_POINTER_AXIS); - ptrev = libinput_event_get_pointer_event(event); - ck_assert(ptrev != NULL); - ck_assert_int_eq(libinput_event_pointer_get_axis(ptrev), axis); - - if (next_event) { - /* Normal scroll event, check dir */ - if (dir > 0) { - ck_assert_int_ge( - libinput_event_pointer_get_axis_value(ptrev), - dir); - } else { - ck_assert_int_le( - libinput_event_pointer_get_axis_value(ptrev), - dir); - } - } else { - /* Last scroll event, must be 0 */ - ck_assert_int_eq( - libinput_event_pointer_get_axis_value(ptrev), - 0); - } - libinput_event_destroy(event); - event = next_event; - next_event = libinput_get_event(li); - } -} - START_TEST(touchpad_2fg_scroll) { struct litest_device *dev = litest_current_device(); @@ -1395,13 +1354,13 @@ START_TEST(touchpad_2fg_scroll) litest_drain_events(li); test_2fg_scroll(dev, 0.1, 40, 0); - check_2fg_scroll(dev, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, 10); + litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, 10); test_2fg_scroll(dev, 0.1, -40, 0); - check_2fg_scroll(dev, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, -10); + litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, -10); test_2fg_scroll(dev, 40, 0.1, 0); - check_2fg_scroll(dev, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, 10); + litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, 10); test_2fg_scroll(dev, -40, 0.1, 0); - check_2fg_scroll(dev, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, -10); + litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, -10); /* 2fg scroll smaller than the threshold should not generate events */ test_2fg_scroll(dev, 0.1, 0.1, 200); From 04a602108b0e26ee242238574d00a7984ca8f633 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 18 Sep 2014 11:15:27 +0200 Subject: [PATCH 89/91] test: Add trackpoint middlebutton scrolling tests Signed-off-by: Hans de Goede Reviewed-by: Peter Hutterer Signed-off-by: Peter Hutterer --- test/Makefile.am | 5 ++ test/litest-trackpoint.c | 2 +- test/litest.h | 1 + test/trackpoint.c | 103 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 test/trackpoint.c diff --git a/test/Makefile.am b/test/Makefile.am index 86859d8c..6a68982c 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -34,6 +34,7 @@ run_tests = \ test-touch \ test-log \ test-touchpad \ + test-trackpoint \ test-misc \ test-keyboard \ test-device @@ -72,6 +73,10 @@ test_touchpad_SOURCES = touchpad.c test_touchpad_LDADD = $(TEST_LIBS) test_touchpad_LDFLAGS = -no-install +test_trackpoint_SOURCES = trackpoint.c +test_trackpoint_LDADD = $(TEST_LIBS) +test_trackpoint_LDFLAGS = -no-install + test_misc_SOURCES = misc.c test_misc_LDADD = $(TEST_LIBS) test_misc_LDFLAGS = -no-install diff --git a/test/litest-trackpoint.c b/test/litest-trackpoint.c index 40b9ed07..01ad34ea 100644 --- a/test/litest-trackpoint.c +++ b/test/litest-trackpoint.c @@ -56,7 +56,7 @@ static int events[] = { struct litest_test_device litest_trackpoint_device = { .type = LITEST_TRACKPOINT, - .features = LITEST_POINTER | LITEST_BUTTON, + .features = LITEST_POINTER | LITEST_BUTTON | LITEST_POINTINGSTICK, .shortname = "trackpoint", .setup = litest_trackpoint_setup, .interface = &interface, diff --git a/test/litest.h b/test/litest.h index fdf815f8..fca6acb3 100644 --- a/test/litest.h +++ b/test/litest.h @@ -61,6 +61,7 @@ enum litest_device_feature { LITEST_APPLE_CLICKPAD = 1 << 8, LITEST_TOPBUTTONPAD = 1 << 9, LITEST_SEMI_MT = 1 << 10, + LITEST_POINTINGSTICK = 1 << 11, }; struct litest_device { diff --git a/test/trackpoint.c b/test/trackpoint.c new file mode 100644 index 00000000..d4dfe415 --- /dev/null +++ b/test/trackpoint.c @@ -0,0 +1,103 @@ +/* + * Copyright © 2014 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 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. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR 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. + */ + +#include + +#include +#include +#include +#include +#include + +#include "libinput-util.h" +#include "litest.h" + +START_TEST(trackpoint_middlebutton) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + litest_drain_events(li); + + /* A quick middle button click should get reported normally */ + litest_button_click(dev, BTN_MIDDLE, 1); + litest_button_click(dev, BTN_MIDDLE, 0); + + litest_assert_button_event(li, BTN_MIDDLE, 1); + litest_assert_button_event(li, BTN_MIDDLE, 0); + + litest_assert_empty_queue(li); +} +END_TEST + +static void +test_2fg_scroll(struct litest_device *dev, double dx, double dy) +{ + struct libinput *li = dev->libinput; + + litest_button_click(dev, BTN_MIDDLE, 1); + + libinput_dispatch(li); + msleep(300); + libinput_dispatch(li); + + litest_event(dev, EV_REL, REL_X, dx); + litest_event(dev, EV_REL, REL_Y, dy); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + + litest_button_click(dev, BTN_MIDDLE, 0); + + libinput_dispatch(li); +} + +START_TEST(trackpoint_scroll) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + litest_drain_events(li); + + test_2fg_scroll(dev, 1, 6); + litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, 6); + test_2fg_scroll(dev, 1, -7); + litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, -7); + test_2fg_scroll(dev, 8, 1); + litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, 8); + test_2fg_scroll(dev, -9, 1); + litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, -9); + + /* scroll smaller than the threshold should not generate events */ + test_2fg_scroll(dev, 1, 1); + /* long middle press without movement should not generate events */ + test_2fg_scroll(dev, 0, 0); + + litest_assert_empty_queue(li); +} +END_TEST + +int main(int argc, char **argv) { + + litest_add("trackpoint:middlebutton", trackpoint_middlebutton, LITEST_POINTINGSTICK, LITEST_ANY); + litest_add("trackpoint:scroll", trackpoint_scroll, LITEST_POINTINGSTICK, LITEST_ANY); + + return litest_run(argc, argv); +} From 81065184e2c3a2340e15fb3738b406a71864a5db Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 22 Sep 2014 09:30:38 +1000 Subject: [PATCH 90/91] evdev: drop unused declaration evdev_proces_event Signed-off-by: Peter Hutterer --- src/evdev.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/evdev.h b/src/evdev.h index 2da30f84..aecbd95a 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -182,9 +182,6 @@ evdev_touchpad_create(struct evdev_device *device); struct evdev_dispatch * evdev_mt_touchpad_create(struct evdev_device *device); -void -evdev_device_proces_event(struct libinput_event *event); - void evdev_device_led_update(struct evdev_device *device, enum libinput_led leds); From fa363ed0e847d28daefbf990b1e183f369d8c96e Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 17 Sep 2014 15:11:41 +1000 Subject: [PATCH 91/91] Drop two semicolons after a macro definition Signed-off-by: Peter Hutterer --- src/libinput-private.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libinput-private.h b/src/libinput-private.h index cf03c032..d3672d00 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -131,8 +131,8 @@ typedef void (*libinput_source_dispatch_t)(void *data); #define log_info(li_, ...) log_msg((li_), LIBINPUT_LOG_PRIORITY_INFO, __VA_ARGS__) #define log_error(li_, ...) log_msg((li_), LIBINPUT_LOG_PRIORITY_ERROR, __VA_ARGS__) #define log_bug_kernel(li_, ...) log_msg((li_), LIBINPUT_LOG_PRIORITY_ERROR, "kernel bug: " __VA_ARGS__) -#define log_bug_libinput(li_, ...) log_msg((li_), LIBINPUT_LOG_PRIORITY_ERROR, "libinput bug: " __VA_ARGS__); -#define log_bug_client(li_, ...) log_msg((li_), LIBINPUT_LOG_PRIORITY_ERROR, "client bug: " __VA_ARGS__); +#define log_bug_libinput(li_, ...) log_msg((li_), LIBINPUT_LOG_PRIORITY_ERROR, "libinput bug: " __VA_ARGS__) +#define log_bug_client(li_, ...) log_msg((li_), LIBINPUT_LOG_PRIORITY_ERROR, "client bug: " __VA_ARGS__) void log_msg(struct libinput *libinput,