From 886b5a2cd8c785e48ddf5ceb4a902cd1f518d2a9 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 4 Apr 2016 10:06:36 +1000 Subject: [PATCH] touchpad: add a middle button software area Middle button interaction is most commonly to paste and it is a single-event interaction (button press). We provided middle button in software button mode by emulating it with a two-finger press with L+R down at the same time. This is also what many touchpads are spectacularly bad at, it is very common to detect the physical button down event before the second finger registers, resulting in left or right clicks where a middle button should be triggered. Unless the fingers are resting on the touchpad for at least one scanout, the success rate for middle button emulation is only at 70% or so. This patch adds a 25%-width middle button area between the left and the right software button, everything else stays the same. To avoid immediate breakage, the middle button emulation remains but may be removed in the future. The doc is updated to only refer to the middle button area now. https://bugs.freedesktop.org/show_bug.cgi?id=94755 Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- doc/clickpad-softbuttons.dox | 14 +++++++++----- doc/svg/software-buttons.svg | 30 +++--------------------------- src/evdev-mt-touchpad-buttons.c | 31 ++++++++++++++++++++++++++++++- src/evdev-mt-touchpad.h | 2 ++ test/touchpad-buttons.c | 30 ++++++++++++++++++++++++++++++ 5 files changed, 74 insertions(+), 33 deletions(-) diff --git a/doc/clickpad-softbuttons.dox b/doc/clickpad-softbuttons.dox index a4f2e444..cf4c8c9e 100644 --- a/doc/clickpad-softbuttons.dox +++ b/doc/clickpad-softbuttons.dox @@ -20,16 +20,20 @@ generated by libinput and passed to the caller in response to a click. @section software_buttons Software button areas On most clickpads, this is the default behavior. The bottom of the touchpad -is split in the middle to generate left or right button events on click. The -height of the button area depends on the hardware but is usually around -10mm. +is split into three distinct areas generate left, middle or right button +events on click. The height of the button area depends on the hardware but +is usually around 10mm. Left, right and middle button events can be triggered as follows: - if a finger is in the main area or the left button area, a click generates left button events. - if a finger is in the right area, a click generates right button events. -- if there is a finger in both the left and right button area, a click - generates middle button events. +- if a finger is in the middle area, a click generates middle button events. + +The middle button is always centered on the touchpad and smaller in size +than the left or right button. The actual size is device-dependent though as +many touchpads do not have visible markings for the middle button the exact +location of the button is not visibly obvious. @image html software-buttons.svg "Left, right and middle-button click with software button areas" diff --git a/doc/svg/software-buttons.svg b/doc/svg/software-buttons.svg index 903535c8..c0bc6104 100644 --- a/doc/svg/software-buttons.svg +++ b/doc/svg/software-buttons.svg @@ -41,8 +41,8 @@ id="namedview4312" showgrid="false" inkscape:zoom="0.57798581" - inkscape:cx="1134.9723" - inkscape:cy="-71.183873" + inkscape:cx="842.57758" + inkscape:cy="-74.644166" inkscape:window-x="0" inkscape:window-y="27" inkscape:window-maximized="1" @@ -106,7 +106,7 @@ id="rect2858-7" style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:5.19376326;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate" /> - - - - - - - diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index 076eab00..a9b75216 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -64,6 +64,7 @@ static inline const char* button_event_to_str(enum button_event event) { switch(event) { CASE_RETURN_STRING(BUTTON_EVENT_IN_BOTTOM_R); + CASE_RETURN_STRING(BUTTON_EVENT_IN_BOTTOM_M); CASE_RETURN_STRING(BUTTON_EVENT_IN_BOTTOM_L); CASE_RETURN_STRING(BUTTON_EVENT_IN_TOP_R); CASE_RETURN_STRING(BUTTON_EVENT_IN_TOP_M); @@ -92,11 +93,21 @@ is_inside_bottom_right_area(const struct tp_dispatch *tp, t->point.x > tp->buttons.bottom_area.rightbutton_left_edge; } +static inline bool +is_inside_bottom_middle_area(const struct tp_dispatch *tp, + const struct tp_touch *t) +{ + return is_inside_bottom_button_area(tp, t) && + !is_inside_bottom_right_area(tp, t) && + t->point.x > tp->buttons.bottom_area.middlebutton_left_edge; +} + static inline bool is_inside_bottom_left_area(const struct tp_dispatch *tp, const struct tp_touch *t) { return is_inside_bottom_button_area(tp, t) && + !is_inside_bottom_middle_area(tp, t) && !is_inside_bottom_right_area(tp, t); } @@ -192,6 +203,7 @@ tp_button_none_handle_event(struct tp_dispatch *tp, { switch (event) { case BUTTON_EVENT_IN_BOTTOM_R: + case BUTTON_EVENT_IN_BOTTOM_M: case BUTTON_EVENT_IN_BOTTOM_L: tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM, event); break; @@ -220,6 +232,7 @@ tp_button_area_handle_event(struct tp_dispatch *tp, { switch (event) { case BUTTON_EVENT_IN_BOTTOM_R: + case BUTTON_EVENT_IN_BOTTOM_M: case BUTTON_EVENT_IN_BOTTOM_L: case BUTTON_EVENT_IN_TOP_R: case BUTTON_EVENT_IN_TOP_M: @@ -243,6 +256,7 @@ tp_button_bottom_handle_event(struct tp_dispatch *tp, { switch (event) { case BUTTON_EVENT_IN_BOTTOM_R: + case BUTTON_EVENT_IN_BOTTOM_M: case BUTTON_EVENT_IN_BOTTOM_L: if (event != t->button.curr) tp_button_set_state(tp, @@ -273,6 +287,7 @@ tp_button_top_handle_event(struct tp_dispatch *tp, { switch (event) { case BUTTON_EVENT_IN_BOTTOM_R: + case BUTTON_EVENT_IN_BOTTOM_M: case BUTTON_EVENT_IN_BOTTOM_L: tp_button_set_state(tp, t, BUTTON_STATE_TOP_TO_IGNORE, event); break; @@ -305,6 +320,7 @@ tp_button_top_new_handle_event(struct tp_dispatch *tp, { switch(event) { case BUTTON_EVENT_IN_BOTTOM_R: + case BUTTON_EVENT_IN_BOTTOM_M: case BUTTON_EVENT_IN_BOTTOM_L: tp_button_set_state(tp, t, BUTTON_STATE_AREA, event); break; @@ -355,6 +371,7 @@ tp_button_top_to_ignore_handle_event(struct tp_dispatch *tp, event); break; case BUTTON_EVENT_IN_BOTTOM_R: + case BUTTON_EVENT_IN_BOTTOM_M: case BUTTON_EVENT_IN_BOTTOM_L: case BUTTON_EVENT_IN_AREA: break; @@ -377,6 +394,7 @@ tp_button_ignore_handle_event(struct tp_dispatch *tp, { switch (event) { case BUTTON_EVENT_IN_BOTTOM_R: + case BUTTON_EVENT_IN_BOTTOM_M: case BUTTON_EVENT_IN_BOTTOM_L: case BUTTON_EVENT_IN_TOP_R: case BUTTON_EVENT_IN_TOP_M: @@ -450,6 +468,8 @@ tp_button_handle_state(struct tp_dispatch *tp, uint64_t time) if (is_inside_bottom_right_area(tp, t)) event = BUTTON_EVENT_IN_BOTTOM_R; + else if (is_inside_bottom_middle_area(tp, t)) + event = BUTTON_EVENT_IN_BOTTOM_M; else if (is_inside_bottom_left_area(tp, t)) event = BUTTON_EVENT_IN_BOTTOM_L; else if (is_inside_top_right_area(tp, t)) @@ -543,7 +563,14 @@ tp_init_softbuttons(struct tp_dispatch *tp, } else { tp->buttons.bottom_area.top_edge = height * .85 + yoffset; } - tp->buttons.bottom_area.rightbutton_left_edge = width/2 + xoffset; + + /* The middle button is 25% of the touchpad and centered. Many + * touchpads don't have markings for the middle button at all so we + * need to make it big enough to reliably hit it but not too big so + * it takes away all the space. + */ + tp->buttons.bottom_area.middlebutton_left_edge = width * 0.375 + xoffset; + tp->buttons.bottom_area.rightbutton_left_edge = width * 0.625 + xoffset; } void @@ -989,6 +1016,8 @@ tp_post_clickpadbutton_buttons(struct tp_dispatch *tp, uint64_t time) break; case BUTTON_EVENT_IN_TOP_M: is_top = 1; + /* fallthrough */ + case BUTTON_EVENT_IN_BOTTOM_M: area |= MIDDLE; break; case BUTTON_EVENT_IN_TOP_R: diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 1f05a03e..7277726e 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -70,6 +70,7 @@ enum touch_palm_state { enum button_event { BUTTON_EVENT_IN_BOTTOM_R = 30, + BUTTON_EVENT_IN_BOTTOM_M, BUTTON_EVENT_IN_BOTTOM_L, BUTTON_EVENT_IN_TOP_R, BUTTON_EVENT_IN_TOP_M, @@ -283,6 +284,7 @@ struct tp_dispatch { struct { int32_t top_edge; /* in device coordinates */ int32_t rightbutton_left_edge; /* in device coordinates */ + int32_t middlebutton_left_edge; /* in device coordinates */ } bottom_area; struct { diff --git a/test/touchpad-buttons.c b/test/touchpad-buttons.c index 064c29ef..080c6702 100644 --- a/test/touchpad-buttons.c +++ b/test/touchpad-buttons.c @@ -987,6 +987,35 @@ START_TEST(clickpad_softbutton_left) } END_TEST +START_TEST(clickpad_softbutton_middle) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + litest_drain_events(li); + + litest_touch_down(dev, 0, 50, 90); + litest_event(dev, EV_KEY, BTN_LEFT, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + + litest_assert_button_event(li, + BTN_MIDDLE, + 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); + + litest_assert_button_event(li, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_RELEASED); + + libinput_dispatch(li); + + litest_assert_empty_queue(li); +} +END_TEST + START_TEST(clickpad_softbutton_right) { struct litest_device *dev = litest_current_device(); @@ -1585,6 +1614,7 @@ litest_setup_tests(void) litest_add("touchpad:click", clickpad_finger_pin, LITEST_CLICKPAD, LITEST_ANY); litest_add("touchpad:softbutton", clickpad_softbutton_left, LITEST_CLICKPAD, LITEST_APPLE_CLICKPAD); + litest_add("touchpad:softbutton", clickpad_softbutton_middle, LITEST_CLICKPAD, LITEST_APPLE_CLICKPAD); litest_add("touchpad:softbutton", clickpad_softbutton_right, LITEST_CLICKPAD, LITEST_APPLE_CLICKPAD); litest_add("touchpad:softbutton", clickpad_softbutton_left_tap_n_drag, LITEST_CLICKPAD, LITEST_APPLE_CLICKPAD); litest_add("touchpad:softbutton", clickpad_softbutton_right_tap_n_drag, LITEST_CLICKPAD, LITEST_APPLE_CLICKPAD);