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, &center);
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