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
33static inline const char*
34thumb_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
49static void
50tp_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
69void
70tp_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
77static void
78tp_thumb_lift(struct tp_dispatch *tp)
79{
80	tp->thumb.state = THUMB_STATE_FINGER;
81	tp->thumb.index = UINT_MAX;
82}
83
84static bool
85tp_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
93static bool
94tp_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
114static bool
115tp_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
132bool
133tp_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
144bool
145tp_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
155bool
156tp_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
167void
168tp_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
181static void
182tp_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
192static void
193tp_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
206void
207tp_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
269void
270tp_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
383void
384tp_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
451struct tp_touch*
452tp_thumb_get_touch(struct tp_dispatch *tp)
453{
454	struct tp_touch *thumb;
455
456	if (tp->thumb.index == UINT_MAX)
457		return NULL;
458
459	tp_for_each_touch(tp, thumb) {
460		if (thumb->index == tp->thumb.index)
461			return thumb;
462	}
463
464	return NULL;
465}
466