evdev-mt-touchpad-thumb.c 12 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
/*
 * Copyright © 2019 Matt Mayfield
 * Copyright © 2019 Red Hat, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

#include "config.h"
#include "evdev-mt-touchpad.h"

28 29 30
/* distance between fingers to assume it is not a scroll */
#define SCROLL_MM_X 35
#define SCROLL_MM_Y 25
31
#define THUMB_TIMEOUT ms2us(100)
32 33 34 35 36

static inline const char*
thumb_state_to_str(enum tp_thumb_state state)
{
	switch(state){
37 38 39 40 41 42 43
	CASE_RETURN_STRING(THUMB_STATE_FINGER);
	CASE_RETURN_STRING(THUMB_STATE_JAILED);
	CASE_RETURN_STRING(THUMB_STATE_PINCH);
	CASE_RETURN_STRING(THUMB_STATE_SUPPRESSED);
	CASE_RETURN_STRING(THUMB_STATE_REVIVED);
	CASE_RETURN_STRING(THUMB_STATE_REVIVED_JAILED);
	CASE_RETURN_STRING(THUMB_STATE_DEAD);
44 45 46 47 48
	}

	return NULL;
}

49
static void
50 51 52
tp_thumb_set_state(struct tp_dispatch *tp,
		   struct tp_touch *t,
		   enum tp_thumb_state state)
53
{
54 55 56
	unsigned int index = t ? t->index : UINT_MAX;

	if (tp->thumb.state == state && tp->thumb.index == index)
57 58 59 60
		return;

	evdev_log_debug(tp->device,
			"thumb: touch %d, %s → %s\n",
61 62
			(int)index,
			thumb_state_to_str(tp->thumb.state),
63 64
			thumb_state_to_str(state));

65 66
	tp->thumb.state = state;
	tp->thumb.index = index;
67
}
68

69
void
70 71 72 73 74 75 76 77 78
tp_thumb_reset(struct tp_dispatch *tp)
{
	tp->thumb.state = THUMB_STATE_FINGER;
	tp->thumb.index = UINT_MAX;
	tp->thumb.pinch_eligible = true;
}

static void
tp_thumb_lift(struct tp_dispatch *tp)
79
{
80 81
	tp->thumb.state = THUMB_STATE_FINGER;
	tp->thumb.index = UINT_MAX;
82 83
}

84 85
static bool
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 94

}

static bool
tp_thumb_detect_pressure_size(const struct tp_dispatch *tp,
95
			      const struct tp_touch *t)
96 97 98 99 100
{
	bool is_thumb = false;

	if (tp->thumb.use_pressure &&
	    t->pressure > tp->thumb.pressure_threshold &&
101
	    tp_thumb_in_exclusion_area(tp, t)) {
102 103 104 105 106 107 108 109 110 111 112 113
		is_thumb = true;
	}

	if (tp->thumb.use_size &&
	    (t->major > tp->thumb.size_threshold) &&
	    (t->minor < (tp->thumb.size_threshold * 0.6))) {
		is_thumb = true;
	}

	return is_thumb;
}

114 115 116
static bool
tp_thumb_needs_jail(const struct tp_dispatch *tp, const struct tp_touch *t)
{
117 118
	if (t->point.y < tp->thumb.upper_thumb_line ||
	    tp->scroll.method == LIBINPUT_CONFIG_SCROLL_EDGE)
119 120 121
		return false;

	if (!tp_thumb_in_exclusion_area(tp, t) &&
122
           (tp->thumb.use_size || tp->thumb.use_pressure) &&
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
	    !tp_thumb_detect_pressure_size(tp, t))
		return false;

	if (t->speed.exceeded_count >= 10)
		return false;

	return true;
}

bool
tp_thumb_ignored(const struct tp_dispatch *tp, const struct tp_touch *t)
{
	return (tp->thumb.detect_thumbs &&
		tp->thumb.index == t->index &&
		(tp->thumb.state == THUMB_STATE_JAILED ||
		 tp->thumb.state == THUMB_STATE_PINCH ||
		 tp->thumb.state == THUMB_STATE_SUPPRESSED ||
		 tp->thumb.state == THUMB_STATE_REVIVED_JAILED ||
		 tp->thumb.state == THUMB_STATE_DEAD));
}

bool
tp_thumb_ignored_for_tap(const struct tp_dispatch *tp,
			 const struct tp_touch *t)
{
	return (tp->thumb.detect_thumbs &&
		tp->thumb.index == t->index &&
		(tp->thumb.state == THUMB_STATE_PINCH ||
		 tp->thumb.state == THUMB_STATE_SUPPRESSED ||
		 tp->thumb.state == THUMB_STATE_DEAD));
}

bool
tp_thumb_ignored_for_gesture(const struct tp_dispatch *tp,
			     const struct tp_touch *t)
{
	return (tp->thumb.detect_thumbs &&
		tp->thumb.index == t->index &&
		(tp->thumb.state == THUMB_STATE_JAILED ||
		 tp->thumb.state == THUMB_STATE_SUPPRESSED ||
		 tp->thumb.state == THUMB_STATE_REVIVED_JAILED ||
		 tp->thumb.state == THUMB_STATE_DEAD));
}

167 168 169
void
tp_thumb_suppress(struct tp_dispatch *tp, struct tp_touch *t)
{
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
	if(tp->thumb.state == THUMB_STATE_FINGER ||
	   tp->thumb.state == THUMB_STATE_JAILED ||
	   tp->thumb.state == THUMB_STATE_PINCH ||
	   tp->thumb.index != t->index) {
		tp_thumb_set_state(tp, t, THUMB_STATE_SUPPRESSED);
		return;
	}

	tp_thumb_set_state(tp, t, THUMB_STATE_DEAD);
}

static void
tp_thumb_pinch(struct tp_dispatch *tp, struct tp_touch *t)
{
	if(tp->thumb.state == THUMB_STATE_FINGER ||
	   tp->thumb.state == THUMB_STATE_JAILED ||
	   tp->thumb.index != t->index)
		tp_thumb_set_state(tp, t, THUMB_STATE_PINCH);
	else if (tp->thumb.state != THUMB_STATE_PINCH)
		tp_thumb_suppress(tp, t);
}

static void
tp_thumb_revive(struct tp_dispatch *tp, struct tp_touch *t)
{
	if((tp->thumb.state != THUMB_STATE_SUPPRESSED &&
	    tp->thumb.state != THUMB_STATE_PINCH) ||
	   (tp->thumb.index != t->index))
		return;

	if(tp_thumb_needs_jail(tp, t))
		tp_thumb_set_state(tp, t, THUMB_STATE_REVIVED_JAILED);
	else
		tp_thumb_set_state(tp, t, THUMB_STATE_REVIVED);
204 205
}

206
void
207 208 209
tp_thumb_update_touch(struct tp_dispatch *tp,
		      struct tp_touch *t,
		      uint64_t time)
210
{
211
	if (!tp->thumb.detect_thumbs)
212
		return;
213

214 215
	/* Once any active touch exceeds the speed threshold, don't
	 * try to detect pinches until all touches lift.
216
	 */
217 218 219 220 221 222 223 224 225 226 227
	if (t->speed.exceeded_count >= 10 &&
	    tp->thumb.pinch_eligible &&
	    tp->gesture.state == GESTURE_STATE_NONE) {
		tp->thumb.pinch_eligible = false;
		if(tp->thumb.state == THUMB_STATE_PINCH) {
			struct tp_touch *thumb;
			tp_for_each_touch(tp, thumb) {
				if (thumb->index != tp->thumb.index)
					continue;

				tp_thumb_set_state(tp, thumb, THUMB_STATE_SUPPRESSED);
228 229 230 231 232
				break;
			}
		}
	}

233 234 235 236 237 238 239 240
	/* Handle the thumb lifting off the touchpad */
	if (t->state == TOUCH_END && t->index == tp->thumb.index) {
		tp_thumb_lift(tp);
		return;
	}

	/* If this touch is not the only one, thumb updates happen by context
	 * instead of here
241
	 */
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
	if (tp->nfingers_down > 1)
		return;

	/* If we arrived here by other fingers lifting off, revive current touch
	 * if appropriate
	 */
	tp_thumb_revive(tp, t);

	/* First new touch below the lower_thumb_line, or below the upper_thumb_
	 * line if hardware can't verify it's a finger, starts as JAILED.
	 */
	if (t->state == TOUCH_BEGIN && tp_thumb_needs_jail(tp, t)) {
		tp_thumb_set_state(tp, t, THUMB_STATE_JAILED);
		return;
	}

	/* If a touch breaks the speed threshold, or leaves the thumb area
	 * (upper or lower, depending on HW detection), it "escapes" jail.
260
	 */
261 262 263 264 265 266
	if (tp->thumb.state == THUMB_STATE_JAILED &&
	    !(tp_thumb_needs_jail(tp, t)))
		tp_thumb_set_state(tp, t, THUMB_STATE_FINGER);
	if (tp->thumb.state == THUMB_STATE_REVIVED_JAILED &&
	    !(tp_thumb_needs_jail(tp, t)))
		tp_thumb_set_state(tp, t, THUMB_STATE_REVIVED);
267 268
}

269
void
270
tp_thumb_update_multifinger(struct tp_dispatch *tp)
271 272 273
{
	struct tp_touch *t;
	struct tp_touch *first = NULL,
274
			*second = NULL,
275 276
			*newest = NULL,
			*oldest = NULL;
277 278
	struct device_coords distance;
	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 286 287 288 289
	tp_for_each_touch(tp, t) {
		if (t->state == TOUCH_NONE ||
		    t->state == TOUCH_HOVERING)
			continue;

290 291 292 293 294 295
		if (t->state == TOUCH_BEGIN)
			newest = t;

		speed_exceeded_count = max(speed_exceeded_count,
		                           t->speed.exceeded_count);

296 297 298 299
		if (!oldest || t->initial_time < oldest->initial_time) {
			oldest = t;
		}

300 301 302 303 304 305 306
		if (!first) {
			first = t;
			continue;
		}

		if (t->point.y > first->point.y) {
			second = first;
307
			first = t;
308 309
			continue;
		}
310

311 312 313
		if (!second || t->point.y > second->point.y ) {
			second = t;
		}
314 315
	}

316 317 318 319 320 321
	if (!first || !second)
		return;

	distance.x = abs(first->point.x - second->point.x);
	distance.y = abs(first->point.y - second->point.y);
	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 326
	 * 2-finger scroll. Also account for a thumb dropping onto the touchpad
	 * while scrolling or swiping.
327 328
	 */
	if (newest &&
329
	    tp->thumb.state == THUMB_STATE_FINGER &&
330
	    tp->nfingers_down >= 2 &&
331 332 333 334 335 336 337 338
	    speed_exceeded_count > 5 &&
	    (tp->scroll.method != LIBINPUT_CONFIG_SCROLL_2FG ||
	     (mm.x > SCROLL_MM_X || mm.y > SCROLL_MM_Y))) {
		evdev_log_debug(tp->device,
				"touch %d is speed-based thumb\n",
				newest->index);
		tp_thumb_suppress(tp, newest);
		return;
339 340
	}

341 342
	/* Contextual thumb detection: When a new touch arrives, check the
	 * timing and position of the two lowest touches.
343
	 *
344 345 346
	 * If both touches are very close, regardless of timing, and no matter
	 * their absolute position on the touchpad, count them both as live
	 * to support responsive two-finger scrolling.
347
	 */
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372

	if (mm.x < SCROLL_MM_X && mm.y < SCROLL_MM_Y) {
		tp_thumb_lift(tp);
		return;
	}

	/* If all the touches arrived within a very short time, and all of them
	 * are above the lower_thumb_line, assume the touches are all live to
	 * enable double, triple, and quadruple taps, clicks, and gestures. (If
	 * there is an actual resting thumb, it will be detected later based on
	 * the behavior of the other touches.)
	 */

	if ((newest->initial_time - oldest->initial_time) < THUMB_TIMEOUT &&
         first->point.y < tp->thumb.lower_thumb_line) {
		tp_thumb_lift(tp);
		return;
	}

	/* If we're past the THUMB_TIMEOUT, and the touches are relatively far
	 * apart, then the new touch is unlikely to be a tap or clickfinger.
	 * Proceed with pre-1.14.901 thumb detection.
	*/

	if (mm.y > SCROLL_MM_Y) {
373 374 375 376
		if (tp->thumb.pinch_eligible)
			tp_thumb_pinch(tp, first);
		else
			tp_thumb_suppress(tp, first);
377
	} else {
378 379
		tp_thumb_lift(tp);
	}
380 381
}

382 383 384 385 386 387 388 389 390 391 392
void
tp_init_thumb(struct tp_dispatch *tp)
{
	struct evdev_device *device = tp->device;
	double w = 0.0, h = 0.0;
	struct device_coords edges;
	struct phys_coords mm = { 0.0, 0.0 };
	uint32_t threshold;
	struct quirks_context *quirks;
	struct quirks *q;

393 394
	tp->thumb.detect_thumbs = false;

395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439
	if (!tp->buttons.is_clickpad)
		return;

	/* 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;

	tp->thumb.detect_thumbs = true;
	tp->thumb.use_pressure = false;
	tp->thumb.pressure_threshold = INT_MAX;

	/* detect thumbs by pressure in the bottom 15mm, detect thumbs by
	 * lingering in the bottom 8mm */
	mm.y = h * 0.85;
	edges = evdev_device_mm_to_units(device, &mm);
	tp->thumb.upper_thumb_line = edges.y;

	mm.y = h * 0.92;
	edges = evdev_device_mm_to_units(device, &mm);
	tp->thumb.lower_thumb_line = edges.y;

	quirks = evdev_libinput_context(device)->quirks;
	q = quirks_fetch_for_device(quirks, device->udev_device);

	if (libevdev_has_event_code(device->evdev, EV_ABS, ABS_MT_PRESSURE)) {
		if (quirks_get_uint32(q,
				      QUIRK_ATTR_THUMB_PRESSURE_THRESHOLD,
				      &threshold)) {
			tp->thumb.use_pressure = true;
			tp->thumb.pressure_threshold = threshold;
		}
	}

	if (libevdev_has_event_code(device->evdev, EV_ABS, ABS_MT_TOUCH_MAJOR)) {
		if (quirks_get_uint32(q,
				      QUIRK_ATTR_THUMB_SIZE_THRESHOLD,
				      &threshold)) {
			tp->thumb.use_size = true;
			tp->thumb.size_threshold = threshold;
		}
	}

440 441
	tp_thumb_reset(tp);

442 443 444 445 446 447 448
	quirks_unref(q);

	evdev_log_debug(device,
			"thumb: enabled thumb detection (area%s%s)\n",
			tp->thumb.use_pressure ? ", pressure" : "",
			tp->thumb.use_size ? ", size" : "");
}