1a46c0ec8Sopenharmony_ci/* 2a46c0ec8Sopenharmony_ci * Copyright © 2015 Red Hat, Inc. 3a46c0ec8Sopenharmony_ci * 4a46c0ec8Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 5a46c0ec8Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 6a46c0ec8Sopenharmony_ci * to deal in the Software without restriction, including without limitation 7a46c0ec8Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8a46c0ec8Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 9a46c0ec8Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 10a46c0ec8Sopenharmony_ci * 11a46c0ec8Sopenharmony_ci * The above copyright notice and this permission notice (including the next 12a46c0ec8Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 13a46c0ec8Sopenharmony_ci * Software. 14a46c0ec8Sopenharmony_ci * 15a46c0ec8Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16a46c0ec8Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17a46c0ec8Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18a46c0ec8Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19a46c0ec8Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20a46c0ec8Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21a46c0ec8Sopenharmony_ci * DEALINGS IN THE SOFTWARE. 22a46c0ec8Sopenharmony_ci */ 23a46c0ec8Sopenharmony_ci 24a46c0ec8Sopenharmony_ci#include "config.h" 25a46c0ec8Sopenharmony_ci 26a46c0ec8Sopenharmony_ci#include <math.h> 27a46c0ec8Sopenharmony_ci#include <stdbool.h> 28a46c0ec8Sopenharmony_ci 29a46c0ec8Sopenharmony_ci#include "evdev-mt-touchpad.h" 30a46c0ec8Sopenharmony_ci 31a46c0ec8Sopenharmony_ci#define QUICK_GESTURE_HOLD_TIMEOUT ms2us(40) 32a46c0ec8Sopenharmony_ci#define DEFAULT_GESTURE_HOLD_TIMEOUT ms2us(180) 33a46c0ec8Sopenharmony_ci#define DEFAULT_GESTURE_SWITCH_TIMEOUT ms2us(100) 34a46c0ec8Sopenharmony_ci#define DEFAULT_GESTURE_SWIPE_TIMEOUT ms2us(150) 35a46c0ec8Sopenharmony_ci#define DEFAULT_GESTURE_PINCH_TIMEOUT ms2us(300) 36a46c0ec8Sopenharmony_ci 37a46c0ec8Sopenharmony_ci#define HOLD_AND_MOTION_THRESHOLD 0.5 /* mm */ 38a46c0ec8Sopenharmony_ci#define PINCH_DISAMBIGUATION_MOVE_THRESHOLD 1.5 /* mm */ 39a46c0ec8Sopenharmony_ci 40a46c0ec8Sopenharmony_cienum gesture_event { 41a46c0ec8Sopenharmony_ci GESTURE_EVENT_RESET, 42a46c0ec8Sopenharmony_ci GESTURE_EVENT_FINGER_DETECTED, 43a46c0ec8Sopenharmony_ci GESTURE_EVENT_HOLD_TIMEOUT, 44a46c0ec8Sopenharmony_ci GESTURE_EVENT_HOLD_AND_MOTION, 45a46c0ec8Sopenharmony_ci GESTURE_EVENT_POINTER_MOTION, 46a46c0ec8Sopenharmony_ci GESTURE_EVENT_SCROLL, 47a46c0ec8Sopenharmony_ci GESTURE_EVENT_SWIPE, 48a46c0ec8Sopenharmony_ci GESTURE_EVENT_PINCH, 49a46c0ec8Sopenharmony_ci}; 50a46c0ec8Sopenharmony_ci 51a46c0ec8Sopenharmony_ci/***************************************** 52a46c0ec8Sopenharmony_ci * DO NOT EDIT THIS FILE! 53a46c0ec8Sopenharmony_ci * 54a46c0ec8Sopenharmony_ci * Look at the state diagram in doc/touchpad-gestures-state-machine.svg 55a46c0ec8Sopenharmony_ci * (generated with https://www.diagrams.net) 56a46c0ec8Sopenharmony_ci * 57a46c0ec8Sopenharmony_ci * Any changes in this file must be represented in the diagram. 58a46c0ec8Sopenharmony_ci */ 59a46c0ec8Sopenharmony_ci 60a46c0ec8Sopenharmony_cistatic inline const char* 61a46c0ec8Sopenharmony_cigesture_state_to_str(enum tp_gesture_state state) 62a46c0ec8Sopenharmony_ci{ 63a46c0ec8Sopenharmony_ci switch (state) { 64a46c0ec8Sopenharmony_ci CASE_RETURN_STRING(GESTURE_STATE_NONE); 65a46c0ec8Sopenharmony_ci CASE_RETURN_STRING(GESTURE_STATE_UNKNOWN); 66a46c0ec8Sopenharmony_ci CASE_RETURN_STRING(GESTURE_STATE_HOLD); 67a46c0ec8Sopenharmony_ci CASE_RETURN_STRING(GESTURE_STATE_HOLD_AND_MOTION); 68a46c0ec8Sopenharmony_ci CASE_RETURN_STRING(GESTURE_STATE_POINTER_MOTION); 69a46c0ec8Sopenharmony_ci CASE_RETURN_STRING(GESTURE_STATE_SCROLL); 70a46c0ec8Sopenharmony_ci CASE_RETURN_STRING(GESTURE_STATE_PINCH); 71a46c0ec8Sopenharmony_ci CASE_RETURN_STRING(GESTURE_STATE_SWIPE); 72a46c0ec8Sopenharmony_ci } 73a46c0ec8Sopenharmony_ci return NULL; 74a46c0ec8Sopenharmony_ci} 75a46c0ec8Sopenharmony_ci 76a46c0ec8Sopenharmony_cistatic inline const char* 77a46c0ec8Sopenharmony_cigesture_event_to_str(enum gesture_event event) 78a46c0ec8Sopenharmony_ci{ 79a46c0ec8Sopenharmony_ci switch(event) { 80a46c0ec8Sopenharmony_ci CASE_RETURN_STRING(GESTURE_EVENT_RESET); 81a46c0ec8Sopenharmony_ci CASE_RETURN_STRING(GESTURE_EVENT_FINGER_DETECTED); 82a46c0ec8Sopenharmony_ci CASE_RETURN_STRING(GESTURE_EVENT_HOLD_TIMEOUT); 83a46c0ec8Sopenharmony_ci CASE_RETURN_STRING(GESTURE_EVENT_HOLD_AND_MOTION); 84a46c0ec8Sopenharmony_ci CASE_RETURN_STRING(GESTURE_EVENT_POINTER_MOTION); 85a46c0ec8Sopenharmony_ci CASE_RETURN_STRING(GESTURE_EVENT_SCROLL); 86a46c0ec8Sopenharmony_ci CASE_RETURN_STRING(GESTURE_EVENT_SWIPE); 87a46c0ec8Sopenharmony_ci CASE_RETURN_STRING(GESTURE_EVENT_PINCH); 88a46c0ec8Sopenharmony_ci } 89a46c0ec8Sopenharmony_ci return NULL; 90a46c0ec8Sopenharmony_ci} 91a46c0ec8Sopenharmony_ci 92a46c0ec8Sopenharmony_cistatic struct device_float_coords 93a46c0ec8Sopenharmony_citp_get_touches_delta(struct tp_dispatch *tp, bool average) 94a46c0ec8Sopenharmony_ci{ 95a46c0ec8Sopenharmony_ci struct tp_touch *t; 96a46c0ec8Sopenharmony_ci unsigned int i, nactive = 0; 97a46c0ec8Sopenharmony_ci struct device_float_coords delta = {0.0, 0.0}; 98a46c0ec8Sopenharmony_ci 99a46c0ec8Sopenharmony_ci for (i = 0; i < tp->num_slots; i++) { 100a46c0ec8Sopenharmony_ci t = &tp->touches[i]; 101a46c0ec8Sopenharmony_ci 102a46c0ec8Sopenharmony_ci if (!tp_touch_active_for_gesture(tp, t)) 103a46c0ec8Sopenharmony_ci continue; 104a46c0ec8Sopenharmony_ci 105a46c0ec8Sopenharmony_ci nactive++; 106a46c0ec8Sopenharmony_ci 107a46c0ec8Sopenharmony_ci if (t->dirty) { 108a46c0ec8Sopenharmony_ci struct device_coords d; 109a46c0ec8Sopenharmony_ci 110a46c0ec8Sopenharmony_ci d = tp_get_delta(t); 111a46c0ec8Sopenharmony_ci 112a46c0ec8Sopenharmony_ci delta.x += d.x; 113a46c0ec8Sopenharmony_ci delta.y += d.y; 114a46c0ec8Sopenharmony_ci } 115a46c0ec8Sopenharmony_ci } 116a46c0ec8Sopenharmony_ci 117a46c0ec8Sopenharmony_ci if (!average || nactive == 0) 118a46c0ec8Sopenharmony_ci return delta; 119a46c0ec8Sopenharmony_ci 120a46c0ec8Sopenharmony_ci delta.x /= nactive; 121a46c0ec8Sopenharmony_ci delta.y /= nactive; 122a46c0ec8Sopenharmony_ci 123a46c0ec8Sopenharmony_ci return delta; 124a46c0ec8Sopenharmony_ci} 125a46c0ec8Sopenharmony_ci 126a46c0ec8Sopenharmony_cistatic void 127a46c0ec8Sopenharmony_citp_gesture_init_scroll(struct tp_dispatch *tp) 128a46c0ec8Sopenharmony_ci{ 129a46c0ec8Sopenharmony_ci struct phys_coords zero = {0.0, 0.0}; 130a46c0ec8Sopenharmony_ci tp->scroll.active.h = false; 131a46c0ec8Sopenharmony_ci tp->scroll.active.v = false; 132a46c0ec8Sopenharmony_ci tp->scroll.duration.h = 0; 133a46c0ec8Sopenharmony_ci tp->scroll.duration.v = 0; 134a46c0ec8Sopenharmony_ci tp->scroll.vector = zero; 135a46c0ec8Sopenharmony_ci tp->scroll.time_prev = 0; 136a46c0ec8Sopenharmony_ci} 137a46c0ec8Sopenharmony_ci 138a46c0ec8Sopenharmony_cistatic inline struct device_float_coords 139a46c0ec8Sopenharmony_citp_get_combined_touches_delta(struct tp_dispatch *tp) 140a46c0ec8Sopenharmony_ci{ 141a46c0ec8Sopenharmony_ci return tp_get_touches_delta(tp, false); 142a46c0ec8Sopenharmony_ci} 143a46c0ec8Sopenharmony_ci 144a46c0ec8Sopenharmony_cistatic inline struct device_float_coords 145a46c0ec8Sopenharmony_citp_get_average_touches_delta(struct tp_dispatch *tp) 146a46c0ec8Sopenharmony_ci{ 147a46c0ec8Sopenharmony_ci return tp_get_touches_delta(tp, true); 148a46c0ec8Sopenharmony_ci} 149a46c0ec8Sopenharmony_ci 150a46c0ec8Sopenharmony_cistatic void 151a46c0ec8Sopenharmony_citp_gesture_start(struct tp_dispatch *tp, uint64_t time) 152a46c0ec8Sopenharmony_ci{ 153a46c0ec8Sopenharmony_ci const struct normalized_coords zero = { 0.0, 0.0 }; 154a46c0ec8Sopenharmony_ci 155a46c0ec8Sopenharmony_ci if (tp->gesture.started) 156a46c0ec8Sopenharmony_ci return; 157a46c0ec8Sopenharmony_ci 158a46c0ec8Sopenharmony_ci switch (tp->gesture.state) { 159a46c0ec8Sopenharmony_ci case GESTURE_STATE_NONE: 160a46c0ec8Sopenharmony_ci case GESTURE_STATE_UNKNOWN: 161a46c0ec8Sopenharmony_ci evdev_log_bug_libinput(tp->device, 162a46c0ec8Sopenharmony_ci "%s in unknown gesture mode\n", 163a46c0ec8Sopenharmony_ci __func__); 164a46c0ec8Sopenharmony_ci break; 165a46c0ec8Sopenharmony_ci case GESTURE_STATE_HOLD: 166a46c0ec8Sopenharmony_ci case GESTURE_STATE_HOLD_AND_MOTION: 167a46c0ec8Sopenharmony_ci gesture_notify_hold(&tp->device->base, time, 168a46c0ec8Sopenharmony_ci tp->gesture.finger_count); 169a46c0ec8Sopenharmony_ci break; 170a46c0ec8Sopenharmony_ci case GESTURE_STATE_SCROLL: 171a46c0ec8Sopenharmony_ci tp_gesture_init_scroll(tp); 172a46c0ec8Sopenharmony_ci break; 173a46c0ec8Sopenharmony_ci case GESTURE_STATE_PINCH: 174a46c0ec8Sopenharmony_ci gesture_notify_pinch(&tp->device->base, time, 175a46c0ec8Sopenharmony_ci LIBINPUT_EVENT_GESTURE_PINCH_BEGIN, 176a46c0ec8Sopenharmony_ci tp->gesture.finger_count, 177a46c0ec8Sopenharmony_ci &zero, &zero, 1.0, 0.0); 178a46c0ec8Sopenharmony_ci break; 179a46c0ec8Sopenharmony_ci case GESTURE_STATE_SWIPE: 180a46c0ec8Sopenharmony_ci gesture_notify_swipe(&tp->device->base, time, 181a46c0ec8Sopenharmony_ci LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN, 182a46c0ec8Sopenharmony_ci tp->gesture.finger_count, 183a46c0ec8Sopenharmony_ci &zero, &zero); 184a46c0ec8Sopenharmony_ci break; 185a46c0ec8Sopenharmony_ci case GESTURE_STATE_POINTER_MOTION: 186a46c0ec8Sopenharmony_ci break; 187a46c0ec8Sopenharmony_ci } 188a46c0ec8Sopenharmony_ci 189a46c0ec8Sopenharmony_ci tp->gesture.started = true; 190a46c0ec8Sopenharmony_ci} 191a46c0ec8Sopenharmony_ci 192a46c0ec8Sopenharmony_cistatic struct device_float_coords 193a46c0ec8Sopenharmony_citp_get_raw_pointer_motion(struct tp_dispatch *tp) 194a46c0ec8Sopenharmony_ci{ 195a46c0ec8Sopenharmony_ci struct device_float_coords raw; 196a46c0ec8Sopenharmony_ci 197a46c0ec8Sopenharmony_ci /* When a clickpad is clicked, combine motion of all active touches */ 198a46c0ec8Sopenharmony_ci if (tp->buttons.is_clickpad && tp->buttons.state) 199a46c0ec8Sopenharmony_ci raw = tp_get_combined_touches_delta(tp); 200a46c0ec8Sopenharmony_ci else 201a46c0ec8Sopenharmony_ci raw = tp_get_average_touches_delta(tp); 202a46c0ec8Sopenharmony_ci 203a46c0ec8Sopenharmony_ci return raw; 204a46c0ec8Sopenharmony_ci} 205a46c0ec8Sopenharmony_ci 206a46c0ec8Sopenharmony_cistatic bool 207a46c0ec8Sopenharmony_citp_has_pending_pointer_motion(struct tp_dispatch *tp, uint64_t time) 208a46c0ec8Sopenharmony_ci{ 209a46c0ec8Sopenharmony_ci struct device_float_coords raw; 210a46c0ec8Sopenharmony_ci 211a46c0ec8Sopenharmony_ci if (!(tp->queued & TOUCHPAD_EVENT_MOTION)) 212a46c0ec8Sopenharmony_ci return false; 213a46c0ec8Sopenharmony_ci 214a46c0ec8Sopenharmony_ci /* Checking for raw pointer motion is enough in this case. 215a46c0ec8Sopenharmony_ci * Calling tp_filter_motion is intentionally omitted to avoid calling 216a46c0ec8Sopenharmony_ci * it twice (here and in tp_gesture_post_pointer_motion) with the same 217a46c0ec8Sopenharmony_ci * event. 218a46c0ec8Sopenharmony_ci */ 219a46c0ec8Sopenharmony_ci raw = tp_get_raw_pointer_motion(tp); 220a46c0ec8Sopenharmony_ci return !device_float_is_zero(raw); 221a46c0ec8Sopenharmony_ci} 222a46c0ec8Sopenharmony_ci 223a46c0ec8Sopenharmony_cistatic void 224a46c0ec8Sopenharmony_citp_gesture_post_pointer_motion(struct tp_dispatch *tp, uint64_t time) 225a46c0ec8Sopenharmony_ci{ 226a46c0ec8Sopenharmony_ci struct device_float_coords raw; 227a46c0ec8Sopenharmony_ci struct normalized_coords delta; 228a46c0ec8Sopenharmony_ci 229a46c0ec8Sopenharmony_ci raw = tp_get_raw_pointer_motion(tp); 230a46c0ec8Sopenharmony_ci delta = tp_filter_motion(tp, &raw, time); 231a46c0ec8Sopenharmony_ci 232a46c0ec8Sopenharmony_ci if (!normalized_is_zero(delta) || !device_float_is_zero(raw)) { 233a46c0ec8Sopenharmony_ci struct device_float_coords unaccel; 234a46c0ec8Sopenharmony_ci 235a46c0ec8Sopenharmony_ci unaccel = tp_scale_to_xaxis(tp, raw); 236a46c0ec8Sopenharmony_ci pointer_notify_motion(&tp->device->base, 237a46c0ec8Sopenharmony_ci time, 238a46c0ec8Sopenharmony_ci &delta, 239a46c0ec8Sopenharmony_ci &unaccel); 240a46c0ec8Sopenharmony_ci } 241a46c0ec8Sopenharmony_ci} 242a46c0ec8Sopenharmony_ci 243a46c0ec8Sopenharmony_cistatic unsigned int 244a46c0ec8Sopenharmony_citp_gesture_get_active_touches(const struct tp_dispatch *tp, 245a46c0ec8Sopenharmony_ci struct tp_touch **touches, 246a46c0ec8Sopenharmony_ci unsigned int count) 247a46c0ec8Sopenharmony_ci{ 248a46c0ec8Sopenharmony_ci unsigned int n = 0; 249a46c0ec8Sopenharmony_ci struct tp_touch *t; 250a46c0ec8Sopenharmony_ci 251a46c0ec8Sopenharmony_ci memset(touches, 0, count * sizeof(struct tp_touch *)); 252a46c0ec8Sopenharmony_ci 253a46c0ec8Sopenharmony_ci tp_for_each_touch(tp, t) { 254a46c0ec8Sopenharmony_ci if (tp_touch_active_for_gesture(tp, t)) { 255a46c0ec8Sopenharmony_ci touches[n++] = t; 256a46c0ec8Sopenharmony_ci if (n == count) 257a46c0ec8Sopenharmony_ci return count; 258a46c0ec8Sopenharmony_ci } 259a46c0ec8Sopenharmony_ci } 260a46c0ec8Sopenharmony_ci 261a46c0ec8Sopenharmony_ci /* 262a46c0ec8Sopenharmony_ci * This can happen when the user does .e.g: 263a46c0ec8Sopenharmony_ci * 1) Put down 1st finger in center (so active) 264a46c0ec8Sopenharmony_ci * 2) Put down 2nd finger in a button area (so inactive) 265a46c0ec8Sopenharmony_ci * 3) Put down 3th finger somewhere, gets reported as a fake finger, 266a46c0ec8Sopenharmony_ci * so gets same coordinates as 1st -> active 267a46c0ec8Sopenharmony_ci * 268a46c0ec8Sopenharmony_ci * We could avoid this by looking at all touches, be we really only 269a46c0ec8Sopenharmony_ci * want to look at real touches. 270a46c0ec8Sopenharmony_ci */ 271a46c0ec8Sopenharmony_ci return n; 272a46c0ec8Sopenharmony_ci} 273a46c0ec8Sopenharmony_ci 274a46c0ec8Sopenharmony_cistatic inline int 275a46c0ec8Sopenharmony_citp_gesture_same_directions(int dir1, int dir2) 276a46c0ec8Sopenharmony_ci{ 277a46c0ec8Sopenharmony_ci /* 278a46c0ec8Sopenharmony_ci * In some cases (semi-mt touchpads) we may seen one finger move 279a46c0ec8Sopenharmony_ci * e.g. N/NE and the other W/NW so we not only check for overlapping 280a46c0ec8Sopenharmony_ci * directions, but also for neighboring bits being set. 281a46c0ec8Sopenharmony_ci * The ((dira & 0x80) && (dirb & 0x01)) checks are to check for bit 0 282a46c0ec8Sopenharmony_ci * and 7 being set as they also represent neighboring directions. 283a46c0ec8Sopenharmony_ci */ 284a46c0ec8Sopenharmony_ci return ((dir1 | (dir1 >> 1)) & dir2) || 285a46c0ec8Sopenharmony_ci ((dir2 | (dir2 >> 1)) & dir1) || 286a46c0ec8Sopenharmony_ci ((dir1 & 0x80) && (dir2 & 0x01)) || 287a46c0ec8Sopenharmony_ci ((dir2 & 0x80) && (dir1 & 0x01)); 288a46c0ec8Sopenharmony_ci} 289a46c0ec8Sopenharmony_ci 290a46c0ec8Sopenharmony_cistatic struct phys_coords 291a46c0ec8Sopenharmony_citp_gesture_mm_moved(struct tp_dispatch *tp, struct tp_touch *t) 292a46c0ec8Sopenharmony_ci{ 293a46c0ec8Sopenharmony_ci struct device_coords delta; 294a46c0ec8Sopenharmony_ci 295a46c0ec8Sopenharmony_ci delta.x = abs(t->point.x - t->gesture.initial.x); 296a46c0ec8Sopenharmony_ci delta.y = abs(t->point.y - t->gesture.initial.y); 297a46c0ec8Sopenharmony_ci 298a46c0ec8Sopenharmony_ci return evdev_device_unit_delta_to_mm(tp->device, &delta); 299a46c0ec8Sopenharmony_ci} 300a46c0ec8Sopenharmony_ci 301a46c0ec8Sopenharmony_cistatic uint32_t 302a46c0ec8Sopenharmony_citp_gesture_get_direction(struct tp_dispatch *tp, struct tp_touch *touch) 303a46c0ec8Sopenharmony_ci{ 304a46c0ec8Sopenharmony_ci struct phys_coords mm; 305a46c0ec8Sopenharmony_ci struct device_float_coords delta; 306a46c0ec8Sopenharmony_ci 307a46c0ec8Sopenharmony_ci delta = device_delta(touch->point, touch->gesture.initial); 308a46c0ec8Sopenharmony_ci mm = tp_phys_delta(tp, delta); 309a46c0ec8Sopenharmony_ci 310a46c0ec8Sopenharmony_ci return phys_get_direction(mm); 311a46c0ec8Sopenharmony_ci} 312a46c0ec8Sopenharmony_ci 313a46c0ec8Sopenharmony_cistatic void 314a46c0ec8Sopenharmony_citp_gesture_get_pinch_info(struct tp_dispatch *tp, 315a46c0ec8Sopenharmony_ci double *distance, 316a46c0ec8Sopenharmony_ci double *angle, 317a46c0ec8Sopenharmony_ci struct device_float_coords *center) 318a46c0ec8Sopenharmony_ci{ 319a46c0ec8Sopenharmony_ci struct normalized_coords normalized; 320a46c0ec8Sopenharmony_ci struct device_float_coords delta; 321a46c0ec8Sopenharmony_ci struct tp_touch *first = tp->gesture.touches[0], 322a46c0ec8Sopenharmony_ci *second = tp->gesture.touches[1]; 323a46c0ec8Sopenharmony_ci 324a46c0ec8Sopenharmony_ci delta = device_delta(first->point, second->point); 325a46c0ec8Sopenharmony_ci normalized = tp_normalize_delta(tp, delta); 326a46c0ec8Sopenharmony_ci *distance = normalized_length(normalized); 327a46c0ec8Sopenharmony_ci *angle = atan2(normalized.y, normalized.x) * 180.0 / M_PI; 328a46c0ec8Sopenharmony_ci 329a46c0ec8Sopenharmony_ci *center = device_average(first->point, second->point); 330a46c0ec8Sopenharmony_ci} 331a46c0ec8Sopenharmony_ci 332a46c0ec8Sopenharmony_cistatic inline void 333a46c0ec8Sopenharmony_citp_gesture_init_pinch(struct tp_dispatch *tp) 334a46c0ec8Sopenharmony_ci{ 335a46c0ec8Sopenharmony_ci tp_gesture_get_pinch_info(tp, 336a46c0ec8Sopenharmony_ci &tp->gesture.initial_distance, 337a46c0ec8Sopenharmony_ci &tp->gesture.angle, 338a46c0ec8Sopenharmony_ci &tp->gesture.center); 339a46c0ec8Sopenharmony_ci tp->gesture.prev_scale = 1.0; 340a46c0ec8Sopenharmony_ci} 341a46c0ec8Sopenharmony_ci 342a46c0ec8Sopenharmony_cistatic void 343a46c0ec8Sopenharmony_citp_gesture_set_scroll_buildup(struct tp_dispatch *tp) 344a46c0ec8Sopenharmony_ci{ 345a46c0ec8Sopenharmony_ci struct device_float_coords d0, d1; 346a46c0ec8Sopenharmony_ci struct device_float_coords average; 347a46c0ec8Sopenharmony_ci struct tp_touch *first = tp->gesture.touches[0], 348a46c0ec8Sopenharmony_ci *second = tp->gesture.touches[1]; 349a46c0ec8Sopenharmony_ci 350a46c0ec8Sopenharmony_ci d0 = device_delta(first->point, first->gesture.initial); 351a46c0ec8Sopenharmony_ci d1 = device_delta(second->point, second->gesture.initial); 352a46c0ec8Sopenharmony_ci 353a46c0ec8Sopenharmony_ci average = device_float_average(d0, d1); 354a46c0ec8Sopenharmony_ci tp->device->scroll.buildup = tp_normalize_delta(tp, average); 355a46c0ec8Sopenharmony_ci} 356a46c0ec8Sopenharmony_ci 357a46c0ec8Sopenharmony_cistatic void 358a46c0ec8Sopenharmony_citp_gesture_apply_scroll_constraints(struct tp_dispatch *tp, 359a46c0ec8Sopenharmony_ci struct device_float_coords *raw, 360a46c0ec8Sopenharmony_ci struct normalized_coords *delta, 361a46c0ec8Sopenharmony_ci uint64_t time) 362a46c0ec8Sopenharmony_ci{ 363a46c0ec8Sopenharmony_ci uint64_t tdelta = 0; 364a46c0ec8Sopenharmony_ci struct phys_coords delta_mm, vector; 365a46c0ec8Sopenharmony_ci double vector_decay, vector_length, slope; 366a46c0ec8Sopenharmony_ci 367a46c0ec8Sopenharmony_ci const uint64_t ACTIVE_THRESHOLD = ms2us(100), 368a46c0ec8Sopenharmony_ci INACTIVE_THRESHOLD = ms2us(50), 369a46c0ec8Sopenharmony_ci EVENT_TIMEOUT = ms2us(100); 370a46c0ec8Sopenharmony_ci 371a46c0ec8Sopenharmony_ci /* Both axes active == true means free scrolling is enabled */ 372a46c0ec8Sopenharmony_ci if (tp->scroll.active.h && tp->scroll.active.v) 373a46c0ec8Sopenharmony_ci return; 374a46c0ec8Sopenharmony_ci 375a46c0ec8Sopenharmony_ci /* Determine time delta since last movement event */ 376a46c0ec8Sopenharmony_ci if (tp->scroll.time_prev != 0) 377a46c0ec8Sopenharmony_ci tdelta = time - tp->scroll.time_prev; 378a46c0ec8Sopenharmony_ci if (tdelta > EVENT_TIMEOUT) 379a46c0ec8Sopenharmony_ci tdelta = 0; 380a46c0ec8Sopenharmony_ci tp->scroll.time_prev = time; 381a46c0ec8Sopenharmony_ci 382a46c0ec8Sopenharmony_ci /* Delta since last movement event in mm */ 383a46c0ec8Sopenharmony_ci delta_mm = tp_phys_delta(tp, *raw); 384a46c0ec8Sopenharmony_ci 385a46c0ec8Sopenharmony_ci /* Old vector data "fades" over time. This is a two-part linear 386a46c0ec8Sopenharmony_ci * approximation of an exponential function - for example, for 387a46c0ec8Sopenharmony_ci * EVENT_TIMEOUT of 100, vector_decay = (0.97)^tdelta. This linear 388a46c0ec8Sopenharmony_ci * approximation allows easier tweaking of EVENT_TIMEOUT and is faster. 389a46c0ec8Sopenharmony_ci */ 390a46c0ec8Sopenharmony_ci if (tdelta > 0) { 391a46c0ec8Sopenharmony_ci double recent, later; 392a46c0ec8Sopenharmony_ci recent = ((EVENT_TIMEOUT / 2.0) - tdelta) / 393a46c0ec8Sopenharmony_ci (EVENT_TIMEOUT / 2.0); 394a46c0ec8Sopenharmony_ci later = (EVENT_TIMEOUT - tdelta) / 395a46c0ec8Sopenharmony_ci (EVENT_TIMEOUT * 2.0); 396a46c0ec8Sopenharmony_ci vector_decay = tdelta <= (0.33 * EVENT_TIMEOUT) ? 397a46c0ec8Sopenharmony_ci recent : later; 398a46c0ec8Sopenharmony_ci } else { 399a46c0ec8Sopenharmony_ci vector_decay = 0.0; 400a46c0ec8Sopenharmony_ci } 401a46c0ec8Sopenharmony_ci 402a46c0ec8Sopenharmony_ci /* Calculate windowed vector from delta + weighted historic data */ 403a46c0ec8Sopenharmony_ci vector.x = (tp->scroll.vector.x * vector_decay) + delta_mm.x; 404a46c0ec8Sopenharmony_ci vector.y = (tp->scroll.vector.y * vector_decay) + delta_mm.y; 405a46c0ec8Sopenharmony_ci vector_length = hypot(vector.x, vector.y); 406a46c0ec8Sopenharmony_ci tp->scroll.vector = vector; 407a46c0ec8Sopenharmony_ci 408a46c0ec8Sopenharmony_ci /* We care somewhat about distance and speed, but more about 409a46c0ec8Sopenharmony_ci * consistency of direction over time. Keep track of the time spent 410a46c0ec8Sopenharmony_ci * primarily along each axis. If one axis is active, time spent NOT 411a46c0ec8Sopenharmony_ci * moving much in the other axis is subtracted, allowing a switch of 412a46c0ec8Sopenharmony_ci * axes in a single scroll + ability to "break out" and go diagonal. 413a46c0ec8Sopenharmony_ci * 414a46c0ec8Sopenharmony_ci * Slope to degree conversions (infinity = 90°, 0 = 0°): 415a46c0ec8Sopenharmony_ci */ 416a46c0ec8Sopenharmony_ci const double DEGREE_75 = 3.73; 417a46c0ec8Sopenharmony_ci const double DEGREE_60 = 1.73; 418a46c0ec8Sopenharmony_ci const double DEGREE_30 = 0.57; 419a46c0ec8Sopenharmony_ci const double DEGREE_15 = 0.27; 420a46c0ec8Sopenharmony_ci slope = (vector.x != 0) ? fabs(vector.y / vector.x) : INFINITY; 421a46c0ec8Sopenharmony_ci 422a46c0ec8Sopenharmony_ci /* Ensure vector is big enough (in mm per EVENT_TIMEOUT) to be confident 423a46c0ec8Sopenharmony_ci * of direction. Larger = harder to enable diagonal/free scrolling. 424a46c0ec8Sopenharmony_ci */ 425a46c0ec8Sopenharmony_ci const double MIN_VECTOR = 0.15; 426a46c0ec8Sopenharmony_ci 427a46c0ec8Sopenharmony_ci if (slope >= DEGREE_30 && vector_length > MIN_VECTOR) { 428a46c0ec8Sopenharmony_ci tp->scroll.duration.v += tdelta; 429a46c0ec8Sopenharmony_ci if (tp->scroll.duration.v > ACTIVE_THRESHOLD) 430a46c0ec8Sopenharmony_ci tp->scroll.duration.v = ACTIVE_THRESHOLD; 431a46c0ec8Sopenharmony_ci if (slope >= DEGREE_75) { 432a46c0ec8Sopenharmony_ci if (tp->scroll.duration.h > tdelta) 433a46c0ec8Sopenharmony_ci tp->scroll.duration.h -= tdelta; 434a46c0ec8Sopenharmony_ci else 435a46c0ec8Sopenharmony_ci tp->scroll.duration.h = 0; 436a46c0ec8Sopenharmony_ci } 437a46c0ec8Sopenharmony_ci } 438a46c0ec8Sopenharmony_ci if (slope < DEGREE_60 && vector_length > MIN_VECTOR) { 439a46c0ec8Sopenharmony_ci tp->scroll.duration.h += tdelta; 440a46c0ec8Sopenharmony_ci if (tp->scroll.duration.h > ACTIVE_THRESHOLD) 441a46c0ec8Sopenharmony_ci tp->scroll.duration.h = ACTIVE_THRESHOLD; 442a46c0ec8Sopenharmony_ci if (slope < DEGREE_15) { 443a46c0ec8Sopenharmony_ci if (tp->scroll.duration.v > tdelta) 444a46c0ec8Sopenharmony_ci tp->scroll.duration.v -= tdelta; 445a46c0ec8Sopenharmony_ci else 446a46c0ec8Sopenharmony_ci tp->scroll.duration.v = 0; 447a46c0ec8Sopenharmony_ci } 448a46c0ec8Sopenharmony_ci } 449a46c0ec8Sopenharmony_ci 450a46c0ec8Sopenharmony_ci if (tp->scroll.duration.h == ACTIVE_THRESHOLD) { 451a46c0ec8Sopenharmony_ci tp->scroll.active.h = true; 452a46c0ec8Sopenharmony_ci if (tp->scroll.duration.v < INACTIVE_THRESHOLD) 453a46c0ec8Sopenharmony_ci tp->scroll.active.v = false; 454a46c0ec8Sopenharmony_ci } 455a46c0ec8Sopenharmony_ci if (tp->scroll.duration.v == ACTIVE_THRESHOLD) { 456a46c0ec8Sopenharmony_ci tp->scroll.active.v = true; 457a46c0ec8Sopenharmony_ci if (tp->scroll.duration.h < INACTIVE_THRESHOLD) 458a46c0ec8Sopenharmony_ci tp->scroll.active.h = false; 459a46c0ec8Sopenharmony_ci } 460a46c0ec8Sopenharmony_ci 461a46c0ec8Sopenharmony_ci /* If vector is big enough in a diagonal direction, always unlock 462a46c0ec8Sopenharmony_ci * both axes regardless of thresholds 463a46c0ec8Sopenharmony_ci */ 464a46c0ec8Sopenharmony_ci if (vector_length > 5.0 && slope < 1.73 && slope >= 0.57) { 465a46c0ec8Sopenharmony_ci tp->scroll.active.v = true; 466a46c0ec8Sopenharmony_ci tp->scroll.active.h = true; 467a46c0ec8Sopenharmony_ci } 468a46c0ec8Sopenharmony_ci 469a46c0ec8Sopenharmony_ci /* If only one axis is active, constrain motion accordingly. If both 470a46c0ec8Sopenharmony_ci * are set, we've detected deliberate diagonal movement; enable free 471a46c0ec8Sopenharmony_ci * scrolling for the life of the gesture. 472a46c0ec8Sopenharmony_ci */ 473a46c0ec8Sopenharmony_ci if (!tp->scroll.active.h && tp->scroll.active.v) 474a46c0ec8Sopenharmony_ci delta->x = 0.0; 475a46c0ec8Sopenharmony_ci if (tp->scroll.active.h && !tp->scroll.active.v) 476a46c0ec8Sopenharmony_ci delta->y = 0.0; 477a46c0ec8Sopenharmony_ci 478a46c0ec8Sopenharmony_ci /* If we haven't determined an axis, use the slope in the meantime */ 479a46c0ec8Sopenharmony_ci if (!tp->scroll.active.h && !tp->scroll.active.v) { 480a46c0ec8Sopenharmony_ci delta->x = (slope >= DEGREE_60) ? 0.0 : delta->x; 481a46c0ec8Sopenharmony_ci delta->y = (slope < DEGREE_30) ? 0.0 : delta->y; 482a46c0ec8Sopenharmony_ci } 483a46c0ec8Sopenharmony_ci} 484a46c0ec8Sopenharmony_ci 485a46c0ec8Sopenharmony_cistatic inline void 486a46c0ec8Sopenharmony_cilog_gesture_bug(struct tp_dispatch *tp, enum gesture_event event) 487a46c0ec8Sopenharmony_ci{ 488a46c0ec8Sopenharmony_ci evdev_log_bug_libinput(tp->device, 489a46c0ec8Sopenharmony_ci "invalid gesture event %s in state %s\n", 490a46c0ec8Sopenharmony_ci gesture_event_to_str(event), 491a46c0ec8Sopenharmony_ci gesture_state_to_str(tp->gesture.state)); 492a46c0ec8Sopenharmony_ci} 493a46c0ec8Sopenharmony_ci 494a46c0ec8Sopenharmony_cistatic bool 495a46c0ec8Sopenharmony_citp_gesture_is_quick_hold(struct tp_dispatch *tp) 496a46c0ec8Sopenharmony_ci{ 497a46c0ec8Sopenharmony_ci /* When 1 or 2 fingers are used to hold, always use a "quick" hold to 498a46c0ec8Sopenharmony_ci * make the hold to stop kinetic scrolling user interaction feel more 499a46c0ec8Sopenharmony_ci * natural. 500a46c0ec8Sopenharmony_ci */ 501a46c0ec8Sopenharmony_ci return (tp->gesture.finger_count == 1) || 502a46c0ec8Sopenharmony_ci (tp->gesture.finger_count == 2); 503a46c0ec8Sopenharmony_ci} 504a46c0ec8Sopenharmony_ci 505a46c0ec8Sopenharmony_cistatic bool 506a46c0ec8Sopenharmony_citp_gesture_use_hold_timer(struct tp_dispatch *tp) 507a46c0ec8Sopenharmony_ci{ 508a46c0ec8Sopenharmony_ci /* When tap is not enabled, always use the timer */ 509a46c0ec8Sopenharmony_ci if (!tp->tap.enabled) 510a46c0ec8Sopenharmony_ci return true; 511a46c0ec8Sopenharmony_ci 512a46c0ec8Sopenharmony_ci /* Always use the timer if it is a quick hold */ 513a46c0ec8Sopenharmony_ci if (tp_gesture_is_quick_hold(tp)) 514a46c0ec8Sopenharmony_ci return true; 515a46c0ec8Sopenharmony_ci 516a46c0ec8Sopenharmony_ci /* If the number of fingers on the touchpad exceeds the number of 517a46c0ec8Sopenharmony_ci * allowed fingers to tap, use the timer. 518a46c0ec8Sopenharmony_ci */ 519a46c0ec8Sopenharmony_ci if (tp->gesture.finger_count > 3) 520a46c0ec8Sopenharmony_ci return true; 521a46c0ec8Sopenharmony_ci 522a46c0ec8Sopenharmony_ci /* If the tap state machine is already in a hold status, for example 523a46c0ec8Sopenharmony_ci * when holding with 3 fingers and then holding with 2, use the timer. 524a46c0ec8Sopenharmony_ci */ 525a46c0ec8Sopenharmony_ci if (tp->tap.state == TAP_STATE_HOLD || 526a46c0ec8Sopenharmony_ci tp->tap.state == TAP_STATE_TOUCH_2_HOLD || 527a46c0ec8Sopenharmony_ci tp->tap.state == TAP_STATE_TOUCH_3_HOLD) 528a46c0ec8Sopenharmony_ci return true; 529a46c0ec8Sopenharmony_ci 530a46c0ec8Sopenharmony_ci /* If the tap state machine is in dead status, use the timer. This 531a46c0ec8Sopenharmony_ci * happens when the user holds after cancelling a gesture/scroll. 532a46c0ec8Sopenharmony_ci */ 533a46c0ec8Sopenharmony_ci if (tp->tap.state == TAP_STATE_DEAD) 534a46c0ec8Sopenharmony_ci return true; 535a46c0ec8Sopenharmony_ci 536a46c0ec8Sopenharmony_ci /* Otherwise, sync the hold notification with the tap state machine */ 537a46c0ec8Sopenharmony_ci return false; 538a46c0ec8Sopenharmony_ci} 539a46c0ec8Sopenharmony_ci 540a46c0ec8Sopenharmony_cistatic void 541a46c0ec8Sopenharmony_citp_gesture_set_hold_timer(struct tp_dispatch *tp, uint64_t time) 542a46c0ec8Sopenharmony_ci{ 543a46c0ec8Sopenharmony_ci uint64_t timeout; 544a46c0ec8Sopenharmony_ci 545a46c0ec8Sopenharmony_ci if (!tp->gesture.hold_enabled) 546a46c0ec8Sopenharmony_ci return; 547a46c0ec8Sopenharmony_ci 548a46c0ec8Sopenharmony_ci if (tp_gesture_use_hold_timer(tp)) { 549a46c0ec8Sopenharmony_ci timeout = tp_gesture_is_quick_hold(tp) ? 550a46c0ec8Sopenharmony_ci QUICK_GESTURE_HOLD_TIMEOUT : 551a46c0ec8Sopenharmony_ci DEFAULT_GESTURE_HOLD_TIMEOUT; 552a46c0ec8Sopenharmony_ci 553a46c0ec8Sopenharmony_ci libinput_timer_set(&tp->gesture.hold_timer, time + timeout); 554a46c0ec8Sopenharmony_ci } 555a46c0ec8Sopenharmony_ci} 556a46c0ec8Sopenharmony_ci 557a46c0ec8Sopenharmony_cistatic void 558a46c0ec8Sopenharmony_citp_gesture_handle_event_on_state_none(struct tp_dispatch *tp, 559a46c0ec8Sopenharmony_ci enum gesture_event event, 560a46c0ec8Sopenharmony_ci uint64_t time) 561a46c0ec8Sopenharmony_ci{ 562a46c0ec8Sopenharmony_ci switch(event) { 563a46c0ec8Sopenharmony_ci case GESTURE_EVENT_RESET: 564a46c0ec8Sopenharmony_ci libinput_timer_cancel(&tp->gesture.hold_timer); 565a46c0ec8Sopenharmony_ci break; 566a46c0ec8Sopenharmony_ci case GESTURE_EVENT_FINGER_DETECTED: 567a46c0ec8Sopenharmony_ci tp_gesture_set_hold_timer(tp, time); 568a46c0ec8Sopenharmony_ci tp->gesture.state = GESTURE_STATE_UNKNOWN; 569a46c0ec8Sopenharmony_ci break; 570a46c0ec8Sopenharmony_ci case GESTURE_EVENT_HOLD_TIMEOUT: 571a46c0ec8Sopenharmony_ci break; 572a46c0ec8Sopenharmony_ci case GESTURE_EVENT_POINTER_MOTION: 573a46c0ec8Sopenharmony_ci tp->gesture.state = GESTURE_STATE_POINTER_MOTION; 574a46c0ec8Sopenharmony_ci break; 575a46c0ec8Sopenharmony_ci case GESTURE_EVENT_SCROLL: 576a46c0ec8Sopenharmony_ci tp->gesture.state = GESTURE_STATE_SCROLL; 577a46c0ec8Sopenharmony_ci break; 578a46c0ec8Sopenharmony_ci case GESTURE_EVENT_HOLD_AND_MOTION: 579a46c0ec8Sopenharmony_ci case GESTURE_EVENT_SWIPE: 580a46c0ec8Sopenharmony_ci case GESTURE_EVENT_PINCH: 581a46c0ec8Sopenharmony_ci log_gesture_bug(tp, event); 582a46c0ec8Sopenharmony_ci break; 583a46c0ec8Sopenharmony_ci } 584a46c0ec8Sopenharmony_ci} 585a46c0ec8Sopenharmony_ci 586a46c0ec8Sopenharmony_cistatic void 587a46c0ec8Sopenharmony_citp_gesture_handle_event_on_state_unknown(struct tp_dispatch *tp, 588a46c0ec8Sopenharmony_ci enum gesture_event event, 589a46c0ec8Sopenharmony_ci uint64_t time) 590a46c0ec8Sopenharmony_ci{ 591a46c0ec8Sopenharmony_ci switch(event) { 592a46c0ec8Sopenharmony_ci case GESTURE_EVENT_RESET: 593a46c0ec8Sopenharmony_ci libinput_timer_cancel(&tp->gesture.hold_timer); 594a46c0ec8Sopenharmony_ci tp->gesture.state = GESTURE_STATE_NONE; 595a46c0ec8Sopenharmony_ci break; 596a46c0ec8Sopenharmony_ci case GESTURE_EVENT_HOLD_TIMEOUT: 597a46c0ec8Sopenharmony_ci tp->gesture.state = GESTURE_STATE_HOLD; 598a46c0ec8Sopenharmony_ci tp_gesture_start(tp, time); 599a46c0ec8Sopenharmony_ci break; 600a46c0ec8Sopenharmony_ci case GESTURE_EVENT_POINTER_MOTION: 601a46c0ec8Sopenharmony_ci /* Don't cancel the hold timer. This pointer motion can end up 602a46c0ec8Sopenharmony_ci * being recognised as hold and motion. */ 603a46c0ec8Sopenharmony_ci tp->gesture.state = GESTURE_STATE_POINTER_MOTION; 604a46c0ec8Sopenharmony_ci break; 605a46c0ec8Sopenharmony_ci case GESTURE_EVENT_SCROLL: 606a46c0ec8Sopenharmony_ci libinput_timer_cancel(&tp->gesture.hold_timer); 607a46c0ec8Sopenharmony_ci tp_gesture_set_scroll_buildup(tp); 608a46c0ec8Sopenharmony_ci tp->gesture.state = GESTURE_STATE_SCROLL; 609a46c0ec8Sopenharmony_ci break; 610a46c0ec8Sopenharmony_ci case GESTURE_EVENT_SWIPE: 611a46c0ec8Sopenharmony_ci libinput_timer_cancel(&tp->gesture.hold_timer); 612a46c0ec8Sopenharmony_ci tp->gesture.state = GESTURE_STATE_SWIPE; 613a46c0ec8Sopenharmony_ci break; 614a46c0ec8Sopenharmony_ci case GESTURE_EVENT_PINCH: 615a46c0ec8Sopenharmony_ci libinput_timer_cancel(&tp->gesture.hold_timer); 616a46c0ec8Sopenharmony_ci tp_gesture_init_pinch(tp); 617a46c0ec8Sopenharmony_ci tp->gesture.state = GESTURE_STATE_PINCH; 618a46c0ec8Sopenharmony_ci break; 619a46c0ec8Sopenharmony_ci case GESTURE_EVENT_HOLD_AND_MOTION: 620a46c0ec8Sopenharmony_ci case GESTURE_EVENT_FINGER_DETECTED: 621a46c0ec8Sopenharmony_ci log_gesture_bug(tp, event); 622a46c0ec8Sopenharmony_ci break; 623a46c0ec8Sopenharmony_ci } 624a46c0ec8Sopenharmony_ci} 625a46c0ec8Sopenharmony_ci 626a46c0ec8Sopenharmony_cistatic void 627a46c0ec8Sopenharmony_citp_gesture_handle_event_on_state_hold(struct tp_dispatch *tp, 628a46c0ec8Sopenharmony_ci enum gesture_event event, 629a46c0ec8Sopenharmony_ci uint64_t time) 630a46c0ec8Sopenharmony_ci{ 631a46c0ec8Sopenharmony_ci switch(event) { 632a46c0ec8Sopenharmony_ci case GESTURE_EVENT_RESET: 633a46c0ec8Sopenharmony_ci libinput_timer_cancel(&tp->gesture.hold_timer); 634a46c0ec8Sopenharmony_ci tp->gesture.state = GESTURE_STATE_NONE; 635a46c0ec8Sopenharmony_ci break; 636a46c0ec8Sopenharmony_ci case GESTURE_EVENT_HOLD_AND_MOTION: 637a46c0ec8Sopenharmony_ci tp->gesture.state = GESTURE_STATE_HOLD_AND_MOTION; 638a46c0ec8Sopenharmony_ci break; 639a46c0ec8Sopenharmony_ci case GESTURE_EVENT_POINTER_MOTION: 640a46c0ec8Sopenharmony_ci tp_gesture_cancel(tp, time); 641a46c0ec8Sopenharmony_ci tp->gesture.state = GESTURE_STATE_POINTER_MOTION; 642a46c0ec8Sopenharmony_ci break; 643a46c0ec8Sopenharmony_ci case GESTURE_EVENT_SCROLL: 644a46c0ec8Sopenharmony_ci tp_gesture_set_scroll_buildup(tp); 645a46c0ec8Sopenharmony_ci tp_gesture_cancel(tp, time); 646a46c0ec8Sopenharmony_ci tp->gesture.state = GESTURE_STATE_SCROLL; 647a46c0ec8Sopenharmony_ci break; 648a46c0ec8Sopenharmony_ci case GESTURE_EVENT_SWIPE: 649a46c0ec8Sopenharmony_ci tp_gesture_cancel(tp, time); 650a46c0ec8Sopenharmony_ci tp->gesture.state = GESTURE_STATE_SWIPE; 651a46c0ec8Sopenharmony_ci break; 652a46c0ec8Sopenharmony_ci case GESTURE_EVENT_PINCH: 653a46c0ec8Sopenharmony_ci tp_gesture_init_pinch(tp); 654a46c0ec8Sopenharmony_ci tp_gesture_cancel(tp, time); 655a46c0ec8Sopenharmony_ci tp->gesture.state = GESTURE_STATE_PINCH; 656a46c0ec8Sopenharmony_ci break; 657a46c0ec8Sopenharmony_ci case GESTURE_EVENT_HOLD_TIMEOUT: 658a46c0ec8Sopenharmony_ci case GESTURE_EVENT_FINGER_DETECTED: 659a46c0ec8Sopenharmony_ci log_gesture_bug(tp, event); 660a46c0ec8Sopenharmony_ci break; 661a46c0ec8Sopenharmony_ci } 662a46c0ec8Sopenharmony_ci} 663a46c0ec8Sopenharmony_ci 664a46c0ec8Sopenharmony_cistatic void 665a46c0ec8Sopenharmony_citp_gesture_handle_event_on_state_hold_and_motion(struct tp_dispatch *tp, 666a46c0ec8Sopenharmony_ci enum gesture_event event, 667a46c0ec8Sopenharmony_ci uint64_t time) 668a46c0ec8Sopenharmony_ci{ 669a46c0ec8Sopenharmony_ci switch(event) { 670a46c0ec8Sopenharmony_ci case GESTURE_EVENT_RESET: 671a46c0ec8Sopenharmony_ci libinput_timer_cancel(&tp->gesture.hold_timer); 672a46c0ec8Sopenharmony_ci tp->gesture.state = GESTURE_STATE_NONE; 673a46c0ec8Sopenharmony_ci break; 674a46c0ec8Sopenharmony_ci case GESTURE_EVENT_POINTER_MOTION: 675a46c0ec8Sopenharmony_ci tp_gesture_cancel(tp, time); 676a46c0ec8Sopenharmony_ci tp->gesture.state = GESTURE_STATE_POINTER_MOTION; 677a46c0ec8Sopenharmony_ci break; 678a46c0ec8Sopenharmony_ci case GESTURE_EVENT_HOLD_AND_MOTION: 679a46c0ec8Sopenharmony_ci case GESTURE_EVENT_FINGER_DETECTED: 680a46c0ec8Sopenharmony_ci case GESTURE_EVENT_HOLD_TIMEOUT: 681a46c0ec8Sopenharmony_ci case GESTURE_EVENT_SCROLL: 682a46c0ec8Sopenharmony_ci case GESTURE_EVENT_SWIPE: 683a46c0ec8Sopenharmony_ci case GESTURE_EVENT_PINCH: 684a46c0ec8Sopenharmony_ci log_gesture_bug(tp, event); 685a46c0ec8Sopenharmony_ci break; 686a46c0ec8Sopenharmony_ci } 687a46c0ec8Sopenharmony_ci} 688a46c0ec8Sopenharmony_ci 689a46c0ec8Sopenharmony_cistatic void 690a46c0ec8Sopenharmony_citp_gesture_handle_event_on_state_pointer_motion(struct tp_dispatch *tp, 691a46c0ec8Sopenharmony_ci enum gesture_event event, 692a46c0ec8Sopenharmony_ci uint64_t time) 693a46c0ec8Sopenharmony_ci{ 694a46c0ec8Sopenharmony_ci struct tp_touch *first; 695a46c0ec8Sopenharmony_ci struct phys_coords first_moved; 696a46c0ec8Sopenharmony_ci double first_mm; 697a46c0ec8Sopenharmony_ci 698a46c0ec8Sopenharmony_ci switch(event) { 699a46c0ec8Sopenharmony_ci case GESTURE_EVENT_RESET: 700a46c0ec8Sopenharmony_ci libinput_timer_cancel(&tp->gesture.hold_timer); 701a46c0ec8Sopenharmony_ci tp->gesture.state = GESTURE_STATE_NONE; 702a46c0ec8Sopenharmony_ci break; 703a46c0ec8Sopenharmony_ci case GESTURE_EVENT_HOLD_TIMEOUT: 704a46c0ec8Sopenharmony_ci if (tp->gesture.finger_count != 1) 705a46c0ec8Sopenharmony_ci break; 706a46c0ec8Sopenharmony_ci 707a46c0ec8Sopenharmony_ci first = tp->gesture.touches[0]; 708a46c0ec8Sopenharmony_ci first_moved = tp_gesture_mm_moved(tp, first); 709a46c0ec8Sopenharmony_ci first_mm = hypot(first_moved.x, first_moved.y); 710a46c0ec8Sopenharmony_ci 711a46c0ec8Sopenharmony_ci if (first_mm < HOLD_AND_MOTION_THRESHOLD) { 712a46c0ec8Sopenharmony_ci tp->gesture.state = GESTURE_STATE_HOLD_AND_MOTION; 713a46c0ec8Sopenharmony_ci tp_gesture_start(tp, time); 714a46c0ec8Sopenharmony_ci } 715a46c0ec8Sopenharmony_ci break; 716a46c0ec8Sopenharmony_ci case GESTURE_EVENT_HOLD_AND_MOTION: 717a46c0ec8Sopenharmony_ci case GESTURE_EVENT_FINGER_DETECTED: 718a46c0ec8Sopenharmony_ci case GESTURE_EVENT_POINTER_MOTION: 719a46c0ec8Sopenharmony_ci case GESTURE_EVENT_SCROLL: 720a46c0ec8Sopenharmony_ci case GESTURE_EVENT_SWIPE: 721a46c0ec8Sopenharmony_ci case GESTURE_EVENT_PINCH: 722a46c0ec8Sopenharmony_ci log_gesture_bug(tp, event); 723a46c0ec8Sopenharmony_ci break; 724a46c0ec8Sopenharmony_ci } 725a46c0ec8Sopenharmony_ci} 726a46c0ec8Sopenharmony_ci 727a46c0ec8Sopenharmony_cistatic void 728a46c0ec8Sopenharmony_citp_gesture_handle_event_on_state_scroll(struct tp_dispatch *tp, 729a46c0ec8Sopenharmony_ci enum gesture_event event, 730a46c0ec8Sopenharmony_ci uint64_t time) 731a46c0ec8Sopenharmony_ci{ 732a46c0ec8Sopenharmony_ci switch(event) { 733a46c0ec8Sopenharmony_ci case GESTURE_EVENT_RESET: 734a46c0ec8Sopenharmony_ci libinput_timer_cancel(&tp->gesture.hold_timer); 735a46c0ec8Sopenharmony_ci tp->gesture.state = GESTURE_STATE_NONE; 736a46c0ec8Sopenharmony_ci break; 737a46c0ec8Sopenharmony_ci case GESTURE_EVENT_PINCH: 738a46c0ec8Sopenharmony_ci tp_gesture_init_pinch(tp); 739a46c0ec8Sopenharmony_ci tp_gesture_cancel(tp, time); 740a46c0ec8Sopenharmony_ci tp->gesture.state = GESTURE_STATE_PINCH; 741a46c0ec8Sopenharmony_ci break; 742a46c0ec8Sopenharmony_ci case GESTURE_EVENT_HOLD_AND_MOTION: 743a46c0ec8Sopenharmony_ci case GESTURE_EVENT_FINGER_DETECTED: 744a46c0ec8Sopenharmony_ci case GESTURE_EVENT_HOLD_TIMEOUT: 745a46c0ec8Sopenharmony_ci case GESTURE_EVENT_POINTER_MOTION: 746a46c0ec8Sopenharmony_ci case GESTURE_EVENT_SCROLL: 747a46c0ec8Sopenharmony_ci case GESTURE_EVENT_SWIPE: 748a46c0ec8Sopenharmony_ci log_gesture_bug(tp, event); 749a46c0ec8Sopenharmony_ci break; 750a46c0ec8Sopenharmony_ci } 751a46c0ec8Sopenharmony_ci} 752a46c0ec8Sopenharmony_ci 753a46c0ec8Sopenharmony_cistatic void 754a46c0ec8Sopenharmony_citp_gesture_handle_event_on_state_pinch(struct tp_dispatch *tp, 755a46c0ec8Sopenharmony_ci enum gesture_event event, 756a46c0ec8Sopenharmony_ci uint64_t time) 757a46c0ec8Sopenharmony_ci{ 758a46c0ec8Sopenharmony_ci switch(event) { 759a46c0ec8Sopenharmony_ci case GESTURE_EVENT_RESET: 760a46c0ec8Sopenharmony_ci libinput_timer_cancel(&tp->gesture.hold_timer); 761a46c0ec8Sopenharmony_ci tp->gesture.state = GESTURE_STATE_NONE; 762a46c0ec8Sopenharmony_ci break; 763a46c0ec8Sopenharmony_ci case GESTURE_EVENT_HOLD_AND_MOTION: 764a46c0ec8Sopenharmony_ci case GESTURE_EVENT_FINGER_DETECTED: 765a46c0ec8Sopenharmony_ci case GESTURE_EVENT_HOLD_TIMEOUT: 766a46c0ec8Sopenharmony_ci case GESTURE_EVENT_POINTER_MOTION: 767a46c0ec8Sopenharmony_ci case GESTURE_EVENT_SCROLL: 768a46c0ec8Sopenharmony_ci case GESTURE_EVENT_SWIPE: 769a46c0ec8Sopenharmony_ci case GESTURE_EVENT_PINCH: 770a46c0ec8Sopenharmony_ci log_gesture_bug(tp, event); 771a46c0ec8Sopenharmony_ci break; 772a46c0ec8Sopenharmony_ci } 773a46c0ec8Sopenharmony_ci} 774a46c0ec8Sopenharmony_ci 775a46c0ec8Sopenharmony_cistatic void 776a46c0ec8Sopenharmony_citp_gesture_handle_event_on_state_swipe(struct tp_dispatch *tp, 777a46c0ec8Sopenharmony_ci enum gesture_event event, 778a46c0ec8Sopenharmony_ci uint64_t time) 779a46c0ec8Sopenharmony_ci{ 780a46c0ec8Sopenharmony_ci switch(event) { 781a46c0ec8Sopenharmony_ci case GESTURE_EVENT_RESET: 782a46c0ec8Sopenharmony_ci libinput_timer_cancel(&tp->gesture.hold_timer); 783a46c0ec8Sopenharmony_ci tp->gesture.state = GESTURE_STATE_NONE; 784a46c0ec8Sopenharmony_ci break; 785a46c0ec8Sopenharmony_ci case GESTURE_EVENT_HOLD_AND_MOTION: 786a46c0ec8Sopenharmony_ci case GESTURE_EVENT_FINGER_DETECTED: 787a46c0ec8Sopenharmony_ci case GESTURE_EVENT_HOLD_TIMEOUT: 788a46c0ec8Sopenharmony_ci case GESTURE_EVENT_POINTER_MOTION: 789a46c0ec8Sopenharmony_ci case GESTURE_EVENT_SCROLL: 790a46c0ec8Sopenharmony_ci case GESTURE_EVENT_SWIPE: 791a46c0ec8Sopenharmony_ci case GESTURE_EVENT_PINCH: 792a46c0ec8Sopenharmony_ci log_gesture_bug(tp, event); 793a46c0ec8Sopenharmony_ci break; 794a46c0ec8Sopenharmony_ci } 795a46c0ec8Sopenharmony_ci} 796a46c0ec8Sopenharmony_ci 797a46c0ec8Sopenharmony_cistatic void 798a46c0ec8Sopenharmony_citp_gesture_handle_event(struct tp_dispatch *tp, 799a46c0ec8Sopenharmony_ci enum gesture_event event, 800a46c0ec8Sopenharmony_ci uint64_t time) 801a46c0ec8Sopenharmony_ci{ 802a46c0ec8Sopenharmony_ci enum tp_gesture_state oldstate; 803a46c0ec8Sopenharmony_ci 804a46c0ec8Sopenharmony_ci oldstate = tp->gesture.state; 805a46c0ec8Sopenharmony_ci 806a46c0ec8Sopenharmony_ci switch(tp->gesture.state) { 807a46c0ec8Sopenharmony_ci case GESTURE_STATE_NONE: 808a46c0ec8Sopenharmony_ci tp_gesture_handle_event_on_state_none(tp, event, time); 809a46c0ec8Sopenharmony_ci break; 810a46c0ec8Sopenharmony_ci case GESTURE_STATE_UNKNOWN: 811a46c0ec8Sopenharmony_ci tp_gesture_handle_event_on_state_unknown(tp, event, time); 812a46c0ec8Sopenharmony_ci break; 813a46c0ec8Sopenharmony_ci case GESTURE_STATE_HOLD: 814a46c0ec8Sopenharmony_ci tp_gesture_handle_event_on_state_hold(tp, event, time); 815a46c0ec8Sopenharmony_ci break; 816a46c0ec8Sopenharmony_ci case GESTURE_STATE_HOLD_AND_MOTION: 817a46c0ec8Sopenharmony_ci tp_gesture_handle_event_on_state_hold_and_motion(tp, event, time); 818a46c0ec8Sopenharmony_ci break; 819a46c0ec8Sopenharmony_ci case GESTURE_STATE_POINTER_MOTION: 820a46c0ec8Sopenharmony_ci tp_gesture_handle_event_on_state_pointer_motion(tp, event, time); 821a46c0ec8Sopenharmony_ci break; 822a46c0ec8Sopenharmony_ci case GESTURE_STATE_SCROLL: 823a46c0ec8Sopenharmony_ci tp_gesture_handle_event_on_state_scroll(tp, event, time); 824a46c0ec8Sopenharmony_ci break; 825a46c0ec8Sopenharmony_ci case GESTURE_STATE_PINCH: 826a46c0ec8Sopenharmony_ci tp_gesture_handle_event_on_state_pinch(tp, event, time); 827a46c0ec8Sopenharmony_ci break; 828a46c0ec8Sopenharmony_ci case GESTURE_STATE_SWIPE: 829a46c0ec8Sopenharmony_ci tp_gesture_handle_event_on_state_swipe(tp, event, time); 830a46c0ec8Sopenharmony_ci break; 831a46c0ec8Sopenharmony_ci } 832a46c0ec8Sopenharmony_ci 833a46c0ec8Sopenharmony_ci if (oldstate != tp->gesture.state) { 834a46c0ec8Sopenharmony_ci evdev_log_debug(tp->device, 835a46c0ec8Sopenharmony_ci "gesture state %s → %s → %s\n", 836a46c0ec8Sopenharmony_ci gesture_state_to_str(oldstate), 837a46c0ec8Sopenharmony_ci gesture_event_to_str(event), 838a46c0ec8Sopenharmony_ci gesture_state_to_str(tp->gesture.state)); 839a46c0ec8Sopenharmony_ci } 840a46c0ec8Sopenharmony_ci} 841a46c0ec8Sopenharmony_ci 842a46c0ec8Sopenharmony_cistatic void 843a46c0ec8Sopenharmony_citp_gesture_hold_timeout(uint64_t now, void *data) 844a46c0ec8Sopenharmony_ci{ 845a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = data; 846a46c0ec8Sopenharmony_ci 847a46c0ec8Sopenharmony_ci if (tp_tap_dragging_or_double_tapping(tp) || tp_tap_dragging(tp)) 848a46c0ec8Sopenharmony_ci return; 849a46c0ec8Sopenharmony_ci 850a46c0ec8Sopenharmony_ci tp_gesture_handle_event(tp, GESTURE_EVENT_HOLD_TIMEOUT, now); 851a46c0ec8Sopenharmony_ci} 852a46c0ec8Sopenharmony_ci 853a46c0ec8Sopenharmony_civoid 854a46c0ec8Sopenharmony_citp_gesture_tap_timeout(struct tp_dispatch *tp, uint64_t time) 855a46c0ec8Sopenharmony_ci{ 856a46c0ec8Sopenharmony_ci if (!tp->gesture.hold_enabled) 857a46c0ec8Sopenharmony_ci return; 858a46c0ec8Sopenharmony_ci 859a46c0ec8Sopenharmony_ci if (!tp_gesture_is_quick_hold(tp)) 860a46c0ec8Sopenharmony_ci tp_gesture_handle_event(tp, GESTURE_EVENT_HOLD_TIMEOUT, time); 861a46c0ec8Sopenharmony_ci} 862a46c0ec8Sopenharmony_ci 863a46c0ec8Sopenharmony_cistatic void 864a46c0ec8Sopenharmony_citp_gesture_detect_motion_gestures(struct tp_dispatch *tp, uint64_t time) 865a46c0ec8Sopenharmony_ci{ 866a46c0ec8Sopenharmony_ci struct tp_touch *first = tp->gesture.touches[0], 867a46c0ec8Sopenharmony_ci *second = tp->gesture.touches[1], 868a46c0ec8Sopenharmony_ci *thumb; 869a46c0ec8Sopenharmony_ci uint32_t dir1, dir2; 870a46c0ec8Sopenharmony_ci struct device_coords delta; 871a46c0ec8Sopenharmony_ci struct phys_coords first_moved, second_moved, distance_mm; 872a46c0ec8Sopenharmony_ci double first_mm, second_mm; /* movement since gesture start in mm */ 873a46c0ec8Sopenharmony_ci double thumb_mm, finger_mm; 874a46c0ec8Sopenharmony_ci double min_move = 1.5; /* min movement threshold in mm - count this touch */ 875a46c0ec8Sopenharmony_ci double max_move = 4.0; /* max movement threshold in mm - ignore other touch */ 876a46c0ec8Sopenharmony_ci bool is_hold_and_motion; 877a46c0ec8Sopenharmony_ci 878a46c0ec8Sopenharmony_ci first_moved = tp_gesture_mm_moved(tp, first); 879a46c0ec8Sopenharmony_ci first_mm = hypot(first_moved.x, first_moved.y); 880a46c0ec8Sopenharmony_ci 881a46c0ec8Sopenharmony_ci if (tp->gesture.finger_count == 1) { 882a46c0ec8Sopenharmony_ci if (!tp_has_pending_pointer_motion(tp, time)) 883a46c0ec8Sopenharmony_ci return; 884a46c0ec8Sopenharmony_ci 885a46c0ec8Sopenharmony_ci is_hold_and_motion = (first_mm < HOLD_AND_MOTION_THRESHOLD); 886a46c0ec8Sopenharmony_ci 887a46c0ec8Sopenharmony_ci if (tp->gesture.state == GESTURE_STATE_HOLD && 888a46c0ec8Sopenharmony_ci is_hold_and_motion) { 889a46c0ec8Sopenharmony_ci tp_gesture_handle_event(tp, 890a46c0ec8Sopenharmony_ci GESTURE_EVENT_HOLD_AND_MOTION, 891a46c0ec8Sopenharmony_ci time); 892a46c0ec8Sopenharmony_ci return; 893a46c0ec8Sopenharmony_ci } 894a46c0ec8Sopenharmony_ci 895a46c0ec8Sopenharmony_ci if (tp->gesture.state == GESTURE_STATE_HOLD_AND_MOTION && 896a46c0ec8Sopenharmony_ci is_hold_and_motion) 897a46c0ec8Sopenharmony_ci return; 898a46c0ec8Sopenharmony_ci 899a46c0ec8Sopenharmony_ci tp_gesture_handle_event(tp, 900a46c0ec8Sopenharmony_ci GESTURE_EVENT_POINTER_MOTION, 901a46c0ec8Sopenharmony_ci time); 902a46c0ec8Sopenharmony_ci return; 903a46c0ec8Sopenharmony_ci } 904a46c0ec8Sopenharmony_ci 905a46c0ec8Sopenharmony_ci /* If we have more fingers than slots, we don't know where the 906a46c0ec8Sopenharmony_ci * fingers are. Default to swipe */ 907a46c0ec8Sopenharmony_ci if (tp->gesture.enabled && tp->gesture.finger_count > 2 && 908a46c0ec8Sopenharmony_ci tp->gesture.finger_count > tp->num_slots) { 909a46c0ec8Sopenharmony_ci tp_gesture_handle_event(tp, GESTURE_EVENT_SWIPE, time); 910a46c0ec8Sopenharmony_ci return; 911a46c0ec8Sopenharmony_ci } 912a46c0ec8Sopenharmony_ci 913a46c0ec8Sopenharmony_ci /* Need more margin for error when there are more fingers */ 914a46c0ec8Sopenharmony_ci max_move += 2.0 * (tp->gesture.finger_count - 2); 915a46c0ec8Sopenharmony_ci min_move += 0.5 * (tp->gesture.finger_count - 2); 916a46c0ec8Sopenharmony_ci 917a46c0ec8Sopenharmony_ci second_moved = tp_gesture_mm_moved(tp, second); 918a46c0ec8Sopenharmony_ci second_mm = hypot(second_moved.x, second_moved.y); 919a46c0ec8Sopenharmony_ci 920a46c0ec8Sopenharmony_ci delta.x = abs(first->point.x - second->point.x); 921a46c0ec8Sopenharmony_ci delta.y = abs(first->point.y - second->point.y); 922a46c0ec8Sopenharmony_ci distance_mm = evdev_device_unit_delta_to_mm(tp->device, &delta); 923a46c0ec8Sopenharmony_ci 924a46c0ec8Sopenharmony_ci /* If both touches moved less than a mm, we cannot decide yet */ 925a46c0ec8Sopenharmony_ci if (first_mm < 1 && second_mm < 1) 926a46c0ec8Sopenharmony_ci return; 927a46c0ec8Sopenharmony_ci 928a46c0ec8Sopenharmony_ci /* Pick the thumb as the lowest point on the touchpad */ 929a46c0ec8Sopenharmony_ci if (first->point.y > second->point.y) { 930a46c0ec8Sopenharmony_ci thumb = first; 931a46c0ec8Sopenharmony_ci thumb_mm = first_mm; 932a46c0ec8Sopenharmony_ci finger_mm = second_mm; 933a46c0ec8Sopenharmony_ci } else { 934a46c0ec8Sopenharmony_ci thumb = second; 935a46c0ec8Sopenharmony_ci thumb_mm = second_mm; 936a46c0ec8Sopenharmony_ci finger_mm = first_mm; 937a46c0ec8Sopenharmony_ci } 938a46c0ec8Sopenharmony_ci 939a46c0ec8Sopenharmony_ci /* If both touches are within 7mm vertically and 40mm horizontally 940a46c0ec8Sopenharmony_ci * past the timeout, assume scroll/swipe */ 941a46c0ec8Sopenharmony_ci if ((!tp->gesture.enabled || 942a46c0ec8Sopenharmony_ci (distance_mm.x < 40.0 && distance_mm.y < 7.0)) && 943a46c0ec8Sopenharmony_ci time > (tp->gesture.initial_time + DEFAULT_GESTURE_SWIPE_TIMEOUT)) { 944a46c0ec8Sopenharmony_ci if (tp->gesture.finger_count == 2) 945a46c0ec8Sopenharmony_ci tp_gesture_handle_event(tp, GESTURE_EVENT_SCROLL, time); 946a46c0ec8Sopenharmony_ci else 947a46c0ec8Sopenharmony_ci tp_gesture_handle_event(tp, GESTURE_EVENT_SWIPE, time); 948a46c0ec8Sopenharmony_ci 949a46c0ec8Sopenharmony_ci return; 950a46c0ec8Sopenharmony_ci } 951a46c0ec8Sopenharmony_ci 952a46c0ec8Sopenharmony_ci /* If one touch exceeds the max_move threshold while the other has not 953a46c0ec8Sopenharmony_ci * yet passed the min_move threshold, there is either a resting thumb, 954a46c0ec8Sopenharmony_ci * or the user is doing "one-finger-scroll," where one touch stays in 955a46c0ec8Sopenharmony_ci * place while the other moves. 956a46c0ec8Sopenharmony_ci */ 957a46c0ec8Sopenharmony_ci if (first_mm >= max_move || second_mm >= max_move) { 958a46c0ec8Sopenharmony_ci /* If thumb detection is enabled, and thumb is still while 959a46c0ec8Sopenharmony_ci * finger moves, cancel gestures and mark lower as thumb. 960a46c0ec8Sopenharmony_ci * This applies to all gestures (2, 3, 4+ fingers), but allows 961a46c0ec8Sopenharmony_ci * more thumb motion on >2 finger gestures during detection. 962a46c0ec8Sopenharmony_ci */ 963a46c0ec8Sopenharmony_ci if (tp->thumb.detect_thumbs && thumb_mm < min_move) { 964a46c0ec8Sopenharmony_ci tp_thumb_suppress(tp, thumb); 965a46c0ec8Sopenharmony_ci tp_gesture_cancel(tp, time); 966a46c0ec8Sopenharmony_ci return; 967a46c0ec8Sopenharmony_ci } 968a46c0ec8Sopenharmony_ci 969a46c0ec8Sopenharmony_ci /* If gestures detection is disabled, or if finger is still 970a46c0ec8Sopenharmony_ci * while thumb moves, assume this is "one-finger scrolling." 971a46c0ec8Sopenharmony_ci * This applies only to 2-finger gestures. 972a46c0ec8Sopenharmony_ci */ 973a46c0ec8Sopenharmony_ci if ((!tp->gesture.enabled || finger_mm < min_move) && 974a46c0ec8Sopenharmony_ci tp->gesture.finger_count == 2) { 975a46c0ec8Sopenharmony_ci tp_gesture_handle_event(tp, GESTURE_EVENT_SCROLL, time); 976a46c0ec8Sopenharmony_ci return; 977a46c0ec8Sopenharmony_ci } 978a46c0ec8Sopenharmony_ci 979a46c0ec8Sopenharmony_ci /* If more than 2 fingers are involved, and the thumb moves 980a46c0ec8Sopenharmony_ci * while the fingers stay still, assume a pinch if eligible. 981a46c0ec8Sopenharmony_ci */ 982a46c0ec8Sopenharmony_ci if (finger_mm < min_move && 983a46c0ec8Sopenharmony_ci tp->gesture.finger_count > 2 && 984a46c0ec8Sopenharmony_ci tp->gesture.enabled && 985a46c0ec8Sopenharmony_ci tp->thumb.pinch_eligible) { 986a46c0ec8Sopenharmony_ci tp_gesture_handle_event(tp, GESTURE_EVENT_PINCH, time); 987a46c0ec8Sopenharmony_ci return; 988a46c0ec8Sopenharmony_ci } 989a46c0ec8Sopenharmony_ci } 990a46c0ec8Sopenharmony_ci 991a46c0ec8Sopenharmony_ci /* If either touch is still below the min_move threshold, we can't 992a46c0ec8Sopenharmony_ci * tell what kind of gesture this is. 993a46c0ec8Sopenharmony_ci */ 994a46c0ec8Sopenharmony_ci if ((first_mm < min_move) || (second_mm < min_move)) 995a46c0ec8Sopenharmony_ci return; 996a46c0ec8Sopenharmony_ci 997a46c0ec8Sopenharmony_ci /* Both touches have exceeded the min_move threshold, so we have a 998a46c0ec8Sopenharmony_ci * valid gesture. Update gesture initial time and get directions so 999a46c0ec8Sopenharmony_ci * we know if it's a pinch or swipe/scroll. 1000a46c0ec8Sopenharmony_ci */ 1001a46c0ec8Sopenharmony_ci dir1 = tp_gesture_get_direction(tp, first); 1002a46c0ec8Sopenharmony_ci dir2 = tp_gesture_get_direction(tp, second); 1003a46c0ec8Sopenharmony_ci 1004a46c0ec8Sopenharmony_ci /* If we can't accurately detect pinches, or if the touches are moving 1005a46c0ec8Sopenharmony_ci * the same way, this is a scroll or swipe. 1006a46c0ec8Sopenharmony_ci */ 1007a46c0ec8Sopenharmony_ci if (tp->gesture.finger_count > tp->num_slots || 1008a46c0ec8Sopenharmony_ci tp_gesture_same_directions(dir1, dir2)) { 1009a46c0ec8Sopenharmony_ci if (tp->gesture.finger_count == 2) { 1010a46c0ec8Sopenharmony_ci tp_gesture_handle_event(tp, GESTURE_EVENT_SCROLL, time); 1011a46c0ec8Sopenharmony_ci return; 1012a46c0ec8Sopenharmony_ci } 1013a46c0ec8Sopenharmony_ci 1014a46c0ec8Sopenharmony_ci if (tp->gesture.enabled) { 1015a46c0ec8Sopenharmony_ci tp_gesture_handle_event(tp, GESTURE_EVENT_SWIPE, time); 1016a46c0ec8Sopenharmony_ci return; 1017a46c0ec8Sopenharmony_ci } 1018a46c0ec8Sopenharmony_ci } 1019a46c0ec8Sopenharmony_ci 1020a46c0ec8Sopenharmony_ci /* If the touches are moving away from each other, this is a pinch */ 1021a46c0ec8Sopenharmony_ci tp_gesture_handle_event(tp, GESTURE_EVENT_PINCH, time); 1022a46c0ec8Sopenharmony_ci} 1023a46c0ec8Sopenharmony_ci 1024a46c0ec8Sopenharmony_cistatic bool 1025a46c0ec8Sopenharmony_citp_gesture_is_pinch(struct tp_dispatch *tp) 1026a46c0ec8Sopenharmony_ci{ 1027a46c0ec8Sopenharmony_ci struct tp_touch *first = tp->gesture.touches[0], 1028a46c0ec8Sopenharmony_ci *second = tp->gesture.touches[1]; 1029a46c0ec8Sopenharmony_ci uint32_t dir1, dir2; 1030a46c0ec8Sopenharmony_ci struct phys_coords first_moved, second_moved; 1031a46c0ec8Sopenharmony_ci double first_mm, second_mm; 1032a46c0ec8Sopenharmony_ci 1033a46c0ec8Sopenharmony_ci dir1 = tp_gesture_get_direction(tp, first); 1034a46c0ec8Sopenharmony_ci dir2 = tp_gesture_get_direction(tp, second); 1035a46c0ec8Sopenharmony_ci if (tp_gesture_same_directions(dir1, dir2)) 1036a46c0ec8Sopenharmony_ci return false; 1037a46c0ec8Sopenharmony_ci 1038a46c0ec8Sopenharmony_ci first_moved = tp_gesture_mm_moved(tp, first); 1039a46c0ec8Sopenharmony_ci first_mm = hypot(first_moved.x, first_moved.y); 1040a46c0ec8Sopenharmony_ci if (first_mm < PINCH_DISAMBIGUATION_MOVE_THRESHOLD) 1041a46c0ec8Sopenharmony_ci return false; 1042a46c0ec8Sopenharmony_ci 1043a46c0ec8Sopenharmony_ci second_moved = tp_gesture_mm_moved(tp, second); 1044a46c0ec8Sopenharmony_ci second_mm = hypot(second_moved.x, second_moved.y); 1045a46c0ec8Sopenharmony_ci if (second_mm < PINCH_DISAMBIGUATION_MOVE_THRESHOLD) 1046a46c0ec8Sopenharmony_ci return false; 1047a46c0ec8Sopenharmony_ci 1048a46c0ec8Sopenharmony_ci return true; 1049a46c0ec8Sopenharmony_ci} 1050a46c0ec8Sopenharmony_ci 1051a46c0ec8Sopenharmony_cistatic void 1052a46c0ec8Sopenharmony_citp_gesture_handle_state_none(struct tp_dispatch *tp, uint64_t time) 1053a46c0ec8Sopenharmony_ci{ 1054a46c0ec8Sopenharmony_ci struct tp_touch *first, *second; 1055a46c0ec8Sopenharmony_ci struct tp_touch *touches[4]; 1056a46c0ec8Sopenharmony_ci unsigned int ntouches; 1057a46c0ec8Sopenharmony_ci unsigned int i; 1058a46c0ec8Sopenharmony_ci 1059a46c0ec8Sopenharmony_ci ntouches = tp_gesture_get_active_touches(tp, touches, 4); 1060a46c0ec8Sopenharmony_ci 1061a46c0ec8Sopenharmony_ci first = touches[0]; 1062a46c0ec8Sopenharmony_ci second = touches[1]; 1063a46c0ec8Sopenharmony_ci 1064a46c0ec8Sopenharmony_ci if (ntouches == 0) 1065a46c0ec8Sopenharmony_ci return; 1066a46c0ec8Sopenharmony_ci 1067a46c0ec8Sopenharmony_ci if (ntouches == 1) { 1068a46c0ec8Sopenharmony_ci first->gesture.initial = first->point; 1069a46c0ec8Sopenharmony_ci tp->gesture.touches[0] = first; 1070a46c0ec8Sopenharmony_ci 1071a46c0ec8Sopenharmony_ci tp_gesture_handle_event(tp, 1072a46c0ec8Sopenharmony_ci GESTURE_EVENT_FINGER_DETECTED, 1073a46c0ec8Sopenharmony_ci time); 1074a46c0ec8Sopenharmony_ci return; 1075a46c0ec8Sopenharmony_ci } 1076a46c0ec8Sopenharmony_ci 1077a46c0ec8Sopenharmony_ci if (!tp->gesture.enabled) { 1078a46c0ec8Sopenharmony_ci if (ntouches == 2) 1079a46c0ec8Sopenharmony_ci tp_gesture_handle_event(tp, GESTURE_EVENT_SCROLL, time); 1080a46c0ec8Sopenharmony_ci 1081a46c0ec8Sopenharmony_ci return; 1082a46c0ec8Sopenharmony_ci } 1083a46c0ec8Sopenharmony_ci 1084a46c0ec8Sopenharmony_ci /* For 3+ finger gestures, we only really need to track two touches. 1085a46c0ec8Sopenharmony_ci * The human hand's finger arrangement means that for a pinch, the 1086a46c0ec8Sopenharmony_ci * bottom-most touch will always be the thumb, and the top-most touch 1087a46c0ec8Sopenharmony_ci * will always be one of the fingers. 1088a46c0ec8Sopenharmony_ci * 1089a46c0ec8Sopenharmony_ci * For 3+ finger swipes, the fingers will likely (but not necessarily) 1090a46c0ec8Sopenharmony_ci * be in a horizontal line. They all move together, regardless, so it 1091a46c0ec8Sopenharmony_ci * doesn't really matter which two of those touches we track. 1092a46c0ec8Sopenharmony_ci * 1093a46c0ec8Sopenharmony_ci * Tracking top and bottom is a change from previous versions, where 1094a46c0ec8Sopenharmony_ci * we tracked leftmost and rightmost. This change enables: 1095a46c0ec8Sopenharmony_ci * 1096a46c0ec8Sopenharmony_ci * - More accurate pinch detection if thumb is near the center 1097a46c0ec8Sopenharmony_ci * - Better resting-thumb detection while two-finger scrolling 1098a46c0ec8Sopenharmony_ci * - On capable hardware, allow 3- or 4-finger swipes with resting 1099a46c0ec8Sopenharmony_ci * thumb or held-down clickpad 1100a46c0ec8Sopenharmony_ci */ 1101a46c0ec8Sopenharmony_ci if (ntouches > 2) { 1102a46c0ec8Sopenharmony_ci second = touches[0]; 1103a46c0ec8Sopenharmony_ci 1104a46c0ec8Sopenharmony_ci for (i = 1; i < ntouches && i < tp->num_slots; i++) { 1105a46c0ec8Sopenharmony_ci if (touches[i]->point.y < first->point.y) 1106a46c0ec8Sopenharmony_ci first = touches[i]; 1107a46c0ec8Sopenharmony_ci else if (touches[i]->point.y >= second->point.y) 1108a46c0ec8Sopenharmony_ci second = touches[i]; 1109a46c0ec8Sopenharmony_ci } 1110a46c0ec8Sopenharmony_ci 1111a46c0ec8Sopenharmony_ci if (first == second) 1112a46c0ec8Sopenharmony_ci return; 1113a46c0ec8Sopenharmony_ci 1114a46c0ec8Sopenharmony_ci } 1115a46c0ec8Sopenharmony_ci 1116a46c0ec8Sopenharmony_ci tp->gesture.initial_time = time; 1117a46c0ec8Sopenharmony_ci first->gesture.initial = first->point; 1118a46c0ec8Sopenharmony_ci second->gesture.initial = second->point; 1119a46c0ec8Sopenharmony_ci tp->gesture.touches[0] = first; 1120a46c0ec8Sopenharmony_ci tp->gesture.touches[1] = second; 1121a46c0ec8Sopenharmony_ci 1122a46c0ec8Sopenharmony_ci tp_gesture_handle_event(tp, GESTURE_EVENT_FINGER_DETECTED, time); 1123a46c0ec8Sopenharmony_ci} 1124a46c0ec8Sopenharmony_ci 1125a46c0ec8Sopenharmony_cistatic void 1126a46c0ec8Sopenharmony_citp_gesture_handle_state_unknown(struct tp_dispatch *tp, uint64_t time, 1127a46c0ec8Sopenharmony_ci bool ignore_motion) 1128a46c0ec8Sopenharmony_ci{ 1129a46c0ec8Sopenharmony_ci if (!ignore_motion) 1130a46c0ec8Sopenharmony_ci tp_gesture_detect_motion_gestures(tp, time); 1131a46c0ec8Sopenharmony_ci} 1132a46c0ec8Sopenharmony_ci 1133a46c0ec8Sopenharmony_cistatic void 1134a46c0ec8Sopenharmony_citp_gesture_handle_state_hold(struct tp_dispatch *tp, uint64_t time, 1135a46c0ec8Sopenharmony_ci bool ignore_motion) 1136a46c0ec8Sopenharmony_ci{ 1137a46c0ec8Sopenharmony_ci tp_gesture_start(tp, time); 1138a46c0ec8Sopenharmony_ci 1139a46c0ec8Sopenharmony_ci if (!ignore_motion) 1140a46c0ec8Sopenharmony_ci tp_gesture_detect_motion_gestures(tp, time); 1141a46c0ec8Sopenharmony_ci} 1142a46c0ec8Sopenharmony_ci 1143a46c0ec8Sopenharmony_cistatic void 1144a46c0ec8Sopenharmony_citp_gesture_handle_state_hold_and_pointer_motion(struct tp_dispatch *tp, uint64_t time) 1145a46c0ec8Sopenharmony_ci{ 1146a46c0ec8Sopenharmony_ci if (tp->queued & TOUCHPAD_EVENT_MOTION) 1147a46c0ec8Sopenharmony_ci tp_gesture_post_pointer_motion(tp, time); 1148a46c0ec8Sopenharmony_ci 1149a46c0ec8Sopenharmony_ci tp_gesture_detect_motion_gestures(tp, time); 1150a46c0ec8Sopenharmony_ci} 1151a46c0ec8Sopenharmony_ci 1152a46c0ec8Sopenharmony_cistatic void 1153a46c0ec8Sopenharmony_citp_gesture_handle_state_pointer_motion(struct tp_dispatch *tp, uint64_t time) 1154a46c0ec8Sopenharmony_ci{ 1155a46c0ec8Sopenharmony_ci if (tp->queued & TOUCHPAD_EVENT_MOTION) 1156a46c0ec8Sopenharmony_ci tp_gesture_post_pointer_motion(tp, time); 1157a46c0ec8Sopenharmony_ci} 1158a46c0ec8Sopenharmony_ci 1159a46c0ec8Sopenharmony_cistatic void 1160a46c0ec8Sopenharmony_citp_gesture_handle_state_scroll(struct tp_dispatch *tp, uint64_t time) 1161a46c0ec8Sopenharmony_ci{ 1162a46c0ec8Sopenharmony_ci struct device_float_coords raw; 1163a46c0ec8Sopenharmony_ci struct normalized_coords delta; 1164a46c0ec8Sopenharmony_ci 1165a46c0ec8Sopenharmony_ci if (tp->scroll.method != LIBINPUT_CONFIG_SCROLL_2FG) 1166a46c0ec8Sopenharmony_ci return; 1167a46c0ec8Sopenharmony_ci 1168a46c0ec8Sopenharmony_ci /* We may confuse a pinch for a scroll initially, 1169a46c0ec8Sopenharmony_ci * allow ourselves to correct our guess. 1170a46c0ec8Sopenharmony_ci */ 1171a46c0ec8Sopenharmony_ci if (time < (tp->gesture.initial_time + DEFAULT_GESTURE_PINCH_TIMEOUT) && 1172a46c0ec8Sopenharmony_ci tp_gesture_is_pinch(tp)) { 1173a46c0ec8Sopenharmony_ci tp_gesture_handle_event(tp, GESTURE_EVENT_PINCH, time); 1174a46c0ec8Sopenharmony_ci return; 1175a46c0ec8Sopenharmony_ci } 1176a46c0ec8Sopenharmony_ci 1177a46c0ec8Sopenharmony_ci raw = tp_get_average_touches_delta(tp); 1178a46c0ec8Sopenharmony_ci 1179a46c0ec8Sopenharmony_ci /* scroll is not accelerated by default */ 1180a46c0ec8Sopenharmony_ci delta = tp_filter_scroll(tp, &raw, time); 1181a46c0ec8Sopenharmony_ci 1182a46c0ec8Sopenharmony_ci if (normalized_is_zero(delta)) 1183a46c0ec8Sopenharmony_ci return; 1184a46c0ec8Sopenharmony_ci 1185a46c0ec8Sopenharmony_ci tp_gesture_start(tp, time); 1186a46c0ec8Sopenharmony_ci tp_gesture_apply_scroll_constraints(tp, &raw, &delta, time); 1187a46c0ec8Sopenharmony_ci evdev_post_scroll(tp->device, 1188a46c0ec8Sopenharmony_ci time, 1189a46c0ec8Sopenharmony_ci LIBINPUT_POINTER_AXIS_SOURCE_FINGER, 1190a46c0ec8Sopenharmony_ci &delta); 1191a46c0ec8Sopenharmony_ci} 1192a46c0ec8Sopenharmony_ci 1193a46c0ec8Sopenharmony_cistatic void 1194a46c0ec8Sopenharmony_citp_gesture_handle_state_swipe(struct tp_dispatch *tp, uint64_t time) 1195a46c0ec8Sopenharmony_ci{ 1196a46c0ec8Sopenharmony_ci struct device_float_coords raw; 1197a46c0ec8Sopenharmony_ci struct normalized_coords delta, unaccel; 1198a46c0ec8Sopenharmony_ci 1199a46c0ec8Sopenharmony_ci raw = tp_get_average_touches_delta(tp); 1200a46c0ec8Sopenharmony_ci delta = tp_filter_motion(tp, &raw, time); 1201a46c0ec8Sopenharmony_ci 1202a46c0ec8Sopenharmony_ci if (!normalized_is_zero(delta) || !device_float_is_zero(raw)) { 1203a46c0ec8Sopenharmony_ci unaccel = tp_filter_motion_unaccelerated(tp, &raw, time); 1204a46c0ec8Sopenharmony_ci tp_gesture_start(tp, time); 1205a46c0ec8Sopenharmony_ci gesture_notify_swipe(&tp->device->base, time, 1206a46c0ec8Sopenharmony_ci LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE, 1207a46c0ec8Sopenharmony_ci tp->gesture.finger_count, 1208a46c0ec8Sopenharmony_ci &delta, &unaccel); 1209a46c0ec8Sopenharmony_ci } 1210a46c0ec8Sopenharmony_ci} 1211a46c0ec8Sopenharmony_ci 1212a46c0ec8Sopenharmony_cistatic void 1213a46c0ec8Sopenharmony_citp_gesture_handle_state_pinch(struct tp_dispatch *tp, uint64_t time) 1214a46c0ec8Sopenharmony_ci{ 1215a46c0ec8Sopenharmony_ci double angle, angle_delta, distance, scale; 1216a46c0ec8Sopenharmony_ci struct device_float_coords center, fdelta; 1217a46c0ec8Sopenharmony_ci struct normalized_coords delta, unaccel; 1218a46c0ec8Sopenharmony_ci 1219a46c0ec8Sopenharmony_ci tp_gesture_get_pinch_info(tp, &distance, &angle, ¢er); 1220a46c0ec8Sopenharmony_ci 1221a46c0ec8Sopenharmony_ci scale = distance / tp->gesture.initial_distance; 1222a46c0ec8Sopenharmony_ci 1223a46c0ec8Sopenharmony_ci angle_delta = angle - tp->gesture.angle; 1224a46c0ec8Sopenharmony_ci tp->gesture.angle = angle; 1225a46c0ec8Sopenharmony_ci if (angle_delta > 180.0) 1226a46c0ec8Sopenharmony_ci angle_delta -= 360.0; 1227a46c0ec8Sopenharmony_ci else if (angle_delta < -180.0) 1228a46c0ec8Sopenharmony_ci angle_delta += 360.0; 1229a46c0ec8Sopenharmony_ci 1230a46c0ec8Sopenharmony_ci fdelta = device_float_delta(center, tp->gesture.center); 1231a46c0ec8Sopenharmony_ci tp->gesture.center = center; 1232a46c0ec8Sopenharmony_ci 1233a46c0ec8Sopenharmony_ci delta = tp_filter_motion(tp, &fdelta, time); 1234a46c0ec8Sopenharmony_ci 1235a46c0ec8Sopenharmony_ci if (normalized_is_zero(delta) && device_float_is_zero(fdelta) && 1236a46c0ec8Sopenharmony_ci scale == tp->gesture.prev_scale && angle_delta == 0.0) 1237a46c0ec8Sopenharmony_ci return; 1238a46c0ec8Sopenharmony_ci 1239a46c0ec8Sopenharmony_ci unaccel = tp_filter_motion_unaccelerated(tp, &fdelta, time); 1240a46c0ec8Sopenharmony_ci tp_gesture_start(tp, time); 1241a46c0ec8Sopenharmony_ci gesture_notify_pinch(&tp->device->base, time, 1242a46c0ec8Sopenharmony_ci LIBINPUT_EVENT_GESTURE_PINCH_UPDATE, 1243a46c0ec8Sopenharmony_ci tp->gesture.finger_count, 1244a46c0ec8Sopenharmony_ci &delta, &unaccel, scale, angle_delta); 1245a46c0ec8Sopenharmony_ci 1246a46c0ec8Sopenharmony_ci tp->gesture.prev_scale = scale; 1247a46c0ec8Sopenharmony_ci} 1248a46c0ec8Sopenharmony_ci 1249a46c0ec8Sopenharmony_cistatic void 1250a46c0ec8Sopenharmony_citp_gesture_post_gesture(struct tp_dispatch *tp, uint64_t time, 1251a46c0ec8Sopenharmony_ci bool ignore_motion) 1252a46c0ec8Sopenharmony_ci{ 1253a46c0ec8Sopenharmony_ci if (tp->gesture.state == GESTURE_STATE_NONE) 1254a46c0ec8Sopenharmony_ci tp_gesture_handle_state_none(tp, time); 1255a46c0ec8Sopenharmony_ci 1256a46c0ec8Sopenharmony_ci if (tp->gesture.state == GESTURE_STATE_UNKNOWN) 1257a46c0ec8Sopenharmony_ci tp_gesture_handle_state_unknown(tp, time, ignore_motion); 1258a46c0ec8Sopenharmony_ci 1259a46c0ec8Sopenharmony_ci if (tp->gesture.state == GESTURE_STATE_HOLD) 1260a46c0ec8Sopenharmony_ci tp_gesture_handle_state_hold(tp, time, ignore_motion); 1261a46c0ec8Sopenharmony_ci 1262a46c0ec8Sopenharmony_ci if (tp->gesture.state == GESTURE_STATE_POINTER_MOTION) 1263a46c0ec8Sopenharmony_ci tp_gesture_handle_state_pointer_motion(tp, time); 1264a46c0ec8Sopenharmony_ci 1265a46c0ec8Sopenharmony_ci if (tp->gesture.state == GESTURE_STATE_HOLD_AND_MOTION) 1266a46c0ec8Sopenharmony_ci tp_gesture_handle_state_hold_and_pointer_motion(tp, time); 1267a46c0ec8Sopenharmony_ci 1268a46c0ec8Sopenharmony_ci if (tp->gesture.state == GESTURE_STATE_SCROLL) 1269a46c0ec8Sopenharmony_ci tp_gesture_handle_state_scroll(tp, time); 1270a46c0ec8Sopenharmony_ci 1271a46c0ec8Sopenharmony_ci if (tp->gesture.state == GESTURE_STATE_SWIPE) 1272a46c0ec8Sopenharmony_ci tp_gesture_handle_state_swipe(tp, time); 1273a46c0ec8Sopenharmony_ci 1274a46c0ec8Sopenharmony_ci if (tp->gesture.state == GESTURE_STATE_PINCH) 1275a46c0ec8Sopenharmony_ci tp_gesture_handle_state_pinch(tp, time); 1276a46c0ec8Sopenharmony_ci} 1277a46c0ec8Sopenharmony_ci 1278a46c0ec8Sopenharmony_cistatic bool 1279a46c0ec8Sopenharmony_citp_gesture_thumb_moved(struct tp_dispatch *tp) 1280a46c0ec8Sopenharmony_ci{ 1281a46c0ec8Sopenharmony_ci struct tp_touch *thumb; 1282a46c0ec8Sopenharmony_ci struct phys_coords thumb_moved; 1283a46c0ec8Sopenharmony_ci double thumb_mm; 1284a46c0ec8Sopenharmony_ci 1285a46c0ec8Sopenharmony_ci thumb = tp_thumb_get_touch(tp); 1286a46c0ec8Sopenharmony_ci if (!thumb) 1287a46c0ec8Sopenharmony_ci return false; 1288a46c0ec8Sopenharmony_ci 1289a46c0ec8Sopenharmony_ci if (!tp_touch_active_for_gesture(tp, thumb)) 1290a46c0ec8Sopenharmony_ci return false; 1291a46c0ec8Sopenharmony_ci 1292a46c0ec8Sopenharmony_ci thumb_moved = tp_gesture_mm_moved(tp, thumb); 1293a46c0ec8Sopenharmony_ci thumb_mm = hypot(thumb_moved.x, thumb_moved.y); 1294a46c0ec8Sopenharmony_ci return thumb_mm >= PINCH_DISAMBIGUATION_MOVE_THRESHOLD; 1295a46c0ec8Sopenharmony_ci} 1296a46c0ec8Sopenharmony_ci 1297a46c0ec8Sopenharmony_civoid 1298a46c0ec8Sopenharmony_citp_gesture_post_events(struct tp_dispatch *tp, uint64_t time, 1299a46c0ec8Sopenharmony_ci bool ignore_motion) 1300a46c0ec8Sopenharmony_ci{ 1301a46c0ec8Sopenharmony_ci if (tp->gesture.finger_count == 0) 1302a46c0ec8Sopenharmony_ci return; 1303a46c0ec8Sopenharmony_ci 1304a46c0ec8Sopenharmony_ci /* When tap-and-dragging, force 1fg mode. On clickpads, if the 1305a46c0ec8Sopenharmony_ci * physical button is down, don't allow gestures unless the button 1306a46c0ec8Sopenharmony_ci * is held down by a *thumb*, specifically. 1307a46c0ec8Sopenharmony_ci */ 1308a46c0ec8Sopenharmony_ci if (tp_tap_dragging(tp) || 1309a46c0ec8Sopenharmony_ci (tp->buttons.is_clickpad && tp->buttons.state && 1310a46c0ec8Sopenharmony_ci tp->thumb.state == THUMB_STATE_FINGER)) { 1311a46c0ec8Sopenharmony_ci if (tp->gesture.state != GESTURE_STATE_POINTER_MOTION) { 1312a46c0ec8Sopenharmony_ci tp_gesture_cancel(tp, time); 1313a46c0ec8Sopenharmony_ci tp_gesture_handle_event(tp, 1314a46c0ec8Sopenharmony_ci GESTURE_EVENT_POINTER_MOTION, 1315a46c0ec8Sopenharmony_ci time); 1316a46c0ec8Sopenharmony_ci } 1317a46c0ec8Sopenharmony_ci tp->gesture.finger_count = 1; 1318a46c0ec8Sopenharmony_ci tp->gesture.finger_count_pending = 0; 1319a46c0ec8Sopenharmony_ci } 1320a46c0ec8Sopenharmony_ci 1321a46c0ec8Sopenharmony_ci /* Don't send events when we're unsure in which mode we are */ 1322a46c0ec8Sopenharmony_ci if (tp->gesture.finger_count_pending) 1323a46c0ec8Sopenharmony_ci return; 1324a46c0ec8Sopenharmony_ci 1325a46c0ec8Sopenharmony_ci /* When pinching, the thumb tends to move slower than the finger, 1326a46c0ec8Sopenharmony_ci * so we may suppress it too early. Give it some time to move. 1327a46c0ec8Sopenharmony_ci */ 1328a46c0ec8Sopenharmony_ci if (time < (tp->gesture.initial_time + DEFAULT_GESTURE_PINCH_TIMEOUT) && 1329a46c0ec8Sopenharmony_ci tp_gesture_thumb_moved(tp)) 1330a46c0ec8Sopenharmony_ci tp_thumb_reset(tp); 1331a46c0ec8Sopenharmony_ci 1332a46c0ec8Sopenharmony_ci if (tp->gesture.finger_count <= 4) 1333a46c0ec8Sopenharmony_ci tp_gesture_post_gesture(tp, time, ignore_motion); 1334a46c0ec8Sopenharmony_ci} 1335a46c0ec8Sopenharmony_ci 1336a46c0ec8Sopenharmony_civoid 1337a46c0ec8Sopenharmony_citp_gesture_stop_twofinger_scroll(struct tp_dispatch *tp, uint64_t time) 1338a46c0ec8Sopenharmony_ci{ 1339a46c0ec8Sopenharmony_ci if (tp->scroll.method != LIBINPUT_CONFIG_SCROLL_2FG) 1340a46c0ec8Sopenharmony_ci return; 1341a46c0ec8Sopenharmony_ci 1342a46c0ec8Sopenharmony_ci evdev_stop_scroll(tp->device, 1343a46c0ec8Sopenharmony_ci time, 1344a46c0ec8Sopenharmony_ci LIBINPUT_POINTER_AXIS_SOURCE_FINGER); 1345a46c0ec8Sopenharmony_ci} 1346a46c0ec8Sopenharmony_ci 1347a46c0ec8Sopenharmony_cistatic void 1348a46c0ec8Sopenharmony_citp_gesture_end(struct tp_dispatch *tp, uint64_t time, bool cancelled) 1349a46c0ec8Sopenharmony_ci{ 1350a46c0ec8Sopenharmony_ci enum tp_gesture_state state = tp->gesture.state; 1351a46c0ec8Sopenharmony_ci 1352a46c0ec8Sopenharmony_ci if (!tp->gesture.started) { 1353a46c0ec8Sopenharmony_ci tp_gesture_handle_event(tp, GESTURE_EVENT_RESET, time); 1354a46c0ec8Sopenharmony_ci return; 1355a46c0ec8Sopenharmony_ci } 1356a46c0ec8Sopenharmony_ci 1357a46c0ec8Sopenharmony_ci switch (state) { 1358a46c0ec8Sopenharmony_ci case GESTURE_STATE_NONE: 1359a46c0ec8Sopenharmony_ci case GESTURE_STATE_UNKNOWN: 1360a46c0ec8Sopenharmony_ci evdev_log_bug_libinput(tp->device, 1361a46c0ec8Sopenharmony_ci "%s in unknown gesture mode\n", 1362a46c0ec8Sopenharmony_ci __func__); 1363a46c0ec8Sopenharmony_ci break; 1364a46c0ec8Sopenharmony_ci case GESTURE_STATE_HOLD: 1365a46c0ec8Sopenharmony_ci case GESTURE_STATE_HOLD_AND_MOTION: 1366a46c0ec8Sopenharmony_ci gesture_notify_hold_end(&tp->device->base, time, 1367a46c0ec8Sopenharmony_ci tp->gesture.finger_count, cancelled); 1368a46c0ec8Sopenharmony_ci break; 1369a46c0ec8Sopenharmony_ci case GESTURE_STATE_SCROLL: 1370a46c0ec8Sopenharmony_ci tp_gesture_stop_twofinger_scroll(tp, time); 1371a46c0ec8Sopenharmony_ci break; 1372a46c0ec8Sopenharmony_ci case GESTURE_STATE_PINCH: 1373a46c0ec8Sopenharmony_ci gesture_notify_pinch_end(&tp->device->base, time, 1374a46c0ec8Sopenharmony_ci tp->gesture.finger_count, 1375a46c0ec8Sopenharmony_ci tp->gesture.prev_scale, 1376a46c0ec8Sopenharmony_ci cancelled); 1377a46c0ec8Sopenharmony_ci break; 1378a46c0ec8Sopenharmony_ci case GESTURE_STATE_SWIPE: 1379a46c0ec8Sopenharmony_ci gesture_notify_swipe_end(&tp->device->base, 1380a46c0ec8Sopenharmony_ci time, 1381a46c0ec8Sopenharmony_ci tp->gesture.finger_count, 1382a46c0ec8Sopenharmony_ci cancelled); 1383a46c0ec8Sopenharmony_ci break; 1384a46c0ec8Sopenharmony_ci case GESTURE_STATE_POINTER_MOTION: 1385a46c0ec8Sopenharmony_ci break; 1386a46c0ec8Sopenharmony_ci } 1387a46c0ec8Sopenharmony_ci 1388a46c0ec8Sopenharmony_ci tp->gesture.started = false; 1389a46c0ec8Sopenharmony_ci tp_gesture_handle_event(tp, GESTURE_EVENT_RESET, time); 1390a46c0ec8Sopenharmony_ci} 1391a46c0ec8Sopenharmony_ci 1392a46c0ec8Sopenharmony_civoid 1393a46c0ec8Sopenharmony_citp_gesture_cancel(struct tp_dispatch *tp, uint64_t time) 1394a46c0ec8Sopenharmony_ci{ 1395a46c0ec8Sopenharmony_ci tp_gesture_end(tp, time, true); 1396a46c0ec8Sopenharmony_ci} 1397a46c0ec8Sopenharmony_ci 1398a46c0ec8Sopenharmony_civoid 1399a46c0ec8Sopenharmony_citp_gesture_cancel_motion_gestures(struct tp_dispatch *tp, uint64_t time) 1400a46c0ec8Sopenharmony_ci{ 1401a46c0ec8Sopenharmony_ci if (tp->gesture.started && tp->gesture.state != GESTURE_STATE_HOLD) 1402a46c0ec8Sopenharmony_ci tp_gesture_end(tp, time, true); 1403a46c0ec8Sopenharmony_ci} 1404a46c0ec8Sopenharmony_ci 1405a46c0ec8Sopenharmony_civoid 1406a46c0ec8Sopenharmony_citp_gesture_stop(struct tp_dispatch *tp, uint64_t time) 1407a46c0ec8Sopenharmony_ci{ 1408a46c0ec8Sopenharmony_ci tp_gesture_end(tp, time, false); 1409a46c0ec8Sopenharmony_ci} 1410a46c0ec8Sopenharmony_ci 1411a46c0ec8Sopenharmony_cistatic void 1412a46c0ec8Sopenharmony_citp_gesture_finger_count_switch_timeout(uint64_t now, void *data) 1413a46c0ec8Sopenharmony_ci{ 1414a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = data; 1415a46c0ec8Sopenharmony_ci 1416a46c0ec8Sopenharmony_ci if (!tp->gesture.finger_count_pending) 1417a46c0ec8Sopenharmony_ci return; 1418a46c0ec8Sopenharmony_ci 1419a46c0ec8Sopenharmony_ci tp_gesture_cancel(tp, now); /* End current gesture */ 1420a46c0ec8Sopenharmony_ci tp->gesture.finger_count = tp->gesture.finger_count_pending; 1421a46c0ec8Sopenharmony_ci tp->gesture.finger_count_pending = 0; 1422a46c0ec8Sopenharmony_ci} 1423a46c0ec8Sopenharmony_ci 1424a46c0ec8Sopenharmony_civoid 1425a46c0ec8Sopenharmony_citp_gesture_handle_state(struct tp_dispatch *tp, uint64_t time) 1426a46c0ec8Sopenharmony_ci{ 1427a46c0ec8Sopenharmony_ci unsigned int active_touches = 0; 1428a46c0ec8Sopenharmony_ci struct tp_touch *t; 1429a46c0ec8Sopenharmony_ci 1430a46c0ec8Sopenharmony_ci tp_for_each_touch(tp, t) { 1431a46c0ec8Sopenharmony_ci if (tp_touch_active_for_gesture(tp, t)) 1432a46c0ec8Sopenharmony_ci active_touches++; 1433a46c0ec8Sopenharmony_ci } 1434a46c0ec8Sopenharmony_ci 1435a46c0ec8Sopenharmony_ci if (active_touches != tp->gesture.finger_count) { 1436a46c0ec8Sopenharmony_ci /* If all fingers are lifted immediately end the gesture */ 1437a46c0ec8Sopenharmony_ci if (active_touches == 0) { 1438a46c0ec8Sopenharmony_ci tp_gesture_stop(tp, time); 1439a46c0ec8Sopenharmony_ci tp->gesture.finger_count = 0; 1440a46c0ec8Sopenharmony_ci tp->gesture.finger_count_pending = 0; 1441a46c0ec8Sopenharmony_ci /* Immediately switch to new mode to avoid initial latency */ 1442a46c0ec8Sopenharmony_ci } else if (!tp->gesture.started) { 1443a46c0ec8Sopenharmony_ci tp->gesture.finger_count = active_touches; 1444a46c0ec8Sopenharmony_ci tp->gesture.finger_count_pending = 0; 1445a46c0ec8Sopenharmony_ci /* If in UNKNOWN or POINTER_MOTION state, go back to 1446a46c0ec8Sopenharmony_ci * NONE to re-evaluate leftmost and rightmost touches 1447a46c0ec8Sopenharmony_ci */ 1448a46c0ec8Sopenharmony_ci if (tp->gesture.state == GESTURE_STATE_UNKNOWN || 1449a46c0ec8Sopenharmony_ci tp->gesture.state == GESTURE_STATE_POINTER_MOTION) { 1450a46c0ec8Sopenharmony_ci tp_gesture_handle_event(tp, 1451a46c0ec8Sopenharmony_ci GESTURE_EVENT_RESET, 1452a46c0ec8Sopenharmony_ci time); 1453a46c0ec8Sopenharmony_ci } 1454a46c0ec8Sopenharmony_ci /* Else debounce finger changes */ 1455a46c0ec8Sopenharmony_ci } else if (active_touches != tp->gesture.finger_count_pending) { 1456a46c0ec8Sopenharmony_ci tp->gesture.finger_count_pending = active_touches; 1457a46c0ec8Sopenharmony_ci libinput_timer_set(&tp->gesture.finger_count_switch_timer, 1458a46c0ec8Sopenharmony_ci time + DEFAULT_GESTURE_SWITCH_TIMEOUT); 1459a46c0ec8Sopenharmony_ci } 1460a46c0ec8Sopenharmony_ci } else { 1461a46c0ec8Sopenharmony_ci tp->gesture.finger_count_pending = 0; 1462a46c0ec8Sopenharmony_ci } 1463a46c0ec8Sopenharmony_ci} 1464a46c0ec8Sopenharmony_ci 1465a46c0ec8Sopenharmony_cistatic bool 1466a46c0ec8Sopenharmony_citp_gesture_are_gestures_enabled(struct tp_dispatch *tp) 1467a46c0ec8Sopenharmony_ci{ 1468a46c0ec8Sopenharmony_ci return (!tp->semi_mt && tp->num_slots > 1); 1469a46c0ec8Sopenharmony_ci} 1470a46c0ec8Sopenharmony_ci 1471a46c0ec8Sopenharmony_cistatic enum libinput_config_status 1472a46c0ec8Sopenharmony_citp_gesture_set_hold_enabled(struct libinput_device *device, 1473a46c0ec8Sopenharmony_ci enum libinput_config_hold_state enabled) 1474a46c0ec8Sopenharmony_ci{ 1475a46c0ec8Sopenharmony_ci struct evdev_dispatch *dispatch = evdev_device(device)->dispatch; 1476a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = tp_dispatch(dispatch); 1477a46c0ec8Sopenharmony_ci 1478a46c0ec8Sopenharmony_ci if (!tp_gesture_are_gestures_enabled(tp)) 1479a46c0ec8Sopenharmony_ci return LIBINPUT_CONFIG_STATUS_UNSUPPORTED; 1480a46c0ec8Sopenharmony_ci 1481a46c0ec8Sopenharmony_ci tp->gesture.hold_enabled = (enabled == LIBINPUT_CONFIG_HOLD_ENABLED); 1482a46c0ec8Sopenharmony_ci 1483a46c0ec8Sopenharmony_ci return LIBINPUT_CONFIG_STATUS_SUCCESS; 1484a46c0ec8Sopenharmony_ci} 1485a46c0ec8Sopenharmony_ci 1486a46c0ec8Sopenharmony_cistatic enum libinput_config_hold_state 1487a46c0ec8Sopenharmony_citp_gesture_is_hold_enabled(struct libinput_device *device) 1488a46c0ec8Sopenharmony_ci{ 1489a46c0ec8Sopenharmony_ci struct evdev_dispatch *dispatch = evdev_device(device)->dispatch; 1490a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = tp_dispatch(dispatch); 1491a46c0ec8Sopenharmony_ci 1492a46c0ec8Sopenharmony_ci return tp->gesture.hold_enabled ? LIBINPUT_CONFIG_HOLD_ENABLED : 1493a46c0ec8Sopenharmony_ci LIBINPUT_CONFIG_HOLD_DISABLED; 1494a46c0ec8Sopenharmony_ci} 1495a46c0ec8Sopenharmony_ci 1496a46c0ec8Sopenharmony_cistatic enum libinput_config_hold_state 1497a46c0ec8Sopenharmony_citp_gesture_get_hold_default(struct libinput_device *device) 1498a46c0ec8Sopenharmony_ci{ 1499a46c0ec8Sopenharmony_ci struct evdev_dispatch *dispatch = evdev_device(device)->dispatch; 1500a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = tp_dispatch(dispatch); 1501a46c0ec8Sopenharmony_ci 1502a46c0ec8Sopenharmony_ci return tp_gesture_are_gestures_enabled(tp) ? 1503a46c0ec8Sopenharmony_ci LIBINPUT_CONFIG_HOLD_ENABLED : 1504a46c0ec8Sopenharmony_ci LIBINPUT_CONFIG_HOLD_DISABLED; 1505a46c0ec8Sopenharmony_ci} 1506a46c0ec8Sopenharmony_ci 1507a46c0ec8Sopenharmony_civoid 1508a46c0ec8Sopenharmony_citp_init_gesture(struct tp_dispatch *tp) 1509a46c0ec8Sopenharmony_ci{ 1510a46c0ec8Sopenharmony_ci char timer_name[64]; 1511a46c0ec8Sopenharmony_ci 1512a46c0ec8Sopenharmony_ci tp->gesture.config.set_hold_enabled = tp_gesture_set_hold_enabled; 1513a46c0ec8Sopenharmony_ci tp->gesture.config.get_hold_enabled = tp_gesture_is_hold_enabled; 1514a46c0ec8Sopenharmony_ci tp->gesture.config.get_hold_default = tp_gesture_get_hold_default; 1515a46c0ec8Sopenharmony_ci tp->device->base.config.gesture = &tp->gesture.config; 1516a46c0ec8Sopenharmony_ci 1517a46c0ec8Sopenharmony_ci /* two-finger scrolling is always enabled, this flag just 1518a46c0ec8Sopenharmony_ci * decides whether we detect pinch. semi-mt devices are too 1519a46c0ec8Sopenharmony_ci * unreliable to do pinch gestures. */ 1520a46c0ec8Sopenharmony_ci tp->gesture.enabled = tp_gesture_are_gestures_enabled(tp); 1521a46c0ec8Sopenharmony_ci 1522a46c0ec8Sopenharmony_ci tp->gesture.state = GESTURE_STATE_NONE; 1523a46c0ec8Sopenharmony_ci tp->gesture.hold_enabled = tp_gesture_are_gestures_enabled(tp); 1524a46c0ec8Sopenharmony_ci 1525a46c0ec8Sopenharmony_ci snprintf(timer_name, 1526a46c0ec8Sopenharmony_ci sizeof(timer_name), 1527a46c0ec8Sopenharmony_ci "%s gestures", 1528a46c0ec8Sopenharmony_ci evdev_device_get_sysname(tp->device)); 1529a46c0ec8Sopenharmony_ci libinput_timer_init(&tp->gesture.finger_count_switch_timer, 1530a46c0ec8Sopenharmony_ci tp_libinput_context(tp), 1531a46c0ec8Sopenharmony_ci timer_name, 1532a46c0ec8Sopenharmony_ci tp_gesture_finger_count_switch_timeout, tp); 1533a46c0ec8Sopenharmony_ci 1534a46c0ec8Sopenharmony_ci snprintf(timer_name, 1535a46c0ec8Sopenharmony_ci sizeof(timer_name), 1536a46c0ec8Sopenharmony_ci "%s hold", 1537a46c0ec8Sopenharmony_ci evdev_device_get_sysname(tp->device)); 1538a46c0ec8Sopenharmony_ci libinput_timer_init(&tp->gesture.hold_timer, 1539a46c0ec8Sopenharmony_ci tp_libinput_context(tp), 1540a46c0ec8Sopenharmony_ci timer_name, 1541a46c0ec8Sopenharmony_ci tp_gesture_hold_timeout, tp); 1542a46c0ec8Sopenharmony_ci} 1543a46c0ec8Sopenharmony_ci 1544a46c0ec8Sopenharmony_civoid 1545a46c0ec8Sopenharmony_citp_remove_gesture(struct tp_dispatch *tp) 1546a46c0ec8Sopenharmony_ci{ 1547a46c0ec8Sopenharmony_ci libinput_timer_cancel(&tp->gesture.finger_count_switch_timer); 1548a46c0ec8Sopenharmony_ci libinput_timer_cancel(&tp->gesture.hold_timer); 1549a46c0ec8Sopenharmony_ci} 1550