From: Peter Hutterer Date: Thu, 27 Mar 2014 23:44:11 +0000 (+1000) Subject: touchpad: Add clickpad-style software buttons X-Git-Tag: 0.3.0~39^2~9 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=e192ecc6e934fd5f77dbc9d8a21746947e8fa61d;p=platform%2Fupstream%2Flibinput.git touchpad: Add clickpad-style software buttons Almost all non Apple touchpads have visible markings for software button areas, so limit clickfinger behavior to Apple clickpads, and implement software button areas for others. This is a slightly fancier implementation than the simplest model and ported over from libtouchpad. It implements a state machine for the software buttons with left and right buttons currently implemented. Buttons are oriented left-to-right, in a horizontal bar. No random button placement allowed. In general, the procedure is: - if a finger sets down in the left button area, a click is a left click - if a finger sets down in the right button area, a click is a right click - if a finger leaves the button area, a click is a left click - if a finger starts outside the button area, a click is a left click Two timeouts are used to handle buttons more smoothly: - if a finger sets down in a button area but "immediately" moves over to a different area, that area takes effect on a click. - if a finger leaves a button area and "immediately" clicks or moves back into the area, the button still takes effect on a click. - if a finger changes between areas and stays there for a timeout, that area takes effect on a click. Note the button area states are named BOTTOM_foo to make it easier to later add support for a top button area such as can be found on the Thinkpad [2-5]40 series. Co-authored-by: Hans de Goede Signed-off-by: Peter Hutterer Signed-off-by: Hans de Goede Reviewed-by: Jonas Ã…dahl Reviewed-by: Hans de Goede Reviewed-by: Peter Hutterer --- diff --git a/doc/Makefile.am b/doc/Makefile.am index 75fa98a4..a33638da 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -1,4 +1,4 @@ -EXTRA_DIST = touchpad-tap-state-machine.svg +EXTRA_DIST = touchpad-tap-state-machine.svg touchpad-softbutton-state-machine.svg if HAVE_DOXYGEN diff --git a/doc/touchpad-softbutton-state-machine.svg b/doc/touchpad-softbutton-state-machine.svg new file mode 100644 index 00000000..1838e354 --- /dev/null +++ b/doc/touchpad-softbutton-state-machine.svg @@ -0,0 +1,173 @@ + + + + + + + + +NONE + +on-entry: + +curr = none + + + + +BOTTOM_NEW + +on-entry: + +curr = button + +start inner timeout + + + + +AREA + +on-entry: + +curr =area + + + + +finger in + +area + + + + +BOTTOM + + + + +finger + +up + + + + +phys + +button + +press + + + + + + +inner + +timeout + + + + + + + + +finger in + +AREA + + + + + + +BOTTOM_TO_AREA + +on-entry: + +start outer timeout + + + + + + +outer + +timeout + + + + + + + + +finger in + +bottom + + + + + + + + +finger in + +area + + + + + + + + +finger in + +bottom + +button != curr + + + + + + + + +ANY + + + + + + + + + + + + +finger in + +bottom + +button == curr + + + + + + + + + + + diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index 21241de7..1ddd8df9 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -20,11 +20,345 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include +#include +#include #include +#include +#include +#include +#include #include "evdev-mt-touchpad.h" #define DEFAULT_BUTTON_MOTION_THRESHOLD 0.02 /* 2% of size */ +#define DEFAULT_BUTTON_ENTER_TIMEOUT 100 /* ms */ +#define DEFAULT_BUTTON_LEAVE_TIMEOUT 300 /* ms */ + +/***************************************** + * BEFORE YOU EDIT THIS FILE, look at the state diagram in + * doc/touchpad-softbutton-state-machine.svg, or online at + * https://drive.google.com/file/d/0B1NwWmji69nocUs1cVJTbkdwMFk/edit?usp=sharing + * (it's a http://draw.io diagram) + * + * Any changes in this file must be represented in the diagram. + * + * The state machine only affects the soft button area code. + */ + +#define CASE_RETURN_STRING(a) case a: return #a; + +static inline const char* +button_state_to_str(enum button_state state) { + switch(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); + } + return NULL; +} + +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_L); + CASE_RETURN_STRING(BUTTON_EVENT_IN_AREA); + CASE_RETURN_STRING(BUTTON_EVENT_UP); + CASE_RETURN_STRING(BUTTON_EVENT_PRESS); + CASE_RETURN_STRING(BUTTON_EVENT_RELEASE); + CASE_RETURN_STRING(BUTTON_EVENT_TIMEOUT); + } + return NULL; +} + +static inline bool +is_inside_button_area(struct tp_dispatch *tp, struct tp_touch *t) +{ + return t->y >= tp->buttons.area.top_edge; +} + +static inline bool +is_inside_right_area(struct tp_dispatch *tp, struct tp_touch *t) +{ + return is_inside_button_area(tp, t) && + t->x > tp->buttons.area.rightbutton_left_edge; +} + +static inline bool +is_inside_left_area(struct tp_dispatch *tp, struct tp_touch *t) +{ + return is_inside_button_area(tp, t) && + !is_inside_right_area(tp, t); +} + +static void +tp_button_set_timer(struct tp_dispatch *tp, uint32_t timeout) +{ + struct itimerspec its; + its.it_interval.tv_sec = 0; + its.it_interval.tv_nsec = 0; + its.it_value.tv_sec = timeout / 1000; + its.it_value.tv_nsec = (timeout % 1000) * 1000 * 1000; + timerfd_settime(tp->buttons.timer_fd, TFD_TIMER_ABSTIME, &its, NULL); +} + +static void +tp_button_set_enter_timer(struct tp_dispatch *tp, struct tp_touch *t) +{ + t->button.timeout = t->millis + DEFAULT_BUTTON_ENTER_TIMEOUT; + tp_button_set_timer(tp, t->button.timeout); +} + +static void +tp_button_set_leave_timer(struct tp_dispatch *tp, struct tp_touch *t) +{ + t->button.timeout = t->millis + DEFAULT_BUTTON_LEAVE_TIMEOUT; + tp_button_set_timer(tp, t->button.timeout); +} + +static void +tp_button_clear_timer(struct tp_dispatch *tp, struct tp_touch *t) +{ + t->button.timeout = 0; +} + +/* + * tp_button_set_state, change state and implement on-entry behavior + * as described in the state machine diagram. + */ +static void +tp_button_set_state(struct tp_dispatch *tp, struct tp_touch *t, + enum button_state new_state, enum button_event event) +{ + tp_button_clear_timer(tp, t); + + t->button.state = new_state; + switch (t->button.state) { + case BUTTON_STATE_NONE: + t->button.curr = 0; + break; + case BUTTON_STATE_AREA: + t->button.curr = BUTTON_EVENT_IN_AREA; + 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; + } +} + +static void +tp_button_none_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: + tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM_NEW, event); + break; + 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: + case BUTTON_EVENT_TIMEOUT: + break; + } +} + +static void +tp_button_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: + case BUTTON_EVENT_IN_AREA: + 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_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_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_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_AREA: + 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; + } +} + +static void +tp_button_handle_event(struct tp_dispatch *tp, + struct tp_touch *t, + enum button_event event, + uint32_t time) +{ + enum button_state current = t->button.state; + + switch(t->button.state) { + case BUTTON_STATE_NONE: + tp_button_none_handle_event(tp, t, event); + break; + case BUTTON_STATE_AREA: + tp_button_area_handle_event(tp, t, event); + break; + 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; + } + + if (current != t->button.state) + log_debug("button state: from %s, event %s to %s\n", + button_state_to_str(current), + button_event_to_str(event), + button_state_to_str(t->button.state)); +} + +int +tp_button_handle_state(struct tp_dispatch *tp, uint32_t time) +{ + struct tp_touch *t; + + tp_for_each_touch(tp, t) { + if (t->state == TOUCH_NONE) + continue; + + if (t->state == TOUCH_END) { + tp_button_handle_event(tp, t, BUTTON_EVENT_UP, time); + } else if (t->dirty) { + if (is_inside_right_area(tp, t)) + tp_button_handle_event(tp, t, BUTTON_EVENT_IN_BOTTOM_R, time); + else if (is_inside_left_area(tp, t)) + tp_button_handle_event(tp, t, BUTTON_EVENT_IN_BOTTOM_L, time); + else + tp_button_handle_event(tp, t, BUTTON_EVENT_IN_AREA, time); + } + if (tp->queued & TOUCHPAD_EVENT_BUTTON_RELEASE) + tp_button_handle_event(tp, t, BUTTON_EVENT_RELEASE, time); + if (tp->queued & TOUCHPAD_EVENT_BUTTON_PRESS) + tp_button_handle_event(tp, t, BUTTON_EVENT_PRESS, time); + } + + return 0; +} + +static int +tp_button_handle_timeout(struct tp_dispatch *tp, uint32_t now) +{ + struct tp_touch *t; + uint32_t min_timeout = INT_MAX; + + tp_for_each_touch(tp, t) { + if (t->button.timeout != 0 && t->button.timeout <= now) { + tp_button_clear_timer(tp, t); + tp_button_handle_event(tp, t, BUTTON_EVENT_TIMEOUT, now); + } + if (t->button.timeout != 0) + min_timeout = min(t->button.timeout, min_timeout); + } + + return min_timeout == INT_MAX ? 0 : min_timeout; +} int tp_process_button(struct tp_dispatch *tp, @@ -43,6 +377,28 @@ tp_process_button(struct tp_dispatch *tp, return 0; } +static void +tp_button_timeout_handler(void *data) +{ + struct tp_dispatch *tp = data; + uint64_t expires; + int len; + struct timespec ts; + uint32_t now; + + len = read(tp->buttons.timer_fd, &expires, sizeof expires); + if (len != sizeof expires) + /* This will only happen if the application made the fd + * non-blocking, but this function should only be called + * upon the timeout, so lets continue anyway. */ + log_error("timerfd read error: %s\n", strerror(errno)); + + clock_gettime(CLOCK_MONOTONIC, &ts); + now = ts.tv_sec * 1000 + ts.tv_nsec / 1000000; + + tp_button_handle_timeout(tp, now); +} + int tp_init_buttons(struct tp_dispatch *tp, struct evdev_device *device) @@ -60,9 +416,48 @@ tp_init_buttons(struct tp_dispatch *tp, tp->buttons.motion_dist = diagonal * DEFAULT_BUTTON_MOTION_THRESHOLD; + if (libevdev_get_id_vendor(device->evdev) == 0x5ac) /* Apple */ + tp->buttons.use_clickfinger = true; + + tp->buttons.use_softbuttons = !tp->buttons.use_clickfinger && + !tp->buttons.has_buttons; + + if (tp->buttons.use_softbuttons) { + tp->buttons.area.top_edge = height * .8 + device->abs.min_y; + tp->buttons.area.rightbutton_left_edge = width/2 + device->abs.min_x; + tp->buttons.timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); + + if (tp->buttons.timer_fd == -1) + return -1; + + tp->buttons.source = + libinput_add_fd(tp->device->base.seat->libinput, + tp->buttons.timer_fd, + tp_button_timeout_handler, + tp); + if (tp->buttons.source == NULL) + return -1; + } else { + tp->buttons.area.top_edge = INT_MAX; + } + return 0; } +void +tp_destroy_buttons(struct tp_dispatch *tp) +{ + if (tp->buttons.source) { + libinput_remove_source(tp->device->base.seat->libinput, + tp->buttons.source); + tp->buttons.source = NULL; + } + if (tp->buttons.timer_fd > -1) { + close(tp->buttons.timer_fd); + tp->buttons.timer_fd = -1; + } +} + static int tp_post_clickfinger_buttons(struct tp_dispatch *tp, uint32_t time) { @@ -131,10 +526,63 @@ tp_post_physical_buttons(struct tp_dispatch *tp, uint32_t time) return 0; } +static int +tp_post_softbutton_buttons(struct tp_dispatch *tp, uint32_t time) +{ + uint32_t current, old, button; + enum libinput_pointer_button_state state; + + current = tp->buttons.state; + old = tp->buttons.old_state; + + if (current == old) + return 0; + + if (tp->nfingers_down == 0 || tp->nfingers_down > 2) + return 0; + + if (current) { + struct tp_touch *t; + button = 0; + + tp_for_each_touch(tp, t) { + if (t->button.curr == BUTTON_EVENT_IN_BOTTOM_R) + button |= 0x2; + else if (t->button.curr == BUTTON_EVENT_IN_BOTTOM_L) + button |= 0x1; + } + + switch (button) { + case 0: /* only in area */ + case 1: /* only left area */ + button = BTN_LEFT; + break; + case 2: /* only right area */ + button = BTN_RIGHT; + break; + case 3: /* left + right area */ + button = BTN_MIDDLE; + break; + } + + tp->buttons.active = button; + state = LIBINPUT_POINTER_BUTTON_STATE_PRESSED; + } else { + state = LIBINPUT_POINTER_BUTTON_STATE_RELEASED; + button = tp->buttons.active; + } + + pointer_notify_button(&tp->device->base, + time, + button, + state); + return 1; +} + int tp_post_button_events(struct tp_dispatch *tp, uint32_t time) { - int rc; + int rc = 0; if ((tp->queued & (TOUCHPAD_EVENT_BUTTON_PRESS|TOUCHPAD_EVENT_BUTTON_RELEASE)) == 0) @@ -142,8 +590,11 @@ tp_post_button_events(struct tp_dispatch *tp, uint32_t time) if (tp->buttons.has_buttons) rc = tp_post_physical_buttons(tp, time); - else + else if (tp->buttons.use_clickfinger) rc = tp_post_clickfinger_buttons(tp, time); + else if (tp->buttons.use_softbuttons) + rc = tp_post_softbutton_buttons(tp, time); + return rc; } diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index c86b0578..7f73f6ee 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -399,6 +399,8 @@ tp_process_state(struct tp_dispatch *tp, uint32_t time) tp_unpin_finger(tp, t); } + tp_button_handle_state(tp, time); + /* * We have a physical button down event on a clickpad. To avoid * spurious pointer moves by the clicking finger we pin all fingers. @@ -596,6 +598,7 @@ tp_destroy(struct evdev_dispatch *dispatch) (struct tp_dispatch*)dispatch; tp_destroy_tap(tp); + tp_destroy_buttons(tp); if (tp->filter) tp->filter->interface->destroy(tp->filter); @@ -608,10 +611,18 @@ static struct evdev_dispatch_interface tp_interface = { tp_destroy }; +static void +tp_init_touch(struct tp_dispatch *tp, + struct tp_touch *t) +{ + t->button.state = BUTTON_STATE_NONE; +} + static int tp_init_slots(struct tp_dispatch *tp, struct evdev_device *device) { + size_t i; const struct input_absinfo *absinfo; absinfo = libevdev_get_abs_info(device->evdev, ABS_MT_SLOT); @@ -649,6 +660,9 @@ tp_init_slots(struct tp_dispatch *tp, if (!tp->touches) return -1; + for (i = 0; i < tp->ntouches; i++) + tp_init_touch(tp, &tp->touches[i]); + return 0; } @@ -690,6 +704,7 @@ tp_init(struct tp_dispatch *tp, tp->base.interface = &tp_interface; tp->device = device; tp->tap.timer_fd = -1; + tp->buttons.timer_fd = -1; if (tp_init_slots(tp, device) != 0) return -1; diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 85cf7e54..8d8dd840 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -46,6 +46,24 @@ enum touch_state { TOUCH_END }; +enum button_event { + BUTTON_EVENT_IN_BOTTOM_R = 30, + BUTTON_EVENT_IN_BOTTOM_L, + BUTTON_EVENT_IN_AREA, + BUTTON_EVENT_UP, + BUTTON_EVENT_PRESS, + BUTTON_EVENT_RELEASE, + BUTTON_EVENT_TIMEOUT, +}; + +enum button_state { + BUTTON_STATE_NONE, + BUTTON_STATE_AREA, + BUTTON_STATE_BOTTOM, + BUTTON_STATE_BOTTOM_NEW, + BUTTON_STATE_BOTTOM_TO_AREA, +}; + enum scroll_state { SCROLL_STATE_NONE, SCROLL_STATE_SCROLLING @@ -101,6 +119,14 @@ struct tp_touch { int32_t center_x; int32_t center_y; } pinned; + + /* Software-button state and timeout if applicable */ + struct { + enum button_state state; + /* We use button_event here so we can use == on events */ + enum button_event curr; + uint32_t timeout; + } button; }; struct tp_dispatch { @@ -129,10 +155,27 @@ struct tp_dispatch { struct { bool has_buttons; /* true for physical LMR buttons */ + bool use_clickfinger; /* number of fingers decides button number */ + bool use_softbuttons; /* use software-button area */ uint32_t state; uint32_t old_state; uint32_t motion_dist; /* for pinned touches */ unsigned int active; /* currently active button, for release event */ + + /* Only used if has_buttons is false. The software button area is always + * a horizontal strip across the touchpad. Depending on the + * rightbutton_left_edge value, the buttons are split according to the + * edge settings. + */ + struct { + int32_t top_edge; + int32_t rightbutton_left_edge; + } area; + + unsigned int timeout; /* current timeout in ms */ + + int timer_fd; + struct libinput_source *source; } buttons; /* physical buttons */ struct { @@ -172,6 +215,9 @@ tp_destroy_tap(struct tp_dispatch *tp); int tp_init_buttons(struct tp_dispatch *tp, struct evdev_device *device); +void +tp_destroy_buttons(struct tp_dispatch *tp); + int tp_process_button(struct tp_dispatch *tp, const struct input_event *e, @@ -180,4 +226,7 @@ tp_process_button(struct tp_dispatch *tp, int tp_post_button_events(struct tp_dispatch *tp, uint32_t time); +int +tp_button_handle_state(struct tp_dispatch *tp, uint32_t time); + #endif diff --git a/src/libinput.h b/src/libinput.h index 85c7d717..d771e21c 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -38,6 +38,46 @@ extern "C" { * behind an API. */ +/** + * @page tpbuttons Touchpad button behavior + * + * For touchpad devices without physical buttons, libinput enables an + * emulated right button area through either of two methods. + * + * Software button areas + * ===================== + * On most touchpads, the bottom area of the touchpad is split into a a left + * and a right-button area. Pressing the touchpad down with a finger in + * those areas will generate clicks as shown in the diagram below: + * + * @code + +------------------------+ + | | + | | + | LEFT | + | | + | | + +------------------------+ + | LEFT | RIGHT | + +------------------------+ + * @endcode + * + * Generally, the touchpad will emulate a right-button click if the finger + * was set down in the right button area and did not leave the + * right button area before clicking, even if another finger was already + * down on the touchpad in another area. + * A middle click is generated by clicking the touchpad when one finger is + * in the bottom left button area, and one finger is in the botton right + * button area. + * The exact behavior of the touchpad is implementation-dependent. + * + * Clickfinger + * =========== + * On Apple touchpads, no button areas are provided. Instead, use a + * two-finger click for a right button click, and a three-finger click for a + * middle button click. + */ + /** * @ingroup fixed_point * diff --git a/test/touchpad.c b/test/touchpad.c index f4d78391..bbae6cd8 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -217,7 +217,7 @@ END_TEST START_TEST(touchpad_1fg_clickfinger) { - struct litest_device *dev = litest_current_device(); + struct litest_device *dev = litest_create_device(LITEST_BCM5974); struct libinput *li = dev->libinput; struct libinput_event *event; struct libinput_event_pointer *ptrev; @@ -237,12 +237,14 @@ START_TEST(touchpad_1fg_clickfinger) LIBINPUT_POINTER_BUTTON_STATE_PRESSED); assert_button_event(li, BTN_LEFT, LIBINPUT_POINTER_BUTTON_STATE_RELEASED); + + litest_delete_device(dev); } END_TEST START_TEST(touchpad_2fg_clickfinger) { - struct litest_device *dev = litest_current_device(); + struct litest_device *dev = litest_create_device(LITEST_BCM5974); struct libinput *li = dev->libinput; struct libinput_event *event; struct libinput_event_pointer *ptrev; @@ -264,6 +266,8 @@ START_TEST(touchpad_2fg_clickfinger) LIBINPUT_POINTER_BUTTON_STATE_PRESSED); assert_button_event(li, BTN_RIGHT, LIBINPUT_POINTER_BUTTON_STATE_RELEASED); + + litest_delete_device(dev); } END_TEST @@ -362,8 +366,8 @@ int main(int argc, char **argv) { litest_add("touchpad:tap", touchpad_1fg_tap_n_drag, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:tap", touchpad_2fg_tap, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); - litest_add("touchpad:clickfinger", touchpad_1fg_clickfinger, LITEST_TOUCHPAD, LITEST_ANY); - litest_add("touchpad:clickfinger", touchpad_2fg_clickfinger, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); + litest_add_no_device("touchpad:clickfinger", touchpad_1fg_clickfinger); + litest_add_no_device("touchpad:clickfinger", touchpad_2fg_clickfinger); litest_add("touchpad:click", touchpad_btn_left, LITEST_TOUCHPAD, LITEST_CLICKPAD); litest_add("touchpad:click", clickpad_btn_left, LITEST_CLICKPAD, LITEST_ANY);