1 /*
2 * Copyright © 2019 Matt Mayfield
3 * Copyright © 2019 Red Hat, Inc.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 */
24
25 #include "config.h"
26 #include "evdev-mt-touchpad.h"
27
28 /* distance between fingers to assume it is not a scroll */
29 #define SCROLL_MM_X 35
30 #define SCROLL_MM_Y 25
31 #define THUMB_TIMEOUT ms2us(100)
32
33 static inline const char*
thumb_state_to_str(enum tp_thumb_state state)34 thumb_state_to_str(enum tp_thumb_state state)
35 {
36 switch(state){
37 CASE_RETURN_STRING(THUMB_STATE_FINGER);
38 CASE_RETURN_STRING(THUMB_STATE_JAILED);
39 CASE_RETURN_STRING(THUMB_STATE_PINCH);
40 CASE_RETURN_STRING(THUMB_STATE_SUPPRESSED);
41 CASE_RETURN_STRING(THUMB_STATE_REVIVED);
42 CASE_RETURN_STRING(THUMB_STATE_REVIVED_JAILED);
43 CASE_RETURN_STRING(THUMB_STATE_DEAD);
44 }
45
46 return NULL;
47 }
48
49 static void
tp_thumb_set_state(struct tp_dispatch * tp,struct tp_touch * t,enum tp_thumb_state state)50 tp_thumb_set_state(struct tp_dispatch *tp,
51 struct tp_touch *t,
52 enum tp_thumb_state state)
53 {
54 unsigned int index = t ? t->index : UINT_MAX;
55
56 if (tp->thumb.state == state && tp->thumb.index == index)
57 return;
58
59 evdev_log_debug(tp->device,
60 "thumb: touch %d, %s → %s\n",
61 (int)index,
62 thumb_state_to_str(tp->thumb.state),
63 thumb_state_to_str(state));
64
65 tp->thumb.state = state;
66 tp->thumb.index = index;
67 }
68
69 void
tp_thumb_reset(struct tp_dispatch * tp)70 tp_thumb_reset(struct tp_dispatch *tp)
71 {
72 tp->thumb.state = THUMB_STATE_FINGER;
73 tp->thumb.index = UINT_MAX;
74 tp->thumb.pinch_eligible = true;
75 }
76
77 static void
tp_thumb_lift(struct tp_dispatch * tp)78 tp_thumb_lift(struct tp_dispatch *tp)
79 {
80 tp->thumb.state = THUMB_STATE_FINGER;
81 tp->thumb.index = UINT_MAX;
82 }
83
84 static bool
tp_thumb_in_exclusion_area(const struct tp_dispatch * tp,const struct tp_touch * t)85 tp_thumb_in_exclusion_area(const struct tp_dispatch *tp,
86 const struct tp_touch *t)
87 {
88 return (t->point.y > tp->thumb.lower_thumb_line &&
89 tp->scroll.method != LIBINPUT_CONFIG_SCROLL_EDGE);
90
91 }
92
93 static bool
tp_thumb_detect_pressure_size(const struct tp_dispatch * tp,const struct tp_touch * t)94 tp_thumb_detect_pressure_size(const struct tp_dispatch *tp,
95 const struct tp_touch *t)
96 {
97 bool is_thumb = false;
98
99 if (tp->thumb.use_pressure &&
100 t->pressure > tp->thumb.pressure_threshold &&
101 tp_thumb_in_exclusion_area(tp, t)) {
102 is_thumb = true;
103 }
104
105 if (tp->thumb.use_size &&
106 (t->major > tp->thumb.size_threshold) &&
107 (t->minor < (tp->thumb.size_threshold * 0.6))) {
108 is_thumb = true;
109 }
110
111 return is_thumb;
112 }
113
114 static bool
tp_thumb_needs_jail(const struct tp_dispatch * tp,const struct tp_touch * t)115 tp_thumb_needs_jail(const struct tp_dispatch *tp, const struct tp_touch *t)
116 {
117 if (t->point.y < tp->thumb.upper_thumb_line ||
118 tp->scroll.method == LIBINPUT_CONFIG_SCROLL_EDGE)
119 return false;
120
121 if (!tp_thumb_in_exclusion_area(tp, t) &&
122 (tp->thumb.use_size || tp->thumb.use_pressure) &&
123 !tp_thumb_detect_pressure_size(tp, t))
124 return false;
125
126 if (t->speed.exceeded_count >= 10)
127 return false;
128
129 return true;
130 }
131
132 bool
tp_thumb_ignored(const struct tp_dispatch * tp,const struct tp_touch * t)133 tp_thumb_ignored(const struct tp_dispatch *tp, const struct tp_touch *t)
134 {
135 return (tp->thumb.detect_thumbs &&
136 tp->thumb.index == t->index &&
137 (tp->thumb.state == THUMB_STATE_JAILED ||
138 tp->thumb.state == THUMB_STATE_PINCH ||
139 tp->thumb.state == THUMB_STATE_SUPPRESSED ||
140 tp->thumb.state == THUMB_STATE_REVIVED_JAILED ||
141 tp->thumb.state == THUMB_STATE_DEAD));
142 }
143
144 bool
tp_thumb_ignored_for_tap(const struct tp_dispatch * tp,const struct tp_touch * t)145 tp_thumb_ignored_for_tap(const struct tp_dispatch *tp,
146 const struct tp_touch *t)
147 {
148 return (tp->thumb.detect_thumbs &&
149 tp->thumb.index == t->index &&
150 (tp->thumb.state == THUMB_STATE_PINCH ||
151 tp->thumb.state == THUMB_STATE_SUPPRESSED ||
152 tp->thumb.state == THUMB_STATE_DEAD));
153 }
154
155 bool
tp_thumb_ignored_for_gesture(const struct tp_dispatch * tp,const struct tp_touch * t)156 tp_thumb_ignored_for_gesture(const struct tp_dispatch *tp,
157 const struct tp_touch *t)
158 {
159 return (tp->thumb.detect_thumbs &&
160 tp->thumb.index == t->index &&
161 (tp->thumb.state == THUMB_STATE_JAILED ||
162 tp->thumb.state == THUMB_STATE_SUPPRESSED ||
163 tp->thumb.state == THUMB_STATE_REVIVED_JAILED ||
164 tp->thumb.state == THUMB_STATE_DEAD));
165 }
166
167 void
tp_thumb_suppress(struct tp_dispatch * tp,struct tp_touch * t)168 tp_thumb_suppress(struct tp_dispatch *tp, struct tp_touch *t)
169 {
170 if(tp->thumb.state == THUMB_STATE_FINGER ||
171 tp->thumb.state == THUMB_STATE_JAILED ||
172 tp->thumb.state == THUMB_STATE_PINCH ||
173 tp->thumb.index != t->index) {
174 tp_thumb_set_state(tp, t, THUMB_STATE_SUPPRESSED);
175 return;
176 }
177
178 tp_thumb_set_state(tp, t, THUMB_STATE_DEAD);
179 }
180
181 static void
tp_thumb_pinch(struct tp_dispatch * tp,struct tp_touch * t)182 tp_thumb_pinch(struct tp_dispatch *tp, struct tp_touch *t)
183 {
184 if(tp->thumb.state == THUMB_STATE_FINGER ||
185 tp->thumb.state == THUMB_STATE_JAILED ||
186 tp->thumb.index != t->index)
187 tp_thumb_set_state(tp, t, THUMB_STATE_PINCH);
188 else if (tp->thumb.state != THUMB_STATE_PINCH)
189 tp_thumb_suppress(tp, t);
190 }
191
192 static void
tp_thumb_revive(struct tp_dispatch * tp,struct tp_touch * t)193 tp_thumb_revive(struct tp_dispatch *tp, struct tp_touch *t)
194 {
195 if((tp->thumb.state != THUMB_STATE_SUPPRESSED &&
196 tp->thumb.state != THUMB_STATE_PINCH) ||
197 (tp->thumb.index != t->index))
198 return;
199
200 if(tp_thumb_needs_jail(tp, t))
201 tp_thumb_set_state(tp, t, THUMB_STATE_REVIVED_JAILED);
202 else
203 tp_thumb_set_state(tp, t, THUMB_STATE_REVIVED);
204 }
205
206 void
tp_thumb_update_touch(struct tp_dispatch * tp,struct tp_touch * t,uint64_t time)207 tp_thumb_update_touch(struct tp_dispatch *tp,
208 struct tp_touch *t,
209 uint64_t time)
210 {
211 if (!tp->thumb.detect_thumbs)
212 return;
213
214 /* Once any active touch exceeds the speed threshold, don't
215 * try to detect pinches until all touches lift.
216 */
217 if (t->speed.exceeded_count >= 10 &&
218 tp->thumb.pinch_eligible &&
219 tp->gesture.state == GESTURE_STATE_NONE) {
220 tp->thumb.pinch_eligible = false;
221 if(tp->thumb.state == THUMB_STATE_PINCH) {
222 struct tp_touch *thumb;
223 tp_for_each_touch(tp, thumb) {
224 if (thumb->index != tp->thumb.index)
225 continue;
226
227 tp_thumb_set_state(tp, thumb, THUMB_STATE_SUPPRESSED);
228 break;
229 }
230 }
231 }
232
233 /* Handle the thumb lifting off the touchpad */
234 if (t->state == TOUCH_END && t->index == tp->thumb.index) {
235 tp_thumb_lift(tp);
236 return;
237 }
238
239 /* If this touch is not the only one, thumb updates happen by context
240 * instead of here
241 */
242 if (tp->nfingers_down > 1)
243 return;
244
245 /* If we arrived here by other fingers lifting off, revive current touch
246 * if appropriate
247 */
248 tp_thumb_revive(tp, t);
249
250 /* First new touch below the lower_thumb_line, or below the upper_thumb_
251 * line if hardware can't verify it's a finger, starts as JAILED.
252 */
253 if (t->state == TOUCH_BEGIN && tp_thumb_needs_jail(tp, t)) {
254 tp_thumb_set_state(tp, t, THUMB_STATE_JAILED);
255 return;
256 }
257
258 /* If a touch breaks the speed threshold, or leaves the thumb area
259 * (upper or lower, depending on HW detection), it "escapes" jail.
260 */
261 if (tp->thumb.state == THUMB_STATE_JAILED &&
262 !(tp_thumb_needs_jail(tp, t)))
263 tp_thumb_set_state(tp, t, THUMB_STATE_FINGER);
264 if (tp->thumb.state == THUMB_STATE_REVIVED_JAILED &&
265 !(tp_thumb_needs_jail(tp, t)))
266 tp_thumb_set_state(tp, t, THUMB_STATE_REVIVED);
267 }
268
269 void
tp_thumb_update_multifinger(struct tp_dispatch * tp)270 tp_thumb_update_multifinger(struct tp_dispatch *tp)
271 {
272 struct tp_touch *t;
273 struct tp_touch *first = NULL,
274 *second = NULL,
275 *newest = NULL,
276 *oldest = NULL;
277 struct device_coords distance;
278 struct phys_coords mm;
279
280 unsigned int speed_exceeded_count = 0;
281
282 /* Get the first and second bottom-most touches, the max speed exceeded
283 * count overall, and the newest and oldest touches.
284 */
285 tp_for_each_touch(tp, t) {
286 if (t->state == TOUCH_NONE ||
287 t->state == TOUCH_HOVERING)
288 continue;
289
290 if (t->state == TOUCH_BEGIN)
291 newest = t;
292
293 speed_exceeded_count = max(speed_exceeded_count,
294 t->speed.exceeded_count);
295
296 if (!oldest || t->initial_time < oldest->initial_time) {
297 oldest = t;
298 }
299
300 if (!first) {
301 first = t;
302 continue;
303 }
304
305 if (t->point.y > first->point.y) {
306 second = first;
307 first = t;
308 continue;
309 }
310
311 if (!second || t->point.y > second->point.y ) {
312 second = t;
313 }
314 }
315
316 if (!first || !second)
317 return;
318
319 distance.x = abs(first->point.x - second->point.x);
320 distance.y = abs(first->point.y - second->point.y);
321 mm = evdev_device_unit_delta_to_mm(tp->device, &distance);
322
323 /* Speed-based thumb detection: if an existing finger is moving, and
324 * a new touch arrives, mark it as a thumb if it doesn't qualify as a
325 * 2-finger scroll. Also account for a thumb dropping onto the touchpad
326 * while scrolling or swiping.
327 */
328 if (newest &&
329 tp->thumb.state == THUMB_STATE_FINGER &&
330 tp->nfingers_down >= 2 &&
331 speed_exceeded_count > 5 &&
332 (tp->scroll.method != LIBINPUT_CONFIG_SCROLL_2FG ||
333 (mm.x > SCROLL_MM_X || mm.y > SCROLL_MM_Y))) {
334 evdev_log_debug(tp->device,
335 "touch %d is speed-based thumb\n",
336 newest->index);
337 tp_thumb_suppress(tp, newest);
338 return;
339 }
340
341 /* Contextual thumb detection: When a new touch arrives, check the
342 * timing and position of the two lowest touches.
343 *
344 * If both touches are very close, regardless of timing, and no matter
345 * their absolute position on the touchpad, count them both as live
346 * to support responsive two-finger scrolling.
347 */
348
349 if (mm.x < SCROLL_MM_X && mm.y < SCROLL_MM_Y) {
350 tp_thumb_lift(tp);
351 return;
352 }
353
354 /* If all the touches arrived within a very short time, and all of them
355 * are above the lower_thumb_line, assume the touches are all live to
356 * enable double, triple, and quadruple taps, clicks, and gestures. (If
357 * there is an actual resting thumb, it will be detected later based on
358 * the behavior of the other touches.)
359 */
360
361 if (newest &&
362 (newest->initial_time - oldest->initial_time) < THUMB_TIMEOUT &&
363 first->point.y < tp->thumb.lower_thumb_line) {
364 tp_thumb_lift(tp);
365 return;
366 }
367
368 /* If we're past the THUMB_TIMEOUT, and the touches are relatively far
369 * apart, then the new touch is unlikely to be a tap or clickfinger.
370 * Proceed with pre-1.14.901 thumb detection.
371 */
372
373 if (mm.y > SCROLL_MM_Y) {
374 if (tp->thumb.pinch_eligible)
375 tp_thumb_pinch(tp, first);
376 else
377 tp_thumb_suppress(tp, first);
378 } else {
379 tp_thumb_lift(tp);
380 }
381 }
382
383 void
tp_init_thumb(struct tp_dispatch * tp)384 tp_init_thumb(struct tp_dispatch *tp)
385 {
386 struct evdev_device *device = tp->device;
387 double w = 0.0, h = 0.0;
388 struct device_coords edges;
389 struct phys_coords mm = { 0.0, 0.0 };
390 uint32_t threshold;
391 struct quirks_context *quirks;
392 struct quirks *q;
393
394 tp->thumb.detect_thumbs = false;
395
396 if (!tp->buttons.is_clickpad)
397 return;
398
399 /* if the touchpad is less than 50mm high, skip thumb detection.
400 * it's too small to meaningfully interact with a thumb on the
401 * touchpad */
402 evdev_device_get_size(device, &w, &h);
403 if (h < 50)
404 return;
405
406 tp->thumb.detect_thumbs = true;
407 tp->thumb.use_pressure = false;
408 tp->thumb.pressure_threshold = INT_MAX;
409
410 /* detect thumbs by pressure in the bottom 15mm, detect thumbs by
411 * lingering in the bottom 8mm */
412 mm.y = h * 0.85;
413 edges = evdev_device_mm_to_units(device, &mm);
414 tp->thumb.upper_thumb_line = edges.y;
415
416 mm.y = h * 0.92;
417 edges = evdev_device_mm_to_units(device, &mm);
418 tp->thumb.lower_thumb_line = edges.y;
419
420 quirks = evdev_libinput_context(device)->quirks;
421 q = quirks_fetch_for_device(quirks, device->udev_device);
422
423 if (libevdev_has_event_code(device->evdev, EV_ABS, ABS_MT_PRESSURE)) {
424 if (quirks_get_uint32(q,
425 QUIRK_ATTR_THUMB_PRESSURE_THRESHOLD,
426 &threshold)) {
427 tp->thumb.use_pressure = true;
428 tp->thumb.pressure_threshold = threshold;
429 }
430 }
431
432 if (libevdev_has_event_code(device->evdev, EV_ABS, ABS_MT_TOUCH_MAJOR)) {
433 if (quirks_get_uint32(q,
434 QUIRK_ATTR_THUMB_SIZE_THRESHOLD,
435 &threshold)) {
436 tp->thumb.use_size = true;
437 tp->thumb.size_threshold = threshold;
438 }
439 }
440
441 tp_thumb_reset(tp);
442
443 quirks_unref(q);
444
445 evdev_log_debug(device,
446 "thumb: enabled thumb detection (area%s%s)\n",
447 tp->thumb.use_pressure ? ", pressure" : "",
448 tp->thumb.use_size ? ", size" : "");
449 }
450