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