• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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