diff --git a/doc/clickpad-softbuttons.dox b/doc/clickpad-softbuttons.dox index 2653c3b7..d9ebfbbc 100644 --- a/doc/clickpad-softbuttons.dox +++ b/doc/clickpad-softbuttons.dox @@ -66,6 +66,12 @@ The Xorg synaptics driver uses 30% of the touchpad dimensions as threshold, libinput does not have this restriction. If two fingers are on the pad while clicking, that is a two-finger click. +Clickfinger configuration can be enabled through the +libinput_device_config_click_set_method() call. If clickfingers are +enabled on a touchpad with top software buttons, the top area will keep +acting as softbuttons for use with the trackpoint. Clickfingers will be used +everywhere else on the touchpad. + @section special_clickpads Special Clickpads The Lenovo *40 series laptops have a clickpad that provides two software button sections, one at diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index 6af3fcf1..ca7495f8 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -494,10 +494,9 @@ tp_release_all_buttons(struct tp_dispatch *tp, } } -void +static void tp_init_softbuttons(struct tp_dispatch *tp, - struct evdev_device *device, - double topbutton_size_mult) + struct evdev_device *device) { int width, height; const struct input_absinfo *absinfo_x, *absinfo_y; @@ -523,6 +522,26 @@ tp_init_softbuttons(struct tp_dispatch *tp, } tp->buttons.bottom_area.rightbutton_left_edge = width/2 + xoffset; +} + +void +tp_init_top_softbuttons(struct tp_dispatch *tp, + struct evdev_device *device, + double topbutton_size_mult) +{ + int width, height; + const struct input_absinfo *absinfo_x, *absinfo_y; + int xoffset, yoffset; + int yres; + + absinfo_x = device->abs.absinfo_x; + absinfo_y = device->abs.absinfo_y; + + xoffset = absinfo_x->minimum, + yoffset = absinfo_y->minimum; + yres = absinfo_y->resolution; + width = abs(absinfo_x->maximum - absinfo_x->minimum); + height = abs(absinfo_y->maximum - absinfo_y->minimum); if (tp->buttons.has_topbuttons) { /* T440s has the top button line 5mm from the top, event @@ -545,6 +564,89 @@ tp_init_softbuttons(struct tp_dispatch *tp, } } +static inline uint32_t +tp_button_config_click_get_methods(struct libinput_device *device) +{ + struct evdev_device *evdev = (struct evdev_device*)device; + struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch; + uint32_t methods = LIBINPUT_CONFIG_CLICK_METHOD_NONE; + + if (tp->buttons.is_clickpad) { + methods |= LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS; + if (tp->has_mt) + methods |= LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER; + } + + return methods; +} + +static void +tp_switch_click_method(struct tp_dispatch *tp) +{ + /* + * All we need to do when switching click methods is to change the + * bottom_area.top_edge so that when in clickfinger mode the bottom + * touchpad area is not dead wrt finger movement starting there. + * + * We do not need to take any state into account, fingers which are + * already down will simply keep the state / area they have assigned + * until they are released, and the post_button_events path is state + * agnostic. + */ + + switch (tp->buttons.click_method) { + case LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS: + tp_init_softbuttons(tp, tp->device); + break; + case LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER: + case LIBINPUT_CONFIG_CLICK_METHOD_NONE: + tp->buttons.bottom_area.top_edge = INT_MAX; + break; + } +} + +static enum libinput_config_status +tp_button_config_click_set_method(struct libinput_device *device, + enum libinput_config_click_method method) +{ + struct evdev_device *evdev = (struct evdev_device*)device; + struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch; + + tp->buttons.click_method = method; + tp_switch_click_method(tp); + + return LIBINPUT_CONFIG_STATUS_SUCCESS; +} + +static enum libinput_config_click_method +tp_button_config_click_get_method(struct libinput_device *device) +{ + struct evdev_device *evdev = (struct evdev_device*)device; + struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch; + + return tp->buttons.click_method; +} + +static enum libinput_config_click_method +tp_click_get_default_method(struct tp_dispatch *tp) +{ + if (!tp->buttons.is_clickpad) + return LIBINPUT_CONFIG_CLICK_METHOD_NONE; + else if (libevdev_get_id_vendor(tp->device->evdev) == VENDOR_ID_APPLE) + return LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER; + else + return LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS; +} + +static enum libinput_config_click_method +tp_button_config_click_get_default_method(struct libinput_device *device) +{ + struct evdev_device *evdev = (struct evdev_device*)device; + struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch; + + return tp_click_get_default_method(tp); +} + int tp_init_buttons(struct tp_dispatch *tp, struct evdev_device *device) @@ -582,15 +684,16 @@ tp_init_buttons(struct tp_dispatch *tp, tp->buttons.motion_dist = diagonal * DEFAULT_BUTTON_MOTION_THRESHOLD; - if (libevdev_get_id_vendor(device->evdev) == VENDOR_ID_APPLE) - tp->buttons.use_clickfinger = true; + tp->buttons.config_method.get_methods = tp_button_config_click_get_methods; + tp->buttons.config_method.set_method = tp_button_config_click_set_method; + tp->buttons.config_method.get_method = tp_button_config_click_get_method; + tp->buttons.config_method.get_default_method = tp_button_config_click_get_default_method; + tp->device->base.config.click_method = &tp->buttons.config_method; - if (tp->buttons.is_clickpad && !tp->buttons.use_clickfinger) { - tp_init_softbuttons(tp, device, 1.0); - } else { - tp->buttons.bottom_area.top_edge = INT_MAX; - tp->buttons.top_area.bottom_edge = INT_MIN; - } + tp->buttons.click_method = tp_click_get_default_method(tp); + tp_switch_click_method(tp); + + tp_init_top_softbuttons(tp, device, 1.0); tp_for_each_touch(tp, t) { t->button.state = BUTTON_STATE_NONE; @@ -611,43 +714,6 @@ tp_remove_buttons(struct tp_dispatch *tp) libinput_timer_cancel(&t->button.timer); } -static int -tp_post_clickfinger_buttons(struct tp_dispatch *tp, uint64_t time) -{ - uint32_t current, old, button; - enum libinput_button_state state; - - current = tp->buttons.state; - old = tp->buttons.old_state; - - if (current == old) - return 0; - - if (current) { - switch (tp->nfingers_down) { - case 1: button = BTN_LEFT; break; - case 2: button = BTN_RIGHT; break; - case 3: button = BTN_MIDDLE; break; - default: - return 0; - } - tp->buttons.active = button; - state = LIBINPUT_BUTTON_STATE_PRESSED; - } else { - button = tp->buttons.active; - tp->buttons.active = 0; - state = LIBINPUT_BUTTON_STATE_RELEASED; - } - - if (button) { - evdev_pointer_notify_button(tp->device, - time, - button, - state); - } - return 1; -} - static int tp_post_physical_buttons(struct tp_dispatch *tp, uint64_t time) { @@ -683,12 +749,12 @@ tp_post_physical_buttons(struct tp_dispatch *tp, uint64_t time) return 0; } -static void -tp_notify_softbutton(struct tp_dispatch *tp, - uint64_t time, - uint32_t button, - uint32_t is_topbutton, - enum libinput_button_state state) +static int +tp_notify_clickpadbutton(struct tp_dispatch *tp, + uint64_t time, + uint32_t button, + uint32_t is_topbutton, + enum libinput_button_state state) { /* If we've a trackpoint, send top buttons through the trackpoint */ if (is_topbutton && tp->buttons.trackpoint) { @@ -702,18 +768,38 @@ tp_notify_softbutton(struct tp_dispatch *tp, event.value = (state == LIBINPUT_BUTTON_STATE_PRESSED) ? 1 : 0; dispatch->interface->process(dispatch, tp->buttons.trackpoint, &event, time); - return; + return 1; } /* Ignore button events not for the trackpoint while suspended */ if (tp->device->suspended) - return; + return 0; + + /* + * If the user has requested clickfinger replace the button chosen + * by the softbutton code with one based on the number of fingers. + */ + if (tp->buttons.click_method == LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER && + state == LIBINPUT_BUTTON_STATE_PRESSED) { + switch (tp->nfingers_down) { + case 1: button = BTN_LEFT; break; + case 2: button = BTN_RIGHT; break; + case 3: button = BTN_MIDDLE; break; + default: + button = 0; + } + tp->buttons.active = button; + + if (!button) + return 0; + } evdev_pointer_notify_button(tp->device, time, button, state); + return 1; } static int -tp_post_softbutton_buttons(struct tp_dispatch *tp, uint64_t time) +tp_post_clickpadbutton_buttons(struct tp_dispatch *tp, uint64_t time) { uint32_t current, old, button, is_top; enum libinput_button_state state; @@ -783,22 +869,18 @@ tp_post_softbutton_buttons(struct tp_dispatch *tp, uint64_t time) tp->buttons.click_pending = false; if (button) - tp_notify_softbutton(tp, time, button, is_top, state); + return tp_notify_clickpadbutton(tp, time, button, is_top, state); - return 1; + return 0; } int tp_post_button_events(struct tp_dispatch *tp, uint64_t time) { - if (tp->buttons.is_clickpad) { - if (tp->buttons.use_clickfinger) - return tp_post_clickfinger_buttons(tp, time); - else - return tp_post_softbutton_buttons(tp, time); - } - - return tp_post_physical_buttons(tp, time); + if (tp->buttons.is_clickpad) + return tp_post_clickpadbutton_buttons(tp, time); + else + return tp_post_physical_buttons(tp, time); } int diff --git a/src/evdev-mt-touchpad-edge-scroll.c b/src/evdev-mt-touchpad-edge-scroll.c index 26a86141..8d0a13e3 100644 --- a/src/evdev-mt-touchpad-edge-scroll.c +++ b/src/evdev-mt-touchpad-edge-scroll.c @@ -294,6 +294,7 @@ tp_edge_scroll_handle_state(struct tp_dispatch *tp, uint64_t time) switch (t->state) { case TOUCH_NONE: + case TOUCH_HOVERING: break; case TOUCH_BEGIN: tp_edge_scroll_handle_event(tp, t, SCROLL_EVENT_TOUCH); diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 34b107e2..5221be60 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -135,27 +135,96 @@ tp_get_touch(struct tp_dispatch *tp, unsigned int slot) return &tp->touches[slot]; } -static inline void -tp_begin_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) +static inline unsigned int +tp_fake_finger_count(struct tp_dispatch *tp) { - if (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE) + /* don't count BTN_TOUCH */ + return ffs(tp->fake_touches >> 1); +} + +static inline bool +tp_fake_finger_is_touching(struct tp_dispatch *tp) +{ + return tp->fake_touches & 0x1; +} + +static inline void +tp_fake_finger_set(struct tp_dispatch *tp, + unsigned int code, + bool is_press) +{ + unsigned int shift; + + switch (code) { + case BTN_TOUCH: + shift = 0; + break; + case BTN_TOOL_FINGER: + shift = 1; + break; + case BTN_TOOL_DOUBLETAP: + case BTN_TOOL_TRIPLETAP: + case BTN_TOOL_QUADTAP: + shift = code - BTN_TOOL_DOUBLETAP + 2; + break; + default: + return; + } + + if (is_press) + tp->fake_touches |= 1 << shift; + else + tp->fake_touches &= ~(0x1 << shift); +} + +static inline void +tp_new_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) +{ + if (t->state == TOUCH_BEGIN || + t->state == TOUCH_UPDATE || + t->state == TOUCH_HOVERING) return; + /* we begin the touch as hovering because until BTN_TOUCH happens we + * don't know if it's a touch down or not. And BTN_TOUCH may happen + * after ABS_MT_TRACKING_ID */ tp_motion_history_reset(t); t->dirty = true; - t->state = TOUCH_BEGIN; + t->has_ended = false; + t->state = TOUCH_HOVERING; t->pinned.is_pinned = false; t->millis = time; - tp->nfingers_down++; - assert(tp->nfingers_down >= 1); tp->queued |= TOUCHPAD_EVENT_MOTION; } +static inline void +tp_begin_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) +{ + t->dirty = true; + t->state = TOUCH_BEGIN; + t->millis = time; + tp->nfingers_down++; + assert(tp->nfingers_down >= 1); +} + +/** + * End a touch, even if the touch sequence is still active. + */ static inline void tp_end_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) { - if (t->state == TOUCH_END || t->state == TOUCH_NONE) + switch (t->state) { + case TOUCH_HOVERING: + t->state = TOUCH_NONE; + /* fallthough */ + case TOUCH_NONE: + case TOUCH_END: return; + case TOUCH_BEGIN: + case TOUCH_UPDATE: + break; + + } t->dirty = true; t->is_pointer = false; @@ -168,6 +237,16 @@ tp_end_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) tp->queued |= TOUCHPAD_EVENT_MOTION; } +/** + * End the touch sequence on ABS_MT_TRACKING_ID -1 or when the BTN_TOOL_* 0 is received. + */ +static inline void +tp_end_sequence(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) +{ + t->has_ended = true; + tp_end_touch(tp, t, time); +} + static double tp_estimate_delta(int x0, int x1, int x2, int x3) { @@ -218,9 +297,9 @@ tp_process_absolute(struct tp_dispatch *tp, break; case ABS_MT_TRACKING_ID: if (e->value != -1) - tp_begin_touch(tp, t, time); + tp_new_touch(tp, t, time); else - tp_end_touch(tp, t, time); + tp_end_sequence(tp, t, time); } } @@ -253,41 +332,21 @@ tp_process_fake_touch(struct tp_dispatch *tp, uint64_t time) { struct tp_touch *t; - unsigned int fake_touches; unsigned int nfake_touches; unsigned int i, start; - unsigned int shift; - if (e->code != BTN_TOUCH && - (e->code < BTN_TOOL_DOUBLETAP || e->code > BTN_TOOL_QUADTAP)) - return; + tp_fake_finger_set(tp, e->code, e->value != 0); - shift = e->code == BTN_TOUCH ? 0 : (e->code - BTN_TOOL_DOUBLETAP + 1); + nfake_touches = tp_fake_finger_count(tp); - if (e->value) - tp->fake_touches |= 1 << shift; - else - tp->fake_touches &= ~(0x1 << shift); - - fake_touches = tp->fake_touches; - nfake_touches = 0; - while (fake_touches) { - nfake_touches++; - fake_touches >>= 1; - } - - /* For single touch tps we use BTN_TOUCH for begin / end of touch 0 */ start = tp->has_mt ? tp->real_touches : 0; for (i = start; i < tp->ntouches; i++) { t = tp_get_touch(tp, i); if (i < nfake_touches) - tp_begin_touch(tp, t, time); + tp_new_touch(tp, t, time); else - tp_end_touch(tp, t, time); + tp_end_sequence(tp, t, time); } - - /* On mt the actual touch info may arrive after BTN_TOOL_FOO */ - assert(tp->has_mt || tp->nfingers_down == nfake_touches); } static void @@ -302,6 +361,7 @@ tp_process_key(struct tp_dispatch *tp, tp_process_button(tp, e, time); break; case BTN_TOUCH: + case BTN_TOOL_FINGER: case BTN_TOOL_DOUBLETAP: case BTN_TOOL_TRIPLETAP: case BTN_TOOL_QUADTAP: @@ -325,7 +385,12 @@ tp_unpin_finger(struct tp_dispatch *tp, struct tp_touch *t) tp->buttons.motion_dist * tp->buttons.motion_dist) { t->pinned.is_pinned = false; tp_set_pointer(tp, t); + return; } + + /* The finger may slowly drift, adjust the center */ + t->pinned.center_x = t->x + t->pinned.center_x / 2; + t->pinned.center_y = t->y + t->pinned.center_y / 2; } static void @@ -554,6 +619,61 @@ tp_remove_scroll(struct tp_dispatch *tp) tp_remove_edge_scroll(tp); } +static void +tp_unhover_touches(struct tp_dispatch *tp, uint64_t time) +{ + struct tp_touch *t; + unsigned int nfake_touches; + int i; + + if (!tp->fake_touches && !tp->nfingers_down) + return; + + nfake_touches = tp_fake_finger_count(tp); + if (tp->nfingers_down == nfake_touches && + ((tp->nfingers_down == 0 && !tp_fake_finger_is_touching(tp)) || + (tp->nfingers_down > 0 && tp_fake_finger_is_touching(tp)))) + return; + + /* if BTN_TOUCH is set and we have less fingers down than fake + * touches, switch each hovering touch to BEGIN + * until nfingers_down matches nfake_touches + */ + if (tp_fake_finger_is_touching(tp) && + tp->nfingers_down < nfake_touches) { + for (i = 0; i < (int)tp->ntouches; i++) { + t = tp_get_touch(tp, i); + + if (t->state == TOUCH_HOVERING) { + tp_begin_touch(tp, t, time); + + if (tp->nfingers_down >= nfake_touches) + break; + } + } + } + + /* if BTN_TOUCH is unset end all touches, we're hovering now. If we + * have too many touches also end some of them. This is done in + * reverse order. + */ + if (tp->nfingers_down > nfake_touches || + !tp_fake_finger_is_touching(tp)) { + for (i = tp->ntouches - 1; i >= 0; i--) { + t = tp_get_touch(tp, i); + + if (t->state == TOUCH_HOVERING) + continue; + + tp_end_touch(tp, t, time); + + if (tp_fake_finger_is_touching(tp) && + tp->nfingers_down == nfake_touches) + break; + } + } +} + static void tp_process_state(struct tp_dispatch *tp, uint64_t time) { @@ -561,6 +681,8 @@ tp_process_state(struct tp_dispatch *tp, uint64_t time) struct tp_touch *first = tp_get_touch(tp, 0); unsigned int i; + tp_unhover_touches(tp, time); + for (i = 0; i < tp->ntouches; i++) { t = tp_get_touch(tp, i); @@ -606,13 +728,18 @@ tp_post_process_state(struct tp_dispatch *tp, uint64_t time) struct tp_touch *t; tp_for_each_touch(tp, t) { + if (!t->dirty) continue; - if (t->state == TOUCH_END) - t->state = TOUCH_NONE; - else if (t->state == TOUCH_BEGIN) + if (t->state == TOUCH_END) { + if (t->has_ended) + t->state = TOUCH_NONE; + else + t->state = TOUCH_HOVERING; + } else if (t->state == TOUCH_BEGIN) { t->state = TOUCH_UPDATE; + } t->dirty = false; } @@ -793,7 +920,7 @@ tp_clear_state(struct tp_dispatch *tp) tp_release_all_taps(tp, now); tp_for_each_touch(tp, t) { - tp_end_touch(tp, t, now); + tp_end_sequence(tp, t, now); } tp_handle_state(tp, now); @@ -811,7 +938,7 @@ tp_suspend(struct tp_dispatch *tp, struct evdev_device *device) if (tp->buttons.has_topbuttons) { evdev_notify_suspended_device(device); /* Enlarge topbutton area while suspended */ - tp_init_softbuttons(tp, device, 1.5); + tp_init_top_softbuttons(tp, device, 1.5); } else { evdev_device_suspend(device); } @@ -824,7 +951,7 @@ tp_resume(struct tp_dispatch *tp, struct evdev_device *device) /* tap state-machine is offline while suspended, reset state */ tp_clear_state(tp); /* restore original topbutton area size */ - tp_init_softbuttons(tp, device, 1.0); + tp_init_top_softbuttons(tp, device, 1.0); evdev_notify_resumed_device(device); } else { evdev_device_resume(device); @@ -954,6 +1081,7 @@ tp_init_touch(struct tp_dispatch *tp, struct tp_touch *t) { t->tp = tp; + t->has_ended = true; } static int diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 5807f08c..3479e5ea 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -53,6 +53,7 @@ enum touchpad_model { enum touch_state { TOUCH_NONE = 0, + TOUCH_HOVERING, TOUCH_BEGIN, TOUCH_UPDATE, TOUCH_END @@ -130,6 +131,7 @@ struct tp_motion { struct tp_touch { struct tp_dispatch *tp; enum touch_state state; + bool has_ended; /* TRACKING_ID == -1 */ bool dirty; bool is_pointer; /* the pointer-controlling touch */ int32_t x; @@ -197,7 +199,12 @@ struct tp_dispatch { unsigned int real_touches; /* number of slots */ unsigned int ntouches; /* no slots inc. fakes */ struct tp_touch *touches; /* len == ntouches */ - unsigned int fake_touches; /* fake touch mask */ + /* bit 0: BTN_TOUCH + * bit 1: BTN_TOOL_FINGER + * bit 2: BTN_TOOL_DOUBLETAP + * ... + */ + unsigned int fake_touches; struct { int32_t margin_x; @@ -236,7 +243,10 @@ struct tp_dispatch { } top_area; struct evdev_device *trackpoint; - } buttons; /* physical buttons */ + + enum libinput_config_click_method click_method; + struct libinput_device_config_click_method config_method; + } buttons; struct { struct libinput_device_config_scroll_method config_method; @@ -299,9 +309,9 @@ int tp_init_buttons(struct tp_dispatch *tp, struct evdev_device *device); void -tp_init_softbuttons(struct tp_dispatch *tp, - struct evdev_device *device, - double topbutton_size_mult); +tp_init_top_softbuttons(struct tp_dispatch *tp, + struct evdev_device *device, + double topbutton_size_mult); void tp_remove_buttons(struct tp_dispatch *tp); diff --git a/src/libinput-private.h b/src/libinput-private.h index d7f28e34..8f11d73d 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -150,6 +150,14 @@ struct libinput_device_config_scroll_method { uint32_t (*get_default_button)(struct libinput_device *device); }; +struct libinput_device_config_click_method { + uint32_t (*get_methods)(struct libinput_device *device); + enum libinput_config_status (*set_method)(struct libinput_device *device, + enum libinput_config_click_method method); + enum libinput_config_click_method (*get_method)(struct libinput_device *device); + enum libinput_config_click_method (*get_default_method)(struct libinput_device *device); +}; + struct libinput_device_config { struct libinput_device_config_tap *tap; struct libinput_device_config_calibration *calibration; @@ -158,6 +166,7 @@ struct libinput_device_config { struct libinput_device_config_natural_scroll *natural_scroll; struct libinput_device_config_left_handed *left_handed; struct libinput_device_config_scroll_method *scroll_method; + struct libinput_device_config_click_method *click_method; }; struct libinput_device { diff --git a/src/libinput.c b/src/libinput.c index 2469c864..95d9c6c8 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -1961,6 +1961,56 @@ libinput_device_config_left_handed_get_default(struct libinput_device *device) return device->config.left_handed->get_default(device); } +LIBINPUT_EXPORT uint32_t +libinput_device_config_click_get_methods(struct libinput_device *device) +{ + if (device->config.click_method) + return device->config.click_method->get_methods(device); + else + return 0; +} + +LIBINPUT_EXPORT enum libinput_config_status +libinput_device_config_click_set_method(struct libinput_device *device, + enum libinput_config_click_method method) +{ + if ((libinput_device_config_click_get_methods(device) & method) != method) + return LIBINPUT_CONFIG_STATUS_UNSUPPORTED; + + /* Check method is a single valid method */ + switch (method) { + case LIBINPUT_CONFIG_CLICK_METHOD_NONE: + case LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS: + case LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER: + break; + default: + return LIBINPUT_CONFIG_STATUS_INVALID; + } + + if (device->config.click_method) + return device->config.click_method->set_method(device, method); + else /* method must be _NONE to get here */ + return LIBINPUT_CONFIG_STATUS_SUCCESS; +} + +LIBINPUT_EXPORT enum libinput_config_click_method +libinput_device_config_click_get_method(struct libinput_device *device) +{ + if (device->config.click_method) + return device->config.click_method->get_method(device); + else + return LIBINPUT_CONFIG_CLICK_METHOD_NONE; +} + +LIBINPUT_EXPORT enum libinput_config_click_method +libinput_device_config_click_get_default_method(struct libinput_device *device) +{ + if (device->config.click_method) + return device->config.click_method->get_default_method(device); + else + return LIBINPUT_CONFIG_CLICK_METHOD_NONE; +} + LIBINPUT_EXPORT uint32_t libinput_device_config_scroll_get_methods(struct libinput_device *device) { diff --git a/src/libinput.h b/src/libinput.h index 649484d6..50db7e56 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -2498,6 +2498,108 @@ libinput_device_config_left_handed_get(struct libinput_device *device); int libinput_device_config_left_handed_get_default(struct libinput_device *device); +/** + * @ingroup config + * + * The click method defines when to generate software-emulated + * buttons, usually on a device that does not have a specific physical + * button available. + */ +enum libinput_config_click_method { + /** + * Do not send software-emulated button events. This has no effect + * on physical button generations. + */ + LIBINPUT_CONFIG_CLICK_METHOD_NONE = 0, + /** + * Use software-button areas (see @ref clickfinger) to generate + * button events. + */ + LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS = (1 << 0), + /** + * The number of fingers decides which button press to generate. + */ + LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER = (1 << 1), +}; + +/** + * @ingroup config + * + * Check which button click methods a device supports. The button click + * method defines when to generate software-emulated buttons, usually on a + * device that does not have a specific physical button available. + * + * @param device The device to configure + * + * @return A bitmask of possible methods. + * + * @see libinput_device_config_click_get_methods + * @see libinput_device_config_click_set_method + * @see libinput_device_config_click_get_method + */ +uint32_t +libinput_device_config_click_get_methods(struct libinput_device *device); + +/** + * @ingroup config + * + * Set the button click method for this device. The button click + * method defines when to generate software-emulated buttons, usually on a + * device that does not have a specific physical button available. + * + * @note The selected click method may not take effect immediately. The + * device may require changing to a neutral state first before activating + * the new method. + * + * @param device The device to configure + * @param method The button click method + * + * @return A config status code + * + * @see libinput_device_config_click_get_methods + * @see libinput_device_config_click_get_method + * @see libinput_device_config_click_get_default_method + */ +enum libinput_config_status +libinput_device_config_click_set_method(struct libinput_device *device, + enum libinput_config_click_method method); +/** + * @ingroup config + * + * Get the button click method for this device. The button click + * method defines when to generate software-emulated buttons, usually on a + * device that does not have a specific physical button available. + * + * @param device The device to configure + * + * @return The current button click method for this device + * + * @see libinput_device_config_click_get_methods + * @see libinput_device_config_click_set_method + * @see libinput_device_config_click_get_default_method + */ +enum libinput_config_click_method +libinput_device_config_click_get_method(struct libinput_device *device); + +/** + * @ingroup config + * + * Get the default button click method for this device. The button click + * method defines when to generate software-emulated buttons, usually on a + * device that does not have a specific physical button available. + * + * @param device The device to configure + * + * @return The default button click method for this device + * + * @see libinput_device_config_click_get_methods + * @see libinput_device_config_click_set_method + * @see libinput_device_config_click_get_method + */ +enum libinput_config_click_method +libinput_device_config_click_get_default_method(struct libinput_device *device); + + /** * @ingroup config * diff --git a/src/libinput.sym b/src/libinput.sym index 8f4672d6..e49f0f34 100644 --- a/src/libinput.sym +++ b/src/libinput.sym @@ -139,3 +139,11 @@ global: local: *; }; + +LIBINPUT_0.9.0 { +global: + libinput_device_config_click_get_default_method; + libinput_device_config_click_get_method; + libinput_device_config_click_get_methods; + libinput_device_config_click_set_method; +} LIBINPUT_0.8.0; diff --git a/test/Makefile.am b/test/Makefile.am index b66576ef..df3bd838 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -21,6 +21,7 @@ liblitest_la_SOURCES = \ litest-ms-surface-cover.c \ litest-qemu-usb-tablet.c \ litest-synaptics.c \ + litest-synaptics-hover.c \ litest-synaptics-st.c \ litest-synaptics-t440.c \ litest-trackpoint.c \ diff --git a/test/litest-alps-semi-mt.c b/test/litest-alps-semi-mt.c index 12b93b8c..4d95ac90 100644 --- a/test/litest-alps-semi-mt.c +++ b/test/litest-alps-semi-mt.c @@ -31,21 +31,6 @@ #include "litest.h" #include "litest-int.h" -static int tracking_id; - -/* this is a semi-mt device, so we keep track of the touches that the tests - * send and modify them so that the first touch is always slot 0 and sends - * the top-left of the bounding box, the second is always slot 1 and sends - * the bottom-right of the bounding box. - * Lifting any of two fingers terminates slot 1 - */ -struct alps { - /* The actual touches requested by the test for the two slots - * in the 0..100 range used by litest */ - struct { - double x, y; - } touches[2]; -}; static void alps_create(struct litest_device *d); @@ -56,141 +41,28 @@ litest_alps_setup(void) litest_set_current_device(d); } -static void -send_abs_xy(struct litest_device *d, double x, double y) -{ - struct input_event e; - int val; - - e.type = EV_ABS; - e.code = ABS_X; - e.value = LITEST_AUTO_ASSIGN; - val = litest_auto_assign_value(d, &e, 0, x, y); - litest_event(d, EV_ABS, ABS_X, val); - - e.code = ABS_Y; - val = litest_auto_assign_value(d, &e, 0, x, y); - litest_event(d, EV_ABS, ABS_Y, val); -} - -static void -send_abs_mt_xy(struct litest_device *d, double x, double y) -{ - struct input_event e; - int val; - - e.type = EV_ABS; - e.code = ABS_MT_POSITION_X; - e.value = LITEST_AUTO_ASSIGN; - val = litest_auto_assign_value(d, &e, 0, x, y); - litest_event(d, EV_ABS, ABS_MT_POSITION_X, val); - - e.code = ABS_MT_POSITION_Y; - e.value = LITEST_AUTO_ASSIGN; - val = litest_auto_assign_value(d, &e, 0, x, y); - litest_event(d, EV_ABS, ABS_MT_POSITION_Y, val); -} - static void alps_touch_down(struct litest_device *d, unsigned int slot, double x, double y) { - struct alps *alps = d->private; - double t, l, r = 0, b = 0; /* top, left, right, bottom */ + struct litest_semi_mt *semi_mt = d->private; - if (d->ntouches_down > 2 || slot > 1) - return; - - if (d->ntouches_down == 1) { - l = x; - t = y; - } else { - int other = (slot + 1) % 2; - l = min(x, alps->touches[other].x); - t = min(y, alps->touches[other].y); - r = max(x, alps->touches[other].x); - b = max(y, alps->touches[other].y); - } - - send_abs_xy(d, l, t); - - litest_event(d, EV_ABS, ABS_MT_SLOT, 0); - - if (d->ntouches_down == 1) - litest_event(d, EV_ABS, ABS_MT_TRACKING_ID, ++tracking_id); - - send_abs_mt_xy(d, l, t); - - if (d->ntouches_down == 2) { - litest_event(d, EV_ABS, ABS_MT_SLOT, 1); - litest_event(d, EV_ABS, ABS_MT_TRACKING_ID, ++tracking_id); - - send_abs_mt_xy(d, r, b); - } - - litest_event(d, EV_SYN, SYN_REPORT, 0); - - alps->touches[slot].x = x; - alps->touches[slot].y = y; + litest_semi_mt_touch_down(d, semi_mt, slot, x, y); } static void alps_touch_move(struct litest_device *d, unsigned int slot, double x, double y) { - struct alps *alps = d->private; - double t, l, r = 0, b = 0; /* top, left, right, bottom */ + struct litest_semi_mt *semi_mt = d->private; - if (d->ntouches_down > 2 || slot > 1) - return; - - if (d->ntouches_down == 1) { - l = x; - t = y; - } else { - int other = (slot + 1) % 2; - l = min(x, alps->touches[other].x); - t = min(y, alps->touches[other].y); - r = max(x, alps->touches[other].x); - b = max(y, alps->touches[other].y); - } - - send_abs_xy(d, l, t); - - litest_event(d, EV_ABS, ABS_MT_SLOT, 0); - send_abs_mt_xy(d, l, t); - - if (d->ntouches_down == 2) { - litest_event(d, EV_ABS, ABS_MT_SLOT, 1); - send_abs_mt_xy(d, r, b); - } - - litest_event(d, EV_SYN, SYN_REPORT, 0); - - alps->touches[slot].x = x; - alps->touches[slot].y = y; + litest_semi_mt_touch_move(d, semi_mt, slot, x, y); } static void alps_touch_up(struct litest_device *d, unsigned int slot) { - struct alps *alps = d->private; + struct litest_semi_mt *semi_mt = d->private; - /* note: ntouches_down is decreased before we get here */ - if (d->ntouches_down >= 2 || slot > 1) - return; - - litest_event(d, EV_ABS, ABS_MT_SLOT, d->ntouches_down); - litest_event(d, EV_ABS, ABS_MT_TRACKING_ID, -1); - - /* if we have one finger left, send x/y coords for that finger left. - this is likely to happen with a real touchpad */ - if (d->ntouches_down == 1) { - int other = (slot + 1) % 2; - send_abs_xy(d, alps->touches[other].x, alps->touches[other].y); - litest_event(d, EV_ABS, ABS_MT_SLOT, 0); - send_abs_mt_xy(d, alps->touches[other].x, alps->touches[other].y); - } - - litest_event(d, EV_SYN, SYN_REPORT, 0); + litest_semi_mt_touch_up(d, semi_mt, slot); } static struct litest_device_interface interface = { @@ -247,10 +119,10 @@ struct litest_test_device litest_alps_device = { static void alps_create(struct litest_device *d) { - struct alps *alps = zalloc(sizeof(*alps)); - assert(alps); + struct litest_semi_mt *semi_mt = zalloc(sizeof(*semi_mt)); + assert(semi_mt); - d->private = alps; + d->private = semi_mt; d->uinput = litest_create_uinput_device_from_description(litest_alps_device.name, litest_alps_device.id, diff --git a/test/litest-synaptics-hover.c b/test/litest-synaptics-hover.c new file mode 100644 index 00000000..7ba56bc8 --- /dev/null +++ b/test/litest-synaptics-hover.c @@ -0,0 +1,132 @@ +/* + * Copyright © 2014 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "libinput-util.h" + +#include "litest.h" +#include "litest-int.h" + +static void +synaptics_hover_create(struct litest_device *d); + +static void +litest_synaptics_hover_setup(void) +{ + struct litest_device *d = litest_create_device(LITEST_SYNAPTICS_HOVER_SEMI_MT); + litest_set_current_device(d); +} + +static void +synaptics_hover_touch_down(struct litest_device *d, unsigned int slot, double x, double y) +{ + struct litest_semi_mt *semi_mt = d->private; + + litest_semi_mt_touch_down(d, semi_mt, slot, x, y); +} + +static void +synaptics_hover_touch_move(struct litest_device *d, unsigned int slot, double x, double y) +{ + struct litest_semi_mt *semi_mt = d->private; + + litest_semi_mt_touch_move(d, semi_mt, slot, x, y); +} + +static void +synaptics_hover_touch_up(struct litest_device *d, unsigned int slot) +{ + struct litest_semi_mt *semi_mt = d->private; + + litest_semi_mt_touch_up(d, semi_mt, slot); +} + +static struct litest_device_interface interface = { + .touch_down = synaptics_hover_touch_down, + .touch_move = synaptics_hover_touch_move, + .touch_up = synaptics_hover_touch_up, +}; + +static struct input_id input_id = { + .bustype = 0x11, + .vendor = 0x2, + .product = 0x7, +}; + +static int events[] = { + EV_KEY, BTN_LEFT, + EV_KEY, BTN_RIGHT, + EV_KEY, BTN_TOOL_FINGER, + EV_KEY, BTN_TOUCH, + EV_KEY, BTN_TOOL_DOUBLETAP, + EV_KEY, BTN_TOOL_TRIPLETAP, + INPUT_PROP_MAX, INPUT_PROP_POINTER, + INPUT_PROP_MAX, INPUT_PROP_SEMI_MT, + -1, -1, +}; + +static struct input_absinfo absinfo[] = { + { ABS_X, 1472, 5472, 0, 0, 60 }, + { ABS_Y, 1408, 4498, 0, 0, 85 }, + { ABS_PRESSURE, 0, 255, 0, 0, 0 }, + { ABS_TOOL_WIDTH, 0, 15, 0, 0, 0 }, + { ABS_MT_SLOT, 0, 1, 0, 0, 0 }, + { ABS_MT_POSITION_X, 1472, 5472, 0, 0, 60 }, + { ABS_MT_POSITION_Y, 1408, 4498, 0, 0, 85 }, + { ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 }, + { .value = -1 } +}; + +struct litest_test_device litest_synaptics_hover_device = { + .type = LITEST_SYNAPTICS_HOVER_SEMI_MT, + .features = LITEST_TOUCHPAD | LITEST_SEMI_MT | LITEST_BUTTON, + .shortname = "synaptics hover", + .setup = litest_synaptics_hover_setup, + .interface = &interface, + .create = synaptics_hover_create, + + .name = "SynPS/2 Synaptics TouchPad", + .id = &input_id, + .events = events, + .absinfo = absinfo, +}; + +static void +synaptics_hover_create(struct litest_device *d) +{ + struct litest_semi_mt *semi_mt = zalloc(sizeof(*semi_mt)); + assert(semi_mt); + + d->private = semi_mt; + + d->uinput = litest_create_uinput_device_from_description( + litest_synaptics_hover_device.name, + litest_synaptics_hover_device.id, + absinfo, + events); + d->interface = &interface; +} diff --git a/test/litest.c b/test/litest.c index c436771f..723b59a8 100644 --- a/test/litest.c +++ b/test/litest.c @@ -94,6 +94,7 @@ extern struct litest_test_device litest_generic_singletouch_device; extern struct litest_test_device litest_qemu_tablet_device; extern struct litest_test_device litest_xen_virtual_pointer_device; extern struct litest_test_device litest_vmware_virtmouse_device; +extern struct litest_test_device litest_synaptics_hover_device; struct litest_test_device* devices[] = { &litest_synaptics_clickpad_device, @@ -113,6 +114,7 @@ struct litest_test_device* devices[] = { &litest_qemu_tablet_device, &litest_xen_virtual_pointer_device, &litest_vmware_virtmouse_device, + &litest_synaptics_hover_device, NULL, }; @@ -1386,3 +1388,144 @@ litest_pop_event_frame(struct litest_device *dev) dev->skip_ev_syn = false; litest_event(dev, EV_SYN, SYN_REPORT, 0); } + +static void +send_abs_xy(struct litest_device *d, double x, double y) +{ + struct input_event e; + int val; + + e.type = EV_ABS; + e.code = ABS_X; + e.value = LITEST_AUTO_ASSIGN; + val = litest_auto_assign_value(d, &e, 0, x, y); + litest_event(d, EV_ABS, ABS_X, val); + + e.code = ABS_Y; + val = litest_auto_assign_value(d, &e, 0, x, y); + litest_event(d, EV_ABS, ABS_Y, val); +} + +static void +send_abs_mt_xy(struct litest_device *d, double x, double y) +{ + struct input_event e; + int val; + + e.type = EV_ABS; + e.code = ABS_MT_POSITION_X; + e.value = LITEST_AUTO_ASSIGN; + val = litest_auto_assign_value(d, &e, 0, x, y); + litest_event(d, EV_ABS, ABS_MT_POSITION_X, val); + + e.code = ABS_MT_POSITION_Y; + e.value = LITEST_AUTO_ASSIGN; + val = litest_auto_assign_value(d, &e, 0, x, y); + litest_event(d, EV_ABS, ABS_MT_POSITION_Y, val); +} + +void +litest_semi_mt_touch_down(struct litest_device *d, + struct litest_semi_mt *semi_mt, + unsigned int slot, + double x, double y) +{ + double t, l, r = 0, b = 0; /* top, left, right, bottom */ + + if (d->ntouches_down > 2 || slot > 1) + return; + + if (d->ntouches_down == 1) { + l = x; + t = y; + } else { + int other = (slot + 1) % 2; + l = min(x, semi_mt->touches[other].x); + t = min(y, semi_mt->touches[other].y); + r = max(x, semi_mt->touches[other].x); + b = max(y, semi_mt->touches[other].y); + } + + send_abs_xy(d, l, t); + + litest_event(d, EV_ABS, ABS_MT_SLOT, 0); + + if (d->ntouches_down == 1) + litest_event(d, EV_ABS, ABS_MT_TRACKING_ID, ++semi_mt->tracking_id); + + send_abs_mt_xy(d, l, t); + + if (d->ntouches_down == 2) { + litest_event(d, EV_ABS, ABS_MT_SLOT, 1); + litest_event(d, EV_ABS, ABS_MT_TRACKING_ID, ++semi_mt->tracking_id); + + send_abs_mt_xy(d, r, b); + } + + litest_event(d, EV_SYN, SYN_REPORT, 0); + + semi_mt->touches[slot].x = x; + semi_mt->touches[slot].y = y; +} + +void +litest_semi_mt_touch_move(struct litest_device *d, + struct litest_semi_mt *semi_mt, + unsigned int slot, + double x, double y) +{ + double t, l, r = 0, b = 0; /* top, left, right, bottom */ + + if (d->ntouches_down > 2 || slot > 1) + return; + + if (d->ntouches_down == 1) { + l = x; + t = y; + } else { + int other = (slot + 1) % 2; + l = min(x, semi_mt->touches[other].x); + t = min(y, semi_mt->touches[other].y); + r = max(x, semi_mt->touches[other].x); + b = max(y, semi_mt->touches[other].y); + } + + send_abs_xy(d, l, t); + + litest_event(d, EV_ABS, ABS_MT_SLOT, 0); + send_abs_mt_xy(d, l, t); + + if (d->ntouches_down == 2) { + litest_event(d, EV_ABS, ABS_MT_SLOT, 1); + send_abs_mt_xy(d, r, b); + } + + litest_event(d, EV_SYN, SYN_REPORT, 0); + + semi_mt->touches[slot].x = x; + semi_mt->touches[slot].y = y; +} + +void +litest_semi_mt_touch_up(struct litest_device *d, + struct litest_semi_mt *semi_mt, + unsigned int slot) +{ + /* note: ntouches_down is decreased before we get here */ + if (d->ntouches_down >= 2 || slot > 1) + return; + + litest_event(d, EV_ABS, ABS_MT_SLOT, d->ntouches_down); + litest_event(d, EV_ABS, ABS_MT_TRACKING_ID, -1); + + /* if we have one finger left, send x/y coords for that finger left. + this is likely to happen with a real touchpad */ + if (d->ntouches_down == 1) { + int other = (slot + 1) % 2; + send_abs_xy(d, semi_mt->touches[other].x, semi_mt->touches[other].y); + litest_event(d, EV_ABS, ABS_MT_SLOT, 0); + send_abs_mt_xy(d, semi_mt->touches[other].x, semi_mt->touches[other].y); + } + + litest_event(d, EV_SYN, SYN_REPORT, 0); +} diff --git a/test/litest.h b/test/litest.h index a34c4407..f0570f53 100644 --- a/test/litest.h +++ b/test/litest.h @@ -49,10 +49,11 @@ enum litest_device_type { LITEST_QEMU_TABLET = -13, LITEST_XEN_VIRTUAL_POINTER = -14, LITEST_VMWARE_VIRTMOUSE = -15, - LITEST_WACOM_BAMBOO = -16, - LITEST_WACOM_CINTIQ = -17, - LITEST_WACOM_INTUOS = -18, - LITEST_WACOM_ISDV4 = -19, + LITEST_SYNAPTICS_HOVER_SEMI_MT = -16, + LITEST_WACOM_BAMBOO = -17, + LITEST_WACOM_CINTIQ = -18, + LITEST_WACOM_INTUOS = -19, + LITEST_WACOM_ISDV4 = -20, }; enum litest_device_feature { @@ -215,6 +216,33 @@ void litest_timeout_buttonscroll(void); void litest_push_event_frame(struct litest_device *dev); void litest_pop_event_frame(struct litest_device *dev); +/* this is a semi-mt device, so we keep track of the touches that the tests + * send and modify them so that the first touch is always slot 0 and sends + * the top-left of the bounding box, the second is always slot 1 and sends + * the bottom-right of the bounding box. + * Lifting any of two fingers terminates slot 1 + */ +struct litest_semi_mt { + int tracking_id; + /* The actual touches requested by the test for the two slots + * in the 0..100 range used by litest */ + struct { + double x, y; + } touches[2]; +}; + +void litest_semi_mt_touch_down(struct litest_device *d, + struct litest_semi_mt *semi_mt, + unsigned int slot, + double x, double y); +void litest_semi_mt_touch_move(struct litest_device *d, + struct litest_semi_mt *semi_mt, + unsigned int slot, + double x, double y); +void litest_semi_mt_touch_up(struct litest_device *d, + struct litest_semi_mt *semi_mt, + unsigned int slot); + #ifndef ck_assert_notnull #define ck_assert_notnull(ptr) ck_assert_ptr_ne(ptr, NULL) #endif diff --git a/test/touchpad.c b/test/touchpad.c index c5edecc8..ab9a12e0 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -711,10 +711,96 @@ START_TEST(touchpad_3fg_tap_btntool_inverted) } END_TEST +START_TEST(touchpad_click_defaults_clickfinger) +{ + struct litest_device *dev = litest_current_device(); + struct libinput_device *device = dev->libinput_device; + uint32_t methods, method; + enum libinput_config_status status; + + /* call this test for apple touchpads */ + + methods = libinput_device_config_click_get_methods(device); + ck_assert(methods & LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS); + ck_assert(methods & LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER); + + method = libinput_device_config_click_get_method(device); + ck_assert_int_eq(method, LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER); + method = libinput_device_config_click_get_default_method(device); + ck_assert_int_eq(method, LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER); + + status = libinput_device_config_click_set_method(device, + LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); + status = libinput_device_config_click_set_method(device, + LIBINPUT_CONFIG_CLICK_METHOD_NONE); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); +} +END_TEST + +START_TEST(touchpad_click_defaults_btnarea) +{ + struct litest_device *dev = litest_current_device(); + struct libinput_device *device = dev->libinput_device; + uint32_t methods, method; + enum libinput_config_status status; + + /* call this test for non-apple clickpads */ + + methods = libinput_device_config_click_get_methods(device); + ck_assert(methods & LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER); + ck_assert(methods & LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS); + + method = libinput_device_config_click_get_method(device); + ck_assert_int_eq(method, LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS); + method = libinput_device_config_click_get_default_method(device); + ck_assert_int_eq(method, LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS); + + status = libinput_device_config_click_set_method(device, + LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); + status = libinput_device_config_click_set_method(device, + LIBINPUT_CONFIG_CLICK_METHOD_NONE); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); +} +END_TEST + +START_TEST(touchpad_click_defaults_none) +{ + struct litest_device *dev = litest_current_device(); + struct libinput_device *device = dev->libinput_device; + uint32_t methods, method; + enum libinput_config_status status; + + /* call this test for non-clickpads */ + + methods = libinput_device_config_click_get_methods(device); + ck_assert_int_eq(methods, 0); + + method = libinput_device_config_click_get_method(device); + ck_assert_int_eq(method, LIBINPUT_CONFIG_CLICK_METHOD_NONE); + method = libinput_device_config_click_get_default_method(device); + ck_assert_int_eq(method, LIBINPUT_CONFIG_CLICK_METHOD_NONE); + + status = libinput_device_config_click_set_method(device, + LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED); + status = libinput_device_config_click_set_method(device, + LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED); +} +END_TEST + + START_TEST(touchpad_1fg_clickfinger) { - struct litest_device *dev = litest_create_device(LITEST_BCM5974); + struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; + enum libinput_config_status status; + + status = libinput_device_config_click_set_method(dev->libinput_device, + LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); litest_drain_events(li); @@ -731,15 +817,18 @@ START_TEST(touchpad_1fg_clickfinger) LIBINPUT_BUTTON_STATE_PRESSED); litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED); - - litest_delete_device(dev); } END_TEST START_TEST(touchpad_2fg_clickfinger) { - struct litest_device *dev = litest_create_device(LITEST_BCM5974); + struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; + enum libinput_config_status status; + + status = libinput_device_config_click_set_method(dev->libinput_device, + LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); litest_drain_events(li); @@ -754,12 +843,202 @@ START_TEST(touchpad_2fg_clickfinger) libinput_dispatch(li); + litest_assert_button_event(li, BTN_RIGHT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, BTN_RIGHT, + LIBINPUT_BUTTON_STATE_RELEASED); +} +END_TEST + +START_TEST(touchpad_clickfinger_to_area_method) +{ + struct litest_device *dev = litest_current_device(); + enum libinput_config_status status; + struct libinput *li = dev->libinput; + + litest_drain_events(li); + + status = libinput_device_config_click_set_method(dev->libinput_device, + LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); + + litest_touch_down(dev, 0, 90, 90); + litest_event(dev, EV_KEY, BTN_LEFT, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_event(dev, EV_KEY, BTN_LEFT, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_touch_up(dev, 0); + libinput_dispatch(li); + + litest_assert_button_event(li, BTN_RIGHT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, BTN_RIGHT, + LIBINPUT_BUTTON_STATE_RELEASED); + + status = libinput_device_config_click_set_method(dev->libinput_device, + LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); + + litest_drain_events(li); + + /* use bottom right corner to catch accidental softbutton right */ + litest_touch_down(dev, 0, 90, 90); + litest_event(dev, EV_KEY, BTN_LEFT, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_event(dev, EV_KEY, BTN_LEFT, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_touch_up(dev, 0); + libinput_dispatch(li); + + litest_assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + +} +END_TEST + +START_TEST(touchpad_clickfinger_to_area_method_while_down) +{ + struct litest_device *dev = litest_current_device(); + enum libinput_config_status status; + struct libinput *li = dev->libinput; + + litest_drain_events(li); + + status = libinput_device_config_click_set_method(dev->libinput_device, + LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); + + litest_touch_down(dev, 0, 90, 90); + litest_event(dev, EV_KEY, BTN_LEFT, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + litest_assert_button_event(li, BTN_RIGHT, + LIBINPUT_BUTTON_STATE_PRESSED); + + status = libinput_device_config_click_set_method(dev->libinput_device, + LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); + + litest_event(dev, EV_KEY, BTN_LEFT, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_touch_up(dev, 0); + libinput_dispatch(li); + + litest_assert_button_event(li, BTN_RIGHT, + LIBINPUT_BUTTON_STATE_RELEASED); + + + litest_drain_events(li); + + /* use bottom right corner to catch accidental softbutton right */ + litest_touch_down(dev, 0, 90, 90); + litest_event(dev, EV_KEY, BTN_LEFT, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_event(dev, EV_KEY, BTN_LEFT, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_touch_up(dev, 0); + libinput_dispatch(li); + + litest_assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + +} +END_TEST + +START_TEST(touchpad_area_to_clickfinger_method) +{ + struct litest_device *dev = litest_current_device(); + enum libinput_config_status status; + struct libinput *li = dev->libinput; + + status = libinput_device_config_click_set_method(dev->libinput_device, + LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); + + litest_drain_events(li); + + /* use bottom right corner to catch accidental softbutton right */ + litest_touch_down(dev, 0, 90, 90); + litest_event(dev, EV_KEY, BTN_LEFT, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_event(dev, EV_KEY, BTN_LEFT, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_touch_up(dev, 0); + libinput_dispatch(li); + + litest_assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + + status = libinput_device_config_click_set_method(dev->libinput_device, + LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); + + litest_touch_down(dev, 0, 90, 90); + litest_event(dev, EV_KEY, BTN_LEFT, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_event(dev, EV_KEY, BTN_LEFT, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_touch_up(dev, 0); + libinput_dispatch(li); + + litest_assert_button_event(li, BTN_RIGHT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, BTN_RIGHT, + LIBINPUT_BUTTON_STATE_RELEASED); + +} +END_TEST + +START_TEST(touchpad_area_to_clickfinger_method_while_down) +{ + struct litest_device *dev = litest_current_device(); + enum libinput_config_status status; + struct libinput *li = dev->libinput; + + status = libinput_device_config_click_set_method(dev->libinput_device, + LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); + + litest_drain_events(li); + + /* use bottom right corner to catch accidental softbutton right */ + litest_touch_down(dev, 0, 90, 90); + litest_event(dev, EV_KEY, BTN_LEFT, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + + status = libinput_device_config_click_set_method(dev->libinput_device, + LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); + + litest_event(dev, EV_KEY, BTN_LEFT, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_touch_up(dev, 0); + libinput_dispatch(li); + + litest_assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + + litest_touch_down(dev, 0, 90, 90); + litest_event(dev, EV_KEY, BTN_LEFT, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_event(dev, EV_KEY, BTN_LEFT, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_touch_up(dev, 0); + libinput_dispatch(li); + litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED); litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED); - litest_delete_device(dev); } END_TEST @@ -1371,6 +1650,99 @@ START_TEST(clickpad_topsoftbuttons_move_out_ignore) } END_TEST +START_TEST(clickpad_topsoftbuttons_clickfinger) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + libinput_device_config_click_set_method(dev->libinput_device, + LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER); + litest_drain_events(li); + + litest_touch_down(dev, 0, 90, 5); + litest_event(dev, EV_KEY, BTN_LEFT, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_event(dev, EV_KEY, BTN_LEFT, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_touch_up(dev, 0); + + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + + litest_assert_empty_queue(li); + + litest_touch_down(dev, 0, 90, 5); + litest_touch_down(dev, 1, 10, 5); + litest_event(dev, EV_KEY, BTN_LEFT, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_event(dev, EV_KEY, BTN_LEFT, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_touch_up(dev, 0); + litest_touch_up(dev, 1); + + litest_assert_button_event(li, + BTN_RIGHT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_RIGHT, + LIBINPUT_BUTTON_STATE_RELEASED); +} +END_TEST + +START_TEST(clickpad_topsoftbuttons_clickfinger_dev_disabled) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct litest_device *trackpoint = litest_add_device(li, + LITEST_TRACKPOINT); + + libinput_device_config_click_set_method(dev->libinput_device, + LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER); + libinput_device_config_send_events_set_mode(dev->libinput_device, + LIBINPUT_CONFIG_SEND_EVENTS_DISABLED); + litest_drain_events(li); + + litest_touch_down(dev, 0, 90, 5); + litest_event(dev, EV_KEY, BTN_LEFT, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_event(dev, EV_KEY, BTN_LEFT, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_touch_up(dev, 0); + + litest_assert_button_event(li, + BTN_RIGHT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_RIGHT, + LIBINPUT_BUTTON_STATE_RELEASED); + + litest_assert_empty_queue(li); + + litest_touch_down(dev, 0, 90, 5); + litest_touch_down(dev, 1, 10, 5); + litest_event(dev, EV_KEY, BTN_LEFT, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_event(dev, EV_KEY, BTN_LEFT, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_touch_up(dev, 0); + litest_touch_up(dev, 1); + + litest_assert_button_event(li, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_RELEASED); + + litest_delete_device(trackpoint); +} +END_TEST + + static void test_2fg_scroll(struct litest_device *dev, double dx, double dy, int want_sleep) { @@ -2207,8 +2579,387 @@ START_TEST(touchpad_left_handed_clickpad_delayed) } END_TEST -int main(int argc, char **argv) { +static void +hover_continue(struct litest_device *dev, unsigned int slot, + int x, int y) +{ + litest_event(dev, EV_ABS, ABS_MT_SLOT, slot); + litest_event(dev, EV_ABS, ABS_MT_POSITION_X, x); + litest_event(dev, EV_ABS, ABS_MT_POSITION_Y, y); + litest_event(dev, EV_ABS, ABS_X, x); + litest_event(dev, EV_ABS, ABS_Y, y); + litest_event(dev, EV_ABS, ABS_PRESSURE, 10); + litest_event(dev, EV_ABS, ABS_TOOL_WIDTH, 6); + /* WARNING: no SYN_REPORT! */ +} +static void +hover_start(struct litest_device *dev, unsigned int slot, + int x, int y) +{ + static unsigned int tracking_id; + + litest_event(dev, EV_ABS, ABS_MT_SLOT, slot); + litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, ++tracking_id); + hover_continue(dev, slot, x, y); + /* WARNING: no SYN_REPORT! */ +} + +START_TEST(touchpad_hover_noevent) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + int i; + int x = 2400, + y = 2400; + + litest_drain_events(li); + + hover_start(dev, 0, x, y); + litest_event(dev, EV_KEY, BTN_TOOL_FINGER, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + + for (i = 0; i < 10; i++) { + x += 200; + y -= 200; + litest_event(dev, EV_ABS, ABS_MT_POSITION_X, x); + litest_event(dev, EV_ABS, ABS_MT_POSITION_Y, y); + litest_event(dev, EV_ABS, ABS_X, x); + litest_event(dev, EV_ABS, ABS_Y, y); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + } + + litest_event(dev, EV_KEY, BTN_TOOL_FINGER, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(touchpad_hover_down) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_event *event; + int i; + int x = 2400, + y = 2400; + + litest_drain_events(li); + + hover_start(dev, 0, x, y); + litest_event(dev, EV_KEY, BTN_TOOL_FINGER, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + + for (i = 0; i < 10; i++) { + x += 200; + y -= 200; + litest_event(dev, EV_ABS, ABS_MT_POSITION_X, x); + litest_event(dev, EV_ABS, ABS_MT_POSITION_Y, y); + litest_event(dev, EV_ABS, ABS_X, x); + litest_event(dev, EV_ABS, ABS_Y, y); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + } + + litest_assert_empty_queue(li); + + litest_event(dev, EV_ABS, ABS_X, x + 100); + litest_event(dev, EV_ABS, ABS_Y, y + 100); + litest_event(dev, EV_KEY, BTN_TOUCH, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + for (i = 0; i < 10; i++) { + x -= 200; + y += 200; + litest_event(dev, EV_ABS, ABS_MT_POSITION_X, x); + litest_event(dev, EV_ABS, ABS_MT_POSITION_Y, y); + litest_event(dev, EV_ABS, ABS_X, x); + litest_event(dev, EV_ABS, ABS_Y, y); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + } + + libinput_dispatch(li); + + ck_assert_int_ne(libinput_next_event_type(li), + LIBINPUT_EVENT_NONE); + while ((event = libinput_get_event(li)) != NULL) { + ck_assert_int_eq(libinput_event_get_type(event), + LIBINPUT_EVENT_POINTER_MOTION); + libinput_event_destroy(event); + libinput_dispatch(li); + } + + /* go back to hover */ + hover_continue(dev, 0, x, y); + litest_event(dev, EV_KEY, BTN_TOUCH, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + + for (i = 0; i < 10; i++) { + x += 200; + y -= 200; + litest_event(dev, EV_ABS, ABS_MT_POSITION_X, x); + litest_event(dev, EV_ABS, ABS_MT_POSITION_Y, y); + litest_event(dev, EV_ABS, ABS_X, x); + litest_event(dev, EV_ABS, ABS_Y, y); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + } + + litest_event(dev, EV_KEY, BTN_TOOL_FINGER, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(touchpad_hover_down_hover_down) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_event *event; + int i, j; + int x = 1400, + y = 1400; + + litest_drain_events(li); + + /* hover */ + hover_start(dev, 0, x, y); + litest_event(dev, EV_KEY, BTN_TOOL_FINGER, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_assert_empty_queue(li); + + for (i = 0; i < 3; i++) { + /* touch */ + litest_event(dev, EV_ABS, ABS_X, x + 100); + litest_event(dev, EV_ABS, ABS_Y, y + 100); + litest_event(dev, EV_KEY, BTN_TOUCH, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + + for (j = 0; j < 5; j++) { + x += 200; + y += 200; + litest_event(dev, EV_ABS, ABS_MT_POSITION_X, x); + litest_event(dev, EV_ABS, ABS_MT_POSITION_Y, y); + litest_event(dev, EV_ABS, ABS_X, x); + litest_event(dev, EV_ABS, ABS_Y, y); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + } + + libinput_dispatch(li); + + ck_assert_int_ne(libinput_next_event_type(li), + LIBINPUT_EVENT_NONE); + while ((event = libinput_get_event(li)) != NULL) { + ck_assert_int_eq(libinput_event_get_type(event), + LIBINPUT_EVENT_POINTER_MOTION); + libinput_event_destroy(event); + libinput_dispatch(li); + } + + /* go back to hover */ + hover_continue(dev, 0, x, y); + litest_event(dev, EV_KEY, BTN_TOUCH, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + + for (j = 0; j < 5; j++) { + x += 200; + y += 200; + litest_event(dev, EV_ABS, ABS_MT_POSITION_X, x); + litest_event(dev, EV_ABS, ABS_MT_POSITION_Y, y); + litest_event(dev, EV_ABS, ABS_X, x); + litest_event(dev, EV_ABS, ABS_Y, y); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + } + + litest_assert_empty_queue(li); + } + + /* touch */ + litest_event(dev, EV_KEY, BTN_TOOL_FINGER, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + + litest_assert_empty_queue(li); + + /* start a new touch to be sure */ + litest_touch_down(dev, 0, 50, 50); + litest_touch_move_to(dev, 0, 50, 50, 70, 70, 10, 10); + litest_touch_up(dev, 0); + + libinput_dispatch(li); + ck_assert_int_ne(libinput_next_event_type(li), + LIBINPUT_EVENT_NONE); + while ((event = libinput_get_event(li)) != NULL) { + ck_assert_int_eq(libinput_event_get_type(event), + LIBINPUT_EVENT_POINTER_MOTION); + libinput_event_destroy(event); + libinput_dispatch(li); + } +} +END_TEST + +START_TEST(touchpad_hover_down_up) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + int i; + int x = 1400, + y = 1400; + + litest_drain_events(li); + + /* hover two fingers, then touch */ + hover_start(dev, 0, x, y); + litest_event(dev, EV_KEY, BTN_TOOL_FINGER, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_assert_empty_queue(li); + + hover_start(dev, 1, x, y); + litest_event(dev, EV_KEY, BTN_TOOL_FINGER, 0); + litest_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_assert_empty_queue(li); + + litest_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, 0); + litest_event(dev, EV_KEY, BTN_TOOL_TRIPLETAP, 1); + litest_event(dev, EV_KEY, BTN_TOUCH, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + + litest_assert_empty_queue(li); + + /* hover first finger, end second in same frame */ + litest_event(dev, EV_ABS, ABS_MT_SLOT, 1); + litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1); + litest_event(dev, EV_KEY, BTN_TOOL_TRIPLETAP, 0); + litest_event(dev, EV_KEY, BTN_TOOL_FINGER, 1); + litest_event(dev, EV_KEY, BTN_TOUCH, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + + litest_assert_empty_queue(li); + + litest_event(dev, EV_KEY, BTN_TOUCH, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + + /* now move the finger */ + for (i = 0; i < 10; i++) { + litest_event(dev, EV_ABS, ABS_MT_SLOT, 0); + litest_event(dev, EV_ABS, ABS_MT_POSITION_X, x); + litest_event(dev, EV_ABS, ABS_MT_POSITION_Y, y); + litest_event(dev, EV_ABS, ABS_X, x); + litest_event(dev, EV_ABS, ABS_Y, y); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + x -= 100; + y -= 100; + } + + litest_assert_only_typed_events(li, + LIBINPUT_EVENT_POINTER_MOTION); + + litest_event(dev, EV_ABS, ABS_MT_SLOT, 0); + litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1); + litest_event(dev, EV_KEY, BTN_TOOL_FINGER, 0); + litest_event(dev, EV_KEY, BTN_TOUCH, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); +} +END_TEST + +START_TEST(touchpad_hover_2fg_noevent) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + int i; + int x = 2400, + y = 2400; + + litest_drain_events(li); + + hover_start(dev, 0, x, y); + litest_event(dev, EV_KEY, BTN_TOOL_FINGER, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + + hover_start(dev, 1, x + 500, y + 500); + litest_event(dev, EV_KEY, BTN_TOOL_FINGER, 0); + litest_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + + for (i = 0; i < 10; i++) { + x += 200; + y -= 200; + litest_event(dev, EV_ABS, ABS_MT_SLOT, 0); + litest_event(dev, EV_ABS, ABS_MT_POSITION_X, x); + litest_event(dev, EV_ABS, ABS_MT_POSITION_Y, y); + litest_event(dev, EV_ABS, ABS_MT_SLOT, 1); + litest_event(dev, EV_ABS, ABS_MT_POSITION_X, x + 500); + litest_event(dev, EV_ABS, ABS_MT_POSITION_Y, y + 500); + litest_event(dev, EV_ABS, ABS_X, x); + litest_event(dev, EV_ABS, ABS_Y, y); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + } + + litest_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + + litest_assert_empty_queue(li); + + litest_event(dev, EV_KEY, BTN_TOOL_FINGER, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(touchpad_hover_2fg_1fg_down) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_event *event; + int i; + int x = 2400, + y = 2400; + + litest_drain_events(li); + + /* two slots active, but BTN_TOOL_FINGER only */ + hover_start(dev, 0, x, y); + hover_start(dev, 1, x + 500, y + 500); + litest_event(dev, EV_KEY, BTN_TOUCH, 1); + litest_event(dev, EV_KEY, BTN_TOOL_FINGER, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + + for (i = 0; i < 10; i++) { + x += 200; + y -= 200; + litest_event(dev, EV_ABS, ABS_MT_SLOT, 0); + litest_event(dev, EV_ABS, ABS_MT_POSITION_X, x); + litest_event(dev, EV_ABS, ABS_MT_POSITION_Y, y); + litest_event(dev, EV_ABS, ABS_MT_SLOT, 1); + litest_event(dev, EV_ABS, ABS_MT_POSITION_X, x + 500); + litest_event(dev, EV_ABS, ABS_MT_POSITION_Y, y + 500); + litest_event(dev, EV_ABS, ABS_X, x); + litest_event(dev, EV_ABS, ABS_Y, y); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + } + + litest_event(dev, EV_KEY, BTN_TOUCH, 0); + litest_event(dev, EV_KEY, BTN_TOOL_FINGER, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + + libinput_dispatch(li); + + ck_assert_int_ne(libinput_next_event_type(li), + LIBINPUT_EVENT_NONE); + while ((event = libinput_get_event(li)) != NULL) { + ck_assert_int_eq(libinput_event_get_type(event), + LIBINPUT_EVENT_POINTER_MOTION); + libinput_event_destroy(event); + libinput_dispatch(li); + } +} +END_TEST + +int main(int argc, char **argv) { litest_add("touchpad:motion", touchpad_1fg_motion, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:motion", touchpad_2fg_no_motion, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); @@ -2246,8 +2997,18 @@ int main(int argc, char **argv) { litest_add("touchpad:tap", clickpad_1fg_tap_click, LITEST_CLICKPAD, LITEST_ANY); litest_add("touchpad:tap", clickpad_2fg_tap_click, LITEST_CLICKPAD, LITEST_SINGLE_TOUCH|LITEST_APPLE_CLICKPAD); - litest_add_no_device("touchpad:clickfinger", touchpad_1fg_clickfinger); - litest_add_no_device("touchpad:clickfinger", touchpad_2fg_clickfinger); + litest_add("touchpad:clickfinger", touchpad_1fg_clickfinger, LITEST_CLICKPAD, LITEST_ANY); + litest_add("touchpad:clickfinger", touchpad_2fg_clickfinger, LITEST_CLICKPAD, LITEST_ANY); + litest_add("touchpad:clickfinger", touchpad_clickfinger_to_area_method, LITEST_CLICKPAD, LITEST_ANY); + litest_add("touchpad:clickfinger", + touchpad_clickfinger_to_area_method_while_down, LITEST_CLICKPAD, LITEST_ANY); + litest_add("touchpad:clickfinger", touchpad_area_to_clickfinger_method, LITEST_CLICKPAD, LITEST_ANY); + litest_add("touchpad:clickfinger", + touchpad_area_to_clickfinger_method_while_down, LITEST_CLICKPAD, LITEST_ANY); + + litest_add("touchpad:click", touchpad_click_defaults_clickfinger, LITEST_APPLE_CLICKPAD, LITEST_ANY); + litest_add("touchpad:click", touchpad_click_defaults_btnarea, LITEST_CLICKPAD, LITEST_APPLE_CLICKPAD); + litest_add("touchpad:click", touchpad_click_defaults_none, LITEST_TOUCHPAD, LITEST_CLICKPAD); litest_add("touchpad:click", touchpad_btn_left, LITEST_TOUCHPAD, LITEST_CLICKPAD); litest_add("touchpad:click", clickpad_btn_left, LITEST_CLICKPAD, LITEST_ANY); @@ -2266,6 +3027,8 @@ int main(int argc, char **argv) { litest_add("touchpad:topsoftbuttons", clickpad_topsoftbuttons_right, LITEST_TOPBUTTONPAD, LITEST_ANY); litest_add("touchpad:topsoftbuttons", clickpad_topsoftbuttons_middle, LITEST_TOPBUTTONPAD, LITEST_ANY); litest_add("touchpad:topsoftbuttons", clickpad_topsoftbuttons_move_out_ignore, LITEST_TOPBUTTONPAD, LITEST_ANY); + litest_add("touchpad:topsoftbuttons", clickpad_topsoftbuttons_clickfinger, LITEST_TOPBUTTONPAD, LITEST_ANY); + litest_add("touchpad:topsoftbuttons", clickpad_topsoftbuttons_clickfinger_dev_disabled, LITEST_TOPBUTTONPAD, LITEST_ANY); litest_add("touchpad:scroll", touchpad_2fg_scroll, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); litest_add("touchpad:scroll", touchpad_2fg_scroll_slow_distance, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); @@ -2295,5 +3058,14 @@ int main(int argc, char **argv) { litest_add("touchpad:left-handed", touchpad_left_handed_delayed, LITEST_TOUCHPAD, LITEST_CLICKPAD); litest_add("touchpad:left-handed", touchpad_left_handed_clickpad_delayed, LITEST_CLICKPAD, LITEST_APPLE_CLICKPAD); + /* Hover tests aren't generic, they only work on this device and + * ignore the semi-mt capability (it doesn't matter for the tests */ + litest_add_for_device("touchpad:hover", touchpad_hover_noevent, LITEST_SYNAPTICS_HOVER_SEMI_MT); + litest_add_for_device("touchpad:hover", touchpad_hover_down, LITEST_SYNAPTICS_HOVER_SEMI_MT); + litest_add_for_device("touchpad:hover", touchpad_hover_down_up, LITEST_SYNAPTICS_HOVER_SEMI_MT); + litest_add_for_device("touchpad:hover", touchpad_hover_down_hover_down, LITEST_SYNAPTICS_HOVER_SEMI_MT); + litest_add_for_device("touchpad:hover", touchpad_hover_2fg_noevent, LITEST_SYNAPTICS_HOVER_SEMI_MT); + litest_add_for_device("touchpad:hover", touchpad_hover_2fg_1fg_down, LITEST_SYNAPTICS_HOVER_SEMI_MT); + return litest_run(argc, argv); } diff --git a/tools/shared.c b/tools/shared.c index b4f41d04..2cff52c4 100644 --- a/tools/shared.c +++ b/tools/shared.c @@ -43,6 +43,7 @@ enum options { OPT_NATURAL_SCROLL_DISABLE, OPT_LEFT_HANDED_ENABLE, OPT_LEFT_HANDED_DISABLE, + OPT_CLICK_METHOD, }; static void @@ -69,6 +70,7 @@ tools_usage() "--disable-natural-scrolling.... enable/disable natural scrolling\n" "--enable-left-handed\n" "--disable-left-handed.... enable/disable left-handed button configuration\n" + "--set-click-method=[none|clickfinger|buttonareas] .... set the desired click method\n" "\n" "These options apply to all applicable devices, if a feature\n" "is not explicitly specified it is left at each device's default.\n" @@ -86,6 +88,7 @@ tools_init_options(struct tools_options *options) options->tapping = -1; options->natural_scroll = -1; options->left_handed = -1; + options->click_method = -1; options->backend = BACKEND_UDEV; options->seat = "seat0"; } @@ -107,6 +110,7 @@ tools_parse_args(int argc, char **argv, struct tools_options *options) { "disable-natural-scrolling", 0, 0, OPT_NATURAL_SCROLL_DISABLE }, { "enable-left-handed", 0, 0, OPT_LEFT_HANDED_ENABLE }, { "disable-left-handed", 0, 0, OPT_LEFT_HANDED_DISABLE }, + { "set-click-method", 1, 0, OPT_CLICK_METHOD }, { 0, 0, 0, 0} }; @@ -153,6 +157,25 @@ tools_parse_args(int argc, char **argv, struct tools_options *options) case OPT_LEFT_HANDED_DISABLE: options->left_handed = 0; break; + case OPT_CLICK_METHOD: + if (!optarg) { + tools_usage(); + return 1; + } + if (strcmp(optarg, "none") == 0) { + options->click_method = + LIBINPUT_CONFIG_CLICK_METHOD_NONE; + } else if (strcmp(optarg, "clickfinger") == 0) { + options->click_method = + LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER; + } else if (strcmp(optarg, "buttonareas") == 0) { + options->click_method = + LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS; + } else { + tools_usage(); + return 1; + } + break; default: tools_usage(); return 1; @@ -263,4 +286,7 @@ tools_device_apply_config(struct libinput_device *device, options->natural_scroll); if (options->left_handed != -1) libinput_device_config_left_handed_set(device, options->left_handed); + + if (options->click_method != -1) + libinput_device_config_click_set_method(device, options->click_method); } diff --git a/tools/shared.h b/tools/shared.h index 4388cea6..fcf748fd 100644 --- a/tools/shared.h +++ b/tools/shared.h @@ -39,6 +39,7 @@ struct tools_options { int tapping; int natural_scroll; int left_handed; + enum libinput_config_click_method click_method; }; void tools_init_options(struct tools_options *options);