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);