diff --git a/configure.ac b/configure.ac index fd402e20..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], [4]) +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=3:0:0 +LIBINPUT_LT_VERSION=5:0:0 AC_SUBST(LIBINPUT_LT_VERSION) AM_SILENT_RULES([yes]) @@ -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" 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/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/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/Makefile.am b/src/Makefile.am index 44108367..0ee6a47e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -36,7 +36,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 diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index bd3c0e25..21417ab9 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -484,6 +484,67 @@ 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; + } +} + +void +tp_init_softbuttons(struct tp_dispatch *tp, + struct evdev_device *device, + double topbutton_size_mult) +{ + 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%. 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 + topsize_mm * yres; + } else { + 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; + } else { + tp->buttons.top_area.bottom_edge = INT_MIN; + } +} + int tp_init_buttons(struct tp_dispatch *tp, struct evdev_device *device) @@ -521,39 +582,11 @@ 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) { - 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, - make the buttons 6mm high */ - if (yres > 1) { - tp->buttons.top_area.bottom_edge = - yoffset + 6 * yres; - } else { - tp->buttons.top_area.bottom_edge = height * .08 + 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, 1.0); } else { tp->buttons.bottom_area.top_edge = INT_MAX; tp->buttons.top_area.bottom_edge = INT_MIN; @@ -606,11 +639,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; } @@ -632,10 +666,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++; @@ -646,16 +680,46 @@ 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; + } + + /* Ignore button events not for the trackpoint while suspended */ + if (tp->device->suspended) + 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; @@ -668,15 +732,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: @@ -698,20 +765,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) - pointer_notify_button(&tp->device->base, - time, - button, - state); + tp_notify_softbutton(tp, time, button, is_top, state); + return 1; } diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index 0f1f837d..8f055fc1 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 @@ -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; @@ -253,6 +252,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; @@ -541,7 +541,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) { @@ -616,7 +619,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); @@ -625,32 +628,34 @@ 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; + struct tp_dispatch *tp = NULL; 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; - struct tp_dispatch *tp; + struct tp_dispatch *tp = NULL; 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 +667,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 @@ -688,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 5db204f6..86923330 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; @@ -160,7 +163,7 @@ tp_end_touch(struct tp_dispatch *tp, struct tp_touch *t) 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 @@ -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); } } @@ -245,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 && @@ -266,23 +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 (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; - t->fake =true; - } + if (i < nfake_touches) + tp_begin_touch(tp, t, time); + 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 @@ -300,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; } } @@ -406,17 +402,25 @@ 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); + + /* 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; 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); @@ -447,15 +451,15 @@ 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; } + tp->old_nfingers_down = tp->nfingers_down; tp->buttons.old_state = tp->buttons.state; tp->queued = TOUCHPAD_EVENT_NONE; @@ -488,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 @@ -544,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; } @@ -559,11 +523,17 @@ 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); if (consumed) { - tp_stop_scroll_events(tp, time); + evdev_stop_scroll(tp->device, time); return; } @@ -589,6 +559,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, @@ -609,9 +588,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; } } @@ -630,9 +607,144 @@ tp_destroy(struct evdev_dispatch *dispatch) free(tp); } +static void +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; + + /* 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); +} + +static void +tp_suspend(struct tp_dispatch *tp, struct evdev_device *device) +{ + tp_clear_state(tp, 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); + /* Enlarge topbutton area while suspended */ + tp_init_softbuttons(tp, device, 1.5); + } else { + evdev_device_suspend(device); + } +} + +static void +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); + } +} + +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->buttons.trackpoint == NULL && + (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) + 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 (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) + 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 + tp_destroy, + tp_device_added, + tp_device_removed, + tp_device_removed, /* device_suspended, treat as remove */ + tp_device_added, /* device_resumed, treat as add */ + tp_tag_device, }; static void @@ -646,41 +758,43 @@ 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; - } + 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, + 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; @@ -740,14 +854,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) @@ -757,17 +863,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; @@ -776,7 +885,6 @@ tp_init_palmdetect(struct tp_dispatch *tp, return 0; } - static int tp_init(struct tp_dispatch *tp, struct evdev_device *device) @@ -801,9 +909,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; @@ -816,9 +921,83 @@ tp_init(struct tp_dispatch *tp, if (tp_init_palmdetect(tp, device) != 0) return -1; + device->seat_caps |= EVDEV_DEVICE_POINTER; + return 0; } +static uint32_t +tp_sendevents_get_modes(struct libinput_device *device) +{ + 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 +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; + case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE: + tp_suspend_conditional(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) { @@ -833,5 +1012,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 47791201..107195f3 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), @@ -101,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; @@ -151,10 +152,13 @@ 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 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 */ @@ -179,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. @@ -194,11 +199,9 @@ struct tp_dispatch { int32_t rightbutton_left_edge; int32_t leftbutton_right_edge; } top_area; - } buttons; /* physical buttons */ - struct { - enum libinput_pointer_axis direction; - } scroll; + struct evdev_device *trackpoint; + } buttons; /* physical buttons */ enum touchpad_event queued; @@ -213,6 +216,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) \ @@ -236,6 +244,11 @@ 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, + double topbutton_size_mult); + void tp_destroy_buttons(struct tp_dispatch *tp); @@ -244,6 +257,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); @@ -256,4 +273,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/src/evdev.c b/src/evdev.c index 1b95bb7e..6bc1a396 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -24,6 +24,7 @@ #include "config.h" #include +#include #include #include #include "linux/input.h" @@ -40,6 +41,83 @@ #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, + EVDEV_KEY_TYPE_KEY, + EVDEV_KEY_TYPE_BUTTON, +}; + +static void +hw_set_key_down(struct evdev_device *device, int code, int pressed) +{ + long_set_bit_state(device->hw_key_mask, code, pressed); +} + +static int +hw_is_key_down(struct evdev_device *device, int code) +{ + return long_bit_is_set(device->hw_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) +{ + 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) @@ -74,19 +152,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) { - if (!device->abs.apply_calibration) { - *x = device->abs.x; - *y = device->abs.y; + if (!device->abs.apply_calibration) return; - } else { - *x = device->abs.x * device->abs.calibration[0] + - device->abs.y * device->abs.calibration[1] + - device->abs.calibration[2]; - *y = device->abs.x * device->abs.calibration[3] + - device->abs.y * device->abs.calibration[4] + - device->abs.calibration[5]; - } + matrix_mult_vec(&device->abs.calibration, x, y); } static inline double @@ -118,7 +187,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; @@ -135,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); @@ -163,6 +241,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; @@ -177,6 +256,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: @@ -212,11 +292,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; @@ -254,6 +338,54 @@ 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_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) @@ -271,13 +403,12 @@ 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; - if (e->code > KEY_MAX) - return; - if (e->code == BTN_TOUCH) { if (!device->is_mt) evdev_process_touch_button(device, time, e->value); @@ -286,36 +417,48 @@ 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); - break; + type = get_key_type(e->code); - default: - /* Only let KEY_* codes pass through. */ - if (!(e->code <= KEY_MICMUTE || - (e->code >= KEY_OK && e->code <= KEY_LIGHTS_TOGGLE))) + /* 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 (!hw_is_key_down(device, e->code)) + return; + } + } - keyboard_notify_key( - &device->base, + hw_set_key_down(device, e->code, e->value); + + switch (type) { + case EVDEV_KEY_TYPE_NONE: + break; + case EVDEV_KEY_TYPE_KEY: + evdev_keyboard_notify_key( + device, time, e->code, e->value ? LIBINPUT_KEY_STATE_PRESSED : 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, + e->code, + e->value ? LIBINPUT_BUTTON_STATE_PRESSED : + LIBINPUT_BUTTON_STATE_RELEASED); + break; } } @@ -450,6 +593,27 @@ 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 +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, @@ -483,20 +647,137 @@ fallback_destroy(struct evdev_dispatch *dispatch) free(dispatch); } +static void +fallback_tag_device(struct evdev_device *device, + struct udev_device *udev_device) +{ + evdev_tag_external_mouse(device, udev_device); + evdev_tag_trackpoint(device, udev_device); +} + +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 evdev_device *device = (struct evdev_device*)libinput_device; + + matrix_to_farray6(&device->abs.default_calibration, matrix); + + return !matrix_is_identity(&device->abs.default_calibration); +} + struct evdev_dispatch_interface fallback_interface = { fallback_process, - fallback_destroy + fallback_destroy, + NULL, /* device_added */ + NULL, /* device_removed */ + NULL, /* device_suspended */ + NULL, /* device_resumed */ + fallback_tag_device, }; -static struct evdev_dispatch * -fallback_dispatch_create(void) +static uint32_t +evdev_sendevents_get_modes(struct libinput_device *device) { - struct evdev_dispatch *dispatch = malloc(sizeof *dispatch); + 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 = 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; + 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; + + 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; } @@ -591,6 +872,36 @@ 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 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) { @@ -620,6 +931,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 +941,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 +958,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,14 +968,14 @@ 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; 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; @@ -690,6 +1004,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; @@ -703,6 +1026,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; } else if (!libevdev_has_event_code(device->evdev, EV_KEY, BTN_TOOL_FINGER) && libevdev_has_event_code(device->evdev, EV_KEY, BTN_TOOL_PEN) && has_abs) { @@ -713,22 +1037,23 @@ evdev_configure_device(struct evdev_device *device) device->devname, device->devnode); } - 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; @@ -762,10 +1087,37 @@ 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; + + /* 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); +} + 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; @@ -789,10 +1141,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); @@ -801,6 +1154,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; @@ -808,8 +1162,12 @@ 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; - libinput_seat_ref(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; @@ -821,7 +1179,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; @@ -831,7 +1189,9 @@ evdev_device_create(struct libinput_seat *seat, goto err; list_insert(seat->devices_list.prev, &device->base.link); - notify_added_device(&device->base); + + evdev_tag_device(device); + evdev_notify_added_device(device); return device; @@ -846,12 +1206,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 * @@ -885,10 +1241,75 @@ evdev_device_get_id_vendor(struct evdev_device *device) } void -evdev_device_calibrate(struct evdev_device *device, float calibration[6]) +evdev_device_set_default_calibration(struct evdev_device *device, + const float calibration[6]) { - device->abs.apply_calibration = 1; - memcpy(device->abs.calibration, calibration, sizeof device->abs.calibration); + 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]) +{ + 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. + */ + + /* 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, + 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 @@ -919,7 +1340,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); @@ -929,16 +1351,256 @@ evdev_device_get_size(struct evdev_device *device, } void -evdev_device_remove(struct evdev_device *device) +evdev_post_scroll(struct evdev_device *device, + uint64_t time, + double dx, + double dy) { - if (device->source) + 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) +{ + struct libinput *libinput = device->base.seat->libinput; + uint64_t time; + int code; + + if ((time = libinput_now(libinput)) == 0) + return; + + for (code = 0; code < KEY_CNT; code++) { + 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: + evdev_keyboard_notify_key( + device, + time, + code, + LIBINPUT_KEY_STATE_RELEASED); + break; + case EVDEV_KEY_TYPE_BUTTON: + evdev_pointer_notify_button( + device, + time, + code, + LIBINPUT_BUTTON_STATE_RELEASED); + break; + } + } + } +} + +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); + device->source = NULL; + } - if (device->mtdev) + release_pressed_keys(device); + + 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; +} + +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) +{ + struct libinput *libinput = device->base.seat->libinput; + int fd; + + 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)) { + 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)); + + evdev_notify_resumed_device(device); + + return 0; +} + +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 + * 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); @@ -960,5 +1622,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 bfa47b17..3699f912 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, @@ -49,6 +51,12 @@ enum evdev_device_seat_capability { EVDEV_DEVICE_TABLET = (1 << 3), }; +enum evdev_device_tags { + EVDEV_TAG_EXTERNAL_MOUSE = (1 << 0), + EVDEV_TAG_INTERNAL_TOUCHPAD = (1 << 1), + EVDEV_TAG_TRACKPOINT = (1 << 2), +}; + struct mt_slot { int32_t seat_slot; int32_t x, y; @@ -64,16 +72,20 @@ struct evdev_device { char *output_name; char *devnode; char *sysname; + char *syspath; const char *devname; 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; - float calibration[6]; + struct matrix calibration; + struct matrix default_calibration; /* from LIBINPUT_CALIBRATION_MATRIX */ + struct matrix usermatrix; /* as supplied by the caller */ } abs; struct { @@ -87,14 +99,31 @@ struct evdev_device { int dx, dy; } rel; + struct { + struct libinput_timer timer; + bool has_middle_button_scroll; + bool middle_button_scroll_active; + double threshold; + uint32_t direction; + } scroll; + enum evdev_event_type pending_event; enum evdev_device_seat_capability seat_caps; + enum evdev_device_tags tags; int is_mt; + int suspended; struct { struct motion_filter *filter; } pointer; + + /* Bitmask of pressed keys used to ignore initial release events from + * the kernel. */ + unsigned long hw_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) @@ -110,16 +139,43 @@ 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); + + /* 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); }; 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 * 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); @@ -130,9 +186,6 @@ evdev_mt_touchpad_create(struct evdev_device *device); struct evdev_dispatch * evdev_tablet_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); @@ -155,7 +208,11 @@ unsigned int evdev_device_get_id_vendor(struct evdev_device *device); void -evdev_device_calibrate(struct evdev_device *device, float calibration[6]); +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]); int evdev_device_has_capability(struct evdev_device *device, @@ -175,6 +232,39 @@ double evdev_device_transform_y(struct evdev_device *device, double y, uint32_t height); +int +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, + 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_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); 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, diff --git a/src/libinput-private.h b/src/libinput-private.h index 369bb8f3..7d6ec1a8 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" @@ -86,13 +88,33 @@ 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_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_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 { @@ -120,8 +142,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, @@ -255,4 +277,17 @@ tablet_notify_button(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 8c28eb49..cc7edc93 100644 --- a/src/libinput-util.h +++ b/src/libinput-util.h @@ -25,6 +25,8 @@ #include #include +#include +#include #include "libinput.h" @@ -72,9 +74,11 @@ 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++) + 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)) @@ -174,4 +178,130 @@ 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); +} + +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/src/libinput.c b/src/libinput.c index e52527be..91ead8a2 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -662,7 +662,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; } @@ -1605,8 +1604,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; @@ -1614,20 +1617,96 @@ 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); } + +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); +} + +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 ed56c0bb..0685220a 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. */ @@ -513,6 +545,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. * @@ -528,6 +563,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. * @@ -1676,24 +1714,18 @@ 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 * - * 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 + * @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 @@ -1764,6 +1796,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 * @@ -1776,7 +1818,7 @@ libinput_config_status_to_str(enum libinput_config_status status); * * @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); @@ -1791,7 +1833,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. @@ -1802,7 +1845,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 @@ -1812,13 +1855,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); /** @@ -1827,15 +1871,241 @@ 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); +/** + * @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. 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 + * 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]); + +/** + * 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 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/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) { diff --git a/src/udev-seat.c b/src/udev-seat.c index 635fabfd..f2be66e3 100644 --- a/src/udev-seat.c +++ b/src/udev-seat.c @@ -47,8 +47,10 @@ 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]; struct udev_seat *seat; device_seat = udev_device_get_property_value(udev_device, "ID_SEAT"); @@ -60,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"); @@ -76,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) { @@ -89,25 +92,25 @@ 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", - &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_set_default_calibration(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"); @@ -216,7 +219,6 @@ udev_input_remove_devices(struct udev_input *input) } } - static void udev_input_disable(struct libinput *libinput) { diff --git a/test/Makefile.am b/test/Makefile.am index 64596275..5bad5892 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -15,7 +15,9 @@ liblitest_la_SOURCES = \ ../src/libinput-util.c \ litest.h \ litest-int.h \ + litest-alps-semi-mt.c \ litest-bcm5974.c \ + litest-generic-singletouch.c \ litest-keyboard.c \ litest-mouse.c \ litest-synaptics.c \ @@ -37,8 +39,10 @@ run_tests = \ test-log \ test-tablet \ test-touchpad \ + test-trackpoint \ test-misc \ - test-keyboard + test-keyboard \ + test-device build_tests = \ test-build-cxx \ test-build-linker \ @@ -52,23 +56,23 @@ 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_tablet_SOURCES = tablet.c test_tablet_CFLAGS = $(AM_CPPFLAGS) @@ -77,15 +81,23 @@ test_tablet_LDFLAGS = -static test_touchpad_SOURCES = touchpad.c test_touchpad_LDADD = $(TEST_LIBS) -test_touchpad_LDFLAGS = -static +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 = -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 + +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 diff --git a/test/device.c b/test/device.c new file mode 100644 index 00000000..8a03b5ca --- /dev/null +++ b/test/device.c @@ -0,0 +1,589 @@ +/* + * 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" +#include "libinput-util.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_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(); + 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_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(); + 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 + +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 + +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|LITEST_TABLET); + litest_add("device:sendevents", device_sendevents_config_touchpad, LITEST_TOUCHPAD, LITEST_TABLET); + litest_add("device:sendevents", device_sendevents_config_default, LITEST_ANY, LITEST_TABLET); + litest_add("device:sendevents", device_disable, LITEST_POINTER, LITEST_TABLET); + litest_add("device:sendevents", device_disable_touchpad, LITEST_TOUCHPAD, LITEST_TABLET); + litest_add("device:sendevents", device_disable_events_pending, LITEST_POINTER, LITEST_TOUCHPAD|LITEST_TABLET); + litest_add("device:sendevents", device_double_disable, LITEST_ANY, LITEST_TABLET); + litest_add("device:sendevents", device_double_enable, LITEST_ANY, LITEST_TABLET); + 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); + + litest_add("device:sendevents", device_disable_topsoftbutton, LITEST_TOPBUTTONPAD, LITEST_ANY); + + return litest_run(argc, argv); +} diff --git a/test/keyboard.c b/test/keyboard.c index a55405cb..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) @@ -112,10 +113,189 @@ 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 + +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/litest-alps-semi-mt.c b/test/litest-alps-semi-mt.c new file mode 100644 index 00000000..12b93b8c --- /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 = 0, b = 0; /* 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 = 0, b = 0; /* 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-bcm5974.c b/test/litest-bcm5974.c index 10a9eb46..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 }, }; @@ -65,13 +61,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/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-int.h b/test/litest-int.h index c6c3e4a6..d989b2f0 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-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-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-trackpoint.c b/test/litest-trackpoint.c index 25a377c4..01ad34ea 100644 --- a/test/litest-trackpoint.c +++ b/test/litest-trackpoint.c @@ -49,12 +49,14 @@ 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, }; 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.c b/test/litest.c index 6ce5e6b5..1d076e03 100644 --- a/test/litest.c +++ b/test/litest.c @@ -24,6 +24,7 @@ #include "config.h" #endif +#include #include #include #include @@ -88,6 +89,8 @@ extern struct litest_test_device litest_wacom_bamboo_tablet_device; extern struct litest_test_device litest_wacom_cintiq_tablet_device; extern struct litest_test_device litest_wacom_intuos_tablet_device; extern struct litest_test_device litest_wacom_isdv4_tablet_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, @@ -102,10 +105,11 @@ struct litest_test_device* devices[] = { &litest_wacom_cintiq_tablet_device, &litest_wacom_intuos_tablet_device, &litest_wacom_isdv4_tablet_device, + &litest_alps_device, + &litest_generic_singletouch_device, NULL, }; - static struct list all_tests; static void @@ -163,6 +167,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); @@ -187,11 +194,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; @@ -199,10 +203,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)); @@ -211,7 +213,39 @@ 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; + + assert(type < LITEST_NO_DEVICE); + + 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 @@ -243,7 +277,6 @@ is_debugger_attached(void) return rc; } - static void litest_list_tests(struct list *tests) { @@ -339,7 +372,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); @@ -429,7 +462,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, @@ -539,6 +571,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, @@ -584,10 +628,12 @@ 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); libevdev_uinput_destroy(d->uinput); + free(d->private); memset(d,0, sizeof(*d)); free(d); } @@ -600,10 +646,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; @@ -631,6 +677,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, @@ -638,6 +694,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; @@ -645,7 +705,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++; } @@ -662,6 +722,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; @@ -671,7 +735,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++; } @@ -690,7 +754,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++; } @@ -832,6 +896,53 @@ 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; + type = va_arg(args, int); + } + 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) { @@ -952,8 +1063,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); } @@ -963,7 +1072,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); @@ -1047,3 +1160,63 @@ 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; + + litest_wait_for_event(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); +} + +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 2b3c5d78..205b6711 100644 --- a/test/litest.h +++ b/test/litest.h @@ -35,18 +35,20 @@ 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_WACOM_BAMBOO, - LITEST_WACOM_CINTIQ, - LITEST_WACOM_INTUOS, - LITEST_WACOM_ISDV4, + 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, + LITEST_WACOM_BAMBOO = -12, + LITEST_WACOM_CINTIQ = -13, + LITEST_WACOM_INTUOS = -14, + LITEST_WACOM_ISDV4 = -15, }; enum litest_device_feature { @@ -62,9 +64,11 @@ enum litest_device_feature { LITEST_SINGLE_TOUCH = 1 << 7, LITEST_APPLE_CLICKPAD = 1 << 8, LITEST_TOPBUTTONPAD = 1 << 9, - LITEST_TABLET = 1 << 10, - LITEST_DISTANCE = 1 << 11, - LITEST_TOOL_SERIAL = 1 << 12, + LITEST_SEMI_MT = 1 << 10, + LITEST_POINTINGSTICK = 1 << 11, + LITEST_TABLET = 1 << 12, + LITEST_DISTANCE = 1 << 13, + LITEST_TOOL_SERIAL = 1 << 14, }; struct litest_device { @@ -74,6 +78,9 @@ struct litest_device { bool owns_context; struct libinput_device *libinput_device; struct litest_device_interface *interface; + + int ntouches_down; + void *private; /* device-specific data */ }; struct libinput *litest_create_context(void); @@ -85,10 +92,16 @@ struct axis_replacement { 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); 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, @@ -116,6 +129,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, @@ -143,8 +159,14 @@ 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); +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/misc.c b/test/misc.c index c73ccdc0..d54ef5dd 100644 --- a/test/misc.c +++ b/test/misc.c @@ -469,6 +469,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); @@ -480,5 +557,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); } diff --git a/test/path.c b/test/path.c index 99b474eb..ecb78397 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; @@ -244,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); @@ -258,6 +256,8 @@ START_TEST(path_add_invalid_path) while ((event = libinput_get_event(li))) ck_abort(); + + libinput_unref(li); } END_TEST @@ -492,7 +492,6 @@ START_TEST(path_add_device_suspend_resume) ck_assert_int_eq(nevents, 2); - libinput_suspend(li); libinput_dispatch(li); @@ -576,7 +575,6 @@ START_TEST(path_add_device_suspend_resume_fail) ck_assert_int_eq(nevents, 2); - libinput_suspend(li); libinput_dispatch(li); @@ -667,7 +665,6 @@ START_TEST(path_add_device_suspend_resume_remove_device) ck_assert_int_eq(nevents, 2); - libinput_suspend(li); libinput_dispatch(li); @@ -796,26 +793,26 @@ START_TEST(path_seat_recycle) } 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("path:seat events", path_added_seat, LITEST_ANY, LITEST_ANY); +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); + 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_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("path:device events", path_add_invalid_path, LITEST_ANY, LITEST_ANY); - 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_for_device("path:device events", path_add_device, LITEST_SYNAPTICS_CLICKPAD); + litest_add_no_device("path:device events", path_add_invalid_path); + 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/pointer.c b/test/pointer.c index aa75274b..f704372e 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) @@ -145,13 +132,104 @@ 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); } } 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) { @@ -296,12 +374,37 @@ 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); 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); + 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 6f6eec0a..1c0a4b6e 100644 --- a/test/touch.c +++ b/test/touch.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include "libinput-util.h" @@ -112,7 +113,6 @@ START_TEST(touch_abs_transform) } END_TEST - START_TEST(touch_many_slots) { struct libinput *libinput; @@ -213,6 +213,194 @@ 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_config_calibration_set_matrix(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_config_calibration_set_matrix(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_config_calibration_set_matrix(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) { @@ -220,6 +408,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); } diff --git a/test/touchpad.c b/test/touchpad.c index c1bdbd53..522cee65 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -89,34 +89,14 @@ 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(); 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); @@ -125,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); @@ -143,7 +123,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); @@ -155,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) { @@ -183,8 +164,37 @@ 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); +} +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); } @@ -195,7 +205,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); @@ -206,11 +217,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); } @@ -221,7 +232,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); @@ -232,11 +244,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); } @@ -247,25 +259,29 @@ 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); - /* 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); - 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); } @@ -276,7 +292,44 @@ 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); + + /* 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); + + 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); +} +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); @@ -293,10 +346,10 @@ 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); + 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); } @@ -307,7 +360,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); @@ -325,10 +379,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); } @@ -415,7 +469,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); @@ -432,14 +487,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); } @@ -451,7 +506,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); @@ -462,8 +518,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); @@ -478,10 +534,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); @@ -489,13 +545,119 @@ 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); } 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, + LIBINPUT_CONFIG_TAP_ENABLED); + + 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); + + litest_assert_button_event(li, BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); + msleep(300); /* tap-n-drag timeout */ + litest_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); + + litest_assert_button_event(li, BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); + msleep(300); /* tap-n-drag timeout */ + litest_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); + + litest_assert_button_event(li, BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); + msleep(300); /* tap-n-drag timeout */ + litest_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); @@ -512,10 +674,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); } @@ -539,10 +701,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); } @@ -562,10 +724,42 @@ 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 + +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); + + 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 @@ -601,8 +795,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); @@ -625,8 +819,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 @@ -641,16 +835,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); @@ -670,16 +864,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); @@ -693,7 +887,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); @@ -708,14 +903,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); @@ -723,8 +918,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); } @@ -735,7 +930,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); @@ -750,24 +946,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 @@ -795,9 +991,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 */ @@ -840,9 +1036,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); } @@ -868,9 +1064,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); @@ -931,9 +1127,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); } @@ -956,18 +1152,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); } @@ -990,18 +1186,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); } @@ -1018,18 +1214,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); } @@ -1046,18 +1242,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); } @@ -1074,18 +1270,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); } @@ -1150,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(); @@ -1199,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); @@ -1218,7 +1373,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 @@ -1227,8 +1383,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 @@ -1237,7 +1395,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 @@ -1245,10 +1415,14 @@ 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); + if (libinput_device_get_id_vendor(dev->libinput_device) == 0x5ac) /* Apple */ + return 1; - return width >= 80; + rc = libinput_device_get_size(dev->libinput_device, &width, &height); + + return rc == 0 && width >= 80; } START_TEST(touchpad_palm_detect_at_edge) @@ -1351,7 +1525,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); @@ -1415,15 +1589,21 @@ 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_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); 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 */ @@ -1431,9 +1611,13 @@ 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); + 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); 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); +} diff --git a/test/udev.c b/test/udev.c index f07decb6..95206635 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; @@ -408,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); } 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 diff --git a/tools/event-gui.c b/tools/event-gui.c index cd01f989..f9dba829 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 @@ -427,7 +436,6 @@ parse_opts(int argc, char *argv[]) return 0; } - static int open_restricted(const char *path, int flags, void *user_data) {