diff --git a/configure.ac b/configure.ac index 860324ac..fd402e20 100644 --- a/configure.ac +++ b/configure.ac @@ -20,7 +20,7 @@ AC_SUBST([LIBINPUT_VERSION], [libinput_version]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_MACRO_DIR([m4]) -AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz]) +AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz subdir-objects]) # Before making a release, the LIBINPUT_LT_VERSION string should be # modified. @@ -57,6 +57,8 @@ PKG_PROG_PKG_CONFIG() PKG_CHECK_MODULES(MTDEV, [mtdev >= 1.1.0]) PKG_CHECK_MODULES(LIBUDEV, [libudev]) PKG_CHECK_MODULES(LIBEVDEV, [libevdev >= 0.4]) +AC_CHECK_LIB([m], [atan2]) +AC_CHECK_LIB([rt], [clock_gettime]) if test "x$GCC" = "xyes"; then GCC_CXXFLAGS="-Wall -Wextra -Wno-unused-parameter -g -fvisibility=hidden" @@ -119,3 +121,12 @@ AC_CONFIG_FILES([Makefile test/Makefile tools/Makefile]) AC_OUTPUT + +AC_MSG_RESULT([ + Prefix ${prefix} + + Build documentation ${have_doxygen} + Build tests ${build_tests} + Tests use valgrind ${VALGRIND} + Build GUI event tool ${build_eventgui} + ]) diff --git a/doc/touchpad-softbutton-state-machine.svg b/doc/touchpad-softbutton-state-machine.svg index 05254d0e..1d569bf6 100644 --- a/doc/touchpad-softbutton-state-machine.svg +++ b/doc/touchpad-softbutton-state-machine.svg @@ -1,532 +1,360 @@ + - - - - - - - -NONE - -on-entry: - -curr = none - - - - -BOTTOM_NEW - -on-entry: - -curr = button - -start enter timeout - - - - -AREA - -on-entry: - -curr =area - -set_pointer() - - - - -finger in - -area or top - - - - -BOTTOM - - - - -finger - -up - - - - -phys - -button - -press - - - - - - -enter - -timeout - - - - - - - - -finger in - -area or top - - - - - - -BOTTOM_TO_AREA - -on-entry: - -start leave timeout - - - - - - -leave - -timeout - - - - - - - - -finger in - -bottom - - - - - - - - -finger in - -area - - - - - - - - -finger in - -bottom - -button != curr - - - - - - - - - - - - -finger in - -bottom - -button == curr - - - - - - - - - - - - - - - - - - - -Check state of - -all touches - - - - - - - - - -tp_post_softbutton_buttons() - - - - -!buttons.click_pend - -&& current == old - - - - - - -yes - - - - - - -no - - - - - - -current = buttons.state & 0x01 - -old = buttons.old_state & 0x01 - -button = 0 - - - - - - -All touches are in state none - - - - - - -no - - - - - - -yes - - - - -buttons.click_pend = 1 - - - - - - -(Some touches are in middle) || - -((Some touches are in right) && - -(Some touches are in left)) - - - - - - -yes - - - - -button = BTN_MIDDLE - - - - - - -current - - - - - - -no - - - - - - -yes - - - - -Some touches are in right - - - - - - -yes - - - - - - -no - - - - -button = BTN_RIGHT - - - - -button = BTN_LEFT - - - - - - -no - - - - -buttons.active = button - -state = BUTTON_PRESSED - - - - - - - - - - -button = buttons.active - -buttons.active = 0 - -state = BUTTON_RELEASED - - - - -buttons.click_pend = 0 - - - - - - - - -button - - - - - - -no - - - - - - -yes - - - - -pointer_notify_button( - -button, state) - - - - - - - - - - -finger in - -top - - - - - - - -TOP_NEW - -on-entry: - -curr = button - -start enter timeout - - - - - - - -TOP - - - - - -phys - -button - -press - - - - -enter - -timeout - - - - -finger in - -top - -button != curr - - - - - - - - - - - - - - - - - - -finger in - -area or - -bottom - - - - - - - - -TOP_TO_IGNORE - -on-entry: - -start leave timeout - - - - -finger in - -area or bottom - - - - -finger in - -top - -button == curr - - - - - - - - - - - - - - -IGNORE - -on-entry: - -curr =none - - - - -leave - -timeout - - - - - - - - - - + + + + + + + NONE + on-entry: + curr = none + + + + BOTTOM + on-entry: + curr = button + + + + AREA + on-entry: + curr =area + set_pointer() + + + + finger in + area or top + + + + finger + up + + + + + + finger in + bottom + + + + + + + + finger in + area + + + + + + + + finger in + bottom + button != curr + + + + + + + + + + + + + Check state of + all touches + + + + + + + + + tp_post_softbutton_buttons() + + + + !buttons.click_pend + && current == old + + + + + + yes + + + + + + no + + + + + + current = buttons.state & 0x01 + old = buttons.old_state & 0x01 + button = 0 + + + + + + All touches are in state none + + + + + + no + + + + + + yes + + + + buttons.click_pend = 1 + + + + + + (Some touches are in middle) || + ((Some touches are in right) && + (Some touches are in left)) + + + + + + yes + + + + button = BTN_MIDDLE + + + + + + current + + + + + + no + + + + + + yes + + + + Some touches are in right + + + + + + yes + + + + + + no + + + + button = BTN_RIGHT + + + + button = BTN_LEFT + + + + + + no + + + + buttons.active = button + state = BUTTON_PRESSED + + + + + + + + + + button = buttons.active + buttons.active = 0 + state = BUTTON_RELEASED + + + + buttons.click_pend = 0 + + + + + + + + button + + + + + + no + + + + + + yes + + + + pointer_notify_button( + button, state) + + + + + + + + + + finger in + top + + + + + + TOP_NEW + on-entry: + curr = button + start enter timeout + + + + + + + TOP + + + + + phys + button + press + + + + enter + timeout + + + + finger in + top + button != curr + + + + + + + + + + + + + + + + + + finger in + area or + bottom + + + + + + + + TOP_TO_IGNORE + on-entry: + start leave timeout + + + + finger in + area or bottom + + + + finger in + top + button == curr + + + + + + + + + + + + + + IGNORE + on-entry: + curr =none + + + + leave + timeout + + + + + + + + + + diff --git a/doc/touchpad-tap-state-machine.svg b/doc/touchpad-tap-state-machine.svg index 50ebc713..10739c68 100644 --- a/doc/touchpad-tap-state-machine.svg +++ b/doc/touchpad-tap-state-machine.svg @@ -1,771 +1,756 @@ -IDLETOUCHfirstfinger downfinger upbutton -1presstimeoutmove > thresholdsecondfinger downTOUCH_2secondfinger upbutton -2pressmove > -thresholdtimeoutbutton 1releasebutton -2releaseTAPPEDtimeoutfirstfinger downDRAGGINGfirstfinger upbtn1releaseIDLEthirdfinger downTOUCH_3secondfinger upbutton 3pressbutton 3releasemove > thresholdIDLEtimeoutthirdfinger upfirstfinger upIDLEfourthfinger downDRAGGING_OR_DOUBLETAPtimeoutfirstfinger upbutton -1releasebutton -1pressbtn1releasesecondfinger downmove > thresholdHOLDfirstfinger upsecondfinger downTOUCH_2_HOLDsecondfinger upfirstfinger upthirdfinger downTOUCH_3_HOLDsecondfinger upthirdfinger upfourthfinger downDEADsecondfinger upthirdfinger upfourthfinger downfirstfinger upfirstfinger upfirstfinger upIDLEif fingercount == 0secondfinger upDRAGGING_2firstfinger upsecondfinger downthirdfinger downbtn1releasephysbuttonpressfourthfinger downphysbuttonpressbutton 1releaseDRAGGING_WAITtimeoutfirstfinger down + + + + + + + + + + + + + IDLE + + + + TOUCH + + + + first + finger down + + + + + + finger up + + + + + + button 1 + press + + + + timeout + + + + + + move > + threshold + + + + + + second + finger down + + + + + + TOUCH_2 + + + + second + finger up + + + + + + button 2 + press + + + + move > + threshold + + + + timeout + + + + + + + + button 1 + release + + + + button 2 + release + + + + + + + + TAPPED + + + + timeout + + + + + + first + finger down + + + + + + DRAGGING + + + + first + finger up + + + + btn1 + release + + + + + + + + + + IDLE + + + + third + finger down + + + + + + TOUCH_3 + + + + + + button 3 + press + + + + button 3 + release + + + + + + move > + threshold + + + + + + IDLE + + + + timeout + + + + + + first + finger up + + + + + + IDLE + + + + fourth + finger down + + + + + + + + DRAGGING_OR_DOUBLETAP + + + + + + timeout + + + + + + first + finger up + + + + + + button 1 + release + + + + button 1 + press + + + + btn1 + release + + + + + + + + + + + + second + finger down + + + + + + move > + threshold + + + + + + + + HOLD + + + + first + finger up + + + + + + + + second + finger down + + + + + + + + + + TOUCH_2_HOLD + + + + second + finger up + + + + + + first + finger up + + + + + + + + + + third + finger down + + + + + + + + + + TOUCH_3_HOLD + + + + + + fourth + finger down + + + + DEAD + + + + + + + + + + any finger up + + + + fourth + finger up + + + + any finger up + + + + + + + + yes + + + + any finger up + + + + + + + + + + + + IDLE + + + + if finger + count == 0 + + + + + + + + + + second + finger up + + + + DRAGGING_2 + + + + + + + + first + finger up + + + + + + + + + + second + finger down + + + + + + + + + + third + finger down + + + + + + btn1 + release + + + + + + phys + button + press + + + + + + + + + + + + + + + + phys + button + press + + + + + + button 1 + release + + + + + + + + + + + + + + DRAGGING_WAIT + + + + timeout + + + + + + + + + + first + finger down + + + + + + TOUCH_TOUCH + + + + TOUCH_IDLE + + + + + + + + + + + + TOUCH_DEAD + + + + + + + + + + + + + + yes + + + + TOUCH_DEAD + + + + + + + + + + TOUCH_IDLE + + + + + + TOUCH_TOUCH + + + + + + + + TOUCH_IDLE + + + + + + TOUCH_IDLE + + + + + + TOUCH_IDLE + + + + + + TOUCH_TOUCH + + + + + + that finger + TOUCH_IDLE + + + + + + TOUCH_DEAD + + + + + + + + + + that finger + TOUCH_IDLE + + + + + + + + no + + + + TOUCH_TOUCH + + + + + + TOUCH_IDLE + + + + + + TOUCH_TOUCH + + + + + + TOUCH_DEAD + + + + + + + + TOUCH_IDLE + + + + + + TOUCH_TOUCH + + + + + + + + TOUCH_TOUCH + + + + + + TOUCH_IDLE + + + + + + TOUCH_IDLE + + + + + + TOUCH_TOUCH + + + + + + TOUCH_IDLE + + + + + + TOUCH_TOUCH + + + + + + that finger + TOUCH_IDLE + + + + + + TOUCH_DEAD + + + + + + TOUCH_DEAD + + + + TOUCH_DEAD + + + + TOUCH_DEAD + + + + + + TOUCH_DEAD + + + + + + TOUCH_DEAD + + + + + + state == + TOUCH_TOUCH + + + + that finger state == + TOUCH_TOUCH + + + + + + no + + + + TOUCH_DEAD + + + + + + TOUCH_DEAD + + + + TOUCH_DEAD + + + diff --git a/src/Makefile.am b/src/Makefile.am index b880a699..44108367 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -29,8 +29,7 @@ libinput_la_SOURCES = \ libinput_la_LIBADD = $(MTDEV_LIBS) \ $(LIBUDEV_LIBS) \ - $(LIBEVDEV_LIBS) \ - -lm + $(LIBEVDEV_LIBS) libinput_la_CFLAGS = -I$(top_srcdir)/include \ $(MTDEV_CFLAGS) \ $(LIBUDEV_CFLAGS) \ diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index 2ac231c7..bd3c0e25 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -52,8 +52,6 @@ button_state_to_str(enum button_state state) { CASE_RETURN_STRING(BUTTON_STATE_NONE); CASE_RETURN_STRING(BUTTON_STATE_AREA); CASE_RETURN_STRING(BUTTON_STATE_BOTTOM); - CASE_RETURN_STRING(BUTTON_STATE_BOTTOM_NEW); - CASE_RETURN_STRING(BUTTON_STATE_BOTTOM_TO_AREA); CASE_RETURN_STRING(BUTTON_STATE_TOP); CASE_RETURN_STRING(BUTTON_STATE_TOP_NEW); CASE_RETURN_STRING(BUTTON_STATE_TOP_TO_IGNORE); @@ -161,13 +159,7 @@ tp_button_set_state(struct tp_dispatch *tp, struct tp_touch *t, tp_set_pointer(tp, t); break; case BUTTON_STATE_BOTTOM: - break; - case BUTTON_STATE_BOTTOM_NEW: t->button.curr = event; - tp_button_set_enter_timer(tp, t); - break; - case BUTTON_STATE_BOTTOM_TO_AREA: - tp_button_set_leave_timer(tp, t); break; case BUTTON_STATE_TOP: break; @@ -192,7 +184,7 @@ tp_button_none_handle_event(struct tp_dispatch *tp, switch (event) { case BUTTON_EVENT_IN_BOTTOM_R: case BUTTON_EVENT_IN_BOTTOM_L: - tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM_NEW, event); + tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM, event); break; case BUTTON_EVENT_IN_TOP_R: case BUTTON_EVENT_IN_TOP_M: @@ -237,92 +229,28 @@ tp_button_area_handle_event(struct tp_dispatch *tp, static void tp_button_bottom_handle_event(struct tp_dispatch *tp, - struct tp_touch *t, - enum button_event event) + struct tp_touch *t, + enum button_event event) { switch (event) { case BUTTON_EVENT_IN_BOTTOM_R: case BUTTON_EVENT_IN_BOTTOM_L: if (event != t->button.curr) - tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM_NEW, - event); - break; - case BUTTON_EVENT_IN_TOP_R: - case BUTTON_EVENT_IN_TOP_M: - case BUTTON_EVENT_IN_TOP_L: - case BUTTON_EVENT_IN_AREA: - tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM_TO_AREA, event); - break; - case BUTTON_EVENT_UP: - tp_button_set_state(tp, t, BUTTON_STATE_NONE, event); - break; - case BUTTON_EVENT_PRESS: - case BUTTON_EVENT_RELEASE: - case BUTTON_EVENT_TIMEOUT: - break; - } -} - -static void -tp_button_bottom_new_handle_event(struct tp_dispatch *tp, - struct tp_touch *t, - enum button_event event) -{ - switch(event) { - case BUTTON_EVENT_IN_BOTTOM_R: - case BUTTON_EVENT_IN_BOTTOM_L: - if (event != t->button.curr) - tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM_NEW, - event); - break; - case BUTTON_EVENT_IN_TOP_R: - case BUTTON_EVENT_IN_TOP_M: - case BUTTON_EVENT_IN_TOP_L: - case BUTTON_EVENT_IN_AREA: - tp_button_set_state(tp, t, BUTTON_STATE_AREA, event); - break; - case BUTTON_EVENT_UP: - tp_button_set_state(tp, t, BUTTON_STATE_NONE, event); - break; - case BUTTON_EVENT_PRESS: - tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM, event); - break; - case BUTTON_EVENT_RELEASE: - break; - case BUTTON_EVENT_TIMEOUT: - tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM, event); - break; - } -} - -static void -tp_button_bottom_to_area_handle_event(struct tp_dispatch *tp, - struct tp_touch *t, - enum button_event event) -{ - switch(event) { - case BUTTON_EVENT_IN_BOTTOM_R: - case BUTTON_EVENT_IN_BOTTOM_L: - if (event == t->button.curr) tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM, event); - else - tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM_NEW, - event); break; case BUTTON_EVENT_IN_TOP_R: case BUTTON_EVENT_IN_TOP_M: case BUTTON_EVENT_IN_TOP_L: case BUTTON_EVENT_IN_AREA: + tp_button_set_state(tp, t, BUTTON_STATE_AREA, event); break; case BUTTON_EVENT_UP: tp_button_set_state(tp, t, BUTTON_STATE_NONE, event); break; case BUTTON_EVENT_PRESS: case BUTTON_EVENT_RELEASE: - break; case BUTTON_EVENT_TIMEOUT: - tp_button_set_state(tp, t, BUTTON_STATE_AREA, event); break; } } @@ -465,12 +393,6 @@ tp_button_handle_event(struct tp_dispatch *tp, case BUTTON_STATE_BOTTOM: tp_button_bottom_handle_event(tp, t, event); break; - case BUTTON_STATE_BOTTOM_NEW: - tp_button_bottom_new_handle_event(tp, t, event); - break; - case BUTTON_STATE_BOTTOM_TO_AREA: - tp_button_bottom_to_area_handle_event(tp, t, event); - break; case BUTTON_STATE_TOP: tp_button_top_handle_event(tp, t, event); break; @@ -581,11 +503,13 @@ tp_init_buttons(struct tp_dispatch *tp, libevdev_has_event_code(device->evdev, EV_KEY, BTN_RIGHT)) { if (tp->buttons.is_clickpad) log_bug_kernel(libinput, - "clickpad advertising right button\n"); + "%s: clickpad advertising right button\n", + device->sysname); } else { if (!tp->buttons.is_clickpad) log_bug_kernel(libinput, - "non clickpad without right button?\n"); + "%s: non clickpad without right button?\n", + device->sysname); } absinfo_x = device->abs.absinfo_x; @@ -603,11 +527,28 @@ tp_init_buttons(struct tp_dispatch *tp, if (tp->buttons.is_clickpad && !tp->buttons.use_clickfinger) { int xoffset = absinfo_x->minimum, yoffset = absinfo_y->minimum; - tp->buttons.bottom_area.top_edge = height * .8 + yoffset; + 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) { - tp->buttons.top_area.bottom_edge = height * .08 + yoffset; + /* 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 { @@ -792,3 +733,9 @@ tp_button_touch_active(struct tp_dispatch *tp, struct tp_touch *t) { return t->button.state == BUTTON_STATE_AREA; } + +bool +tp_button_is_inside_softbutton_area(struct tp_dispatch *tp, struct tp_touch *t) +{ + return is_inside_top_button_area(tp, t) || is_inside_bottom_button_area(tp, t); +} diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index 25412184..0f1f837d 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -95,12 +95,16 @@ tap_event_to_str(enum tap_event event) { static void tp_tap_notify(struct tp_dispatch *tp, + struct tp_touch *t, uint64_t time, int nfingers, enum libinput_button_state state) { int32_t button; + if (t && t->tap.state == TAP_TOUCH_STATE_DEAD) + return; + switch (nfingers) { case 1: button = BTN_LEFT; break; case 2: button = BTN_RIGHT; break; @@ -128,7 +132,9 @@ tp_tap_clear_timer(struct tp_dispatch *tp) } static void -tp_tap_idle_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time) +tp_tap_idle_handle_event(struct tp_dispatch *tp, + struct tp_touch *t, + enum tap_event event, uint64_t time) { struct libinput *libinput = tp->device->base.seat->libinput; @@ -151,7 +157,9 @@ tp_tap_idle_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t } static void -tp_tap_touch_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time) +tp_tap_touch_handle_event(struct tp_dispatch *tp, + struct tp_touch *t, + enum tap_event event, uint64_t time) { switch (event) { @@ -161,7 +169,7 @@ tp_tap_touch_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t break; case TAP_EVENT_RELEASE: tp->tap.state = TAP_STATE_TAPPED; - tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_PRESSED); + tp_tap_notify(tp, t, time, 1, LIBINPUT_BUTTON_STATE_PRESSED); tp_tap_set_timer(tp, time); break; case TAP_EVENT_TIMEOUT: @@ -176,7 +184,9 @@ tp_tap_touch_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t } static void -tp_tap_hold_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time) +tp_tap_hold_handle_event(struct tp_dispatch *tp, + struct tp_touch *t, + enum tap_event event, uint64_t time) { switch (event) { @@ -197,7 +207,9 @@ tp_tap_hold_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t } static void -tp_tap_tapped_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time) +tp_tap_tapped_handle_event(struct tp_dispatch *tp, + struct tp_touch *t, + enum tap_event event, uint64_t time) { struct libinput *libinput = tp->device->base.seat->libinput; @@ -213,17 +225,19 @@ tp_tap_tapped_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_ break; case TAP_EVENT_TIMEOUT: tp->tap.state = TAP_STATE_IDLE; - tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED); + tp_tap_notify(tp, t, time, 1, LIBINPUT_BUTTON_STATE_RELEASED); break; case TAP_EVENT_BUTTON: tp->tap.state = TAP_STATE_DEAD; - tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED); + tp_tap_notify(tp, t, time, 1, LIBINPUT_BUTTON_STATE_RELEASED); break; } } static void -tp_tap_touch2_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time) +tp_tap_touch2_handle_event(struct tp_dispatch *tp, + struct tp_touch *t, + enum tap_event event, uint64_t time) { switch (event) { @@ -233,8 +247,8 @@ tp_tap_touch2_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_ break; case TAP_EVENT_RELEASE: tp->tap.state = TAP_STATE_HOLD; - tp_tap_notify(tp, time, 2, LIBINPUT_BUTTON_STATE_PRESSED); - tp_tap_notify(tp, time, 2, LIBINPUT_BUTTON_STATE_RELEASED); + tp_tap_notify(tp, t, time, 2, LIBINPUT_BUTTON_STATE_PRESSED); + tp_tap_notify(tp, t, time, 2, LIBINPUT_BUTTON_STATE_RELEASED); tp_tap_clear_timer(tp); break; case TAP_EVENT_MOTION: @@ -249,7 +263,9 @@ tp_tap_touch2_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_ } static void -tp_tap_touch2_hold_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time) +tp_tap_touch2_hold_handle_event(struct tp_dispatch *tp, + struct tp_touch *t, + enum tap_event event, uint64_t time) { switch (event) { @@ -271,7 +287,9 @@ tp_tap_touch2_hold_handle_event(struct tp_dispatch *tp, enum tap_event event, ui } static void -tp_tap_touch3_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time) +tp_tap_touch3_handle_event(struct tp_dispatch *tp, + struct tp_touch *t, + enum tap_event event, uint64_t time) { switch (event) { @@ -286,8 +304,8 @@ tp_tap_touch3_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_ break; case TAP_EVENT_RELEASE: tp->tap.state = TAP_STATE_TOUCH_2_HOLD; - tp_tap_notify(tp, time, 3, LIBINPUT_BUTTON_STATE_PRESSED); - tp_tap_notify(tp, time, 3, LIBINPUT_BUTTON_STATE_RELEASED); + tp_tap_notify(tp, t, time, 3, LIBINPUT_BUTTON_STATE_PRESSED); + tp_tap_notify(tp, t, time, 3, LIBINPUT_BUTTON_STATE_RELEASED); break; case TAP_EVENT_BUTTON: tp->tap.state = TAP_STATE_DEAD; @@ -296,7 +314,9 @@ tp_tap_touch3_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_ } static void -tp_tap_touch3_hold_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time) +tp_tap_touch3_hold_handle_event(struct tp_dispatch *tp, + struct tp_touch *t, + enum tap_event event, uint64_t time) { switch (event) { @@ -317,7 +337,9 @@ tp_tap_touch3_hold_handle_event(struct tp_dispatch *tp, enum tap_event event, ui } static void -tp_tap_dragging_or_doubletap_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time) +tp_tap_dragging_or_doubletap_handle_event(struct tp_dispatch *tp, + struct tp_touch *t, + enum tap_event event, uint64_t time) { switch (event) { case TAP_EVENT_TOUCH: @@ -325,9 +347,9 @@ tp_tap_dragging_or_doubletap_handle_event(struct tp_dispatch *tp, enum tap_event break; case TAP_EVENT_RELEASE: tp->tap.state = TAP_STATE_IDLE; - tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED); - tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_PRESSED); - tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED); + tp_tap_notify(tp, t, time, 1, LIBINPUT_BUTTON_STATE_RELEASED); + tp_tap_notify(tp, t, time, 1, LIBINPUT_BUTTON_STATE_PRESSED); + tp_tap_notify(tp, t, time, 1, LIBINPUT_BUTTON_STATE_RELEASED); tp_tap_clear_timer(tp); break; case TAP_EVENT_MOTION: @@ -336,13 +358,15 @@ tp_tap_dragging_or_doubletap_handle_event(struct tp_dispatch *tp, enum tap_event break; case TAP_EVENT_BUTTON: tp->tap.state = TAP_STATE_DEAD; - tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED); + tp_tap_notify(tp, t, time, 1, LIBINPUT_BUTTON_STATE_RELEASED); break; } } static void -tp_tap_dragging_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time) +tp_tap_dragging_handle_event(struct tp_dispatch *tp, + struct tp_touch *t, + enum tap_event event, uint64_t time) { switch (event) { @@ -359,13 +383,15 @@ tp_tap_dragging_handle_event(struct tp_dispatch *tp, enum tap_event event, uint6 break; case TAP_EVENT_BUTTON: tp->tap.state = TAP_STATE_DEAD; - tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED); + tp_tap_notify(tp, t, time, 1, LIBINPUT_BUTTON_STATE_RELEASED); break; } } static void -tp_tap_dragging_wait_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time) +tp_tap_dragging_wait_handle_event(struct tp_dispatch *tp, + struct tp_touch *t, + enum tap_event event, uint64_t time) { switch (event) { @@ -378,17 +404,19 @@ tp_tap_dragging_wait_handle_event(struct tp_dispatch *tp, enum tap_event event, break; case TAP_EVENT_TIMEOUT: tp->tap.state = TAP_STATE_IDLE; - tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED); + tp_tap_notify(tp, t, time, 1, LIBINPUT_BUTTON_STATE_RELEASED); break; case TAP_EVENT_BUTTON: tp->tap.state = TAP_STATE_DEAD; - tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED); + tp_tap_notify(tp, t, time, 1, LIBINPUT_BUTTON_STATE_RELEASED); break; } } static void -tp_tap_dragging2_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time) +tp_tap_dragging2_handle_event(struct tp_dispatch *tp, + struct tp_touch *t, + enum tap_event event, uint64_t time) { switch (event) { @@ -397,7 +425,7 @@ tp_tap_dragging2_handle_event(struct tp_dispatch *tp, enum tap_event event, uint break; case TAP_EVENT_TOUCH: tp->tap.state = TAP_STATE_DEAD; - tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED); + tp_tap_notify(tp, t, time, 1, LIBINPUT_BUTTON_STATE_RELEASED); break; case TAP_EVENT_MOTION: case TAP_EVENT_TIMEOUT: @@ -405,13 +433,16 @@ tp_tap_dragging2_handle_event(struct tp_dispatch *tp, enum tap_event event, uint break; case TAP_EVENT_BUTTON: tp->tap.state = TAP_STATE_DEAD; - tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED); + tp_tap_notify(tp, t, time, 1, LIBINPUT_BUTTON_STATE_RELEASED); break; } } static void -tp_tap_dead_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time) +tp_tap_dead_handle_event(struct tp_dispatch *tp, + struct tp_touch *t, + enum tap_event event, + uint64_t time) { switch (event) { @@ -428,55 +459,58 @@ tp_tap_dead_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t } static void -tp_tap_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time) +tp_tap_handle_event(struct tp_dispatch *tp, + struct tp_touch *t, + enum tap_event event, + uint64_t time) { struct libinput *libinput = tp->device->base.seat->libinput; enum tp_tap_state current; - if (!tp->tap.enabled) - return; - current = tp->tap.state; switch(tp->tap.state) { case TAP_STATE_IDLE: - tp_tap_idle_handle_event(tp, event, time); + if (!tp->tap.enabled) + break; + + tp_tap_idle_handle_event(tp, t, event, time); break; case TAP_STATE_TOUCH: - tp_tap_touch_handle_event(tp, event, time); + tp_tap_touch_handle_event(tp, t, event, time); break; case TAP_STATE_HOLD: - tp_tap_hold_handle_event(tp, event, time); + tp_tap_hold_handle_event(tp, t, event, time); break; case TAP_STATE_TAPPED: - tp_tap_tapped_handle_event(tp, event, time); + tp_tap_tapped_handle_event(tp, t, event, time); break; case TAP_STATE_TOUCH_2: - tp_tap_touch2_handle_event(tp, event, time); + tp_tap_touch2_handle_event(tp, t, event, time); break; case TAP_STATE_TOUCH_2_HOLD: - tp_tap_touch2_hold_handle_event(tp, event, time); + tp_tap_touch2_hold_handle_event(tp, t, event, time); break; case TAP_STATE_TOUCH_3: - tp_tap_touch3_handle_event(tp, event, time); + tp_tap_touch3_handle_event(tp, t, event, time); break; case TAP_STATE_TOUCH_3_HOLD: - tp_tap_touch3_hold_handle_event(tp, event, time); + tp_tap_touch3_hold_handle_event(tp, t, event, time); break; case TAP_STATE_DRAGGING_OR_DOUBLETAP: - tp_tap_dragging_or_doubletap_handle_event(tp, event, time); + tp_tap_dragging_or_doubletap_handle_event(tp, t, event, time); break; case TAP_STATE_DRAGGING: - tp_tap_dragging_handle_event(tp, event, time); + tp_tap_dragging_handle_event(tp, t, event, time); break; case TAP_STATE_DRAGGING_WAIT: - tp_tap_dragging_wait_handle_event(tp, event, time); + tp_tap_dragging_wait_handle_event(tp, t, event, time); break; case TAP_STATE_DRAGGING_2: - tp_tap_dragging2_handle_event(tp, event, time); + tp_tap_dragging2_handle_event(tp, t, event, time); break; case TAP_STATE_DEAD: - tp_tap_dead_handle_event(tp, event, time); + tp_tap_dead_handle_event(tp, t, event, time); break; } @@ -508,19 +542,34 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time) int filter_motion = 0; if (tp->queued & TOUCHPAD_EVENT_BUTTON_PRESS) - tp_tap_handle_event(tp, TAP_EVENT_BUTTON, time); + tp_tap_handle_event(tp, NULL, TAP_EVENT_BUTTON, time); tp_for_each_touch(tp, t) { if (!t->dirty || t->state == TOUCH_NONE) continue; - if (t->state == TOUCH_BEGIN) - tp_tap_handle_event(tp, TAP_EVENT_TOUCH, time); - else if (t->state == TOUCH_END) - tp_tap_handle_event(tp, TAP_EVENT_RELEASE, time); - else if (tp->tap.state != TAP_STATE_IDLE && - tp_tap_exceeds_motion_threshold(tp, t)) - tp_tap_handle_event(tp, TAP_EVENT_MOTION, time); + if (tp->queued & TOUCHPAD_EVENT_BUTTON_PRESS) + t->tap.state = TAP_TOUCH_STATE_DEAD; + + if (t->state == TOUCH_BEGIN) { + t->tap.state = TAP_TOUCH_STATE_TOUCH; + tp_tap_handle_event(tp, t, TAP_EVENT_TOUCH, time); + } else if (t->state == TOUCH_END) { + tp_tap_handle_event(tp, t, TAP_EVENT_RELEASE, time); + t->tap.state = TAP_TOUCH_STATE_DEAD; + } else if (tp->tap.state != TAP_STATE_IDLE && + tp_tap_exceeds_motion_threshold(tp, t)) { + struct tp_touch *tmp; + + /* Any touch exceeding the threshold turns all + * touches into DEAD */ + tp_for_each_touch(tp, tmp) { + if (tmp->tap.state == TAP_TOUCH_STATE_TOUCH) + tmp->tap.state = TAP_TOUCH_STATE_DEAD; + } + + tp_tap_handle_event(tp, t, TAP_EVENT_MOTION, time); + } } /** @@ -550,21 +599,87 @@ static void tp_tap_handle_timeout(uint64_t time, void *data) { struct tp_dispatch *tp = data; + struct tp_touch *t; - tp_tap_handle_event(tp, TAP_EVENT_TIMEOUT, time); + tp_tap_handle_event(tp, NULL, TAP_EVENT_TIMEOUT, time); + + tp_for_each_touch(tp, t) { + if (t->state == TOUCH_NONE || + t->tap.state == TAP_TOUCH_STATE_IDLE) + continue; + + t->tap.state = TAP_TOUCH_STATE_DEAD; + } +} + +static int +tp_tap_config_count(struct libinput_device *device) +{ + struct evdev_dispatch *dispatch; + struct tp_dispatch *tp; + + dispatch = ((struct evdev_device *) device)->dispatch; + tp = container_of(dispatch, tp, base); + + return min(tp->ntouches, 3); /* we only do up to 3 finger tap */ +} + +static enum libinput_config_status +tp_tap_config_set_enabled(struct libinput_device *device, int enabled) +{ + struct evdev_dispatch *dispatch; + struct tp_dispatch *tp; + + dispatch = ((struct evdev_device *) device)->dispatch; + tp = container_of(dispatch, tp, base); + + tp->tap.enabled = enabled; + + return LIBINPUT_CONFIG_STATUS_SUCCESS; +} + +static int +tp_tap_config_is_enabled(struct libinput_device *device) +{ + struct evdev_dispatch *dispatch; + struct tp_dispatch *tp; + + dispatch = ((struct evdev_device *) device)->dispatch; + tp = container_of(dispatch, tp, base); + + return tp->tap.enabled; +} + +static int +tp_tap_config_get_default(struct libinput_device *device) +{ + /** + * Tapping is disabled by default for two reasons: + * * if you don't know that tapping is a thing (or enabled by + * default), you get spurious mouse events that make the desktop + * feel buggy. + * * if you do know what tapping is and you want it, you + * usually know where to enable it, or at least you can search for + * it. + */ + return false; } int tp_init_tap(struct tp_dispatch *tp) { + tp->tap.config.count = tp_tap_config_count; + tp->tap.config.set_enabled = tp_tap_config_set_enabled; + tp->tap.config.get_enabled = tp_tap_config_is_enabled; + tp->tap.config.get_default = tp_tap_config_get_default; + tp->device->base.config.tap = &tp->tap.config; + tp->tap.state = TAP_STATE_IDLE; libinput_timer_init(&tp->tap.timer, tp->device->base.seat->libinput, tp_tap_handle_timeout, tp); - tp->tap.enabled = 1; /* FIXME */ - return 0; } diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index ced92378..1636e7a2 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -25,12 +25,11 @@ #include #include #include +#include #include "evdev-mt-touchpad.h" -#define DEFAULT_CONSTANT_ACCEL_NUMERATOR 100 -#define DEFAULT_MIN_ACCEL_FACTOR 0.20 -#define DEFAULT_MAX_ACCEL_FACTOR 0.40 +#define DEFAULT_ACCEL_NUMERATOR 1200.0 #define DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR 700.0 static inline int @@ -42,30 +41,8 @@ tp_hysteresis(int in, int center, int margin) if (diff > margin) return center + diff - margin; - else if (diff < -margin) + else return center + diff + margin; - return center + diff; -} - -static double -tp_accel_profile(struct motion_filter *filter, - void *data, - double velocity, - uint64_t time) -{ - struct tp_dispatch *tp = - (struct tp_dispatch *) data; - - double accel_factor; - - accel_factor = velocity * tp->accel.constant_factor; - - if (accel_factor > tp->accel.max_factor) - accel_factor = tp->accel.max_factor; - else if (accel_factor < tp->accel.min_factor) - accel_factor = tp->accel.min_factor; - - return accel_factor; } static inline struct tp_motion * @@ -87,7 +64,8 @@ tp_filter_motion(struct tp_dispatch *tp, motion.dx = *dx * tp->accel.x_scale_coeff; motion.dy = *dy * tp->accel.y_scale_coeff; - filter_dispatch(tp->filter, &motion, tp, time); + if (motion.dx != 0.0 || motion.dy != 0.0) + filter_dispatch(tp->filter, &motion, tp, time); *dx = motion.dx; *dy = motion.dy; @@ -171,6 +149,7 @@ tp_end_touch(struct tp_dispatch *tp, struct tp_touch *t) t->dirty = true; t->is_pointer = false; + t->palm.is_palm = false; t->state = TOUCH_END; t->pinned.is_pinned = false; assert(tp->nfingers_down >= 1); @@ -362,6 +341,7 @@ static int tp_touch_active(struct tp_dispatch *tp, struct tp_touch *t) { return (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE) && + !t->palm.is_palm && !t->pinned.is_pinned && tp_button_touch_active(tp, t); } @@ -380,6 +360,47 @@ tp_set_pointer(struct tp_dispatch *tp, struct tp_touch *t) t->is_pointer = true; } +static void +tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) +{ + const int PALM_TIMEOUT = 200; /* ms */ + const int DIRECTIONS = NE|E|SE|SW|W|NW; + + /* If labelled a touch as palm, we unlabel as palm when + we move out of the palm edge zone within the timeout, provided + the direction is within 45 degrees of the horizontal. + */ + if (t->palm.is_palm) { + if (time < t->palm.time + PALM_TIMEOUT && + (t->x > tp->palm.left_edge && t->x < tp->palm.right_edge)) { + int dirs = vector_get_direction(t->x - t->palm.x, t->y - t->palm.y); + if ((dirs & DIRECTIONS) && !(dirs & ~DIRECTIONS)) { + t->palm.is_palm = false; + tp_set_pointer(tp, t); + } + } + return; + } + + /* palm must start in exclusion zone, it's ok to move into + the zone without being a palm */ + if (t->state != TOUCH_BEGIN || + (t->x > tp->palm.left_edge && t->x < tp->palm.right_edge)) + return; + + /* don't detect palm in software button areas, it's + likely that legitimate touches start in the area + covered by the exclusion zone */ + if (tp->buttons.is_clickpad && + tp_button_is_inside_softbutton_area(tp, t)) + return; + + t->palm.is_palm = true; + t->palm.time = time; + t->palm.x = t->x; + t->palm.y = t->y; +} + static void tp_process_state(struct tp_dispatch *tp, uint64_t time) { @@ -392,8 +413,11 @@ tp_process_state(struct tp_dispatch *tp, uint64_t time) t->y = first->y; if (!t->dirty) t->dirty = first->dirty; - } else if (!t->dirty) + } else if (!t->dirty) { continue; + } + + tp_palm_detect(tp, t, time); tp_motion_hysteresis(tp, t); tp_motion_history_push(t); @@ -465,11 +489,11 @@ tp_post_twofinger_scroll(struct tp_dispatch *tp, uint64_t time) tp_filter_motion(tp, &dx, &dy, time); - /* Require at least three px scrolling to start */ - if (dy <= -3.0 || dy >= 3.0) + /* 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 <= -3.0 || dx >= 3.0) + if (dx <= -5.0 || dx >= 5.0) tp->scroll.direction |= (1 << LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL); if (dy != 0.0 && @@ -546,23 +570,23 @@ tp_post_events(struct tp_dispatch *tp, uint64_t time) if (tp_post_scroll_events(tp, time) != 0) return; - if (t->history.count >= TOUCHPAD_MIN_SAMPLES) { - if (!t->is_pointer) { - tp_for_each_touch(tp, t) { - if (t->is_pointer) - break; - } + if (!t->is_pointer) { + tp_for_each_touch(tp, t) { + if (t->is_pointer) + break; } - - if (!t->is_pointer) - return; - - tp_get_delta(t, &dx, &dy); - tp_filter_motion(tp, &dx, &dy, time); - - if (dx != 0.0 || dy != 0.0) - pointer_notify_motion(&tp->device->base, time, dx, dy); } + + if (!t->is_pointer || + !t->dirty || + t->history.count < TOUCHPAD_MIN_SAMPLES) + return; + + tp_get_delta(t, &dx, &dy); + tp_filter_motion(tp, &dx, &dy, time); + + if (dx != 0.0 || dy != 0.0) + pointer_notify_motion(&tp->device->base, time, dx, dy); } static void @@ -601,7 +625,7 @@ tp_destroy(struct evdev_dispatch *dispatch) tp_destroy_tap(tp); tp_destroy_buttons(tp); - motion_filter_destroy(tp->filter); + filter_destroy(tp->filter); free(tp->touches); free(tp); } @@ -666,9 +690,10 @@ tp_init_slots(struct tp_dispatch *tp, return 0; } -static void -calculate_scale_coefficients(struct tp_dispatch *tp) +static int +tp_init_accel(struct tp_dispatch *tp, double diagonal) { + struct motion_filter *accel; int res_x, res_y; if (tp->has_mt) { @@ -683,35 +708,34 @@ calculate_scale_coefficients(struct tp_dispatch *tp) ABS_Y); } - if (res_x <= 0 || res_y <= 0) { - tp->accel.x_scale_coeff = 1.0; - tp->accel.y_scale_coeff = 1.0; - } else if (res_x > res_y) { - tp->accel.x_scale_coeff = res_y / (double) res_x; - tp->accel.y_scale_coeff = 1.0f; + /* + * Not all touchpads report the same amount of units/mm (resolution). + * Normalize motion events to a resolution of 15.74 units/mm + * (== 400 dpi) as base (unaccelerated) speed. This also evens out any + * differences in x and y resolution, so that a circle on the + * touchpad does not turn into an elipse on the screen. + * + * We pick 400dpi as thats one of the many default resolutions + * for USB mice, so we end up with a similar base speed on the device. + */ + if (res_x > 1 && res_y > 1) { + tp->accel.x_scale_coeff = (400/25.4) / res_x; + tp->accel.y_scale_coeff = (400/25.4) / res_y; } else { - tp->accel.y_scale_coeff = res_x / (double) res_y; - tp->accel.x_scale_coeff = 1.0f; + /* + * For touchpads where the driver does not provide resolution, fall + * back to scaling motion events based on the diagonal size in units. + */ + tp->accel.x_scale_coeff = DEFAULT_ACCEL_NUMERATOR / diagonal; + tp->accel.y_scale_coeff = DEFAULT_ACCEL_NUMERATOR / diagonal; } -} -static int -tp_init_accel(struct tp_dispatch *touchpad, double diagonal) -{ - struct motion_filter *accel; - - calculate_scale_coefficients(touchpad); - - touchpad->accel.constant_factor = - DEFAULT_CONSTANT_ACCEL_NUMERATOR / diagonal; - touchpad->accel.min_factor = DEFAULT_MIN_ACCEL_FACTOR; - touchpad->accel.max_factor = DEFAULT_MAX_ACCEL_FACTOR; - - accel = create_pointer_accelator_filter(tp_accel_profile); + accel = create_pointer_accelator_filter( + pointer_accel_profile_smooth_simple); if (accel == NULL) return -1; - touchpad->filter = accel; + tp->filter = accel; return 0; } @@ -724,6 +748,35 @@ tp_init_scroll(struct tp_dispatch *tp) return 0; } +static int +tp_init_palmdetect(struct tp_dispatch *tp, + struct evdev_device *device) +{ + int width; + + /* 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) { + tp->palm.right_edge = INT_MAX; + tp->palm.left_edge = INT_MIN; + return 0; + } + + /* palm edges are 5% of the width on each side */ + tp->palm.right_edge = device->abs.absinfo_x->maximum - width * 0.05; + tp->palm.left_edge = device->abs.absinfo_x->minimum + width * 0.05; + + return 0; +} + + static int tp_init(struct tp_dispatch *tp, struct evdev_device *device) @@ -760,6 +813,9 @@ tp_init(struct tp_dispatch *tp, if (tp_init_buttons(tp, device) != 0) return -1; + if (tp_init_palmdetect(tp, device) != 0) + return -1; + return 0; } diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 7afb3c46..47791201 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -64,8 +64,6 @@ enum button_state { BUTTON_STATE_NONE, BUTTON_STATE_AREA, BUTTON_STATE_BOTTOM, - BUTTON_STATE_BOTTOM_NEW, - BUTTON_STATE_BOTTOM_TO_AREA, BUTTON_STATE_TOP, BUTTON_STATE_TOP_NEW, BUTTON_STATE_TOP_TO_IGNORE, @@ -88,6 +86,12 @@ enum tp_tap_state { TAP_STATE_DEAD, /**< finger count exceeded */ }; +enum tp_tap_touch_state { + TAP_TOUCH_STATE_IDLE = 16, /**< not in touch */ + TAP_TOUCH_STATE_TOUCH, /**< touching, may tap */ + TAP_TOUCH_STATE_DEAD, /**< exceeded motion/timeout */ +}; + struct tp_motion { int32_t x; int32_t y; @@ -131,6 +135,16 @@ struct tp_touch { enum button_event curr; struct libinput_timer timer; } button; + + struct { + enum tp_tap_touch_state state; + } tap; + + struct { + bool is_palm; + int32_t x, y; /* first coordinates if is_palm == true */ + uint32_t time; /* first timestamp if is_palm == true */ + } palm; }; struct tp_dispatch { @@ -152,10 +166,6 @@ struct tp_dispatch { struct motion_filter *filter; struct { - double constant_factor; - double min_factor; - double max_factor; - double x_scale_coeff; double y_scale_coeff; } accel; @@ -193,10 +203,16 @@ struct tp_dispatch { enum touchpad_event queued; struct { + struct libinput_device_config_tap config; bool enabled; struct libinput_timer timer; enum tp_tap_state state; } tap; + + struct { + int32_t right_edge; + int32_t left_edge; + } palm; }; #define tp_for_each_touch(_tp, _t) \ @@ -237,4 +253,7 @@ tp_button_handle_state(struct tp_dispatch *tp, uint64_t time); int 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); + #endif diff --git a/src/evdev.c b/src/evdev.c index 6fc4ecfb..1b95bb7e 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -866,6 +866,24 @@ evdev_device_get_sysname(struct evdev_device *device) return device->sysname; } +const char * +evdev_device_get_name(struct evdev_device *device) +{ + return device->devname; +} + +unsigned int +evdev_device_get_id_product(struct evdev_device *device) +{ + return libevdev_get_id_product(device->evdev); +} + +unsigned int +evdev_device_get_id_vendor(struct evdev_device *device) +{ + return libevdev_get_id_vendor(device->evdev); +} + void evdev_device_calibrate(struct evdev_device *device, float calibration[6]) { @@ -936,7 +954,7 @@ evdev_device_destroy(struct evdev_device *device) if (dispatch) dispatch->interface->destroy(dispatch); - motion_filter_destroy(device->pointer.filter); + filter_destroy(device->pointer.filter); libinput_seat_unref(device->base.seat); libevdev_free(device->evdev); free(device->mt.slots); diff --git a/src/evdev.h b/src/evdev.h index 63624dd8..bfa47b17 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -145,6 +145,15 @@ evdev_device_get_output(struct evdev_device *device); const char * evdev_device_get_sysname(struct evdev_device *device); +const char * +evdev_device_get_name(struct evdev_device *device); + +unsigned int +evdev_device_get_id_product(struct evdev_device *device); + +unsigned int +evdev_device_get_id_vendor(struct evdev_device *device); + void evdev_device_calibrate(struct evdev_device *device, float calibration[6]); diff --git a/src/filter.c b/src/filter.c index 3221d193..d65adfb7 100644 --- a/src/filter.c +++ b/src/filter.c @@ -29,6 +29,7 @@ #include #include "filter.h" +#include "libinput-util.h" void filter_dispatch(struct motion_filter *filter, @@ -38,26 +39,34 @@ filter_dispatch(struct motion_filter *filter, filter->interface->filter(filter, motion, data, time); } +void +filter_destroy(struct motion_filter *filter) +{ + if (!filter) + return; + + filter->interface->destroy(filter); +} + /* * Default parameters for pointer acceleration profiles. */ -#define DEFAULT_CONSTANT_ACCELERATION 10.0 -#define DEFAULT_THRESHOLD 4.0 -#define DEFAULT_ACCELERATION 2.0 +#define DEFAULT_THRESHOLD 0.4 /* in units/ms */ +#define DEFAULT_ACCELERATION 2.0 /* unitless factor */ /* * Pointer acceleration filter constants */ -#define MAX_VELOCITY_DIFF 1.0 +#define MAX_VELOCITY_DIFF 1.0 /* units/ms */ #define MOTION_TIMEOUT 300 /* (ms) */ #define NUM_POINTER_TRACKERS 16 struct pointer_tracker { - double dx; - double dy; - uint64_t time; + double dx; /* delta to most recent event, in device units */ + double dy; /* delta to most recent event, in device units */ + uint64_t time; /* ms */ int dir; }; @@ -67,72 +76,15 @@ struct pointer_accelerator { accel_profile_func_t profile; - double velocity; - double last_velocity; - int last_dx; - int last_dy; + double velocity; /* units/ms */ + double last_velocity; /* units/ms */ + int last_dx; /* device units */ + int last_dy; /* device units */ struct pointer_tracker *trackers; int cur_tracker; }; -enum directions { - N = 1 << 0, - NE = 1 << 1, - E = 1 << 2, - SE = 1 << 3, - S = 1 << 4, - SW = 1 << 5, - W = 1 << 6, - NW = 1 << 7, - UNDEFINED_DIRECTION = 0xff -}; - -static int -get_direction(int dx, int dy) -{ - int dir = UNDEFINED_DIRECTION; - int d1, d2; - double r; - - if (abs(dx) < 2 && abs(dy) < 2) { - if (dx > 0 && dy > 0) - dir = S | SE | E; - else if (dx > 0 && dy < 0) - dir = N | NE | E; - else if (dx < 0 && dy > 0) - dir = S | SW | W; - else if (dx < 0 && dy < 0) - dir = N | NW | W; - else if (dx > 0) - dir = NE | E | SE; - else if (dx < 0) - dir = NW | W | SW; - else if (dy > 0) - dir = SE | S | SW; - else if (dy < 0) - dir = NE | N | NW; - } else { - /* Calculate r within the interval [0 to 8) - * - * r = [0 .. 2π] where 0 is North - * d_f = r / 2π ([0 .. 1)) - * d_8 = 8 * d_f - */ - r = atan2(dy, dx); - r = fmod(r + 2.5*M_PI, 2*M_PI); - r *= 4*M_1_PI; - - /* Mark one or two close enough octants */ - d1 = (int)(r + 0.9) % 8; - d2 = (int)(r + 0.1) % 8; - - dir = (1 << d1) | (1 << d2); - } - - return dir; -} - static void feed_trackers(struct pointer_accelerator *accel, double dx, double dy, @@ -152,7 +104,7 @@ feed_trackers(struct pointer_accelerator *accel, trackers[current].dx = 0.0; trackers[current].dy = 0.0; trackers[current].time = time; - trackers[current].dir = get_direction(dx, dy); + trackers[current].dir = vector_get_direction(dx, dy); } static struct pointer_tracker * @@ -174,7 +126,7 @@ calculate_tracker_velocity(struct pointer_tracker *tracker, uint64_t time) dx = tracker->dx; dy = tracker->dy; distance = sqrt(dx*dx + dy*dy); - return distance / (double)(time - tracker->time); + return distance / (double)(time - tracker->time); /* units/ms */ } static double @@ -218,7 +170,7 @@ calculate_velocity(struct pointer_accelerator *accel, uint64_t time) } } - return result; + return result; /* units/ms */ } static double @@ -245,28 +197,7 @@ calculate_acceleration(struct pointer_accelerator *accel, factor = factor / 6.0; - return factor; -} - -static double -soften_delta(double last_delta, double delta) -{ - if (delta < -1.0 || delta > 1.0) { - if (delta > last_delta) - return delta - 0.5; - else if (delta < last_delta) - return delta + 0.5; - } - - return delta; -} - -static void -apply_softening(struct pointer_accelerator *accel, - struct motion_params *motion) -{ - motion->dx = soften_delta(accel->last_dx, motion->dx); - motion->dy = soften_delta(accel->last_dy, motion->dy); + return factor; /* unitless factor */ } static void @@ -276,8 +207,8 @@ accelerator_filter(struct motion_filter *filter, { struct pointer_accelerator *accel = (struct pointer_accelerator *) filter; - double velocity; - double accel_value; + double velocity; /* units/ms */ + double accel_value; /* unitless factor */ feed_trackers(accel, motion->dx, motion->dy, time); velocity = calculate_velocity(accel, time); @@ -286,8 +217,6 @@ accelerator_filter(struct motion_filter *filter, motion->dx = accel_value * motion->dx; motion->dy = accel_value * motion->dy; - apply_softening(accel, motion); - accel->last_dx = motion->dx; accel->last_dy = motion->dy; @@ -332,15 +261,6 @@ create_pointer_accelator_filter(accel_profile_func_t profile) return &filter->base; } -void -motion_filter_destroy(struct motion_filter *filter) -{ - if (!filter) - return; - - filter->interface->destroy(filter); -} - static inline double calc_penumbral_gradient(double x) { @@ -352,27 +272,39 @@ calc_penumbral_gradient(double x) double pointer_accel_profile_smooth_simple(struct motion_filter *filter, void *data, - double velocity, + double velocity, /* units/ms */ uint64_t time) { - double threshold = DEFAULT_THRESHOLD; - double accel = DEFAULT_ACCELERATION; - double smooth_accel_coefficient; + double threshold = DEFAULT_THRESHOLD; /* units/ms */ + double accel = DEFAULT_ACCELERATION; /* unitless factor */ + double smooth_accel_coefficient; /* unitless factor */ + double factor; /* unitless factor */ - velocity *= DEFAULT_CONSTANT_ACCELERATION; + if (threshold < 0.1) + threshold = 0.1; + if (accel < 1.0) + accel = 1.0; + + /* We use units/ms as velocity but it has no real meaning unless all + devices have the same resolution. For touchpads, we normalize to + 400dpi (15.75 units/mm), but the resolution on USB mice is all + over the place. Though most mice these days have either 400 + dpi (15.75 units/mm), 800 dpi or 1000dpi, excluding gaming mice + that can usually adjust it on the fly anyway and currently go up + to 8200dpi. + */ + if (velocity < (threshold / 2.0)) + return calc_penumbral_gradient(0.5 + velocity / threshold) * 2.0 - 1.0; - if (velocity < 1.0) - return calc_penumbral_gradient(0.5 + velocity * 0.5) * 2.0 - 1.0; - if (threshold < 1.0) - threshold = 1.0; if (velocity <= threshold) - return 1; - velocity /= threshold; - if (velocity >= accel) { + return 1.0; + + factor = velocity/threshold; + if (factor >= accel) return accel; - } else { - smooth_accel_coefficient = - calc_penumbral_gradient(velocity / accel); - return 1.0 + (smooth_accel_coefficient * (accel - 1.0)); - } + + /* factor is between 1.0 and accel, scale this to 0.0 - 1.0 */ + factor = (factor - 1.0) / (accel - 1.0); + smooth_accel_coefficient = calc_penumbral_gradient(factor); + return 1.0 + (smooth_accel_coefficient * (accel - 1.0)); } diff --git a/src/filter.h b/src/filter.h index c0219eeb..31ac7ebe 100644 --- a/src/filter.h +++ b/src/filter.h @@ -25,8 +25,10 @@ #include "config.h" +#include + struct motion_params { - double dx, dy; + double dx, dy; /* in units/ms @ 400dpi */ }; struct motion_filter; @@ -35,6 +37,8 @@ void filter_dispatch(struct motion_filter *filter, struct motion_params *motion, void *data, uint64_t time); +void +filter_destroy(struct motion_filter *filter); struct motion_filter_interface { @@ -59,8 +63,6 @@ typedef double (*accel_profile_func_t)(struct motion_filter *filter, struct motion_filter * create_pointer_accelator_filter(accel_profile_func_t filter); -void -motion_filter_destroy(struct motion_filter *filter); /* * Pointer acceleration profiles. diff --git a/src/libinput-private.h b/src/libinput-private.h index dbdf5e6d..6ec36376 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -83,12 +83,25 @@ struct libinput_seat { uint32_t button_count[KEY_CNT]; }; +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); +}; + +struct libinput_device_config { + struct libinput_device_config_tap *tap; +}; + struct libinput_device { struct libinput_seat *seat; struct list link; void *user_data; int terminated; int refcount; + struct libinput_device_config config; }; struct libinput_tool { diff --git a/src/libinput-util.h b/src/libinput-util.h index fc8e8a08..8c28eb49 100644 --- a/src/libinput-util.h +++ b/src/libinput-util.h @@ -24,6 +24,7 @@ #define LIBINPUT_UTIL_H #include +#include #include "libinput.h" @@ -116,4 +117,61 @@ msleep(unsigned int ms) usleep(ms * 1000); } +enum directions { + N = 1 << 0, + NE = 1 << 1, + E = 1 << 2, + SE = 1 << 3, + S = 1 << 4, + SW = 1 << 5, + W = 1 << 6, + NW = 1 << 7, + UNDEFINED_DIRECTION = 0xff +}; + +static inline int +vector_get_direction(int dx, int dy) +{ + int dir = UNDEFINED_DIRECTION; + int d1, d2; + double r; + + if (abs(dx) < 2 && abs(dy) < 2) { + if (dx > 0 && dy > 0) + dir = S | SE | E; + else if (dx > 0 && dy < 0) + dir = N | NE | E; + else if (dx < 0 && dy > 0) + dir = S | SW | W; + else if (dx < 0 && dy < 0) + dir = N | NW | W; + else if (dx > 0) + dir = NE | E | SE; + else if (dx < 0) + dir = NW | W | SW; + else if (dy > 0) + dir = SE | S | SW; + else if (dy < 0) + dir = NE | N | NW; + } else { + /* Calculate r within the interval [0 to 8) + * + * r = [0 .. 2π] where 0 is North + * d_f = r / 2π ([0 .. 1)) + * d_8 = 8 * d_f + */ + r = atan2(dy, dx); + r = fmod(r + 2.5*M_PI, 2*M_PI); + r *= 4*M_1_PI; + + /* Mark one or two close enough octants */ + d1 = (int)(r + 0.9) % 8; + d2 = (int)(r + 0.1) % 8; + + dir = (1 << d1) | (1 << d2); + } + + return dir; +} + #endif /* LIBINPUT_UTIL_H */ diff --git a/src/libinput.c b/src/libinput.c index 5d26bc91..e013f490 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -1446,6 +1446,24 @@ libinput_device_get_sysname(struct libinput_device *device) return evdev_device_get_sysname((struct evdev_device *) device); } +LIBINPUT_EXPORT const char * +libinput_device_get_name(struct libinput_device *device) +{ + return evdev_device_get_name((struct evdev_device *) device); +} + +LIBINPUT_EXPORT unsigned int +libinput_device_get_id_product(struct libinput_device *device) +{ + return evdev_device_get_id_product((struct evdev_device *) device); +} + +LIBINPUT_EXPORT unsigned int +libinput_device_get_id_vendor(struct libinput_device *device) +{ + return evdev_device_get_id_vendor((struct evdev_device *) device); +} + LIBINPUT_EXPORT const char * libinput_device_get_output_name(struct libinput_device *device) { @@ -1522,3 +1540,58 @@ libinput_event_touch_get_base_event(struct libinput_event_touch *event) { return &event->base; } + +LIBINPUT_EXPORT const char * +libinput_config_status_to_str(enum libinput_config_status status) +{ + const char *str = NULL; + + switch(status) { + case LIBINPUT_CONFIG_STATUS_SUCCESS: + str = "Success"; + break; + case LIBINPUT_CONFIG_STATUS_UNSUPPORTED: + str = "Unsupported configuration option"; + break; + case LIBINPUT_CONFIG_STATUS_INVALID: + str = "Invalid argument range"; + break; + } + + return str; +} + +LIBINPUT_EXPORT int +libinput_device_config_tap_get_finger_count(struct libinput_device *device) +{ + return device->config.tap ? device->config.tap->count(device) : 0; +} + +LIBINPUT_EXPORT enum libinput_config_status +libinput_device_config_tap_set_enabled(struct libinput_device *device, + int enable) +{ + if (enable && + libinput_device_config_tap_get_finger_count(device) == 0) + return LIBINPUT_CONFIG_STATUS_UNSUPPORTED; + + return device->config.tap->set_enabled(device, enable); +} + +LIBINPUT_EXPORT int +libinput_device_config_tap_get_enabled(struct libinput_device *device) +{ + if (libinput_device_config_tap_get_finger_count(device) == 0) + return 0; + + return device->config.tap->get_enabled(device); +} + +LIBINPUT_EXPORT int +libinput_device_config_tap_get_default_enabled(struct libinput_device *device) +{ + if (libinput_device_config_tap_get_finger_count(device) == 0) + return 0; + + return device->config.tap->get_default(device); +} diff --git a/src/libinput.h b/src/libinput.h index 14e533e7..2e6ab06b 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -541,7 +541,7 @@ libinput_event_pointer_get_dy(struct libinput_event_pointer *event); * * Return the current absolute x coordinate of the pointer event, in mm from * the top left corner of the device. To get the corresponding output screen - * coordinate, use libinput_event_pointer_get_x_transformed(). + * coordinate, use libinput_event_pointer_get_absolute_x_transformed(). * * For pointer events that are not of type * LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE, this function returns 0. @@ -559,7 +559,7 @@ libinput_event_pointer_get_absolute_x(struct libinput_event_pointer *event); * * Return the current absolute y coordinate of the pointer event, in mm from * the top left corner of the device. To get the corresponding output screen - * coordinate, use libinput_event_pointer_get_x_transformed(). + * coordinate, use libinput_event_pointer_get_absolute_y_transformed(). * * For pointer events that are not of type * LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE, this function returns 0. @@ -1102,7 +1102,7 @@ libinput_udev_create_context(const struct libinput_interface *interface, */ int libinput_udev_assign_seat(struct libinput *libinput, - const char *seat_id); + const char *seat_id); /** * @ingroup base @@ -1535,12 +1535,53 @@ libinput_device_get_user_data(struct libinput_device *device); * * Get the system name of the device. * + * To get the descriptive device name, use libinput_device_get_name(). + * * @param device A previously obtained device * @return System name of the device + * */ const char * libinput_device_get_sysname(struct libinput_device *device); +/** + * @ingroup device + * + * The descriptive device name as advertised by the kernel and/or the + * hardware itself. To get the sysname for this device, use + * libinput_device_get_sysname(). + * + * The lifetime of the returned string is tied to the struct + * libinput_device. The string may be the empty string but is never NULL. + * + * @param device A previously obtained device + * @return The device name + */ +const char * +libinput_device_get_name(struct libinput_device *device); + +/** + * @ingroup device + * + * Get the product ID for this device. + * + * @param device A previously obtained device + * @return The product ID of this device + */ +unsigned int +libinput_device_get_id_product(struct libinput_device *device); + +/** + * @ingroup device + * + * Get the vendor ID for this device. + * + * @param device A previously obtained device + * @return The vendor ID of this device + */ +unsigned int +libinput_device_get_id_vendor(struct libinput_device *device); + /** * @ingroup device * @@ -1648,8 +1689,113 @@ libinput_device_get_size(struct libinput_device *device, double *width, double *height); + +/** + * @defgroup config Device configuration + * + * Enable, disable, change and/or check for device-specific features. For + * all features, libinput assigns a default based on the hardware + * configuration. This default can be obtained with the respective + * get_default call. + * + * Some configuration option may be dependent on or mutually exclusive with + * with other options. The behavior in those cases is + * implementation-defined, the caller must ensure that the options are set + * in the right order. + */ + +enum libinput_config_status { + LIBINPUT_CONFIG_STATUS_SUCCESS = 0, /**< Config applied successfully */ + LIBINPUT_CONFIG_STATUS_UNSUPPORTED, /**< Configuration not available on + this device */ + LIBINPUT_CONFIG_STATUS_INVALID, /**< Invalid parameter range */ +}; + +/** + * @ingroup config Device configuration + * + * Return a string describing the error. + * + * @param status The status to translate to a string + * @return A human-readable string representing the error or NULL for an + * invalid status. + */ +const char * +libinput_config_status_to_str(enum libinput_config_status status); + +/** + * @ingroup config + * + * Check if the device supports tap-to-click. See + * libinput_device_config_tap_set_enabled() for more information. + * + * @param device The device to configure + * @return The number of fingers that can generate a tap event, or 0 if the + * device does not support tapping. + * + * @see libinput_device_config_tap_set_enabled + * @see libinput_device_config_tap_get_enabled + * @see libinput_device_config_tap_set_enabled_get_default + */ +int +libinput_device_config_tap_get_finger_count(struct libinput_device *device); + +/** + * @ingroup config + * + * Enable or disable tap-to-click on this device, with a default mapping of + * 1, 2, 3 finger tap mapping to left, right, middle click, respectively. + * Tapping is limited by the number of simultaneous touches + * supported by the device, see + * libinput_device_config_tap_get_finger_count(). + * + * @param device The device to configure + * @param enable Non-zero to enable, zero to disable + * + * @return A config status code. Disabling tapping on a device that does not + * support tapping always succeeds. + * + * @see libinput_device_config_tap_get_finger_count + * @see libinput_device_config_tap_get_enabled + * @see libinput_device_config_tap_get_default_enabled + */ +enum libinput_config_status +libinput_device_config_tap_set_enabled(struct libinput_device *device, + int enable); + +/** + * @ingroup config + * + * Check if tap-to-click is enabled on this device. If the device does not + * support tapping, this function always returns 0. + * + * @param device The device to configure + * + * @return 1 if enabled, 0 otherwise. + * + * @see libinput_device_config_tap_get_finger_count + * @see libinput_device_config_tap_set_enabled + * @see libinput_device_config_tap_get_default_enabled + */ +int +libinput_device_config_tap_get_enabled(struct libinput_device *device); + +/** + * @ingroup config + * + * 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 + * + * @see libinput_device_config_tap_get_finger_count + * @see libinput_device_config_tap_set_enabled + * @see libinput_device_config_tap_get_enabled + */ +int +libinput_device_config_tap_get_default_enabled(struct libinput_device *device); + #ifdef __cplusplus } #endif - #endif /* LIBINPUT_H */ diff --git a/src/libinput.pc.in b/src/libinput.pc.in index d2b11363..03e70e78 100644 --- a/src/libinput.pc.in +++ b/src/libinput.pc.in @@ -10,5 +10,5 @@ Description: Input device library Version: @LIBINPUT_VERSION@ Cflags: -I${includedir} Libs: -L${libdir} -linput -Libs.private: -lm +Libs.private: -lm -lrt Requires.private: libudev diff --git a/src/timer.c b/src/timer.c index 3076bb7a..ad0fd7c8 100644 --- a/src/timer.c +++ b/src/timer.c @@ -45,7 +45,7 @@ libinput_timer_arm_timer_fd(struct libinput *libinput) { int r; struct libinput_timer *timer; - struct itimerspec its = { { 0 }, { 0 } }; + struct itimerspec its = { { 0, 0 }, { 0, 0 } }; uint64_t earliest_expire = UINT64_MAX; list_for_each(timer, &libinput->timer.list, link) { diff --git a/test/Makefile.am b/test/Makefile.am index 0b86d350..64596275 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -2,16 +2,17 @@ if BUILD_TESTS AM_CPPFLAGS = -I$(top_srcdir)/include \ -I$(top_srcdir)/src \ $(CHECK_CFLAGS) \ - $(LIBEVDEV_CFLAGS) + $(LIBEVDEV_CFLAGS) \ + $(LIBUDEV_CFLAGS) AM_CFLAGS = $(GCC_CFLAGS) AM_CXXFLAGS = $(GCC_CXXFLAGS) -TEST_LIBS = liblitest.la $(CHECK_LIBS) $(LIBUDEV_LIBS) $(LIBEVDEV_LIBS) $(top_builddir)/src/libinput.la -lm +TEST_LIBS = liblitest.la $(CHECK_LIBS) $(LIBUDEV_LIBS) $(LIBEVDEV_LIBS) $(top_builddir)/src/libinput.la noinst_LTLIBRARIES = liblitest.la liblitest_la_SOURCES = \ - $(top_srcdir)/src/libinput-util.h \ - $(top_srcdir)/src/libinput-util.c \ + ../src/libinput-util.h \ + ../src/libinput-util.c \ litest.h \ litest-int.h \ litest-bcm5974.c \ diff --git a/test/keyboard.c b/test/keyboard.c index bf5d55af..a55405cb 100644 --- a/test/keyboard.c +++ b/test/keyboard.c @@ -41,7 +41,7 @@ START_TEST(keyboard_seat_key_count) libinput = litest_create_context(); for (i = 0; i < num_devices; ++i) { - sprintf(device_name, "Generic keyboard (%d)", i); + sprintf(device_name, "litest Generic keyboard (%d)", i); devices[i] = litest_add_device_with_overrides(libinput, LITEST_KEYBOARD, device_name, diff --git a/test/litest-int.h b/test/litest-int.h index e64f8b85..c6c3e4a6 100644 --- a/test/litest-int.h +++ b/test/litest-int.h @@ -101,7 +101,7 @@ struct litest_device_interface { }; void litest_set_current_device(struct litest_device *device); -int litest_scale(const struct litest_device *d, unsigned int axis, int val); +int litest_scale(const struct litest_device *d, unsigned int axis, double val); void litest_generic_device_teardown(void); #endif diff --git a/test/litest-synaptics-t440.c b/test/litest-synaptics-t440.c index 1b664940..e3f14413 100644 --- a/test/litest-synaptics-t440.c +++ b/test/litest-synaptics-t440.c @@ -91,8 +91,8 @@ static struct input_absinfo absinfo[] = { { 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, 1024, 5112, 0, 0, 75 }, - { ABS_MT_POSITION_Y, 2024, 4832, 0, 0, 129 }, + { ABS_MT_POSITION_X, 1024, 5112, 0, 0, 42 }, + { ABS_MT_POSITION_Y, 2024, 4832, 0, 0, 42 }, { ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 }, { ABS_MT_PRESSURE, 0, 255, 0, 0, 0 }, { .value = -1 } diff --git a/test/litest.c b/test/litest.c index 958d2cfe..6ce5e6b5 100644 --- a/test/litest.c +++ b/test/litest.c @@ -365,7 +365,7 @@ merge_absinfo(const struct input_absinfo *orig, const struct input_absinfo *override) { struct input_absinfo *abs; - int nelem, i; + unsigned int nelem, i; size_t sz = ABS_MAX + 1; if (!orig) @@ -399,7 +399,7 @@ static int* merge_events(const int *orig, const int *override) { int *events; - int nelem, i; + unsigned int nelem, i; size_t sz = KEY_MAX * 3; if (!orig) @@ -596,13 +596,14 @@ void litest_event(struct litest_device *d, unsigned int type, unsigned int code, int value) { - libevdev_uinput_write_event(d->uinput, type, code, value); + int ret = libevdev_uinput_write_event(d->uinput, type, code, value); + ck_assert_int_eq(ret, 0); } static int auto_assign_value(struct litest_device *d, const struct input_event *ev, - int slot, int x, int y) + int slot, double x, double y) { static int tracking_id; int value = ev->value; @@ -632,7 +633,8 @@ auto_assign_value(struct litest_device *d, void -litest_touch_down(struct litest_device *d, unsigned int slot, int x, int y) +litest_touch_down(struct litest_device *d, unsigned int slot, + double x, double y) { struct input_event *ev; @@ -676,7 +678,8 @@ litest_touch_up(struct litest_device *d, unsigned int slot) } void -litest_touch_move(struct litest_device *d, unsigned int slot, int x, int y) +litest_touch_move(struct litest_device *d, unsigned int slot, + double x, double y) { struct input_event *ev; @@ -696,8 +699,8 @@ litest_touch_move(struct litest_device *d, unsigned int slot, int x, int y) void litest_touch_move_to(struct litest_device *d, unsigned int slot, - int x_from, int y_from, - int x_to, int y_to, + double x_from, double y_from, + double x_to, double y_to, int steps) { for (int i = 0; i < steps - 1; i++) @@ -816,7 +819,8 @@ litest_keyboard_key(struct litest_device *d, unsigned int key, bool is_press) litest_button_click(d, key, is_press); } -int litest_scale(const struct litest_device *d, unsigned int axis, int val) +int +litest_scale(const struct litest_device *d, unsigned int axis, double val) { int min, max; ck_assert_int_ge(val, 0); @@ -904,13 +908,14 @@ litest_assert_empty_queue(struct libinput *li) struct libevdev_uinput * litest_create_uinput_device_from_description(const char *name, const struct input_id *id, - const struct input_absinfo *abs, + const struct input_absinfo *abs_info, const int *events) { struct libevdev_uinput *uinput; struct libevdev *dev; int type, code; - int rc; + int rc, fd; + const struct input_absinfo *abs; const struct input_absinfo default_abs = { .value = 0, .minimum = 0, @@ -920,6 +925,7 @@ litest_create_uinput_device_from_description(const char *name, .resolution = 100 }; char buf[512]; + const char *devnode; dev = libevdev_new(); ck_assert(dev != NULL); @@ -932,6 +938,7 @@ litest_create_uinput_device_from_description(const char *name, libevdev_set_id_product(dev, id->product); } + abs = abs_info; while (abs && abs->value != -1) { rc = libevdev_enable_event_code(dev, EV_ABS, abs->value, abs); @@ -960,6 +967,31 @@ litest_create_uinput_device_from_description(const char *name, libevdev_free(dev); + /* uinput does not yet support setting the resolution, so we set it + * afterwards. This is of course racy as hell but the way we + * _generally_ use this function by the time libinput uses the + * device, we're finished here */ + + devnode = libevdev_uinput_get_devnode(uinput); + ck_assert_notnull(devnode); + fd = open(devnode, O_RDONLY); + ck_assert_int_gt(fd, -1); + rc = libevdev_new_from_fd(fd, &dev); + ck_assert_int_eq(rc, 0); + + abs = abs_info; + while (abs && abs->value != -1) { + if (abs->resolution != 0) { + rc = libevdev_kernel_set_abs_info(dev, + abs->value, + abs); + ck_assert_int_eq(rc, 0); + } + abs++; + } + close(fd); + libevdev_free(dev); + return uinput; } diff --git a/test/litest.h b/test/litest.h index 9e159a34..2b3c5d78 100644 --- a/test/litest.h +++ b/test/litest.h @@ -119,16 +119,16 @@ void litest_event(struct litest_device *t, void litest_touch_up(struct litest_device *d, unsigned int slot); void litest_touch_move(struct litest_device *d, unsigned int slot, - int x, - int y); + double x, + double y); void litest_touch_down(struct litest_device *d, unsigned int slot, - int x, - int y); + double x, + double y); void litest_touch_move_to(struct litest_device *d, unsigned int slot, - int x_from, int y_from, - int x_to, int y_to, + double x_from, double y_from, + double x_to, double y_to, int steps); void litest_tablet_proximity_in(struct litest_device *d, int x, int y, diff --git a/test/misc.c b/test/misc.c index c4eaf911..c73ccdc0 100644 --- a/test/misc.c +++ b/test/misc.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include "litest.h" @@ -95,7 +96,7 @@ START_TEST(event_conversion_device_notify) struct libinput_event *event; int device_added = 0, device_removed = 0; - uinput = create_simple_test_device("test device", + uinput = create_simple_test_device("litest test device", EV_REL, REL_X, EV_REL, REL_Y, EV_KEY, BTN_LEFT, @@ -149,7 +150,7 @@ START_TEST(event_conversion_pointer) struct libinput_event *event; int motion = 0, button = 0; - uinput = create_simple_test_device("test device", + uinput = create_simple_test_device("litest test device", EV_REL, REL_X, EV_REL, REL_Y, EV_KEY, BTN_LEFT, @@ -210,7 +211,7 @@ START_TEST(event_conversion_pointer_abs) struct libinput_event *event; int motion = 0, button = 0; - uinput = create_simple_test_device("test device", + uinput = create_simple_test_device("litest test device", EV_ABS, ABS_X, EV_ABS, ABS_Y, EV_KEY, BTN_LEFT, @@ -270,7 +271,7 @@ START_TEST(event_conversion_key) struct libinput_event *event; int key = 0; - uinput = create_simple_test_device("test device", + uinput = create_simple_test_device("litest test device", EV_KEY, KEY_A, EV_KEY, KEY_B, -1, -1); @@ -319,7 +320,7 @@ START_TEST(event_conversion_touch) struct libinput_event *event; int touch = 0; - uinput = create_simple_test_device("test device", + uinput = create_simple_test_device("litest test device", EV_KEY, BTN_TOUCH, EV_ABS, ABS_X, EV_ABS, ABS_Y, @@ -428,6 +429,46 @@ START_TEST(context_ref_counting) } END_TEST +START_TEST(device_ids) +{ + struct litest_device *dev = litest_current_device(); + const char *name; + unsigned int pid, vid; + + name = libevdev_get_name(dev->evdev); + pid = libevdev_get_id_product(dev->evdev); + vid = libevdev_get_id_vendor(dev->evdev); + + ck_assert_str_eq(name, + libinput_device_get_name(dev->libinput_device)); + ck_assert_int_eq(pid, + libinput_device_get_id_product(dev->libinput_device)); + ck_assert_int_eq(vid, + libinput_device_get_id_vendor(dev->libinput_device)); +} +END_TEST + +START_TEST(config_status_string) +{ + const char *strs[3]; + const char *invalid; + size_t i, j; + + strs[0] = libinput_config_status_to_str(LIBINPUT_CONFIG_STATUS_SUCCESS); + strs[1] = libinput_config_status_to_str(LIBINPUT_CONFIG_STATUS_UNSUPPORTED); + strs[2] = libinput_config_status_to_str(LIBINPUT_CONFIG_STATUS_INVALID); + + for (i = 0; i < ARRAY_LENGTH(strs) - 1; i++) + for (j = i + 1; j < ARRAY_LENGTH(strs); j++) + ck_assert_str_ne(strs[i], strs[j]); + + invalid = libinput_config_status_to_str(LIBINPUT_CONFIG_STATUS_INVALID + 1); + ck_assert(invalid == NULL); + invalid = libinput_config_status_to_str(LIBINPUT_CONFIG_STATUS_SUCCESS - 1); + ck_assert(invalid == NULL); +} +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); @@ -436,6 +477,8 @@ int main (int argc, char **argv) { litest_add_no_device("events:conversion", event_conversion_touch); litest_add_no_device("bitfield_helpers", bitfield_helpers); litest_add_no_device("context:refcount", context_ref_counting); + litest_add("device:id", device_ids, LITEST_ANY, LITEST_ANY); + litest_add_no_device("config:status string", config_status_string); return litest_run(argc, argv); } diff --git a/test/pointer.c b/test/pointer.c index 7d5668f8..aa75274b 100644 --- a/test/pointer.c +++ b/test/pointer.c @@ -63,7 +63,7 @@ test_relative_event(struct litest_device *dev, int dx, int dy) ptrev = libinput_event_get_pointer_event(event); ck_assert(ptrev != NULL); - expected_length = sqrt(dx*dx + dy*dy); + expected_length = sqrt(4 * dx*dx + 4 * dy*dy); expected_dir = atan2(dx, dy); ev_dx = libinput_event_pointer_get_dx(ptrev); @@ -72,7 +72,7 @@ test_relative_event(struct litest_device *dev, int dx, int dy) actual_dir = atan2(ev_dx, ev_dy); /* Check the length of the motion vector (tolerate 1.0 indifference). */ - ck_assert(fabs(expected_length - actual_length) < 1.0); + ck_assert(fabs(expected_length) >= actual_length); /* Check the direction of the motion vector (tolerate 2π/4 radians * indifference). */ @@ -102,7 +102,7 @@ START_TEST(pointer_motion_relative) END_TEST static void -test_button_event(struct litest_device *dev, int button, int state) +test_button_event(struct litest_device *dev, unsigned int button, int state) { struct libinput *li = dev->libinput; struct libinput_event *event; @@ -223,7 +223,7 @@ START_TEST(pointer_seat_button_count) libinput = litest_create_context(); for (i = 0; i < num_devices; ++i) { - sprintf(device_name, "Generic mouse (%d)", i); + sprintf(device_name, "litest Generic mouse (%d)", i); devices[i] = litest_add_device_with_overrides(libinput, LITEST_MOUSE, device_name, diff --git a/test/touch.c b/test/touch.c index 2a7c966a..6f6eec0a 100644 --- a/test/touch.c +++ b/test/touch.c @@ -72,15 +72,15 @@ START_TEST(touch_abs_transform) bool tested = false; struct input_absinfo abs[] = { - { ABS_X, 0, 32767, 75, 0, 0 }, - { ABS_Y, 0, 32767, 129, 0, 0 }, + { ABS_X, 0, 32767, 75, 0, 10 }, + { ABS_Y, 0, 32767, 129, 0, 9 }, { ABS_MT_POSITION_X, 0, 32767, 0, 0, 10 }, { ABS_MT_POSITION_Y, 0, 32767, 0, 0, 9 }, { .value = -1 }, }; dev = litest_create_device_with_overrides(LITEST_WACOM_TOUCH, - "Highres touch device", + "litest Highres touch device", NULL, abs, NULL); libinput = dev->libinput; @@ -129,7 +129,7 @@ START_TEST(touch_many_slots) }; dev = litest_create_device_with_overrides(LITEST_WACOM_TOUCH, - "Multi-touch device", + "litest Multi-touch device", NULL, abs, NULL); libinput = dev->libinput; diff --git a/test/touchpad.c b/test/touchpad.c index 288805ef..c1bdbd53 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -90,7 +90,7 @@ START_TEST(touchpad_2fg_no_motion) END_TEST static void -assert_button_event(struct libinput *li, int button, +assert_button_event(struct libinput *li, unsigned int button, enum libinput_button_state state) { struct libinput_event *event; @@ -116,6 +116,8 @@ START_TEST(touchpad_1fg_tap) struct libinput *li = dev->libinput; struct libinput_event *event; + libinput_device_config_tap_set_enabled(dev->libinput_device, 1); + litest_drain_events(li); litest_touch_down(dev, 0, 50, 50); @@ -141,6 +143,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); + litest_drain_events(li); litest_touch_down(dev, 0, 50, 50); @@ -191,6 +195,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); + litest_drain_events(dev->libinput); litest_touch_down(dev, 0, 50, 50); @@ -215,6 +221,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); + litest_drain_events(dev->libinput); litest_touch_down(dev, 0, 50, 50); @@ -239,6 +247,8 @@ START_TEST(touchpad_1fg_tap_click) struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; + libinput_device_config_tap_set_enabled(dev->libinput_device, 1); + litest_drain_events(dev->libinput); /* finger down, button click, finger up @@ -266,6 +276,8 @@ START_TEST(touchpad_2fg_tap_click) struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; + libinput_device_config_tap_set_enabled(dev->libinput_device, 1); + litest_drain_events(dev->libinput); /* two fingers down, button click, fingers up @@ -295,6 +307,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); + litest_drain_events(dev->libinput); /* two fingers down, button click, fingers up @@ -320,11 +334,89 @@ START_TEST(touchpad_2fg_tap_click_apple) } END_TEST +START_TEST(touchpad_no_2fg_tap_after_move) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + litest_drain_events(dev->libinput); + + /* one finger down, move past threshold, + second finger down, first finger up + -> no event + */ + litest_touch_down(dev, 0, 50, 50); + litest_touch_move_to(dev, 0, 50, 50, 90, 90, 10); + litest_drain_events(dev->libinput); + + litest_touch_down(dev, 1, 70, 50); + litest_touch_up(dev, 0); + + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(touchpad_no_2fg_tap_after_timeout) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + litest_drain_events(dev->libinput); + + /* one finger down, wait past tap timeout, + second finger down, first finger up + -> no event + */ + litest_touch_down(dev, 0, 50, 50); + libinput_dispatch(dev->libinput); + msleep(300); + libinput_dispatch(dev->libinput); + litest_drain_events(dev->libinput); + + litest_touch_down(dev, 1, 70, 50); + litest_touch_up(dev, 0); + + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(touchpad_no_first_fg_tap_after_move) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_event *event; + + litest_drain_events(dev->libinput); + + /* one finger down, second finger down, + second finger moves beyond threshold, + first finger up + -> no event + */ + litest_touch_down(dev, 0, 50, 50); + litest_touch_down(dev, 1, 70, 50); + libinput_dispatch(dev->libinput); + litest_touch_move_to(dev, 1, 70, 50, 90, 90, 10); + libinput_dispatch(dev->libinput); + litest_touch_up(dev, 0); + litest_touch_up(dev, 1); + libinput_dispatch(dev->libinput); + + while ((event = libinput_get_event(li))) { + ck_assert_int_ne(libinput_event_get_type(event), + LIBINPUT_EVENT_POINTER_BUTTON); + libinput_event_destroy(event); + } +} +END_TEST + 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); + litest_drain_events(dev->libinput); /* one finger down, up, down, button click, finger up @@ -359,6 +451,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); + litest_drain_events(dev->libinput); /* one finger down, up, down, move, button click, finger up @@ -599,6 +693,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); + litest_drain_events(li); /* Tap in left button area, then finger down, button click @@ -639,6 +735,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); + litest_drain_events(li); /* Tap in right button area, then finger down, button click @@ -827,6 +925,8 @@ START_TEST(clickpad_softbutton_left_2nd_fg_move) event = libinput_get_event(li); } + litest_touch_up(dev, 1); + litest_event(dev, EV_KEY, BTN_LEFT, 0); litest_event(dev, EV_SYN, SYN_REPORT, 0); litest_touch_up(dev, 0); @@ -1027,7 +1127,7 @@ START_TEST(clickpad_topsoftbuttons_move_out_ignore) END_TEST static void -test_2fg_scroll(struct litest_device *dev, int dx, int dy, int sleep) +test_2fg_scroll(struct litest_device *dev, double dx, double dy, int sleep) { struct libinput *li = dev->libinput; @@ -1051,7 +1151,7 @@ test_2fg_scroll(struct litest_device *dev, int dx, int dy, int sleep) } static void -check_2fg_scroll(struct litest_device *dev, int axis, int dir) +check_2fg_scroll(struct litest_device *dev, unsigned int axis, int dir) { struct libinput *li = dev->libinput; struct libinput_event *event, *next_event; @@ -1098,19 +1198,212 @@ START_TEST(touchpad_2fg_scroll) litest_drain_events(li); - /* Note this mixes in a tiny amount of movement in the wrong direction, - which should be ignored */ - test_2fg_scroll(dev, 1, 40, 0); + test_2fg_scroll(dev, 0.1, 40, 0); check_2fg_scroll(dev, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, 10); - test_2fg_scroll(dev, 1, -40, 0); + test_2fg_scroll(dev, 0.1, -40, 0); check_2fg_scroll(dev, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, -10); - test_2fg_scroll(dev, 40, 1, 0); + test_2fg_scroll(dev, 40, 0.1, 0); check_2fg_scroll(dev, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, 10); - test_2fg_scroll(dev, -40, 1, 0); + test_2fg_scroll(dev, -40, 0.1, 0); check_2fg_scroll(dev, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, -10); /* 2fg scroll smaller than the threshold should not generate events */ - test_2fg_scroll(dev, 1, 1, 200); + test_2fg_scroll(dev, 0.1, 0.1, 200); + litest_assert_empty_queue(li); +} +END_TEST + +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); +} +END_TEST + +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), + LIBINPUT_CONFIG_STATUS_UNSUPPORTED); +} +END_TEST + +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); +} +END_TEST + +static int +touchpad_has_palm_detect_size(struct litest_device *dev) +{ + double width, height; + + libinput_device_get_size(dev->libinput_device, &width, &height); + + return width >= 80; +} + +START_TEST(touchpad_palm_detect_at_edge) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + if (!touchpad_has_palm_detect_size(dev)) + return; + + litest_drain_events(li); + + litest_touch_down(dev, 0, 99, 50); + litest_touch_move_to(dev, 0, 99, 50, 99, 70, 5); + litest_touch_up(dev, 0); + + litest_assert_empty_queue(li); + + litest_touch_down(dev, 0, 5, 50); + litest_touch_move_to(dev, 0, 5, 50, 5, 70, 5); + litest_touch_up(dev, 0); +} +END_TEST + +START_TEST(touchpad_palm_detect_at_bottom_corners) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + if (!touchpad_has_palm_detect_size(dev)) + return; + + /* Run for non-clickpads only: make sure the bottom corners trigger + palm detection too */ + litest_drain_events(li); + + litest_touch_down(dev, 0, 99, 95); + litest_touch_move_to(dev, 0, 99, 95, 99, 99, 10); + litest_touch_up(dev, 0); + + litest_assert_empty_queue(li); + + litest_touch_down(dev, 0, 5, 95); + litest_touch_move_to(dev, 0, 5, 95, 5, 99, 5); + litest_touch_up(dev, 0); +} +END_TEST + +START_TEST(touchpad_palm_detect_at_top_corners) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + if (!touchpad_has_palm_detect_size(dev)) + return; + + /* Run for non-clickpads only: make sure the bottom corners trigger + palm detection too */ + litest_drain_events(li); + + litest_touch_down(dev, 0, 99, 5); + litest_touch_move_to(dev, 0, 99, 5, 99, 9, 10); + litest_touch_up(dev, 0); + + litest_assert_empty_queue(li); + + litest_touch_down(dev, 0, 5, 5); + litest_touch_move_to(dev, 0, 5, 5, 5, 9, 5); + litest_touch_up(dev, 0); +} +END_TEST + +START_TEST(touchpad_palm_detect_palm_stays_palm) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + if (!touchpad_has_palm_detect_size(dev)) + return; + + litest_drain_events(li); + + litest_touch_down(dev, 0, 99, 20); + litest_touch_move_to(dev, 0, 99, 20, 75, 99, 5); + litest_touch_up(dev, 0); + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(touchpad_palm_detect_palm_becomes_pointer) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_event *ev; + enum libinput_event_type type; + + if (!touchpad_has_palm_detect_size(dev)) + return; + + litest_drain_events(li); + + litest_touch_down(dev, 0, 99, 50); + litest_touch_move_to(dev, 0, 99, 70, 0, 70, 5); + litest_touch_up(dev, 0); + + libinput_dispatch(li); + + ev = libinput_get_event(li); + ck_assert_notnull(ev); + do { + type = libinput_event_get_type(ev); + ck_assert_int_eq(type, LIBINPUT_EVENT_POINTER_MOTION); + + libinput_event_destroy(ev); + ev = libinput_get_event(li); + } while (ev); + + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(touchpad_palm_detect_no_palm_moving_into_edges) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_event *ev; + enum libinput_event_type type; + + if (!touchpad_has_palm_detect_size(dev)) + return; + + /* moving non-palm into the edge does not label it as palm */ + litest_drain_events(li); + + litest_touch_down(dev, 0, 50, 50); + litest_touch_move_to(dev, 0, 50, 50, 99, 50, 5); + + litest_drain_events(li); + + litest_touch_move_to(dev, 0, 99, 50, 99, 90, 5); + libinput_dispatch(li); + + type = libinput_next_event_type(li); + do { + + ck_assert_int_eq(type, LIBINPUT_EVENT_POINTER_MOTION); + ev = libinput_get_event(li); + libinput_event_destroy(ev); + + type = libinput_next_event_type(li); + libinput_dispatch(li); + } while (type != LIBINPUT_EVENT_NONE); + + litest_touch_up(dev, 0); + libinput_dispatch(li); litest_assert_empty_queue(li); } END_TEST @@ -1127,11 +1420,20 @@ int main(int argc, char **argv) { 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_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); + /* Real buttons don't interfere with tapping, so don't run those for pads with buttons */ litest_add("touchpad:tap", touchpad_1fg_double_tap_click, LITEST_CLICKPAD, LITEST_ANY); 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_is_available, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("touchpad:tap", touchpad_tap_is_not_available, LITEST_ANY, LITEST_TOUCHPAD); + litest_add_no_device("touchpad:clickfinger", touchpad_1fg_clickfinger); litest_add_no_device("touchpad:clickfinger", touchpad_2fg_clickfinger); @@ -1155,5 +1457,12 @@ int main(int argc, char **argv) { litest_add("touchpad:scroll", touchpad_2fg_scroll, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); + litest_add("touchpad:palm", touchpad_palm_detect_at_edge, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("touchpad:palm", touchpad_palm_detect_at_bottom_corners, LITEST_TOUCHPAD, LITEST_CLICKPAD); + litest_add("touchpad:palm", touchpad_palm_detect_at_top_corners, LITEST_TOUCHPAD, LITEST_TOPBUTTONPAD); + litest_add("touchpad:palm", touchpad_palm_detect_palm_becomes_pointer, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("touchpad:palm", touchpad_palm_detect_palm_stays_palm, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("touchpad:palm", touchpad_palm_detect_no_palm_moving_into_edges, LITEST_TOUCHPAD, LITEST_ANY); + return litest_run(argc, argv); } diff --git a/tools/event-debug.c b/tools/event-debug.c index acf9d815..0eb4bcaf 100644 --- a/tools/event-debug.c +++ b/tools/event-debug.c @@ -199,7 +199,7 @@ static void print_event_header(struct libinput_event *ev) { struct libinput_device *dev = libinput_event_get_device(ev); - const char *type; + const char *type = NULL; switch(libinput_event_get_type(ev)) { case LIBINPUT_EVENT_NONE: @@ -561,7 +561,7 @@ handle_and_print_events(struct libinput *li) return rc; } -void +static void mainloop(struct libinput *li) { struct pollfd fds[2]; diff --git a/tools/event-gui.c b/tools/event-gui.c index 7bc38281..cd01f989 100644 --- a/tools/event-gui.c +++ b/tools/event-gui.c @@ -142,8 +142,7 @@ draw(GtkWidget *widget, cairo_t *cr, gpointer data) cairo_set_source_rgb(cr, .2, .4, .8); cairo_save(cr); - cairo_move_to(cr, w->absx, w->absy); - cairo_arc(cr, 0, 0, 10, 0, 2 * M_PI); + cairo_arc(cr, w->absx, w->absy, 10, 0, 2 * M_PI); cairo_fill(cr); cairo_restore(cr); @@ -243,11 +242,11 @@ static void handle_event_absmotion(struct libinput_event *ev, struct window *w) { struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev); - double x = libinput_event_pointer_get_absolute_x(p), - y = libinput_event_pointer_get_absolute_y(p); + double x = libinput_event_pointer_get_absolute_x_transformed(p, w->width), + y = libinput_event_pointer_get_absolute_y_transformed(p, w->height); - w->absx = clip((int)x, 0, w->width); - w->absy = clip((int)y, 0, w->height); + w->absx = x; + w->absy = y; } static void @@ -258,7 +257,7 @@ handle_event_touch(struct libinput_event *ev, struct window *w) struct touch *touch; double x, y; - if (slot == -1 || slot >= ARRAY_LENGTH(w->touches)) + if (slot == -1 || slot >= (int) ARRAY_LENGTH(w->touches)) return; touch = &w->touches[slot]; @@ -268,8 +267,8 @@ handle_event_touch(struct libinput_event *ev, struct window *w) return; } - x = libinput_event_touch_get_x(t), - y = libinput_event_touch_get_y(t); + x = libinput_event_touch_get_x_transformed(t, w->width), + y = libinput_event_touch_get_y_transformed(t, w->height); touch->active = 1; touch->x = (int)x; @@ -442,7 +441,7 @@ close_restricted(int fd, void *user_data) close(fd); } -const static struct libinput_interface interface = { +static const struct libinput_interface interface = { .open_restricted = open_restricted, .close_restricted = close_restricted, };