Commit 988f31fc authored by Peter Hutterer's avatar Peter Hutterer

Merge branch 'thumb-detect-improvements'

parents 5571f2d2 2c781498
......@@ -39,6 +39,7 @@ diagram_files = \
$(srcdir)/svg/pinch-gestures.svg \
$(srcdir)/svg/swipe-gestures.svg \
$(srcdir)/svg/tap-n-drag.svg \
$(srcdir)/svg/thumb-detection.svg \
$(srcdir)/svg/top-software-buttons.svg \
$(srcdir)/svg/touchscreen-gestures.svg \
$(srcdir)/svg/twofinger-scrolling.svg
......
......@@ -80,4 +80,32 @@ Notable behaviors of libinput's disable-while-typing feature:
- Physical buttons work even while the touchpad is disabled. This includes
software-emulated buttons.
@section thumb-detection Thumb detection
Many users rest their thumb on the touchpad while using the index finger to
move the finger around. For clicks, often the thumb is used rather than the
finger. The thumb should otherwise be ignored as a touch, i.e. it should not
count towards @ref clickfinger and it should not cause a single-finger
movement to trigger @ref twofinger_scrolling.
libinput uses two triggers for thumb detection: pressure and
location. A touch exceeding a pressure threshold is considered a thumb if it
is within the thumb detection zone.
@note "Pressure" on touchpads is synonymous with "contact area", a large
touch surface area has a higher pressure and thus hints at a thumb or palm
touching the surface.
Pressure readings are unreliable at the far bottom of the touchpad as a
thumb hanging mostly off the touchpad will have a small surface area.
libinput has a definitive thumb zone where any touch is considered a resting
thumb.
@image html thumb-detection.svg
The picture above shows the two detection areas. In the larger (light red)
area, a touch is labelled as thumb when it exceeds a device-specific
pressure threshold. In the lower (dark red) area, a touch is labelled as
thumb if it remains in that area for a time without moving outside.
*/
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="89.829216mm"
height="59.06765mm"
viewBox="0 0 318.2925 209.29482"
id="svg4184"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="thumb-detection.svg">
<defs
id="defs4186" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.979899"
inkscape:cx="270.39655"
inkscape:cy="139.75035"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1920"
inkscape:window-height="1136"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<metadata
id="metadata4189">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-257.99662,-299.41313)">
<rect
width="313.09872"
height="167.89594"
x="260.59351"
y="302.01001"
id="rect2858-0"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:5.19376326;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate" />
<rect
style="opacity:0.92000002;fill:#7b0000;fill-opacity:0.2983426;stroke:#000000;stroke-width:0.97031647;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4788"
width="307.88782"
height="45.628574"
x="262.8418"
y="421.0347" />
<rect
y="445.40848"
x="262.68912"
height="21.407471"
width="308.19318"
id="rect4149"
style="opacity:0.92000002;fill:#7b0000;fill-opacity:0.2983426;stroke:#000000;stroke-width:0.66495597;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<g
id="g4151">
<path
sodipodi:nodetypes="sszzzcss"
d="m 353.70196,495.15765 c -24.01774,-7.29937 -29.0012,-10.10221 -30.51977,-10.54973 -10.67294,-3.14527 -18.27051,-5.54063 -23.77758,-13.4704 -5.50707,-7.92977 -5.34967,-20.78347 8.87612,-26.31604 14.2258,-5.53257 39.34351,8.79597 60.13061,16.16341 20.7871,7.36744 33.04563,11.44545 39.33422,13.87551 -8.10022,18.05041 -7.22129,21.15857 -10.11054,33.34117 -0.0481,0.20261 -17.87459,-5.12433 -43.93306,-13.04392 z"
id="path2824-1-1-3"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.00100005;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
<path
sodipodi:nodetypes="ccccc"
d="m 324.44991,483.39364 c -10.67294,-1.94747 -17.88441,-5.64478 -21.62691,-8.75386 -8.11652,-9.03765 -6.31775,-15.03428 -3.3272,-13.99784 8.90495,-0.9097 30.20384,9.01528 33.86042,10.17935 -5.80268,11.37909 -1.08919,13.70271 -8.90631,12.57235 z"
id="path2824-7-1-4-3"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
</g>
<g
transform="matrix(0.79657897,0.11742288,-0.14814182,0.631399,276.6631,-158.96703)"
id="g3663-9-5">
<path
d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z"
id="path2820-6-6"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z"
id="path2824-1-1"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
<path
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z"
id="path2824-7-1-4"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
</g>
</g>
</svg>
......@@ -807,7 +807,8 @@ tp_check_clickfinger_distance(struct tp_dispatch *tp,
if (!t1 || !t2)
return 0;
if (t1->is_thumb || t2->is_thumb)
if (t1->thumb.state == THUMB_STATE_YES ||
t2->thumb.state == THUMB_STATE_YES)
return 0;
x = abs(t1->point.x - t2->point.x);
......@@ -869,6 +870,9 @@ tp_clickfinger_set_button(struct tp_dispatch *tp)
if (t->state != TOUCH_BEGIN && t->state != TOUCH_UPDATE)
continue;
if (t->thumb.state == THUMB_STATE_YES)
continue;
if (!first)
first = t;
else if (!second)
......@@ -899,9 +903,8 @@ out:
case 0:
case 1: button = BTN_LEFT; break;
case 2: button = BTN_RIGHT; break;
case 3: button = BTN_MIDDLE; break;
default:
button = 0;
button = BTN_MIDDLE; break;
break;
}
......
......@@ -740,7 +740,7 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time)
/* The simple version: if a touch is a thumb on
* begin we ignore it. All other thumb touches
* follow the normal tap state for now */
if (t->is_thumb) {
if (t->thumb.state == THUMB_STATE_YES) {
t->tap.is_thumb = true;
continue;
}
......@@ -772,7 +772,7 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time)
tp_tap_handle_event(tp, t, TAP_EVENT_MOTION, time);
} else if (tp->tap.state != TAP_STATE_IDLE &&
t->is_thumb &&
t->thumb.state == THUMB_STATE_YES &&
!t->tap.is_thumb) {
tp_tap_handle_event(tp, t, TAP_EVENT_THUMB, time);
}
......
......@@ -208,7 +208,8 @@ tp_begin_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
t->millis = time;
tp->nfingers_down++;
t->palm.time = time;
t->is_thumb = false;
t->thumb.state = THUMB_STATE_MAYBE;
t->thumb.first_touch_time = time;
t->tap.is_thumb = false;
assert(tp->nfingers_down >= 1);
}
......@@ -314,6 +315,8 @@ tp_process_absolute(struct tp_dispatch *tp,
break;
case ABS_MT_PRESSURE:
t->pressure = e->value;
t->dirty = true;
tp->queued |= TOUCHPAD_EVENT_MOTION;
break;
}
}
......@@ -499,7 +502,7 @@ tp_touch_active(struct tp_dispatch *tp, struct tp_touch *t)
return (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE) &&
t->palm.state == PALM_NONE &&
!t->pinned.is_pinned &&
!t->is_thumb &&
t->thumb.state != THUMB_STATE_YES &&
tp_button_touch_active(tp, t) &&
tp_edge_scroll_touch_active(tp, t);
}
......@@ -642,20 +645,63 @@ out:
t->palm.state == PALM_TYPING ? "typing" : "trackpoint");
}
static inline const char*
thumb_state_to_str(enum tp_thumb_state state)
{
switch(state){
CASE_RETURN_STRING(THUMB_STATE_NO);
CASE_RETURN_STRING(THUMB_STATE_YES);
CASE_RETURN_STRING(THUMB_STATE_MAYBE);
}
return NULL;
}
static void
tp_thumb_detect(struct tp_dispatch *tp, struct tp_touch *t)
tp_thumb_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
{
/* once a thumb, always a thumb */
if (!tp->thumb.detect_thumbs || t->is_thumb)
enum tp_thumb_state state = t->thumb.state;
/* once a thumb, always a thumb, once ruled out always ruled out */
if (!tp->thumb.detect_thumbs ||
t->thumb.state != THUMB_STATE_MAYBE)
return;
if (t->point.y < tp->thumb.upper_thumb_line) {
/* if a potential thumb is above the line, it won't ever
* label as thumb */
t->thumb.state = THUMB_STATE_NO;
goto out;
}
/* If the thumb moves by more than 7mm, it's not a resting thumb */
if (t->state == TOUCH_BEGIN)
t->thumb.initial = t->point;
else if (t->state == TOUCH_UPDATE) {
struct device_float_coords delta;
struct normalized_coords normalized;
delta = device_delta(t->point, t->thumb.initial);
normalized = tp_normalize_delta(tp, delta);
if (normalized_length(normalized) >
TP_MM_TO_DPI_NORMALIZED(7)) {
t->thumb.state = THUMB_STATE_NO;
goto out;
}
}
/* Note: a thumb at the edge of the touchpad won't trigger the
* threshold, the surface area is usually too small.
* threshold, the surface area is usually too small. So we have a
* two-stage detection: pressure and time within the area.
* A finger that remains at the very bottom of the touchpad becomes
* a thumb.
*/
if (t->pressure < tp->thumb.threshold)
return;
t->is_thumb = true;
if (t->pressure > tp->thumb.threshold)
t->thumb.state = THUMB_STATE_YES;
else if (t->point.y > tp->thumb.lower_thumb_line &&
tp->scroll.method != LIBINPUT_CONFIG_SCROLL_EDGE &&
t->thumb.first_touch_time + 300 < time)
t->thumb.state = THUMB_STATE_YES;
/* now what? we marked it as thumb, so:
*
......@@ -667,6 +713,12 @@ tp_thumb_detect(struct tp_dispatch *tp, struct tp_touch *t)
* - tapping: honour thumb on begin, ignore it otherwise for now,
* this gets a tad complicated otherwise
*/
out:
if (t->thumb.state != state)
log_debug(tp_libinput_context(tp),
"thumb state: %s → %s\n",
thumb_state_to_str(state),
thumb_state_to_str(t->thumb.state));
}
static void
......@@ -760,6 +812,48 @@ tp_unhover_touches(struct tp_dispatch *tp, uint64_t time)
}
static inline void
tp_position_fake_touches(struct tp_dispatch *tp)
{
struct tp_touch *t;
struct tp_touch *topmost = NULL;
unsigned int start, i;
if (tp_fake_finger_count(tp) <= tp->num_slots)
return;
/* We have at least one fake touch down. Find the top-most real
* touch and copy its coordinates over to to all fake touches.
* This is more reliable than just taking the first touch.
*/
for (i = 0; i < tp->num_slots; i++) {
t = tp_get_touch(tp, i);
if (t->state == TOUCH_END ||
t->state == TOUCH_NONE)
continue;
if (topmost == NULL || t->point.y < topmost->point.y)
topmost = t;
}
if (!topmost) {
log_bug_libinput(tp_libinput_context(tp),
"Unable to find topmost touch\n");
return;
}
start = tp->has_mt ? tp->num_slots : 1;
for (i = start; i < tp->ntouches; i++) {
t = tp_get_touch(tp, i);
if (t->state == TOUCH_NONE)
continue;
t->point = topmost->point;
if (!t->dirty)
t->dirty = topmost->dirty;
}
}
static inline bool
tp_need_motion_history_reset(struct tp_dispatch *tp)
{
......@@ -782,13 +876,13 @@ static void
tp_process_state(struct tp_dispatch *tp, uint64_t time)
{
struct tp_touch *t;
struct tp_touch *first = tp_get_touch(tp, 0);
unsigned int i;
bool restart_filter = false;
bool want_motion_reset;
tp_process_fake_touches(tp, time);
tp_unhover_touches(tp, time);
tp_position_fake_touches(tp);
want_motion_reset = tp_need_motion_history_reset(tp);
......@@ -803,16 +897,10 @@ tp_process_state(struct tp_dispatch *tp, uint64_t time)
t->quirks.reset_motion_history = false;
}
if (i >= tp->num_slots && t->state != TOUCH_NONE) {
t->point = first->point;
if (!t->dirty)
t->dirty = first->dirty;
}
if (!t->dirty)
continue;
tp_thumb_detect(tp, t);
tp_thumb_detect(tp, t, time);
tp_palm_detect(tp, t, time);
tp_motion_hysteresis(tp, t);
......@@ -1635,6 +1723,13 @@ tp_init_thumb(struct tp_dispatch *tp)
{
struct evdev_device *device = tp->device;
const struct input_absinfo *abs;
double w = 0.0, h = 0.0;
int xres, yres;
int ymax;
double threshold;
if (!tp->buttons.is_clickpad)
return 0;
abs = libevdev_get_abs_info(device->evdev, ABS_MT_PRESSURE);
if (!abs)
......@@ -1643,14 +1738,33 @@ tp_init_thumb(struct tp_dispatch *tp)
if (abs->maximum - abs->minimum < 255)
return 0;
/* The touchpads we looked at so far have a clear thumb threshold of
* ~100, you don't reach that with a normal finger interaction.
/* if the touchpad is less than 50mm high, skip thumb detection.
* it's too small to meaningfully interact with a thumb on the
* touchpad */
evdev_device_get_size(device, &w, &h);
if (h < 50)
return 0;
/* Our reference touchpad is the T440s with 42x42 resolution.
* Higher-res touchpads exhibit higher pressure for the same
* interaction. On the T440s, the threshold value is 100, you don't
* reach that with a normal finger interaction.
* Note: "thumb" means massive touch that should not interact, not
* "using the tip of my thumb for a pinch gestures".
*/
tp->thumb.threshold = 100;
xres = tp->device->abs.absinfo_x->resolution;
yres = tp->device->abs.absinfo_y->resolution;
threshold = 100.0 * hypot(xres, yres)/hypot(42, 42);
tp->thumb.threshold = max(100, threshold);
tp->thumb.detect_thumbs = true;
/* detect thumbs by pressure in the bottom 15mm, detect thumbs by
* lingering in the bottom 8mm */
ymax = tp->device->abs.absinfo_y->maximum;
yres = tp->device->abs.absinfo_y->resolution;
tp->thumb.upper_thumb_line = ymax - yres * 15;
tp->thumb.lower_thumb_line = ymax - yres * 8;
return 0;
}
......
......@@ -136,12 +136,17 @@ enum tp_gesture_2fg_state {
GESTURE_2FG_STATE_PINCH,
};
enum tp_thumb_state {
THUMB_STATE_NO,
THUMB_STATE_YES,
THUMB_STATE_MAYBE,
};
struct tp_touch {
struct tp_dispatch *tp;
enum touch_state state;
bool has_ended; /* TRACKING_ID == -1 */
bool dirty;
bool is_thumb;
struct device_coords point;
uint64_t millis;
int distance; /* distance == 0 means touch */
......@@ -204,6 +209,12 @@ struct tp_touch {
struct {
struct device_coords initial;
} gesture;
struct {
enum tp_thumb_state state;
uint64_t first_touch_time;
struct device_coords initial;
} thumb;
};
struct tp_dispatch {
......@@ -336,6 +347,8 @@ struct tp_dispatch {
struct {
bool detect_thumbs;
int threshold;
int upper_thumb_line;
int lower_thumb_line;
} thumb;
};
......
......@@ -1231,6 +1231,9 @@ axis_replacement_value(struct axis_replacement *axes,
{
struct axis_replacement *axis = axes;
if (!axes)
return false;
while (axis->evcode != -1) {
if (axis->evcode == evcode) {
*value = axis->value;
......@@ -1275,9 +1278,6 @@ litest_auto_assign_value(struct litest_device *d,
break;
default:
value = -1;
if (!axes)
break;
if (!axis_replacement_value(axes, ev->code, &value) &&
d->interface->get_axis_default)
d->interface->get_axis_default(d, ev->code, &value);
......
......@@ -288,6 +288,7 @@ START_TEST(touchpad_4fg_clickfinger)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct libinput_event *event;
if (libevdev_get_num_slots(dev->evdev) < 4)
return;
......@@ -311,6 +312,18 @@ START_TEST(touchpad_4fg_clickfinger)
libinput_dispatch(li);
litest_wait_for_event(li);
event = libinput_get_event(li);
litest_is_button_event(event,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_PRESSED);
libinput_event_destroy(event);
event = libinput_get_event(li);
litest_is_button_event(event,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_RELEASED);
libinput_event_destroy(event);
litest_assert_empty_queue(li);
}
END_TEST
......@@ -319,6 +332,7 @@ START_TEST(touchpad_4fg_clickfinger_btntool_2slots)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct libinput_event *event;
if (libevdev_get_num_slots(dev->evdev) >= 3 ||
!libevdev_has_event_code(dev->evdev, EV_KEY, BTN_TOOL_QUADTAP))
......@@ -343,7 +357,17 @@ START_TEST(touchpad_4fg_clickfinger_btntool_2slots)
litest_touch_up(dev, 0);
litest_touch_up(dev, 1);
libinput_dispatch(li);
litest_wait_for_event(li);
event = libinput_get_event(li);
litest_is_button_event(event,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_PRESSED);
libinput_event_destroy(event);
event = libinput_get_event(li);
litest_is_button_event(event,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_RELEASED);
libinput_event_destroy(event);
litest_assert_empty_queue(li);
}
......@@ -353,8 +377,10 @@ START_TEST(touchpad_4fg_clickfinger_btntool_3slots)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct libinput_event *event;
if (libevdev_get_num_slots(dev->evdev) != 3 ||
if (libevdev_get_num_slots(dev->evdev) >= 4 ||
libevdev_get_num_slots(dev->evdev) < 3 ||
!libevdev_has_event_code(dev->evdev, EV_KEY, BTN_TOOL_TRIPLETAP))
return;
......@@ -381,6 +407,18 @@ START_TEST(touchpad_4fg_clickfinger_btntool_3slots)
libinput_dispatch(li);
litest_wait_for_event(li);
event = libinput_get_event(li);
litest_is_button_event(event,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_PRESSED);
libinput_event_destroy(event);
event = libinput_get_event(li);
litest_is_button_event(event,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_RELEASED);
libinput_event_destroy(event);
litest_assert_empty_queue(li);
}
END_TEST
......@@ -682,6 +720,66 @@ START_TEST(touchpad_area_to_clickfinger_method_while_down)
}
END_TEST
START_TEST(touchpad_clickfinger_3fg_tool_position)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
enable_clickfinger(dev);
litest_drain_events(li);
/* one in thumb area, one in normal area. spread is wide so the two
* real fingers don't count together. we expect a 2-finger click */
litest_touch_down(dev, 0, 5, 99);
litest_touch_down(dev, 1, 90, 15);
litest_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, 0);
litest_event(dev, EV_KEY, BTN_TOOL_TRIPLETAP, 1);
litest_event(dev, EV_SYN, SYN_REPORT, 0);
libinput_dispatch(li);
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);
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_4fg_tool_position)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
enable_clickfinger(dev);
litest_drain_events(li);
litest_touch_down(dev, 0, 5, 99);
litest_touch_down(dev, 1, 90, 15);
litest_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, 0);
litest_event(dev, EV_KEY, BTN_TOOL_QUADTAP, 1);
litest_event(dev, EV_SYN, SYN_REPORT, 0);
libinput_dispatch(li);
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);
libinput_dispatch(li);
litest_assert_button_event(li,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_PRESSED);
litest_assert_button_event(li,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_RELEASED);
}
END_TEST
START_TEST(touchpad_btn_left)
{
struct litest_device *dev = litest_current_device();
......@@ -1422,6 +1520,10 @@ litest_setup_tests(void)
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);
/* run those two for the T440 one only so we don't have to worry
* about small touchpads messing with thumb detection expectations */
litest_add_for_device("touchpad:clickfinger", touchpad_clickfinger_3fg_tool_position, LITEST_SYNAPTICS_TOPBUTTONPAD);
litest_add_for_device("touchpad:clickfinger", touchpad_clickfinger_4fg_tool_position, LITEST_SYNAPTICS_TOPBUTTONPAD);
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);
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment