1a46c0ec8Sopenharmony_ci/*
2a46c0ec8Sopenharmony_ci * Copyright © 2014-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 <assert.h>
27a46c0ec8Sopenharmony_ci#include <math.h>
28a46c0ec8Sopenharmony_ci#include <stdbool.h>
29a46c0ec8Sopenharmony_ci#include <limits.h>
30a46c0ec8Sopenharmony_ci
31a46c0ec8Sopenharmony_ci#if HAVE_LIBWACOM
32a46c0ec8Sopenharmony_ci#include <libwacom/libwacom.h>
33a46c0ec8Sopenharmony_ci#endif
34a46c0ec8Sopenharmony_ci
35a46c0ec8Sopenharmony_ci#include "quirks.h"
36a46c0ec8Sopenharmony_ci#include "evdev-mt-touchpad.h"
37a46c0ec8Sopenharmony_ci#include "util-input-event.h"
38a46c0ec8Sopenharmony_ci
39a46c0ec8Sopenharmony_ci#define DEFAULT_TRACKPOINT_ACTIVITY_TIMEOUT ms2us(300)
40a46c0ec8Sopenharmony_ci#define DEFAULT_TRACKPOINT_EVENT_TIMEOUT ms2us(40)
41a46c0ec8Sopenharmony_ci#define DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_1 ms2us(200)
42a46c0ec8Sopenharmony_ci#define DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_2 ms2us(500)
43a46c0ec8Sopenharmony_ci#define FAKE_FINGER_OVERFLOW bit(7)
44a46c0ec8Sopenharmony_ci#define THUMB_IGNORE_SPEED_THRESHOLD 20 /* mm/s */
45a46c0ec8Sopenharmony_ci
46a46c0ec8Sopenharmony_cienum notify {
47a46c0ec8Sopenharmony_ci	DONT_NOTIFY,
48a46c0ec8Sopenharmony_ci	DO_NOTIFY,
49a46c0ec8Sopenharmony_ci};
50a46c0ec8Sopenharmony_ci
51a46c0ec8Sopenharmony_cistatic inline struct tp_history_point*
52a46c0ec8Sopenharmony_citp_motion_history_offset(struct tp_touch *t, int offset)
53a46c0ec8Sopenharmony_ci{
54a46c0ec8Sopenharmony_ci	int offset_index =
55a46c0ec8Sopenharmony_ci		(t->history.index - offset + TOUCHPAD_HISTORY_LENGTH) %
56a46c0ec8Sopenharmony_ci		TOUCHPAD_HISTORY_LENGTH;
57a46c0ec8Sopenharmony_ci
58a46c0ec8Sopenharmony_ci	return &t->history.samples[offset_index];
59a46c0ec8Sopenharmony_ci}
60a46c0ec8Sopenharmony_ci
61a46c0ec8Sopenharmony_cistruct normalized_coords
62a46c0ec8Sopenharmony_citp_filter_motion(struct tp_dispatch *tp,
63a46c0ec8Sopenharmony_ci		 const struct device_float_coords *unaccelerated,
64a46c0ec8Sopenharmony_ci		 uint64_t time)
65a46c0ec8Sopenharmony_ci{
66a46c0ec8Sopenharmony_ci	struct device_float_coords raw;
67a46c0ec8Sopenharmony_ci	const struct normalized_coords zero = { 0.0, 0.0 };
68a46c0ec8Sopenharmony_ci
69a46c0ec8Sopenharmony_ci	if (device_float_is_zero(*unaccelerated))
70a46c0ec8Sopenharmony_ci		return zero;
71a46c0ec8Sopenharmony_ci
72a46c0ec8Sopenharmony_ci	/* Convert to device units with x/y in the same resolution */
73a46c0ec8Sopenharmony_ci	raw = tp_scale_to_xaxis(tp, *unaccelerated);
74a46c0ec8Sopenharmony_ci
75a46c0ec8Sopenharmony_ci	return filter_dispatch(tp->device->pointer.filter,
76a46c0ec8Sopenharmony_ci			       &raw, tp, time);
77a46c0ec8Sopenharmony_ci}
78a46c0ec8Sopenharmony_ci
79a46c0ec8Sopenharmony_cistruct normalized_coords
80a46c0ec8Sopenharmony_citp_filter_motion_unaccelerated(struct tp_dispatch *tp,
81a46c0ec8Sopenharmony_ci			       const struct device_float_coords *unaccelerated,
82a46c0ec8Sopenharmony_ci			       uint64_t time)
83a46c0ec8Sopenharmony_ci{
84a46c0ec8Sopenharmony_ci	struct device_float_coords raw;
85a46c0ec8Sopenharmony_ci	const struct normalized_coords zero = { 0.0, 0.0 };
86a46c0ec8Sopenharmony_ci
87a46c0ec8Sopenharmony_ci	if (device_float_is_zero(*unaccelerated))
88a46c0ec8Sopenharmony_ci		return zero;
89a46c0ec8Sopenharmony_ci
90a46c0ec8Sopenharmony_ci	/* Convert to device units with x/y in the same resolution */
91a46c0ec8Sopenharmony_ci	raw = tp_scale_to_xaxis(tp, *unaccelerated);
92a46c0ec8Sopenharmony_ci
93a46c0ec8Sopenharmony_ci	return filter_dispatch_constant(tp->device->pointer.filter,
94a46c0ec8Sopenharmony_ci					&raw, tp, time);
95a46c0ec8Sopenharmony_ci}
96a46c0ec8Sopenharmony_ci
97a46c0ec8Sopenharmony_cistruct normalized_coords
98a46c0ec8Sopenharmony_citp_filter_scroll(struct tp_dispatch *tp,
99a46c0ec8Sopenharmony_ci		 const struct device_float_coords *unaccelerated,
100a46c0ec8Sopenharmony_ci		 uint64_t time)
101a46c0ec8Sopenharmony_ci{
102a46c0ec8Sopenharmony_ci	struct device_float_coords raw;
103a46c0ec8Sopenharmony_ci	const struct normalized_coords zero = { 0.0, 0.0 };
104a46c0ec8Sopenharmony_ci
105a46c0ec8Sopenharmony_ci	if (device_float_is_zero(*unaccelerated))
106a46c0ec8Sopenharmony_ci		return zero;
107a46c0ec8Sopenharmony_ci
108a46c0ec8Sopenharmony_ci	/* Convert to device units with x/y in the same resolution */
109a46c0ec8Sopenharmony_ci	raw = tp_scale_to_xaxis(tp, *unaccelerated);
110a46c0ec8Sopenharmony_ci
111a46c0ec8Sopenharmony_ci	return filter_dispatch_scroll(tp->device->pointer.filter,
112a46c0ec8Sopenharmony_ci				      &raw, tp, time);
113a46c0ec8Sopenharmony_ci}
114a46c0ec8Sopenharmony_ci
115a46c0ec8Sopenharmony_cistatic inline void
116a46c0ec8Sopenharmony_citp_calculate_motion_speed(struct tp_dispatch *tp,
117a46c0ec8Sopenharmony_ci			  struct tp_touch *t,
118a46c0ec8Sopenharmony_ci			  uint64_t time)
119a46c0ec8Sopenharmony_ci{
120a46c0ec8Sopenharmony_ci	const struct tp_history_point *last;
121a46c0ec8Sopenharmony_ci	struct device_coords delta;
122a46c0ec8Sopenharmony_ci	struct phys_coords mm;
123a46c0ec8Sopenharmony_ci	double distance;
124a46c0ec8Sopenharmony_ci	double speed;
125a46c0ec8Sopenharmony_ci
126a46c0ec8Sopenharmony_ci	/* Don't do this on single-touch or semi-mt devices */
127a46c0ec8Sopenharmony_ci	if (!tp->has_mt || tp->semi_mt)
128a46c0ec8Sopenharmony_ci		return;
129a46c0ec8Sopenharmony_ci
130a46c0ec8Sopenharmony_ci	if (t->state != TOUCH_UPDATE)
131a46c0ec8Sopenharmony_ci		return;
132a46c0ec8Sopenharmony_ci
133a46c0ec8Sopenharmony_ci	/* This doesn't kick in until we have at least 4 events in the
134a46c0ec8Sopenharmony_ci	 * motion history. As a side-effect, this automatically handles the
135a46c0ec8Sopenharmony_ci	 * 2fg scroll where a finger is down and moving fast before the
136a46c0ec8Sopenharmony_ci	 * other finger comes down for the scroll.
137a46c0ec8Sopenharmony_ci	 *
138a46c0ec8Sopenharmony_ci	 * We do *not* reset the speed to 0 here though. The motion history
139a46c0ec8Sopenharmony_ci	 * is reset whenever a new finger is down, so we'd be resetting the
140a46c0ec8Sopenharmony_ci	 * speed and failing.
141a46c0ec8Sopenharmony_ci	 */
142a46c0ec8Sopenharmony_ci	if (t->history.count < 4)
143a46c0ec8Sopenharmony_ci		return;
144a46c0ec8Sopenharmony_ci
145a46c0ec8Sopenharmony_ci	/* TODO: we probably need a speed history here so we can average
146a46c0ec8Sopenharmony_ci	 * across a few events */
147a46c0ec8Sopenharmony_ci	last = tp_motion_history_offset(t, 1);
148a46c0ec8Sopenharmony_ci	delta.x = abs(t->point.x - last->point.x);
149a46c0ec8Sopenharmony_ci	delta.y = abs(t->point.y - last->point.y);
150a46c0ec8Sopenharmony_ci	mm = evdev_device_unit_delta_to_mm(tp->device, &delta);
151a46c0ec8Sopenharmony_ci
152a46c0ec8Sopenharmony_ci	distance = length_in_mm(mm);
153a46c0ec8Sopenharmony_ci	speed = distance/(time - last->time); /* mm/us */
154a46c0ec8Sopenharmony_ci	speed *= 1000000; /* mm/s */
155a46c0ec8Sopenharmony_ci
156a46c0ec8Sopenharmony_ci	t->speed.last_speed = speed;
157a46c0ec8Sopenharmony_ci}
158a46c0ec8Sopenharmony_ci
159a46c0ec8Sopenharmony_cistatic inline void
160a46c0ec8Sopenharmony_citp_motion_history_push(struct tp_touch *t, uint64_t time)
161a46c0ec8Sopenharmony_ci{
162a46c0ec8Sopenharmony_ci	int motion_index = (t->history.index + 1) % TOUCHPAD_HISTORY_LENGTH;
163a46c0ec8Sopenharmony_ci
164a46c0ec8Sopenharmony_ci	if (t->history.count < TOUCHPAD_HISTORY_LENGTH)
165a46c0ec8Sopenharmony_ci		t->history.count++;
166a46c0ec8Sopenharmony_ci
167a46c0ec8Sopenharmony_ci	t->history.samples[motion_index].point = t->point;
168a46c0ec8Sopenharmony_ci	t->history.samples[motion_index].time = time;
169a46c0ec8Sopenharmony_ci	t->history.index = motion_index;
170a46c0ec8Sopenharmony_ci}
171a46c0ec8Sopenharmony_ci
172a46c0ec8Sopenharmony_ci/* Idea: if we got a tuple of *very* quick moves like {Left, Right,
173a46c0ec8Sopenharmony_ci * Left}, or {Right, Left, Right}, it means touchpad jitters since no
174a46c0ec8Sopenharmony_ci * human can move like that within thresholds.
175a46c0ec8Sopenharmony_ci *
176a46c0ec8Sopenharmony_ci * We encode left moves as zeroes, and right as ones. We also drop
177a46c0ec8Sopenharmony_ci * the array to all zeroes when constraints are not satisfied. Then we
178a46c0ec8Sopenharmony_ci * search for the pattern {1,0,1}. It can't match {Left, Right, Left},
179a46c0ec8Sopenharmony_ci * but it does match {Left, Right, Left, Right}, so it's okay.
180a46c0ec8Sopenharmony_ci *
181a46c0ec8Sopenharmony_ci * This only looks at x changes, y changes are ignored.
182a46c0ec8Sopenharmony_ci */
183a46c0ec8Sopenharmony_cistatic inline void
184a46c0ec8Sopenharmony_citp_detect_wobbling(struct tp_dispatch *tp,
185a46c0ec8Sopenharmony_ci		   struct tp_touch *t,
186a46c0ec8Sopenharmony_ci		   uint64_t time)
187a46c0ec8Sopenharmony_ci{
188a46c0ec8Sopenharmony_ci	int dx, dy;
189a46c0ec8Sopenharmony_ci	uint64_t dtime;
190a46c0ec8Sopenharmony_ci	const struct device_coords* prev_point;
191a46c0ec8Sopenharmony_ci
192a46c0ec8Sopenharmony_ci	if (tp->nfingers_down != 1 ||
193a46c0ec8Sopenharmony_ci	    tp->nfingers_down != tp->old_nfingers_down)
194a46c0ec8Sopenharmony_ci		return;
195a46c0ec8Sopenharmony_ci
196a46c0ec8Sopenharmony_ci	if (tp->hysteresis.enabled || t->history.count == 0)
197a46c0ec8Sopenharmony_ci		return;
198a46c0ec8Sopenharmony_ci
199a46c0ec8Sopenharmony_ci	if (!(tp->queued & TOUCHPAD_EVENT_MOTION)) {
200a46c0ec8Sopenharmony_ci		t->hysteresis.x_motion_history = 0;
201a46c0ec8Sopenharmony_ci		return;
202a46c0ec8Sopenharmony_ci	}
203a46c0ec8Sopenharmony_ci
204a46c0ec8Sopenharmony_ci	prev_point = &tp_motion_history_offset(t, 0)->point;
205a46c0ec8Sopenharmony_ci	dx = prev_point->x - t->point.x;
206a46c0ec8Sopenharmony_ci	dy = prev_point->y - t->point.y;
207a46c0ec8Sopenharmony_ci	dtime = time - tp->hysteresis.last_motion_time;
208a46c0ec8Sopenharmony_ci
209a46c0ec8Sopenharmony_ci	tp->hysteresis.last_motion_time = time;
210a46c0ec8Sopenharmony_ci
211a46c0ec8Sopenharmony_ci	if ((dx == 0 && dy != 0) || dtime > ms2us(40)) {
212a46c0ec8Sopenharmony_ci		t->hysteresis.x_motion_history = 0;
213a46c0ec8Sopenharmony_ci		return;
214a46c0ec8Sopenharmony_ci	}
215a46c0ec8Sopenharmony_ci
216a46c0ec8Sopenharmony_ci	t->hysteresis.x_motion_history >>= 1;
217a46c0ec8Sopenharmony_ci	if (dx > 0) { /* right move */
218a46c0ec8Sopenharmony_ci		static const char r_l_r = 0x5; /* {Right, Left, Right} */
219a46c0ec8Sopenharmony_ci
220a46c0ec8Sopenharmony_ci		t->hysteresis.x_motion_history |= bit(2);
221a46c0ec8Sopenharmony_ci		if (t->hysteresis.x_motion_history == r_l_r) {
222a46c0ec8Sopenharmony_ci			tp->hysteresis.enabled = true;
223a46c0ec8Sopenharmony_ci			evdev_log_debug(tp->device,
224a46c0ec8Sopenharmony_ci					"hysteresis enabled. "
225a46c0ec8Sopenharmony_ci					"See %s/touchpad-jitter.html for details\n",
226a46c0ec8Sopenharmony_ci					HTTP_DOC_LINK);
227a46c0ec8Sopenharmony_ci		}
228a46c0ec8Sopenharmony_ci	}
229a46c0ec8Sopenharmony_ci}
230a46c0ec8Sopenharmony_ci
231a46c0ec8Sopenharmony_cistatic inline void
232a46c0ec8Sopenharmony_citp_motion_hysteresis(struct tp_dispatch *tp,
233a46c0ec8Sopenharmony_ci		     struct tp_touch *t)
234a46c0ec8Sopenharmony_ci{
235a46c0ec8Sopenharmony_ci	if (!tp->hysteresis.enabled)
236a46c0ec8Sopenharmony_ci		return;
237a46c0ec8Sopenharmony_ci
238a46c0ec8Sopenharmony_ci	if (t->history.count > 0)
239a46c0ec8Sopenharmony_ci		t->point = evdev_hysteresis(&t->point,
240a46c0ec8Sopenharmony_ci					    &t->hysteresis.center,
241a46c0ec8Sopenharmony_ci					    &tp->hysteresis.margin);
242a46c0ec8Sopenharmony_ci
243a46c0ec8Sopenharmony_ci	t->hysteresis.center = t->point;
244a46c0ec8Sopenharmony_ci}
245a46c0ec8Sopenharmony_ci
246a46c0ec8Sopenharmony_cistatic inline void
247a46c0ec8Sopenharmony_citp_motion_history_reset(struct tp_touch *t)
248a46c0ec8Sopenharmony_ci{
249a46c0ec8Sopenharmony_ci	t->history.count = 0;
250a46c0ec8Sopenharmony_ci}
251a46c0ec8Sopenharmony_ci
252a46c0ec8Sopenharmony_cistatic inline struct tp_touch *
253a46c0ec8Sopenharmony_citp_current_touch(struct tp_dispatch *tp)
254a46c0ec8Sopenharmony_ci{
255a46c0ec8Sopenharmony_ci	return &tp->touches[min(tp->slot, tp->ntouches - 1)];
256a46c0ec8Sopenharmony_ci}
257a46c0ec8Sopenharmony_ci
258a46c0ec8Sopenharmony_cistatic inline struct tp_touch *
259a46c0ec8Sopenharmony_citp_get_touch(struct tp_dispatch *tp, unsigned int slot)
260a46c0ec8Sopenharmony_ci{
261a46c0ec8Sopenharmony_ci	assert(slot < tp->ntouches);
262a46c0ec8Sopenharmony_ci	return &tp->touches[slot];
263a46c0ec8Sopenharmony_ci}
264a46c0ec8Sopenharmony_ci
265a46c0ec8Sopenharmony_cistatic inline unsigned int
266a46c0ec8Sopenharmony_citp_fake_finger_count(struct tp_dispatch *tp)
267a46c0ec8Sopenharmony_ci{
268a46c0ec8Sopenharmony_ci	unsigned int fake_touches =
269a46c0ec8Sopenharmony_ci		tp->fake_touches & ~(FAKE_FINGER_OVERFLOW|0x1);
270a46c0ec8Sopenharmony_ci
271a46c0ec8Sopenharmony_ci	/* Only one of BTN_TOOL_DOUBLETAP/TRIPLETAP/... may be set at any
272a46c0ec8Sopenharmony_ci	 * time */
273a46c0ec8Sopenharmony_ci	if (fake_touches & (fake_touches - 1))
274a46c0ec8Sopenharmony_ci		evdev_log_bug_kernel(tp->device,
275a46c0ec8Sopenharmony_ci				     "Invalid fake finger state %#x\n",
276a46c0ec8Sopenharmony_ci				     tp->fake_touches);
277a46c0ec8Sopenharmony_ci
278a46c0ec8Sopenharmony_ci	if (tp->fake_touches & FAKE_FINGER_OVERFLOW)
279a46c0ec8Sopenharmony_ci		return FAKE_FINGER_OVERFLOW;
280a46c0ec8Sopenharmony_ci
281a46c0ec8Sopenharmony_ci	/* don't count BTN_TOUCH */
282a46c0ec8Sopenharmony_ci	return ffs(tp->fake_touches >> 1);
283a46c0ec8Sopenharmony_ci}
284a46c0ec8Sopenharmony_ci
285a46c0ec8Sopenharmony_cistatic inline bool
286a46c0ec8Sopenharmony_citp_fake_finger_is_touching(struct tp_dispatch *tp)
287a46c0ec8Sopenharmony_ci{
288a46c0ec8Sopenharmony_ci	return tp->fake_touches & 0x1;
289a46c0ec8Sopenharmony_ci}
290a46c0ec8Sopenharmony_ci
291a46c0ec8Sopenharmony_cistatic inline void
292a46c0ec8Sopenharmony_citp_fake_finger_set(struct tp_dispatch *tp,
293a46c0ec8Sopenharmony_ci		   unsigned int code,
294a46c0ec8Sopenharmony_ci		   bool is_press)
295a46c0ec8Sopenharmony_ci{
296a46c0ec8Sopenharmony_ci	unsigned int shift;
297a46c0ec8Sopenharmony_ci
298a46c0ec8Sopenharmony_ci	switch (code) {
299a46c0ec8Sopenharmony_ci	case BTN_TOUCH:
300a46c0ec8Sopenharmony_ci		if (!is_press)
301a46c0ec8Sopenharmony_ci			tp->fake_touches &= ~FAKE_FINGER_OVERFLOW;
302a46c0ec8Sopenharmony_ci		shift = 0;
303a46c0ec8Sopenharmony_ci		break;
304a46c0ec8Sopenharmony_ci	case BTN_TOOL_FINGER:
305a46c0ec8Sopenharmony_ci		shift = 1;
306a46c0ec8Sopenharmony_ci		break;
307a46c0ec8Sopenharmony_ci	case BTN_TOOL_DOUBLETAP:
308a46c0ec8Sopenharmony_ci	case BTN_TOOL_TRIPLETAP:
309a46c0ec8Sopenharmony_ci	case BTN_TOOL_QUADTAP:
310a46c0ec8Sopenharmony_ci		shift = code - BTN_TOOL_DOUBLETAP + 2;
311a46c0ec8Sopenharmony_ci		break;
312a46c0ec8Sopenharmony_ci	/* when QUINTTAP is released we're either switching to 6 fingers
313a46c0ec8Sopenharmony_ci	   (flag stays in place until BTN_TOUCH is released) or
314a46c0ec8Sopenharmony_ci	   one of DOUBLE/TRIPLE/QUADTAP (will clear the flag on press) */
315a46c0ec8Sopenharmony_ci	case BTN_TOOL_QUINTTAP:
316a46c0ec8Sopenharmony_ci		if (is_press)
317a46c0ec8Sopenharmony_ci			tp->fake_touches |= FAKE_FINGER_OVERFLOW;
318a46c0ec8Sopenharmony_ci		return;
319a46c0ec8Sopenharmony_ci	default:
320a46c0ec8Sopenharmony_ci		return;
321a46c0ec8Sopenharmony_ci	}
322a46c0ec8Sopenharmony_ci
323a46c0ec8Sopenharmony_ci	if (is_press) {
324a46c0ec8Sopenharmony_ci		tp->fake_touches &= ~FAKE_FINGER_OVERFLOW;
325a46c0ec8Sopenharmony_ci		tp->fake_touches |= bit(shift);
326a46c0ec8Sopenharmony_ci
327a46c0ec8Sopenharmony_ci	} else {
328a46c0ec8Sopenharmony_ci		tp->fake_touches &= ~bit(shift);
329a46c0ec8Sopenharmony_ci	}
330a46c0ec8Sopenharmony_ci}
331a46c0ec8Sopenharmony_ci
332a46c0ec8Sopenharmony_cistatic inline void
333a46c0ec8Sopenharmony_citp_new_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
334a46c0ec8Sopenharmony_ci{
335a46c0ec8Sopenharmony_ci	if (t->state == TOUCH_BEGIN ||
336a46c0ec8Sopenharmony_ci	    t->state == TOUCH_UPDATE ||
337a46c0ec8Sopenharmony_ci	    t->state == TOUCH_HOVERING)
338a46c0ec8Sopenharmony_ci		return;
339a46c0ec8Sopenharmony_ci
340a46c0ec8Sopenharmony_ci	/* Bug #161: touch ends in the same event frame where it restarts
341a46c0ec8Sopenharmony_ci	   again. That's a kernel bug, so let's complain. */
342a46c0ec8Sopenharmony_ci	if (t->state == TOUCH_MAYBE_END) {
343a46c0ec8Sopenharmony_ci		evdev_log_bug_kernel(tp->device,
344a46c0ec8Sopenharmony_ci				     "touch %d ended and began in in same frame.\n",
345a46c0ec8Sopenharmony_ci				     t->index);
346a46c0ec8Sopenharmony_ci		tp->nfingers_down++;
347a46c0ec8Sopenharmony_ci		t->state = TOUCH_UPDATE;
348a46c0ec8Sopenharmony_ci		t->has_ended = false;
349a46c0ec8Sopenharmony_ci		return;
350a46c0ec8Sopenharmony_ci	}
351a46c0ec8Sopenharmony_ci
352a46c0ec8Sopenharmony_ci	/* we begin the touch as hovering because until BTN_TOUCH happens we
353a46c0ec8Sopenharmony_ci	 * don't know if it's a touch down or not. And BTN_TOUCH may happen
354a46c0ec8Sopenharmony_ci	 * after ABS_MT_TRACKING_ID */
355a46c0ec8Sopenharmony_ci	tp_motion_history_reset(t);
356a46c0ec8Sopenharmony_ci	t->dirty = true;
357a46c0ec8Sopenharmony_ci	t->has_ended = false;
358a46c0ec8Sopenharmony_ci	t->was_down = false;
359a46c0ec8Sopenharmony_ci	t->palm.state = PALM_NONE;
360a46c0ec8Sopenharmony_ci	t->state = TOUCH_HOVERING;
361a46c0ec8Sopenharmony_ci	t->pinned.is_pinned = false;
362a46c0ec8Sopenharmony_ci	t->speed.last_speed = 0;
363a46c0ec8Sopenharmony_ci	t->speed.exceeded_count = 0;
364a46c0ec8Sopenharmony_ci	t->hysteresis.x_motion_history = 0;
365a46c0ec8Sopenharmony_ci	tp->queued |= TOUCHPAD_EVENT_MOTION;
366a46c0ec8Sopenharmony_ci}
367a46c0ec8Sopenharmony_ci
368a46c0ec8Sopenharmony_cistatic inline void
369a46c0ec8Sopenharmony_citp_begin_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
370a46c0ec8Sopenharmony_ci{
371a46c0ec8Sopenharmony_ci	t->dirty = true;
372a46c0ec8Sopenharmony_ci	t->state = TOUCH_BEGIN;
373a46c0ec8Sopenharmony_ci	t->initial_time = time;
374a46c0ec8Sopenharmony_ci	t->was_down = true;
375a46c0ec8Sopenharmony_ci	tp->nfingers_down++;
376a46c0ec8Sopenharmony_ci	t->palm.time = time;
377a46c0ec8Sopenharmony_ci	t->tap.is_thumb = false;
378a46c0ec8Sopenharmony_ci	t->tap.is_palm = false;
379a46c0ec8Sopenharmony_ci	t->speed.exceeded_count = 0;
380a46c0ec8Sopenharmony_ci	assert(tp->nfingers_down >= 1);
381a46c0ec8Sopenharmony_ci	tp->hysteresis.last_motion_time = time;
382a46c0ec8Sopenharmony_ci}
383a46c0ec8Sopenharmony_ci
384a46c0ec8Sopenharmony_ci/**
385a46c0ec8Sopenharmony_ci * Schedule a touch to be ended, based on either the events or some
386a46c0ec8Sopenharmony_ci * attributes of the touch (size, pressure). In some cases we need to
387a46c0ec8Sopenharmony_ci * resurrect a touch that has ended, so this doesn't actually end the touch
388a46c0ec8Sopenharmony_ci * yet. All the TOUCH_MAYBE_END touches get properly ended once the device
389a46c0ec8Sopenharmony_ci * state has been processed once and we know how many zombie touches we
390a46c0ec8Sopenharmony_ci * need.
391a46c0ec8Sopenharmony_ci */
392a46c0ec8Sopenharmony_cistatic inline void
393a46c0ec8Sopenharmony_citp_maybe_end_touch(struct tp_dispatch *tp,
394a46c0ec8Sopenharmony_ci		   struct tp_touch *t,
395a46c0ec8Sopenharmony_ci		   uint64_t time)
396a46c0ec8Sopenharmony_ci{
397a46c0ec8Sopenharmony_ci	switch (t->state) {
398a46c0ec8Sopenharmony_ci	case TOUCH_NONE:
399a46c0ec8Sopenharmony_ci	case TOUCH_MAYBE_END:
400a46c0ec8Sopenharmony_ci		return;
401a46c0ec8Sopenharmony_ci	case TOUCH_END:
402a46c0ec8Sopenharmony_ci		evdev_log_bug_libinput(tp->device,
403a46c0ec8Sopenharmony_ci				       "touch %d: already in TOUCH_END\n",
404a46c0ec8Sopenharmony_ci				       t->index);
405a46c0ec8Sopenharmony_ci		return;
406a46c0ec8Sopenharmony_ci	case TOUCH_HOVERING:
407a46c0ec8Sopenharmony_ci	case TOUCH_BEGIN:
408a46c0ec8Sopenharmony_ci	case TOUCH_UPDATE:
409a46c0ec8Sopenharmony_ci		break;
410a46c0ec8Sopenharmony_ci	}
411a46c0ec8Sopenharmony_ci
412a46c0ec8Sopenharmony_ci	if (t->state != TOUCH_HOVERING) {
413a46c0ec8Sopenharmony_ci		assert(tp->nfingers_down >= 1);
414a46c0ec8Sopenharmony_ci		tp->nfingers_down--;
415a46c0ec8Sopenharmony_ci		t->state = TOUCH_MAYBE_END;
416a46c0ec8Sopenharmony_ci	} else {
417a46c0ec8Sopenharmony_ci		t->state = TOUCH_NONE;
418a46c0ec8Sopenharmony_ci	}
419a46c0ec8Sopenharmony_ci
420a46c0ec8Sopenharmony_ci	t->dirty = true;
421a46c0ec8Sopenharmony_ci}
422a46c0ec8Sopenharmony_ci
423a46c0ec8Sopenharmony_ci/**
424a46c0ec8Sopenharmony_ci * Inverse to tp_maybe_end_touch(), restores a touch back to its previous
425a46c0ec8Sopenharmony_ci * state.
426a46c0ec8Sopenharmony_ci */
427a46c0ec8Sopenharmony_cistatic inline void
428a46c0ec8Sopenharmony_citp_recover_ended_touch(struct tp_dispatch *tp,
429a46c0ec8Sopenharmony_ci		       struct tp_touch *t)
430a46c0ec8Sopenharmony_ci{
431a46c0ec8Sopenharmony_ci	t->dirty = true;
432a46c0ec8Sopenharmony_ci	t->state = TOUCH_UPDATE;
433a46c0ec8Sopenharmony_ci	tp->nfingers_down++;
434a46c0ec8Sopenharmony_ci}
435a46c0ec8Sopenharmony_ci
436a46c0ec8Sopenharmony_ci/**
437a46c0ec8Sopenharmony_ci * End a touch, even if the touch sequence is still active.
438a46c0ec8Sopenharmony_ci * Use tp_maybe_end_touch() instead.
439a46c0ec8Sopenharmony_ci */
440a46c0ec8Sopenharmony_cistatic inline void
441a46c0ec8Sopenharmony_citp_end_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
442a46c0ec8Sopenharmony_ci{
443a46c0ec8Sopenharmony_ci	if (t->state != TOUCH_MAYBE_END) {
444a46c0ec8Sopenharmony_ci		evdev_log_bug_libinput(tp->device,
445a46c0ec8Sopenharmony_ci				       "touch %d should be MAYBE_END, is %d\n",
446a46c0ec8Sopenharmony_ci				       t->index,
447a46c0ec8Sopenharmony_ci				       t->state);
448a46c0ec8Sopenharmony_ci		return;
449a46c0ec8Sopenharmony_ci	}
450a46c0ec8Sopenharmony_ci
451a46c0ec8Sopenharmony_ci	t->dirty = true;
452a46c0ec8Sopenharmony_ci	t->palm.state = PALM_NONE;
453a46c0ec8Sopenharmony_ci	t->state = TOUCH_END;
454a46c0ec8Sopenharmony_ci	t->pinned.is_pinned = false;
455a46c0ec8Sopenharmony_ci	t->palm.time = 0;
456a46c0ec8Sopenharmony_ci	t->speed.exceeded_count = 0;
457a46c0ec8Sopenharmony_ci	tp->queued |= TOUCHPAD_EVENT_MOTION;
458a46c0ec8Sopenharmony_ci}
459a46c0ec8Sopenharmony_ci
460a46c0ec8Sopenharmony_ci/**
461a46c0ec8Sopenharmony_ci * End the touch sequence on ABS_MT_TRACKING_ID -1 or when the BTN_TOOL_* 0 is received.
462a46c0ec8Sopenharmony_ci */
463a46c0ec8Sopenharmony_cistatic inline void
464a46c0ec8Sopenharmony_citp_end_sequence(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
465a46c0ec8Sopenharmony_ci{
466a46c0ec8Sopenharmony_ci	t->has_ended = true;
467a46c0ec8Sopenharmony_ci	tp_maybe_end_touch(tp, t, time);
468a46c0ec8Sopenharmony_ci}
469a46c0ec8Sopenharmony_ci
470a46c0ec8Sopenharmony_cistatic void
471a46c0ec8Sopenharmony_citp_stop_actions(struct tp_dispatch *tp, uint64_t time)
472a46c0ec8Sopenharmony_ci{
473a46c0ec8Sopenharmony_ci	tp_edge_scroll_stop_events(tp, time);
474a46c0ec8Sopenharmony_ci	tp_gesture_cancel(tp, time);
475a46c0ec8Sopenharmony_ci	tp_tap_suspend(tp, time);
476a46c0ec8Sopenharmony_ci}
477a46c0ec8Sopenharmony_ci
478a46c0ec8Sopenharmony_cistruct device_coords
479a46c0ec8Sopenharmony_citp_get_delta(struct tp_touch *t)
480a46c0ec8Sopenharmony_ci{
481a46c0ec8Sopenharmony_ci	struct device_coords delta;
482a46c0ec8Sopenharmony_ci	const struct device_coords zero = { 0.0, 0.0 };
483a46c0ec8Sopenharmony_ci
484a46c0ec8Sopenharmony_ci	if (t->history.count <= 1)
485a46c0ec8Sopenharmony_ci		return zero;
486a46c0ec8Sopenharmony_ci
487a46c0ec8Sopenharmony_ci	delta.x = tp_motion_history_offset(t, 0)->point.x -
488a46c0ec8Sopenharmony_ci		  tp_motion_history_offset(t, 1)->point.x;
489a46c0ec8Sopenharmony_ci	delta.y = tp_motion_history_offset(t, 0)->point.y -
490a46c0ec8Sopenharmony_ci		  tp_motion_history_offset(t, 1)->point.y;
491a46c0ec8Sopenharmony_ci
492a46c0ec8Sopenharmony_ci	return delta;
493a46c0ec8Sopenharmony_ci}
494a46c0ec8Sopenharmony_ci
495a46c0ec8Sopenharmony_cistatic inline int32_t
496a46c0ec8Sopenharmony_cirotated(struct tp_dispatch *tp, unsigned int code, int value)
497a46c0ec8Sopenharmony_ci{
498a46c0ec8Sopenharmony_ci	const struct input_absinfo *absinfo;
499a46c0ec8Sopenharmony_ci
500a46c0ec8Sopenharmony_ci	if (!tp->left_handed.rotate)
501a46c0ec8Sopenharmony_ci		return value;
502a46c0ec8Sopenharmony_ci
503a46c0ec8Sopenharmony_ci	switch (code) {
504a46c0ec8Sopenharmony_ci	case ABS_X:
505a46c0ec8Sopenharmony_ci	case ABS_MT_POSITION_X:
506a46c0ec8Sopenharmony_ci		absinfo = tp->device->abs.absinfo_x;
507a46c0ec8Sopenharmony_ci		break;
508a46c0ec8Sopenharmony_ci	case ABS_Y:
509a46c0ec8Sopenharmony_ci	case ABS_MT_POSITION_Y:
510a46c0ec8Sopenharmony_ci		absinfo = tp->device->abs.absinfo_y;
511a46c0ec8Sopenharmony_ci		break;
512a46c0ec8Sopenharmony_ci	default:
513a46c0ec8Sopenharmony_ci		abort();
514a46c0ec8Sopenharmony_ci	}
515a46c0ec8Sopenharmony_ci	return absinfo->maximum - (value - absinfo->minimum);
516a46c0ec8Sopenharmony_ci}
517a46c0ec8Sopenharmony_ci
518a46c0ec8Sopenharmony_cistatic void
519a46c0ec8Sopenharmony_citp_process_absolute(struct tp_dispatch *tp,
520a46c0ec8Sopenharmony_ci		    const struct input_event *e,
521a46c0ec8Sopenharmony_ci		    uint64_t time)
522a46c0ec8Sopenharmony_ci{
523a46c0ec8Sopenharmony_ci	struct tp_touch *t = tp_current_touch(tp);
524a46c0ec8Sopenharmony_ci
525a46c0ec8Sopenharmony_ci	switch(e->code) {
526a46c0ec8Sopenharmony_ci	case ABS_MT_POSITION_X:
527a46c0ec8Sopenharmony_ci		evdev_device_check_abs_axis_range(tp->device,
528a46c0ec8Sopenharmony_ci						  e->code,
529a46c0ec8Sopenharmony_ci						  e->value);
530a46c0ec8Sopenharmony_ci		t->point.x = rotated(tp, e->code, e->value);
531a46c0ec8Sopenharmony_ci		t->dirty = true;
532a46c0ec8Sopenharmony_ci		tp->queued |= TOUCHPAD_EVENT_MOTION;
533a46c0ec8Sopenharmony_ci		break;
534a46c0ec8Sopenharmony_ci	case ABS_MT_POSITION_Y:
535a46c0ec8Sopenharmony_ci		evdev_device_check_abs_axis_range(tp->device,
536a46c0ec8Sopenharmony_ci						  e->code,
537a46c0ec8Sopenharmony_ci						  e->value);
538a46c0ec8Sopenharmony_ci		t->point.y = rotated(tp, e->code, e->value);
539a46c0ec8Sopenharmony_ci		t->dirty = true;
540a46c0ec8Sopenharmony_ci		tp->queued |= TOUCHPAD_EVENT_MOTION;
541a46c0ec8Sopenharmony_ci		break;
542a46c0ec8Sopenharmony_ci	case ABS_MT_SLOT:
543a46c0ec8Sopenharmony_ci		tp->slot = e->value;
544a46c0ec8Sopenharmony_ci		break;
545a46c0ec8Sopenharmony_ci	case ABS_MT_TRACKING_ID:
546a46c0ec8Sopenharmony_ci		if (e->value != -1) {
547a46c0ec8Sopenharmony_ci			tp->nactive_slots += 1;
548a46c0ec8Sopenharmony_ci			tp_new_touch(tp, t, time);
549a46c0ec8Sopenharmony_ci		} else {
550a46c0ec8Sopenharmony_ci			assert(tp->nactive_slots >= 1);
551a46c0ec8Sopenharmony_ci			tp->nactive_slots -= 1;
552a46c0ec8Sopenharmony_ci			tp_end_sequence(tp, t, time);
553a46c0ec8Sopenharmony_ci		}
554a46c0ec8Sopenharmony_ci		break;
555a46c0ec8Sopenharmony_ci	case ABS_MT_PRESSURE:
556a46c0ec8Sopenharmony_ci		t->pressure = e->value;
557a46c0ec8Sopenharmony_ci		t->dirty = true;
558a46c0ec8Sopenharmony_ci		tp->queued |= TOUCHPAD_EVENT_OTHERAXIS;
559a46c0ec8Sopenharmony_ci		break;
560a46c0ec8Sopenharmony_ci	case ABS_MT_TOOL_TYPE:
561a46c0ec8Sopenharmony_ci		t->is_tool_palm = e->value == MT_TOOL_PALM;
562a46c0ec8Sopenharmony_ci		t->dirty = true;
563a46c0ec8Sopenharmony_ci		tp->queued |= TOUCHPAD_EVENT_OTHERAXIS;
564a46c0ec8Sopenharmony_ci		break;
565a46c0ec8Sopenharmony_ci	case ABS_MT_TOUCH_MAJOR:
566a46c0ec8Sopenharmony_ci		t->major = e->value;
567a46c0ec8Sopenharmony_ci		t->dirty = true;
568a46c0ec8Sopenharmony_ci		tp->queued |= TOUCHPAD_EVENT_OTHERAXIS;
569a46c0ec8Sopenharmony_ci		break;
570a46c0ec8Sopenharmony_ci	case ABS_MT_TOUCH_MINOR:
571a46c0ec8Sopenharmony_ci		t->minor = e->value;
572a46c0ec8Sopenharmony_ci		t->dirty = true;
573a46c0ec8Sopenharmony_ci		tp->queued |= TOUCHPAD_EVENT_OTHERAXIS;
574a46c0ec8Sopenharmony_ci		break;
575a46c0ec8Sopenharmony_ci	}
576a46c0ec8Sopenharmony_ci}
577a46c0ec8Sopenharmony_ci
578a46c0ec8Sopenharmony_cistatic void
579a46c0ec8Sopenharmony_citp_process_absolute_st(struct tp_dispatch *tp,
580a46c0ec8Sopenharmony_ci		       const struct input_event *e,
581a46c0ec8Sopenharmony_ci		       uint64_t time)
582a46c0ec8Sopenharmony_ci{
583a46c0ec8Sopenharmony_ci	struct tp_touch *t = tp_current_touch(tp);
584a46c0ec8Sopenharmony_ci
585a46c0ec8Sopenharmony_ci	switch(e->code) {
586a46c0ec8Sopenharmony_ci	case ABS_X:
587a46c0ec8Sopenharmony_ci		evdev_device_check_abs_axis_range(tp->device,
588a46c0ec8Sopenharmony_ci						  e->code,
589a46c0ec8Sopenharmony_ci						  e->value);
590a46c0ec8Sopenharmony_ci		t->point.x = rotated(tp, e->code, e->value);
591a46c0ec8Sopenharmony_ci		t->dirty = true;
592a46c0ec8Sopenharmony_ci		tp->queued |= TOUCHPAD_EVENT_MOTION;
593a46c0ec8Sopenharmony_ci		break;
594a46c0ec8Sopenharmony_ci	case ABS_Y:
595a46c0ec8Sopenharmony_ci		evdev_device_check_abs_axis_range(tp->device,
596a46c0ec8Sopenharmony_ci						  e->code,
597a46c0ec8Sopenharmony_ci						  e->value);
598a46c0ec8Sopenharmony_ci		t->point.y = rotated(tp, e->code, e->value);
599a46c0ec8Sopenharmony_ci		t->dirty = true;
600a46c0ec8Sopenharmony_ci		tp->queued |= TOUCHPAD_EVENT_MOTION;
601a46c0ec8Sopenharmony_ci		break;
602a46c0ec8Sopenharmony_ci	case ABS_PRESSURE:
603a46c0ec8Sopenharmony_ci		t->pressure = e->value;
604a46c0ec8Sopenharmony_ci		t->dirty = true;
605a46c0ec8Sopenharmony_ci		tp->queued |= TOUCHPAD_EVENT_OTHERAXIS;
606a46c0ec8Sopenharmony_ci		break;
607a46c0ec8Sopenharmony_ci	}
608a46c0ec8Sopenharmony_ci}
609a46c0ec8Sopenharmony_ci
610a46c0ec8Sopenharmony_cistatic inline void
611a46c0ec8Sopenharmony_citp_restore_synaptics_touches(struct tp_dispatch *tp,
612a46c0ec8Sopenharmony_ci			     uint64_t time)
613a46c0ec8Sopenharmony_ci{
614a46c0ec8Sopenharmony_ci	unsigned int i;
615a46c0ec8Sopenharmony_ci	unsigned int nfake_touches;
616a46c0ec8Sopenharmony_ci
617a46c0ec8Sopenharmony_ci	nfake_touches = tp_fake_finger_count(tp);
618a46c0ec8Sopenharmony_ci	if (nfake_touches < 3)
619a46c0ec8Sopenharmony_ci		return;
620a46c0ec8Sopenharmony_ci
621a46c0ec8Sopenharmony_ci	if (tp->nfingers_down >= nfake_touches ||
622a46c0ec8Sopenharmony_ci	    (tp->nfingers_down == tp->num_slots && nfake_touches == tp->num_slots))
623a46c0ec8Sopenharmony_ci		return;
624a46c0ec8Sopenharmony_ci
625a46c0ec8Sopenharmony_ci	/* Synaptics devices may end touch 2 on transition to/from
626a46c0ec8Sopenharmony_ci	 * BTN_TOOL_TRIPLETAP and start it again on the next frame with
627a46c0ec8Sopenharmony_ci	 * different coordinates (bz#91352, gitlab#434). We search the
628a46c0ec8Sopenharmony_ci	 * touches we have, if there is one that has just ended despite us
629a46c0ec8Sopenharmony_ci	 * being on tripletap, we move it back to update.
630a46c0ec8Sopenharmony_ci	 *
631a46c0ec8Sopenharmony_ci	 * Note: we only handle the transition from 2 to 3 touches, not the
632a46c0ec8Sopenharmony_ci	 * other way round (see gitlab#434)
633a46c0ec8Sopenharmony_ci	 */
634a46c0ec8Sopenharmony_ci	for (i = 0; i < tp->num_slots; i++) {
635a46c0ec8Sopenharmony_ci		struct tp_touch *t = tp_get_touch(tp, i);
636a46c0ec8Sopenharmony_ci
637a46c0ec8Sopenharmony_ci		if (t->state != TOUCH_MAYBE_END)
638a46c0ec8Sopenharmony_ci			continue;
639a46c0ec8Sopenharmony_ci
640a46c0ec8Sopenharmony_ci		/* new touch, move it through begin to update immediately */
641a46c0ec8Sopenharmony_ci		tp_recover_ended_touch(tp, t);
642a46c0ec8Sopenharmony_ci	}
643a46c0ec8Sopenharmony_ci}
644a46c0ec8Sopenharmony_ci
645a46c0ec8Sopenharmony_cistatic void
646a46c0ec8Sopenharmony_citp_process_fake_touches(struct tp_dispatch *tp,
647a46c0ec8Sopenharmony_ci			uint64_t time)
648a46c0ec8Sopenharmony_ci{
649a46c0ec8Sopenharmony_ci	struct tp_touch *t;
650a46c0ec8Sopenharmony_ci	unsigned int nfake_touches;
651a46c0ec8Sopenharmony_ci	unsigned int i, start;
652a46c0ec8Sopenharmony_ci
653a46c0ec8Sopenharmony_ci	nfake_touches = tp_fake_finger_count(tp);
654a46c0ec8Sopenharmony_ci	if (nfake_touches == FAKE_FINGER_OVERFLOW)
655a46c0ec8Sopenharmony_ci		return;
656a46c0ec8Sopenharmony_ci
657a46c0ec8Sopenharmony_ci	if (tp->device->model_flags &
658a46c0ec8Sopenharmony_ci	    EVDEV_MODEL_SYNAPTICS_SERIAL_TOUCHPAD)
659a46c0ec8Sopenharmony_ci		tp_restore_synaptics_touches(tp, time);
660a46c0ec8Sopenharmony_ci
661a46c0ec8Sopenharmony_ci	/* ALPS serial touchpads always set 3 slots in the kernel, even
662a46c0ec8Sopenharmony_ci	 * where they support less than that. So we get BTN_TOOL_TRIPLETAP
663a46c0ec8Sopenharmony_ci	 * but never slot 2 because our slot count is wrong.
664a46c0ec8Sopenharmony_ci	 * This also means that the third touch falls through the cracks and
665a46c0ec8Sopenharmony_ci	 * is ignored.
666a46c0ec8Sopenharmony_ci	 *
667a46c0ec8Sopenharmony_ci	 * See https://gitlab.freedesktop.org/libinput/libinput/issues/408
668a46c0ec8Sopenharmony_ci	 *
669a46c0ec8Sopenharmony_ci	 * All touchpad devices have at least one slot so we only do this
670a46c0ec8Sopenharmony_ci	 * for 2 touches or higher.
671a46c0ec8Sopenharmony_ci	 *
672a46c0ec8Sopenharmony_ci	 * There's an bug in libevdev < 1.9.0 affecting slots after a
673a46c0ec8Sopenharmony_ci	 * SYN_DROPPED. Where a user release one or more touches during
674a46c0ec8Sopenharmony_ci	 * SYN_DROPPED and places new ones on the touchpad, we may end up
675a46c0ec8Sopenharmony_ci	 * with fake touches but no active slots.
676a46c0ec8Sopenharmony_ci	 * So let's check for nactive_slots > 0 to make sure we don't lose
677a46c0ec8Sopenharmony_ci	 * all fingers. That's a workaround only, this must be fixed in
678a46c0ec8Sopenharmony_ci	 * libevdev.
679a46c0ec8Sopenharmony_ci	 *
680a46c0ec8Sopenharmony_ci	 * For a long explanation of what happens, see
681a46c0ec8Sopenharmony_ci	 * https://gitlab.freedesktop.org/libevdev/libevdev/merge_requests/19
682a46c0ec8Sopenharmony_ci	 */
683a46c0ec8Sopenharmony_ci	if (tp->device->model_flags & EVDEV_MODEL_ALPS_SERIAL_TOUCHPAD &&
684a46c0ec8Sopenharmony_ci	    nfake_touches > 1 && tp->has_mt &&
685a46c0ec8Sopenharmony_ci	    tp->nactive_slots > 0 &&
686a46c0ec8Sopenharmony_ci	    nfake_touches > tp->nactive_slots &&
687a46c0ec8Sopenharmony_ci	    tp->nactive_slots < tp->num_slots) {
688a46c0ec8Sopenharmony_ci		evdev_log_bug_kernel(tp->device,
689a46c0ec8Sopenharmony_ci				     "Wrong slot count (%d), reducing to %d\n",
690a46c0ec8Sopenharmony_ci				     tp->num_slots,
691a46c0ec8Sopenharmony_ci				     tp->nactive_slots);
692a46c0ec8Sopenharmony_ci		/* This should be safe since we fill the slots from the
693a46c0ec8Sopenharmony_ci		 * first one so hiding the excessive slots shouldn't matter.
694a46c0ec8Sopenharmony_ci		 * There are sequences where we could accidentally lose an
695a46c0ec8Sopenharmony_ci		 * actual touch point but that requires specially crafted
696a46c0ec8Sopenharmony_ci		 * sequences and let's deal with that when it happens.
697a46c0ec8Sopenharmony_ci		 */
698a46c0ec8Sopenharmony_ci		tp->num_slots = tp->nactive_slots;
699a46c0ec8Sopenharmony_ci	}
700a46c0ec8Sopenharmony_ci
701a46c0ec8Sopenharmony_ci	start = tp->has_mt ? tp->num_slots : 0;
702a46c0ec8Sopenharmony_ci	for (i = start; i < tp->ntouches; i++) {
703a46c0ec8Sopenharmony_ci		t = tp_get_touch(tp, i);
704a46c0ec8Sopenharmony_ci		if (i < nfake_touches)
705a46c0ec8Sopenharmony_ci			tp_new_touch(tp, t, time);
706a46c0ec8Sopenharmony_ci		else
707a46c0ec8Sopenharmony_ci			tp_end_sequence(tp, t, time);
708a46c0ec8Sopenharmony_ci	}
709a46c0ec8Sopenharmony_ci}
710a46c0ec8Sopenharmony_ci
711a46c0ec8Sopenharmony_cistatic void
712a46c0ec8Sopenharmony_citp_process_trackpoint_button(struct tp_dispatch *tp,
713a46c0ec8Sopenharmony_ci			     const struct input_event *e,
714a46c0ec8Sopenharmony_ci			     uint64_t time)
715a46c0ec8Sopenharmony_ci{
716a46c0ec8Sopenharmony_ci	struct evdev_dispatch *dispatch;
717a46c0ec8Sopenharmony_ci	struct input_event event;
718a46c0ec8Sopenharmony_ci	struct input_event syn_report = {
719a46c0ec8Sopenharmony_ci		 .input_event_sec = 0,
720a46c0ec8Sopenharmony_ci		 .input_event_usec = 0,
721a46c0ec8Sopenharmony_ci		 .type = EV_SYN,
722a46c0ec8Sopenharmony_ci		 .code = SYN_REPORT,
723a46c0ec8Sopenharmony_ci		 .value = 0
724a46c0ec8Sopenharmony_ci	};
725a46c0ec8Sopenharmony_ci
726a46c0ec8Sopenharmony_ci	if (!tp->buttons.trackpoint)
727a46c0ec8Sopenharmony_ci		return;
728a46c0ec8Sopenharmony_ci
729a46c0ec8Sopenharmony_ci	dispatch = tp->buttons.trackpoint->dispatch;
730a46c0ec8Sopenharmony_ci
731a46c0ec8Sopenharmony_ci	event = *e;
732a46c0ec8Sopenharmony_ci	syn_report.input_event_sec = e->input_event_sec;
733a46c0ec8Sopenharmony_ci	syn_report.input_event_usec = e->input_event_usec;
734a46c0ec8Sopenharmony_ci
735a46c0ec8Sopenharmony_ci	switch (event.code) {
736a46c0ec8Sopenharmony_ci	case BTN_0:
737a46c0ec8Sopenharmony_ci		event.code = BTN_LEFT;
738a46c0ec8Sopenharmony_ci		break;
739a46c0ec8Sopenharmony_ci	case BTN_1:
740a46c0ec8Sopenharmony_ci		event.code = BTN_RIGHT;
741a46c0ec8Sopenharmony_ci		break;
742a46c0ec8Sopenharmony_ci	case BTN_2:
743a46c0ec8Sopenharmony_ci		event.code = BTN_MIDDLE;
744a46c0ec8Sopenharmony_ci		break;
745a46c0ec8Sopenharmony_ci	default:
746a46c0ec8Sopenharmony_ci		return;
747a46c0ec8Sopenharmony_ci	}
748a46c0ec8Sopenharmony_ci
749a46c0ec8Sopenharmony_ci	dispatch->interface->process(dispatch,
750a46c0ec8Sopenharmony_ci				     tp->buttons.trackpoint,
751a46c0ec8Sopenharmony_ci				     &event, time);
752a46c0ec8Sopenharmony_ci	dispatch->interface->process(dispatch,
753a46c0ec8Sopenharmony_ci				     tp->buttons.trackpoint,
754a46c0ec8Sopenharmony_ci				     &syn_report, time);
755a46c0ec8Sopenharmony_ci}
756a46c0ec8Sopenharmony_ci
757a46c0ec8Sopenharmony_cistatic void
758a46c0ec8Sopenharmony_citp_process_key(struct tp_dispatch *tp,
759a46c0ec8Sopenharmony_ci	       const struct input_event *e,
760a46c0ec8Sopenharmony_ci	       uint64_t time)
761a46c0ec8Sopenharmony_ci{
762a46c0ec8Sopenharmony_ci	/* ignore kernel key repeat */
763a46c0ec8Sopenharmony_ci	if (e->value == 2)
764a46c0ec8Sopenharmony_ci		return;
765a46c0ec8Sopenharmony_ci
766a46c0ec8Sopenharmony_ci	switch (e->code) {
767a46c0ec8Sopenharmony_ci		case BTN_LEFT:
768a46c0ec8Sopenharmony_ci		case BTN_MIDDLE:
769a46c0ec8Sopenharmony_ci		case BTN_RIGHT:
770a46c0ec8Sopenharmony_ci			tp_process_button(tp, e, time);
771a46c0ec8Sopenharmony_ci			break;
772a46c0ec8Sopenharmony_ci		case BTN_TOUCH:
773a46c0ec8Sopenharmony_ci		case BTN_TOOL_FINGER:
774a46c0ec8Sopenharmony_ci		case BTN_TOOL_DOUBLETAP:
775a46c0ec8Sopenharmony_ci		case BTN_TOOL_TRIPLETAP:
776a46c0ec8Sopenharmony_ci		case BTN_TOOL_QUADTAP:
777a46c0ec8Sopenharmony_ci		case BTN_TOOL_QUINTTAP:
778a46c0ec8Sopenharmony_ci			tp_fake_finger_set(tp, e->code, !!e->value);
779a46c0ec8Sopenharmony_ci			break;
780a46c0ec8Sopenharmony_ci		case BTN_0:
781a46c0ec8Sopenharmony_ci		case BTN_1:
782a46c0ec8Sopenharmony_ci		case BTN_2:
783a46c0ec8Sopenharmony_ci			tp_process_trackpoint_button(tp, e, time);
784a46c0ec8Sopenharmony_ci			break;
785a46c0ec8Sopenharmony_ci	}
786a46c0ec8Sopenharmony_ci}
787a46c0ec8Sopenharmony_ci
788a46c0ec8Sopenharmony_cistatic void
789a46c0ec8Sopenharmony_citp_process_msc(struct tp_dispatch *tp,
790a46c0ec8Sopenharmony_ci	       const struct input_event *e,
791a46c0ec8Sopenharmony_ci	       uint64_t time)
792a46c0ec8Sopenharmony_ci{
793a46c0ec8Sopenharmony_ci	if (e->code != MSC_TIMESTAMP)
794a46c0ec8Sopenharmony_ci		return;
795a46c0ec8Sopenharmony_ci
796a46c0ec8Sopenharmony_ci	tp->quirks.msc_timestamp.now = e->value;
797a46c0ec8Sopenharmony_ci	tp->queued |= TOUCHPAD_EVENT_TIMESTAMP;
798a46c0ec8Sopenharmony_ci}
799a46c0ec8Sopenharmony_ci
800a46c0ec8Sopenharmony_cistatic void
801a46c0ec8Sopenharmony_citp_unpin_finger(const struct tp_dispatch *tp, struct tp_touch *t)
802a46c0ec8Sopenharmony_ci{
803a46c0ec8Sopenharmony_ci	struct phys_coords mm;
804a46c0ec8Sopenharmony_ci	struct device_coords delta;
805a46c0ec8Sopenharmony_ci
806a46c0ec8Sopenharmony_ci	if (!t->pinned.is_pinned)
807a46c0ec8Sopenharmony_ci		return;
808a46c0ec8Sopenharmony_ci
809a46c0ec8Sopenharmony_ci	delta.x = abs(t->point.x - t->pinned.center.x);
810a46c0ec8Sopenharmony_ci	delta.y = abs(t->point.y - t->pinned.center.y);
811a46c0ec8Sopenharmony_ci
812a46c0ec8Sopenharmony_ci	mm = evdev_device_unit_delta_to_mm(tp->device, &delta);
813a46c0ec8Sopenharmony_ci
814a46c0ec8Sopenharmony_ci	/* 1.5mm movement -> unpin */
815a46c0ec8Sopenharmony_ci	if (hypot(mm.x, mm.y) >= 1.5) {
816a46c0ec8Sopenharmony_ci		t->pinned.is_pinned = false;
817a46c0ec8Sopenharmony_ci		return;
818a46c0ec8Sopenharmony_ci	}
819a46c0ec8Sopenharmony_ci}
820a46c0ec8Sopenharmony_ci
821a46c0ec8Sopenharmony_cistatic void
822a46c0ec8Sopenharmony_citp_pin_fingers(struct tp_dispatch *tp)
823a46c0ec8Sopenharmony_ci{
824a46c0ec8Sopenharmony_ci	struct tp_touch *t;
825a46c0ec8Sopenharmony_ci
826a46c0ec8Sopenharmony_ci	tp_for_each_touch(tp, t) {
827a46c0ec8Sopenharmony_ci		t->pinned.is_pinned = true;
828a46c0ec8Sopenharmony_ci		t->pinned.center = t->point;
829a46c0ec8Sopenharmony_ci	}
830a46c0ec8Sopenharmony_ci}
831a46c0ec8Sopenharmony_ci
832a46c0ec8Sopenharmony_cibool
833a46c0ec8Sopenharmony_citp_touch_active(const struct tp_dispatch *tp, const struct tp_touch *t)
834a46c0ec8Sopenharmony_ci{
835a46c0ec8Sopenharmony_ci	return (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE) &&
836a46c0ec8Sopenharmony_ci		t->palm.state == PALM_NONE &&
837a46c0ec8Sopenharmony_ci		!t->pinned.is_pinned &&
838a46c0ec8Sopenharmony_ci		!tp_thumb_ignored(tp, t) &&
839a46c0ec8Sopenharmony_ci		tp_button_touch_active(tp, t) &&
840a46c0ec8Sopenharmony_ci		tp_edge_scroll_touch_active(tp, t);
841a46c0ec8Sopenharmony_ci}
842a46c0ec8Sopenharmony_ci
843a46c0ec8Sopenharmony_cibool
844a46c0ec8Sopenharmony_citp_touch_active_for_gesture(const struct tp_dispatch *tp, const struct tp_touch *t)
845a46c0ec8Sopenharmony_ci{
846a46c0ec8Sopenharmony_ci	return (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE) &&
847a46c0ec8Sopenharmony_ci		t->palm.state == PALM_NONE &&
848a46c0ec8Sopenharmony_ci		!t->pinned.is_pinned &&
849a46c0ec8Sopenharmony_ci		!tp_thumb_ignored_for_gesture(tp, t) &&
850a46c0ec8Sopenharmony_ci		tp_button_touch_active(tp, t) &&
851a46c0ec8Sopenharmony_ci		tp_edge_scroll_touch_active(tp, t);
852a46c0ec8Sopenharmony_ci}
853a46c0ec8Sopenharmony_ci
854a46c0ec8Sopenharmony_cistatic inline bool
855a46c0ec8Sopenharmony_citp_palm_was_in_side_edge(const struct tp_dispatch *tp, const struct tp_touch *t)
856a46c0ec8Sopenharmony_ci{
857a46c0ec8Sopenharmony_ci	return t->palm.first.x < tp->palm.left_edge ||
858a46c0ec8Sopenharmony_ci	       t->palm.first.x > tp->palm.right_edge;
859a46c0ec8Sopenharmony_ci}
860a46c0ec8Sopenharmony_ci
861a46c0ec8Sopenharmony_cistatic inline bool
862a46c0ec8Sopenharmony_citp_palm_was_in_top_edge(const struct tp_dispatch *tp, const struct tp_touch *t)
863a46c0ec8Sopenharmony_ci{
864a46c0ec8Sopenharmony_ci	return t->palm.first.y < tp->palm.upper_edge;
865a46c0ec8Sopenharmony_ci}
866a46c0ec8Sopenharmony_ci
867a46c0ec8Sopenharmony_cistatic inline bool
868a46c0ec8Sopenharmony_citp_palm_in_side_edge(const struct tp_dispatch *tp, const struct tp_touch *t)
869a46c0ec8Sopenharmony_ci{
870a46c0ec8Sopenharmony_ci	return t->point.x < tp->palm.left_edge ||
871a46c0ec8Sopenharmony_ci	       t->point.x > tp->palm.right_edge;
872a46c0ec8Sopenharmony_ci}
873a46c0ec8Sopenharmony_ci
874a46c0ec8Sopenharmony_cistatic inline bool
875a46c0ec8Sopenharmony_citp_palm_in_top_edge(const struct tp_dispatch *tp, const struct tp_touch *t)
876a46c0ec8Sopenharmony_ci{
877a46c0ec8Sopenharmony_ci	return t->point.y < tp->palm.upper_edge;
878a46c0ec8Sopenharmony_ci}
879a46c0ec8Sopenharmony_ci
880a46c0ec8Sopenharmony_cistatic inline bool
881a46c0ec8Sopenharmony_citp_palm_in_edge(const struct tp_dispatch *tp, const struct tp_touch *t)
882a46c0ec8Sopenharmony_ci{
883a46c0ec8Sopenharmony_ci	return tp_palm_in_side_edge(tp, t) || tp_palm_in_top_edge(tp, t);
884a46c0ec8Sopenharmony_ci}
885a46c0ec8Sopenharmony_ci
886a46c0ec8Sopenharmony_cistatic bool
887a46c0ec8Sopenharmony_citp_palm_detect_dwt_triggered(struct tp_dispatch *tp,
888a46c0ec8Sopenharmony_ci			     struct tp_touch *t,
889a46c0ec8Sopenharmony_ci			     uint64_t time)
890a46c0ec8Sopenharmony_ci{
891a46c0ec8Sopenharmony_ci	if (tp->dwt.dwt_enabled &&
892a46c0ec8Sopenharmony_ci	    tp->dwt.keyboard_active &&
893a46c0ec8Sopenharmony_ci	    t->state == TOUCH_BEGIN) {
894a46c0ec8Sopenharmony_ci		t->palm.state = PALM_TYPING;
895a46c0ec8Sopenharmony_ci		t->palm.first = t->point;
896a46c0ec8Sopenharmony_ci		return true;
897a46c0ec8Sopenharmony_ci	}
898a46c0ec8Sopenharmony_ci
899a46c0ec8Sopenharmony_ci	if (!tp->dwt.keyboard_active &&
900a46c0ec8Sopenharmony_ci		   t->state == TOUCH_UPDATE &&
901a46c0ec8Sopenharmony_ci		   t->palm.state == PALM_TYPING) {
902a46c0ec8Sopenharmony_ci		/* If a touch has started before the first or after the last
903a46c0ec8Sopenharmony_ci		   key press, release it on timeout. Benefit: a palm rested
904a46c0ec8Sopenharmony_ci		   while typing on the touchpad will be ignored, but a touch
905a46c0ec8Sopenharmony_ci		   started once we stop typing will be able to control the
906a46c0ec8Sopenharmony_ci		   pointer (alas not tap, etc.).
907a46c0ec8Sopenharmony_ci		   */
908a46c0ec8Sopenharmony_ci		if (t->palm.time == 0 ||
909a46c0ec8Sopenharmony_ci		    t->palm.time > tp->dwt.keyboard_last_press_time) {
910a46c0ec8Sopenharmony_ci			t->palm.state = PALM_NONE;
911a46c0ec8Sopenharmony_ci			evdev_log_debug(tp->device,
912a46c0ec8Sopenharmony_ci					"palm: touch %d released, timeout after typing\n",
913a46c0ec8Sopenharmony_ci					t->index);
914a46c0ec8Sopenharmony_ci		}
915a46c0ec8Sopenharmony_ci	}
916a46c0ec8Sopenharmony_ci
917a46c0ec8Sopenharmony_ci	return false;
918a46c0ec8Sopenharmony_ci}
919a46c0ec8Sopenharmony_ci
920a46c0ec8Sopenharmony_cistatic bool
921a46c0ec8Sopenharmony_citp_palm_detect_trackpoint_triggered(struct tp_dispatch *tp,
922a46c0ec8Sopenharmony_ci				    struct tp_touch *t,
923a46c0ec8Sopenharmony_ci				    uint64_t time)
924a46c0ec8Sopenharmony_ci{
925a46c0ec8Sopenharmony_ci	if (!tp->palm.monitor_trackpoint)
926a46c0ec8Sopenharmony_ci		return false;
927a46c0ec8Sopenharmony_ci
928a46c0ec8Sopenharmony_ci	if (t->palm.state == PALM_NONE &&
929a46c0ec8Sopenharmony_ci	    t->state == TOUCH_BEGIN &&
930a46c0ec8Sopenharmony_ci	    tp->palm.trackpoint_active) {
931a46c0ec8Sopenharmony_ci		t->palm.state = PALM_TRACKPOINT;
932a46c0ec8Sopenharmony_ci		return true;
933a46c0ec8Sopenharmony_ci	}
934a46c0ec8Sopenharmony_ci
935a46c0ec8Sopenharmony_ci	if (t->palm.state == PALM_TRACKPOINT &&
936a46c0ec8Sopenharmony_ci		   t->state == TOUCH_UPDATE &&
937a46c0ec8Sopenharmony_ci		   !tp->palm.trackpoint_active) {
938a46c0ec8Sopenharmony_ci
939a46c0ec8Sopenharmony_ci		if (t->palm.time == 0 ||
940a46c0ec8Sopenharmony_ci		    t->palm.time > tp->palm.trackpoint_last_event_time) {
941a46c0ec8Sopenharmony_ci			t->palm.state = PALM_NONE;
942a46c0ec8Sopenharmony_ci			evdev_log_debug(tp->device,
943a46c0ec8Sopenharmony_ci				       "palm: touch %d released, timeout after trackpoint\n", t->index);
944a46c0ec8Sopenharmony_ci		}
945a46c0ec8Sopenharmony_ci	}
946a46c0ec8Sopenharmony_ci
947a46c0ec8Sopenharmony_ci	return false;
948a46c0ec8Sopenharmony_ci}
949a46c0ec8Sopenharmony_ci
950a46c0ec8Sopenharmony_cistatic bool
951a46c0ec8Sopenharmony_citp_palm_detect_tool_triggered(struct tp_dispatch *tp,
952a46c0ec8Sopenharmony_ci			      struct tp_touch *t,
953a46c0ec8Sopenharmony_ci			      uint64_t time)
954a46c0ec8Sopenharmony_ci{
955a46c0ec8Sopenharmony_ci	if (!tp->palm.use_mt_tool)
956a46c0ec8Sopenharmony_ci		return false;
957a46c0ec8Sopenharmony_ci
958a46c0ec8Sopenharmony_ci	if (t->palm.state != PALM_NONE &&
959a46c0ec8Sopenharmony_ci	    t->palm.state != PALM_TOOL_PALM)
960a46c0ec8Sopenharmony_ci		return false;
961a46c0ec8Sopenharmony_ci
962a46c0ec8Sopenharmony_ci	if (t->palm.state == PALM_NONE &&
963a46c0ec8Sopenharmony_ci	    t->is_tool_palm)
964a46c0ec8Sopenharmony_ci		t->palm.state = PALM_TOOL_PALM;
965a46c0ec8Sopenharmony_ci	else if (t->palm.state == PALM_TOOL_PALM &&
966a46c0ec8Sopenharmony_ci		 !t->is_tool_palm)
967a46c0ec8Sopenharmony_ci		t->palm.state = PALM_NONE;
968a46c0ec8Sopenharmony_ci
969a46c0ec8Sopenharmony_ci	return t->palm.state == PALM_TOOL_PALM;
970a46c0ec8Sopenharmony_ci}
971a46c0ec8Sopenharmony_ci
972a46c0ec8Sopenharmony_cistatic inline bool
973a46c0ec8Sopenharmony_citp_palm_detect_move_out_of_edge(struct tp_dispatch *tp,
974a46c0ec8Sopenharmony_ci				struct tp_touch *t,
975a46c0ec8Sopenharmony_ci				uint64_t time)
976a46c0ec8Sopenharmony_ci{
977a46c0ec8Sopenharmony_ci	const int PALM_TIMEOUT = ms2us(200);
978a46c0ec8Sopenharmony_ci	int directions = 0;
979a46c0ec8Sopenharmony_ci	struct device_float_coords delta;
980a46c0ec8Sopenharmony_ci	int dirs;
981a46c0ec8Sopenharmony_ci
982a46c0ec8Sopenharmony_ci	if (time < t->palm.time + PALM_TIMEOUT && !tp_palm_in_edge(tp, t)) {
983a46c0ec8Sopenharmony_ci		if (tp_palm_was_in_side_edge(tp, t))
984a46c0ec8Sopenharmony_ci			directions = NE|E|SE|SW|W|NW;
985a46c0ec8Sopenharmony_ci		else if (tp_palm_was_in_top_edge(tp, t))
986a46c0ec8Sopenharmony_ci			directions = S|SE|SW;
987a46c0ec8Sopenharmony_ci
988a46c0ec8Sopenharmony_ci		if (directions) {
989a46c0ec8Sopenharmony_ci			delta = device_delta(t->point, t->palm.first);
990a46c0ec8Sopenharmony_ci			dirs = phys_get_direction(tp_phys_delta(tp, delta));
991a46c0ec8Sopenharmony_ci			if ((dirs & directions) && !(dirs & ~directions))
992a46c0ec8Sopenharmony_ci				return true;
993a46c0ec8Sopenharmony_ci		}
994a46c0ec8Sopenharmony_ci	}
995a46c0ec8Sopenharmony_ci
996a46c0ec8Sopenharmony_ci	return false;
997a46c0ec8Sopenharmony_ci}
998a46c0ec8Sopenharmony_ci
999a46c0ec8Sopenharmony_cistatic inline bool
1000a46c0ec8Sopenharmony_citp_palm_detect_multifinger(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
1001a46c0ec8Sopenharmony_ci{
1002a46c0ec8Sopenharmony_ci	struct tp_touch *other;
1003a46c0ec8Sopenharmony_ci
1004a46c0ec8Sopenharmony_ci	if (tp->nfingers_down < 2)
1005a46c0ec8Sopenharmony_ci		return false;
1006a46c0ec8Sopenharmony_ci
1007a46c0ec8Sopenharmony_ci	/* If we have at least one other active non-palm touch make this
1008a46c0ec8Sopenharmony_ci	 * touch non-palm too. This avoids palm detection during two-finger
1009a46c0ec8Sopenharmony_ci	 * scrolling.
1010a46c0ec8Sopenharmony_ci	 *
1011a46c0ec8Sopenharmony_ci	 * Note: if both touches start in the palm zone within the same
1012a46c0ec8Sopenharmony_ci	 * frame the second touch will still be PALM_NONE and thus detected
1013a46c0ec8Sopenharmony_ci	 * here as non-palm touch. This is too niche to worry about for now.
1014a46c0ec8Sopenharmony_ci	 */
1015a46c0ec8Sopenharmony_ci	tp_for_each_touch(tp, other) {
1016a46c0ec8Sopenharmony_ci		if (other == t)
1017a46c0ec8Sopenharmony_ci			continue;
1018a46c0ec8Sopenharmony_ci
1019a46c0ec8Sopenharmony_ci		if (tp_touch_active(tp, other) &&
1020a46c0ec8Sopenharmony_ci		    other->palm.state == PALM_NONE) {
1021a46c0ec8Sopenharmony_ci			return true;
1022a46c0ec8Sopenharmony_ci		}
1023a46c0ec8Sopenharmony_ci	}
1024a46c0ec8Sopenharmony_ci
1025a46c0ec8Sopenharmony_ci	return false;
1026a46c0ec8Sopenharmony_ci}
1027a46c0ec8Sopenharmony_ci
1028a46c0ec8Sopenharmony_cistatic inline bool
1029a46c0ec8Sopenharmony_citp_palm_detect_touch_size_triggered(struct tp_dispatch *tp,
1030a46c0ec8Sopenharmony_ci				    struct tp_touch *t,
1031a46c0ec8Sopenharmony_ci				    uint64_t time)
1032a46c0ec8Sopenharmony_ci{
1033a46c0ec8Sopenharmony_ci	if (!tp->palm.use_size)
1034a46c0ec8Sopenharmony_ci		return false;
1035a46c0ec8Sopenharmony_ci
1036a46c0ec8Sopenharmony_ci	/* If a finger size is large enough for palm, we stick with that and
1037a46c0ec8Sopenharmony_ci	 * force the user to release and reset the finger */
1038a46c0ec8Sopenharmony_ci	if (t->palm.state != PALM_NONE && t->palm.state != PALM_TOUCH_SIZE)
1039a46c0ec8Sopenharmony_ci		return false;
1040a46c0ec8Sopenharmony_ci
1041a46c0ec8Sopenharmony_ci	if (t->major > tp->palm.size_threshold ||
1042a46c0ec8Sopenharmony_ci	    t->minor > tp->palm.size_threshold) {
1043a46c0ec8Sopenharmony_ci		if (t->palm.state != PALM_TOUCH_SIZE)
1044a46c0ec8Sopenharmony_ci			evdev_log_debug(tp->device,
1045a46c0ec8Sopenharmony_ci					"palm: touch %d size exceeded\n",
1046a46c0ec8Sopenharmony_ci					t->index);
1047a46c0ec8Sopenharmony_ci		t->palm.state = PALM_TOUCH_SIZE;
1048a46c0ec8Sopenharmony_ci		return true;
1049a46c0ec8Sopenharmony_ci	}
1050a46c0ec8Sopenharmony_ci
1051a46c0ec8Sopenharmony_ci	return false;
1052a46c0ec8Sopenharmony_ci}
1053a46c0ec8Sopenharmony_ci
1054a46c0ec8Sopenharmony_cistatic inline bool
1055a46c0ec8Sopenharmony_citp_palm_detect_edge(struct tp_dispatch *tp,
1056a46c0ec8Sopenharmony_ci		    struct tp_touch *t,
1057a46c0ec8Sopenharmony_ci		    uint64_t time)
1058a46c0ec8Sopenharmony_ci{
1059a46c0ec8Sopenharmony_ci	if (t->palm.state == PALM_EDGE) {
1060a46c0ec8Sopenharmony_ci		if (tp_palm_detect_multifinger(tp, t, time)) {
1061a46c0ec8Sopenharmony_ci			t->palm.state = PALM_NONE;
1062a46c0ec8Sopenharmony_ci			evdev_log_debug(tp->device,
1063a46c0ec8Sopenharmony_ci				  "palm: touch %d released, multiple fingers\n",
1064a46c0ec8Sopenharmony_ci				  t->index);
1065a46c0ec8Sopenharmony_ci
1066a46c0ec8Sopenharmony_ci		/* If labelled a touch as palm, we unlabel as palm when
1067a46c0ec8Sopenharmony_ci		   we move out of the palm edge zone within the timeout, provided
1068a46c0ec8Sopenharmony_ci		   the direction is within 45 degrees of the horizontal.
1069a46c0ec8Sopenharmony_ci		 */
1070a46c0ec8Sopenharmony_ci		} else if (tp_palm_detect_move_out_of_edge(tp, t, time)) {
1071a46c0ec8Sopenharmony_ci			t->palm.state = PALM_NONE;
1072a46c0ec8Sopenharmony_ci			evdev_log_debug(tp->device,
1073a46c0ec8Sopenharmony_ci				  "palm: touch %d released, out of edge zone\n",
1074a46c0ec8Sopenharmony_ci				  t->index);
1075a46c0ec8Sopenharmony_ci		}
1076a46c0ec8Sopenharmony_ci		return false;
1077a46c0ec8Sopenharmony_ci	}
1078a46c0ec8Sopenharmony_ci
1079a46c0ec8Sopenharmony_ci	if (tp_palm_detect_multifinger(tp, t, time)) {
1080a46c0ec8Sopenharmony_ci		return false;
1081a46c0ec8Sopenharmony_ci	}
1082a46c0ec8Sopenharmony_ci
1083a46c0ec8Sopenharmony_ci	/* palm must start in exclusion zone, it's ok to move into
1084a46c0ec8Sopenharmony_ci	   the zone without being a palm */
1085a46c0ec8Sopenharmony_ci	if (t->state != TOUCH_BEGIN || !tp_palm_in_edge(tp, t))
1086a46c0ec8Sopenharmony_ci		return false;
1087a46c0ec8Sopenharmony_ci
1088a46c0ec8Sopenharmony_ci	if (tp_touch_get_edge(tp, t) & EDGE_RIGHT)
1089a46c0ec8Sopenharmony_ci		return false;
1090a46c0ec8Sopenharmony_ci
1091a46c0ec8Sopenharmony_ci	t->palm.state = PALM_EDGE;
1092a46c0ec8Sopenharmony_ci	t->palm.time = time;
1093a46c0ec8Sopenharmony_ci	t->palm.first = t->point;
1094a46c0ec8Sopenharmony_ci
1095a46c0ec8Sopenharmony_ci	return true;
1096a46c0ec8Sopenharmony_ci}
1097a46c0ec8Sopenharmony_ci
1098a46c0ec8Sopenharmony_cistatic bool
1099a46c0ec8Sopenharmony_citp_palm_detect_pressure_triggered(struct tp_dispatch *tp,
1100a46c0ec8Sopenharmony_ci				  struct tp_touch *t,
1101a46c0ec8Sopenharmony_ci				  uint64_t time)
1102a46c0ec8Sopenharmony_ci{
1103a46c0ec8Sopenharmony_ci	if (!tp->palm.use_pressure)
1104a46c0ec8Sopenharmony_ci		return false;
1105a46c0ec8Sopenharmony_ci
1106a46c0ec8Sopenharmony_ci	if (t->palm.state != PALM_NONE &&
1107a46c0ec8Sopenharmony_ci	    t->palm.state != PALM_PRESSURE)
1108a46c0ec8Sopenharmony_ci		return false;
1109a46c0ec8Sopenharmony_ci
1110a46c0ec8Sopenharmony_ci	if (t->pressure > tp->palm.pressure_threshold)
1111a46c0ec8Sopenharmony_ci		t->palm.state = PALM_PRESSURE;
1112a46c0ec8Sopenharmony_ci
1113a46c0ec8Sopenharmony_ci	return t->palm.state == PALM_PRESSURE;
1114a46c0ec8Sopenharmony_ci}
1115a46c0ec8Sopenharmony_ci
1116a46c0ec8Sopenharmony_cistatic bool
1117a46c0ec8Sopenharmony_citp_palm_detect_arbitration_triggered(struct tp_dispatch *tp,
1118a46c0ec8Sopenharmony_ci				     struct tp_touch *t,
1119a46c0ec8Sopenharmony_ci				     uint64_t time)
1120a46c0ec8Sopenharmony_ci{
1121a46c0ec8Sopenharmony_ci	if (tp->arbitration.state == ARBITRATION_NOT_ACTIVE)
1122a46c0ec8Sopenharmony_ci		return false;
1123a46c0ec8Sopenharmony_ci
1124a46c0ec8Sopenharmony_ci	t->palm.state = PALM_ARBITRATION;
1125a46c0ec8Sopenharmony_ci
1126a46c0ec8Sopenharmony_ci	return true;
1127a46c0ec8Sopenharmony_ci}
1128a46c0ec8Sopenharmony_ci
1129a46c0ec8Sopenharmony_cistatic void
1130a46c0ec8Sopenharmony_citp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
1131a46c0ec8Sopenharmony_ci{
1132a46c0ec8Sopenharmony_ci	const char *palm_state;
1133a46c0ec8Sopenharmony_ci	enum touch_palm_state oldstate = t->palm.state;
1134a46c0ec8Sopenharmony_ci
1135a46c0ec8Sopenharmony_ci	if (tp_palm_detect_pressure_triggered(tp, t, time))
1136a46c0ec8Sopenharmony_ci		goto out;
1137a46c0ec8Sopenharmony_ci
1138a46c0ec8Sopenharmony_ci	if (tp_palm_detect_arbitration_triggered(tp, t, time))
1139a46c0ec8Sopenharmony_ci		goto out;
1140a46c0ec8Sopenharmony_ci
1141a46c0ec8Sopenharmony_ci	if (tp_palm_detect_dwt_triggered(tp, t, time))
1142a46c0ec8Sopenharmony_ci		goto out;
1143a46c0ec8Sopenharmony_ci
1144a46c0ec8Sopenharmony_ci	if (tp_palm_detect_trackpoint_triggered(tp, t, time))
1145a46c0ec8Sopenharmony_ci		goto out;
1146a46c0ec8Sopenharmony_ci
1147a46c0ec8Sopenharmony_ci	if (tp_palm_detect_tool_triggered(tp, t, time))
1148a46c0ec8Sopenharmony_ci		goto out;
1149a46c0ec8Sopenharmony_ci
1150a46c0ec8Sopenharmony_ci	if (tp_palm_detect_touch_size_triggered(tp, t, time))
1151a46c0ec8Sopenharmony_ci		goto out;
1152a46c0ec8Sopenharmony_ci
1153a46c0ec8Sopenharmony_ci	if (tp_palm_detect_edge(tp, t, time))
1154a46c0ec8Sopenharmony_ci		goto out;
1155a46c0ec8Sopenharmony_ci
1156a46c0ec8Sopenharmony_ci	/* Pressure is highest priority because it cannot be released and
1157a46c0ec8Sopenharmony_ci	 * overrides all other checks. So we check once before anything else
1158a46c0ec8Sopenharmony_ci	 * in case pressure triggers on a non-palm touch. And again after
1159a46c0ec8Sopenharmony_ci	 * everything in case one of the others released but we have a
1160a46c0ec8Sopenharmony_ci	 * pressure trigger now.
1161a46c0ec8Sopenharmony_ci	 */
1162a46c0ec8Sopenharmony_ci	if (tp_palm_detect_pressure_triggered(tp, t, time))
1163a46c0ec8Sopenharmony_ci		goto out;
1164a46c0ec8Sopenharmony_ci
1165a46c0ec8Sopenharmony_ci	return;
1166a46c0ec8Sopenharmony_ciout:
1167a46c0ec8Sopenharmony_ci
1168a46c0ec8Sopenharmony_ci	if (oldstate == t->palm.state)
1169a46c0ec8Sopenharmony_ci		return;
1170a46c0ec8Sopenharmony_ci
1171a46c0ec8Sopenharmony_ci	switch (t->palm.state) {
1172a46c0ec8Sopenharmony_ci	case PALM_EDGE:
1173a46c0ec8Sopenharmony_ci		palm_state = "edge";
1174a46c0ec8Sopenharmony_ci		break;
1175a46c0ec8Sopenharmony_ci	case PALM_TYPING:
1176a46c0ec8Sopenharmony_ci		palm_state = "typing";
1177a46c0ec8Sopenharmony_ci		break;
1178a46c0ec8Sopenharmony_ci	case PALM_TRACKPOINT:
1179a46c0ec8Sopenharmony_ci		palm_state = "trackpoint";
1180a46c0ec8Sopenharmony_ci		break;
1181a46c0ec8Sopenharmony_ci	case PALM_TOOL_PALM:
1182a46c0ec8Sopenharmony_ci		palm_state = "tool-palm";
1183a46c0ec8Sopenharmony_ci		break;
1184a46c0ec8Sopenharmony_ci	case PALM_PRESSURE:
1185a46c0ec8Sopenharmony_ci		palm_state = "pressure";
1186a46c0ec8Sopenharmony_ci		break;
1187a46c0ec8Sopenharmony_ci	case PALM_TOUCH_SIZE:
1188a46c0ec8Sopenharmony_ci		palm_state = "touch size";
1189a46c0ec8Sopenharmony_ci		break;
1190a46c0ec8Sopenharmony_ci	case PALM_ARBITRATION:
1191a46c0ec8Sopenharmony_ci		palm_state = "arbitration";
1192a46c0ec8Sopenharmony_ci		break;
1193a46c0ec8Sopenharmony_ci	case PALM_NONE:
1194a46c0ec8Sopenharmony_ci	default:
1195a46c0ec8Sopenharmony_ci		abort();
1196a46c0ec8Sopenharmony_ci		break;
1197a46c0ec8Sopenharmony_ci	}
1198a46c0ec8Sopenharmony_ci	evdev_log_debug(tp->device,
1199a46c0ec8Sopenharmony_ci		  "palm: touch %d (%s), palm detected (%s)\n",
1200a46c0ec8Sopenharmony_ci		  t->index,
1201a46c0ec8Sopenharmony_ci		  touch_state_to_str(t->state),
1202a46c0ec8Sopenharmony_ci		  palm_state);
1203a46c0ec8Sopenharmony_ci}
1204a46c0ec8Sopenharmony_ci
1205a46c0ec8Sopenharmony_cistatic void
1206a46c0ec8Sopenharmony_citp_unhover_pressure(struct tp_dispatch *tp, uint64_t time)
1207a46c0ec8Sopenharmony_ci{
1208a46c0ec8Sopenharmony_ci	struct tp_touch *t;
1209a46c0ec8Sopenharmony_ci	int i;
1210a46c0ec8Sopenharmony_ci	unsigned int nfake_touches;
1211a46c0ec8Sopenharmony_ci	unsigned int real_fingers_down = 0;
1212a46c0ec8Sopenharmony_ci
1213a46c0ec8Sopenharmony_ci	nfake_touches = tp_fake_finger_count(tp);
1214a46c0ec8Sopenharmony_ci	if (nfake_touches == FAKE_FINGER_OVERFLOW)
1215a46c0ec8Sopenharmony_ci		nfake_touches = 0;
1216a46c0ec8Sopenharmony_ci
1217a46c0ec8Sopenharmony_ci	for (i = 0; i < (int)tp->num_slots; i++) {
1218a46c0ec8Sopenharmony_ci		t = tp_get_touch(tp, i);
1219a46c0ec8Sopenharmony_ci
1220a46c0ec8Sopenharmony_ci		if (t->state == TOUCH_NONE)
1221a46c0ec8Sopenharmony_ci			continue;
1222a46c0ec8Sopenharmony_ci
1223a46c0ec8Sopenharmony_ci		if (t->dirty) {
1224a46c0ec8Sopenharmony_ci			if (t->state == TOUCH_HOVERING) {
1225a46c0ec8Sopenharmony_ci				if (t->pressure >= tp->pressure.high) {
1226a46c0ec8Sopenharmony_ci					evdev_log_debug(tp->device,
1227a46c0ec8Sopenharmony_ci							"pressure: begin touch %d\n",
1228a46c0ec8Sopenharmony_ci							t->index);
1229a46c0ec8Sopenharmony_ci					/* avoid jumps when landing a finger */
1230a46c0ec8Sopenharmony_ci					tp_motion_history_reset(t);
1231a46c0ec8Sopenharmony_ci					tp_begin_touch(tp, t, time);
1232a46c0ec8Sopenharmony_ci				}
1233a46c0ec8Sopenharmony_ci			/* don't unhover for pressure if we have too many
1234a46c0ec8Sopenharmony_ci			 * fake fingers down, see comment below. Except
1235a46c0ec8Sopenharmony_ci			 * for single-finger touches where the real touch
1236a46c0ec8Sopenharmony_ci			 * decides for the rest.
1237a46c0ec8Sopenharmony_ci			 */
1238a46c0ec8Sopenharmony_ci			} else if (nfake_touches <= tp->num_slots ||
1239a46c0ec8Sopenharmony_ci				   tp->num_slots == 1) {
1240a46c0ec8Sopenharmony_ci				if (t->pressure < tp->pressure.low) {
1241a46c0ec8Sopenharmony_ci					evdev_log_debug(tp->device,
1242a46c0ec8Sopenharmony_ci							"pressure: end touch %d\n",
1243a46c0ec8Sopenharmony_ci							t->index);
1244a46c0ec8Sopenharmony_ci					tp_maybe_end_touch(tp, t, time);
1245a46c0ec8Sopenharmony_ci				}
1246a46c0ec8Sopenharmony_ci			}
1247a46c0ec8Sopenharmony_ci		}
1248a46c0ec8Sopenharmony_ci
1249a46c0ec8Sopenharmony_ci		if (t->state == TOUCH_BEGIN ||
1250a46c0ec8Sopenharmony_ci		    t->state == TOUCH_UPDATE)
1251a46c0ec8Sopenharmony_ci			real_fingers_down++;
1252a46c0ec8Sopenharmony_ci	}
1253a46c0ec8Sopenharmony_ci
1254a46c0ec8Sopenharmony_ci	if (nfake_touches <= tp->num_slots ||
1255a46c0ec8Sopenharmony_ci	    tp->nfingers_down == 0)
1256a46c0ec8Sopenharmony_ci		return;
1257a46c0ec8Sopenharmony_ci
1258a46c0ec8Sopenharmony_ci	/* if we have more fake fingers down than slots, we assume
1259a46c0ec8Sopenharmony_ci	 * _all_ fingers have enough pressure, even if some of the slotted
1260a46c0ec8Sopenharmony_ci	 * ones don't. Anything else gets insane quickly.
1261a46c0ec8Sopenharmony_ci	 */
1262a46c0ec8Sopenharmony_ci	if (real_fingers_down > 0) {
1263a46c0ec8Sopenharmony_ci		tp_for_each_touch(tp, t) {
1264a46c0ec8Sopenharmony_ci			if (t->state == TOUCH_HOVERING) {
1265a46c0ec8Sopenharmony_ci				/* avoid jumps when landing a finger */
1266a46c0ec8Sopenharmony_ci				tp_motion_history_reset(t);
1267a46c0ec8Sopenharmony_ci				tp_begin_touch(tp, t, time);
1268a46c0ec8Sopenharmony_ci
1269a46c0ec8Sopenharmony_ci				if (tp->nfingers_down >= nfake_touches)
1270a46c0ec8Sopenharmony_ci					break;
1271a46c0ec8Sopenharmony_ci			}
1272a46c0ec8Sopenharmony_ci		}
1273a46c0ec8Sopenharmony_ci	}
1274a46c0ec8Sopenharmony_ci
1275a46c0ec8Sopenharmony_ci	if (tp->nfingers_down > nfake_touches ||
1276a46c0ec8Sopenharmony_ci	    real_fingers_down == 0) {
1277a46c0ec8Sopenharmony_ci		for (i = tp->ntouches - 1; i >= 0; i--) {
1278a46c0ec8Sopenharmony_ci			t = tp_get_touch(tp, i);
1279a46c0ec8Sopenharmony_ci
1280a46c0ec8Sopenharmony_ci			if (t->state == TOUCH_HOVERING ||
1281a46c0ec8Sopenharmony_ci			    t->state == TOUCH_NONE ||
1282a46c0ec8Sopenharmony_ci			    t->state == TOUCH_MAYBE_END)
1283a46c0ec8Sopenharmony_ci				continue;
1284a46c0ec8Sopenharmony_ci
1285a46c0ec8Sopenharmony_ci			tp_maybe_end_touch(tp, t, time);
1286a46c0ec8Sopenharmony_ci
1287a46c0ec8Sopenharmony_ci			if (real_fingers_down > 0  &&
1288a46c0ec8Sopenharmony_ci			    tp->nfingers_down == nfake_touches)
1289a46c0ec8Sopenharmony_ci				break;
1290a46c0ec8Sopenharmony_ci		}
1291a46c0ec8Sopenharmony_ci	}
1292a46c0ec8Sopenharmony_ci}
1293a46c0ec8Sopenharmony_ci
1294a46c0ec8Sopenharmony_cistatic void
1295a46c0ec8Sopenharmony_citp_unhover_size(struct tp_dispatch *tp, uint64_t time)
1296a46c0ec8Sopenharmony_ci{
1297a46c0ec8Sopenharmony_ci	struct tp_touch *t;
1298a46c0ec8Sopenharmony_ci	int low = tp->touch_size.low,
1299a46c0ec8Sopenharmony_ci	    high = tp->touch_size.high;
1300a46c0ec8Sopenharmony_ci	int i;
1301a46c0ec8Sopenharmony_ci
1302a46c0ec8Sopenharmony_ci	/* We require 5 slots for size handling, so we don't need to care
1303a46c0ec8Sopenharmony_ci	 * about fake touches here */
1304a46c0ec8Sopenharmony_ci
1305a46c0ec8Sopenharmony_ci	for (i = 0; i < (int)tp->num_slots; i++) {
1306a46c0ec8Sopenharmony_ci		t = tp_get_touch(tp, i);
1307a46c0ec8Sopenharmony_ci
1308a46c0ec8Sopenharmony_ci		if (t->state == TOUCH_NONE)
1309a46c0ec8Sopenharmony_ci			continue;
1310a46c0ec8Sopenharmony_ci
1311a46c0ec8Sopenharmony_ci		if (!t->dirty)
1312a46c0ec8Sopenharmony_ci			continue;
1313a46c0ec8Sopenharmony_ci
1314a46c0ec8Sopenharmony_ci		if (t->state == TOUCH_HOVERING) {
1315a46c0ec8Sopenharmony_ci			if ((t->major > high && t->minor > low) ||
1316a46c0ec8Sopenharmony_ci			    (t->major > low && t->minor > high)) {
1317a46c0ec8Sopenharmony_ci				evdev_log_debug(tp->device,
1318a46c0ec8Sopenharmony_ci						"touch-size: begin touch %d\n",
1319a46c0ec8Sopenharmony_ci						t->index);
1320a46c0ec8Sopenharmony_ci				/* avoid jumps when landing a finger */
1321a46c0ec8Sopenharmony_ci				tp_motion_history_reset(t);
1322a46c0ec8Sopenharmony_ci				tp_begin_touch(tp, t, time);
1323a46c0ec8Sopenharmony_ci			}
1324a46c0ec8Sopenharmony_ci		} else {
1325a46c0ec8Sopenharmony_ci			if (t->major < low || t->minor < low) {
1326a46c0ec8Sopenharmony_ci				evdev_log_debug(tp->device,
1327a46c0ec8Sopenharmony_ci						"touch-size: end touch %d\n",
1328a46c0ec8Sopenharmony_ci						t->index);
1329a46c0ec8Sopenharmony_ci				tp_maybe_end_touch(tp, t, time);
1330a46c0ec8Sopenharmony_ci			}
1331a46c0ec8Sopenharmony_ci		}
1332a46c0ec8Sopenharmony_ci	}
1333a46c0ec8Sopenharmony_ci}
1334a46c0ec8Sopenharmony_ci
1335a46c0ec8Sopenharmony_cistatic void
1336a46c0ec8Sopenharmony_citp_unhover_fake_touches(struct tp_dispatch *tp, uint64_t time)
1337a46c0ec8Sopenharmony_ci{
1338a46c0ec8Sopenharmony_ci	struct tp_touch *t;
1339a46c0ec8Sopenharmony_ci	unsigned int nfake_touches;
1340a46c0ec8Sopenharmony_ci	int i;
1341a46c0ec8Sopenharmony_ci
1342a46c0ec8Sopenharmony_ci	if (!tp->fake_touches && !tp->nfingers_down)
1343a46c0ec8Sopenharmony_ci		return;
1344a46c0ec8Sopenharmony_ci
1345a46c0ec8Sopenharmony_ci	nfake_touches = tp_fake_finger_count(tp);
1346a46c0ec8Sopenharmony_ci	if (nfake_touches == FAKE_FINGER_OVERFLOW)
1347a46c0ec8Sopenharmony_ci		return;
1348a46c0ec8Sopenharmony_ci
1349a46c0ec8Sopenharmony_ci	if (tp->nfingers_down == nfake_touches &&
1350a46c0ec8Sopenharmony_ci	    ((tp->nfingers_down == 0 && !tp_fake_finger_is_touching(tp)) ||
1351a46c0ec8Sopenharmony_ci	     (tp->nfingers_down > 0 && tp_fake_finger_is_touching(tp))))
1352a46c0ec8Sopenharmony_ci		return;
1353a46c0ec8Sopenharmony_ci
1354a46c0ec8Sopenharmony_ci	/* if BTN_TOUCH is set and we have less fingers down than fake
1355a46c0ec8Sopenharmony_ci	 * touches, switch each hovering touch to BEGIN
1356a46c0ec8Sopenharmony_ci	 * until nfingers_down matches nfake_touches
1357a46c0ec8Sopenharmony_ci	 */
1358a46c0ec8Sopenharmony_ci	if (tp_fake_finger_is_touching(tp) &&
1359a46c0ec8Sopenharmony_ci	    tp->nfingers_down < nfake_touches) {
1360a46c0ec8Sopenharmony_ci		tp_for_each_touch(tp, t) {
1361a46c0ec8Sopenharmony_ci			if (t->state == TOUCH_HOVERING) {
1362a46c0ec8Sopenharmony_ci				tp_begin_touch(tp, t, time);
1363a46c0ec8Sopenharmony_ci
1364a46c0ec8Sopenharmony_ci				if (tp->nfingers_down >= nfake_touches)
1365a46c0ec8Sopenharmony_ci					break;
1366a46c0ec8Sopenharmony_ci			}
1367a46c0ec8Sopenharmony_ci		}
1368a46c0ec8Sopenharmony_ci	}
1369a46c0ec8Sopenharmony_ci
1370a46c0ec8Sopenharmony_ci	/* if BTN_TOUCH is unset end all touches, we're hovering now. If we
1371a46c0ec8Sopenharmony_ci	 * have too many touches also end some of them. This is done in
1372a46c0ec8Sopenharmony_ci	 * reverse order.
1373a46c0ec8Sopenharmony_ci	 */
1374a46c0ec8Sopenharmony_ci	if (tp->nfingers_down > nfake_touches ||
1375a46c0ec8Sopenharmony_ci	    !tp_fake_finger_is_touching(tp)) {
1376a46c0ec8Sopenharmony_ci		for (i = tp->ntouches - 1; i >= 0; i--) {
1377a46c0ec8Sopenharmony_ci			t = tp_get_touch(tp, i);
1378a46c0ec8Sopenharmony_ci
1379a46c0ec8Sopenharmony_ci			if (t->state == TOUCH_HOVERING ||
1380a46c0ec8Sopenharmony_ci			    t->state == TOUCH_NONE)
1381a46c0ec8Sopenharmony_ci				continue;
1382a46c0ec8Sopenharmony_ci
1383a46c0ec8Sopenharmony_ci			tp_maybe_end_touch(tp, t, time);
1384a46c0ec8Sopenharmony_ci
1385a46c0ec8Sopenharmony_ci			if (tp_fake_finger_is_touching(tp) &&
1386a46c0ec8Sopenharmony_ci			    tp->nfingers_down == nfake_touches)
1387a46c0ec8Sopenharmony_ci				break;
1388a46c0ec8Sopenharmony_ci		}
1389a46c0ec8Sopenharmony_ci	}
1390a46c0ec8Sopenharmony_ci}
1391a46c0ec8Sopenharmony_ci
1392a46c0ec8Sopenharmony_cistatic void
1393a46c0ec8Sopenharmony_citp_unhover_touches(struct tp_dispatch *tp, uint64_t time)
1394a46c0ec8Sopenharmony_ci{
1395a46c0ec8Sopenharmony_ci	if (tp->pressure.use_pressure)
1396a46c0ec8Sopenharmony_ci		tp_unhover_pressure(tp, time);
1397a46c0ec8Sopenharmony_ci	else if (tp->touch_size.use_touch_size)
1398a46c0ec8Sopenharmony_ci		tp_unhover_size(tp, time);
1399a46c0ec8Sopenharmony_ci	else
1400a46c0ec8Sopenharmony_ci		tp_unhover_fake_touches(tp, time);
1401a46c0ec8Sopenharmony_ci
1402a46c0ec8Sopenharmony_ci}
1403a46c0ec8Sopenharmony_ci
1404a46c0ec8Sopenharmony_cistatic inline void
1405a46c0ec8Sopenharmony_citp_position_fake_touches(struct tp_dispatch *tp)
1406a46c0ec8Sopenharmony_ci{
1407a46c0ec8Sopenharmony_ci	struct tp_touch *t;
1408a46c0ec8Sopenharmony_ci	struct tp_touch *topmost = NULL;
1409a46c0ec8Sopenharmony_ci	unsigned int start, i;
1410a46c0ec8Sopenharmony_ci
1411a46c0ec8Sopenharmony_ci	if (tp_fake_finger_count(tp) <= tp->num_slots ||
1412a46c0ec8Sopenharmony_ci	    tp->nfingers_down == 0)
1413a46c0ec8Sopenharmony_ci		return;
1414a46c0ec8Sopenharmony_ci
1415a46c0ec8Sopenharmony_ci	/* We have at least one fake touch down. Find the top-most real
1416a46c0ec8Sopenharmony_ci	 * touch and copy its coordinates over to to all fake touches.
1417a46c0ec8Sopenharmony_ci	 * This is more reliable than just taking the first touch.
1418a46c0ec8Sopenharmony_ci	 */
1419a46c0ec8Sopenharmony_ci	for (i = 0; i < tp->num_slots; i++) {
1420a46c0ec8Sopenharmony_ci		t = tp_get_touch(tp, i);
1421a46c0ec8Sopenharmony_ci		if (t->state == TOUCH_END ||
1422a46c0ec8Sopenharmony_ci		    t->state == TOUCH_NONE)
1423a46c0ec8Sopenharmony_ci			continue;
1424a46c0ec8Sopenharmony_ci
1425a46c0ec8Sopenharmony_ci		if (topmost == NULL || t->point.y < topmost->point.y)
1426a46c0ec8Sopenharmony_ci			topmost = t;
1427a46c0ec8Sopenharmony_ci	}
1428a46c0ec8Sopenharmony_ci
1429a46c0ec8Sopenharmony_ci	if (!topmost) {
1430a46c0ec8Sopenharmony_ci		evdev_log_bug_libinput(tp->device,
1431a46c0ec8Sopenharmony_ci				       "Unable to find topmost touch\n");
1432a46c0ec8Sopenharmony_ci		return;
1433a46c0ec8Sopenharmony_ci	}
1434a46c0ec8Sopenharmony_ci
1435a46c0ec8Sopenharmony_ci	start = tp->has_mt ? tp->num_slots : 1;
1436a46c0ec8Sopenharmony_ci	for (i = start; i < tp->ntouches; i++) {
1437a46c0ec8Sopenharmony_ci		t = tp_get_touch(tp, i);
1438a46c0ec8Sopenharmony_ci		if (t->state == TOUCH_NONE)
1439a46c0ec8Sopenharmony_ci			continue;
1440a46c0ec8Sopenharmony_ci
1441a46c0ec8Sopenharmony_ci		t->point = topmost->point;
1442a46c0ec8Sopenharmony_ci		t->pressure = topmost->pressure;
1443a46c0ec8Sopenharmony_ci		if (!t->dirty)
1444a46c0ec8Sopenharmony_ci			t->dirty = topmost->dirty;
1445a46c0ec8Sopenharmony_ci	}
1446a46c0ec8Sopenharmony_ci}
1447a46c0ec8Sopenharmony_ci
1448a46c0ec8Sopenharmony_cistatic inline bool
1449a46c0ec8Sopenharmony_citp_need_motion_history_reset(struct tp_dispatch *tp)
1450a46c0ec8Sopenharmony_ci{
1451a46c0ec8Sopenharmony_ci	bool rc = false;
1452a46c0ec8Sopenharmony_ci
1453a46c0ec8Sopenharmony_ci	/* Changing the numbers of fingers can cause a jump in the
1454a46c0ec8Sopenharmony_ci	 * coordinates, always reset the motion history for all touches when
1455a46c0ec8Sopenharmony_ci	 * that happens.
1456a46c0ec8Sopenharmony_ci	 */
1457a46c0ec8Sopenharmony_ci	if (tp->nfingers_down != tp->old_nfingers_down)
1458a46c0ec8Sopenharmony_ci		return true;
1459a46c0ec8Sopenharmony_ci
1460a46c0ec8Sopenharmony_ci	/* Quirk: if we had multiple events without x/y axis
1461a46c0ec8Sopenharmony_ci	   information, the next x/y event is going to be a jump. So we
1462a46c0ec8Sopenharmony_ci	   reset that touch to non-dirty effectively swallowing that event
1463a46c0ec8Sopenharmony_ci	   and restarting with the next event again.
1464a46c0ec8Sopenharmony_ci	 */
1465a46c0ec8Sopenharmony_ci	if (tp->device->model_flags & EVDEV_MODEL_LENOVO_T450_TOUCHPAD) {
1466a46c0ec8Sopenharmony_ci		if (tp->queued & TOUCHPAD_EVENT_MOTION) {
1467a46c0ec8Sopenharmony_ci			if (tp->quirks.nonmotion_event_count > 10) {
1468a46c0ec8Sopenharmony_ci				tp->queued &= ~TOUCHPAD_EVENT_MOTION;
1469a46c0ec8Sopenharmony_ci				rc = true;
1470a46c0ec8Sopenharmony_ci			}
1471a46c0ec8Sopenharmony_ci			tp->quirks.nonmotion_event_count = 0;
1472a46c0ec8Sopenharmony_ci		}
1473a46c0ec8Sopenharmony_ci
1474a46c0ec8Sopenharmony_ci		if ((tp->queued & (TOUCHPAD_EVENT_OTHERAXIS|TOUCHPAD_EVENT_MOTION)) ==
1475a46c0ec8Sopenharmony_ci		    TOUCHPAD_EVENT_OTHERAXIS)
1476a46c0ec8Sopenharmony_ci			tp->quirks.nonmotion_event_count++;
1477a46c0ec8Sopenharmony_ci	}
1478a46c0ec8Sopenharmony_ci
1479a46c0ec8Sopenharmony_ci	return rc;
1480a46c0ec8Sopenharmony_ci}
1481a46c0ec8Sopenharmony_ci
1482a46c0ec8Sopenharmony_cistatic bool
1483a46c0ec8Sopenharmony_citp_detect_jumps(const struct tp_dispatch *tp,
1484a46c0ec8Sopenharmony_ci		struct tp_touch *t,
1485a46c0ec8Sopenharmony_ci		uint64_t time)
1486a46c0ec8Sopenharmony_ci{
1487a46c0ec8Sopenharmony_ci	struct device_coords delta;
1488a46c0ec8Sopenharmony_ci	struct phys_coords mm;
1489a46c0ec8Sopenharmony_ci	struct tp_history_point *last;
1490a46c0ec8Sopenharmony_ci	double abs_distance, rel_distance;
1491a46c0ec8Sopenharmony_ci	bool is_jump = false;
1492a46c0ec8Sopenharmony_ci	uint64_t tdelta;
1493a46c0ec8Sopenharmony_ci	/* Reference interval from the touchpad the various thresholds
1494a46c0ec8Sopenharmony_ci	 * were measured from */
1495a46c0ec8Sopenharmony_ci	unsigned int reference_interval = ms2us(12);
1496a46c0ec8Sopenharmony_ci
1497a46c0ec8Sopenharmony_ci	/* On some touchpads the firmware does funky stuff and we cannot
1498a46c0ec8Sopenharmony_ci	 * have our own jump detection, e.g. Lenovo Carbon X1 Gen 6 (see
1499a46c0ec8Sopenharmony_ci	 * issue #506)
1500a46c0ec8Sopenharmony_ci	 */
1501a46c0ec8Sopenharmony_ci	if (tp->jump.detection_disabled)
1502a46c0ec8Sopenharmony_ci		return false;
1503a46c0ec8Sopenharmony_ci
1504a46c0ec8Sopenharmony_ci	/* We haven't seen pointer jumps on Wacom tablets yet, so exclude
1505a46c0ec8Sopenharmony_ci	 * those.
1506a46c0ec8Sopenharmony_ci	 */
1507a46c0ec8Sopenharmony_ci	if (tp->device->model_flags & EVDEV_MODEL_WACOM_TOUCHPAD)
1508a46c0ec8Sopenharmony_ci		return false;
1509a46c0ec8Sopenharmony_ci
1510a46c0ec8Sopenharmony_ci	if (t->history.count == 0) {
1511a46c0ec8Sopenharmony_ci		t->jumps.last_delta_mm = 0.0;
1512a46c0ec8Sopenharmony_ci		return false;
1513a46c0ec8Sopenharmony_ci	}
1514a46c0ec8Sopenharmony_ci
1515a46c0ec8Sopenharmony_ci	/* called before tp_motion_history_push, so offset 0 is the most
1516a46c0ec8Sopenharmony_ci	 * recent coordinate */
1517a46c0ec8Sopenharmony_ci	last = tp_motion_history_offset(t, 0);
1518a46c0ec8Sopenharmony_ci	tdelta = time - last->time;
1519a46c0ec8Sopenharmony_ci
1520a46c0ec8Sopenharmony_ci	/* For test devices we always force the time delta to 12, at least
1521a46c0ec8Sopenharmony_ci	   until the test suite actually does proper intervals. */
1522a46c0ec8Sopenharmony_ci	if (tp->device->model_flags & EVDEV_MODEL_TEST_DEVICE)
1523a46c0ec8Sopenharmony_ci		reference_interval = tdelta;
1524a46c0ec8Sopenharmony_ci
1525a46c0ec8Sopenharmony_ci	/* If the last frame is more than 30ms ago, we have irregular
1526a46c0ec8Sopenharmony_ci	 * frames, who knows what's a pointer jump here and what's
1527a46c0ec8Sopenharmony_ci	 * legitimate movement.... */
1528a46c0ec8Sopenharmony_ci	if (tdelta > 2.5 * reference_interval || tdelta == 0)
1529a46c0ec8Sopenharmony_ci		return false;
1530a46c0ec8Sopenharmony_ci
1531a46c0ec8Sopenharmony_ci	/* We historically expected ~12ms frame intervals, so the numbers
1532a46c0ec8Sopenharmony_ci	   below are normalized to that (and that's also where the
1533a46c0ec8Sopenharmony_ci	   measured data came from) */
1534a46c0ec8Sopenharmony_ci	delta.x = abs(t->point.x - last->point.x);
1535a46c0ec8Sopenharmony_ci	delta.y = abs(t->point.y - last->point.y);
1536a46c0ec8Sopenharmony_ci	mm = evdev_device_unit_delta_to_mm(tp->device, &delta);
1537a46c0ec8Sopenharmony_ci	abs_distance = hypot(mm.x, mm.y) * reference_interval/tdelta;
1538a46c0ec8Sopenharmony_ci	rel_distance = abs_distance - t->jumps.last_delta_mm;
1539a46c0ec8Sopenharmony_ci
1540a46c0ec8Sopenharmony_ci	/* Special case for the ALPS devices in the Lenovo ThinkPad E465,
1541a46c0ec8Sopenharmony_ci	 * E550. These devices send occasional 4095/0 events on two fingers
1542a46c0ec8Sopenharmony_ci	 * before snapping back to the correct position.
1543a46c0ec8Sopenharmony_ci	 * https://gitlab.freedesktop.org/libinput/libinput/-/issues/492
1544a46c0ec8Sopenharmony_ci	 * The specific values are hardcoded here, if this ever happens on
1545a46c0ec8Sopenharmony_ci	 * any other device we can make it absmax/absmin instead.
1546a46c0ec8Sopenharmony_ci	 */
1547a46c0ec8Sopenharmony_ci	if (tp->device->model_flags & EVDEV_MODEL_ALPS_SERIAL_TOUCHPAD &&
1548a46c0ec8Sopenharmony_ci	    t->point.x == 4095 && t->point.y == 0) {
1549a46c0ec8Sopenharmony_ci		t->point = last->point;
1550a46c0ec8Sopenharmony_ci		return true;
1551a46c0ec8Sopenharmony_ci	}
1552a46c0ec8Sopenharmony_ci
1553a46c0ec8Sopenharmony_ci	/* Cursor jump if:
1554a46c0ec8Sopenharmony_ci	 * - current single-event delta is >20mm, or
1555a46c0ec8Sopenharmony_ci	 * - we increased the delta by over 7mm within a 12ms frame.
1556a46c0ec8Sopenharmony_ci	 *   (12ms simply because that's what I measured)
1557a46c0ec8Sopenharmony_ci	 */
1558a46c0ec8Sopenharmony_ci	is_jump = abs_distance > 20.0 || rel_distance > 7;
1559a46c0ec8Sopenharmony_ci	t->jumps.last_delta_mm = abs_distance;
1560a46c0ec8Sopenharmony_ci
1561a46c0ec8Sopenharmony_ci	return is_jump;
1562a46c0ec8Sopenharmony_ci}
1563a46c0ec8Sopenharmony_ci
1564a46c0ec8Sopenharmony_ci/**
1565a46c0ec8Sopenharmony_ci * Rewrite the motion history so that previous points' timestamps are the
1566a46c0ec8Sopenharmony_ci * current point's timestamp minus whatever MSC_TIMESTAMP gives us.
1567a46c0ec8Sopenharmony_ci *
1568a46c0ec8Sopenharmony_ci * This must be called before tp_motion_history_push()
1569a46c0ec8Sopenharmony_ci *
1570a46c0ec8Sopenharmony_ci * @param t The touch point
1571a46c0ec8Sopenharmony_ci * @param jumping_interval The large time interval in µs
1572a46c0ec8Sopenharmony_ci * @param normal_interval Normal hw interval in µs
1573a46c0ec8Sopenharmony_ci * @param time Current time in µs
1574a46c0ec8Sopenharmony_ci */
1575a46c0ec8Sopenharmony_cistatic inline void
1576a46c0ec8Sopenharmony_citp_motion_history_fix_last(struct tp_dispatch *tp,
1577a46c0ec8Sopenharmony_ci			   struct tp_touch *t,
1578a46c0ec8Sopenharmony_ci			   unsigned int jumping_interval,
1579a46c0ec8Sopenharmony_ci			   unsigned int normal_interval,
1580a46c0ec8Sopenharmony_ci			   uint64_t time)
1581a46c0ec8Sopenharmony_ci{
1582a46c0ec8Sopenharmony_ci	if (t->state != TOUCH_UPDATE)
1583a46c0ec8Sopenharmony_ci		return;
1584a46c0ec8Sopenharmony_ci
1585a46c0ec8Sopenharmony_ci	/* We know the coordinates are correct because the touchpad should
1586a46c0ec8Sopenharmony_ci	 * get that bit right. But the timestamps we got from the kernel are
1587a46c0ec8Sopenharmony_ci	 * messed up, so we go back in the history and fix them.
1588a46c0ec8Sopenharmony_ci	 *
1589a46c0ec8Sopenharmony_ci	 * This way the next delta is huge but it's over a large time, so
1590a46c0ec8Sopenharmony_ci	 * the pointer accel code should do the right thing.
1591a46c0ec8Sopenharmony_ci	 */
1592a46c0ec8Sopenharmony_ci	for (int i = 0; i < (int)t->history.count; i++) {
1593a46c0ec8Sopenharmony_ci		struct tp_history_point *p;
1594a46c0ec8Sopenharmony_ci
1595a46c0ec8Sopenharmony_ci		p = tp_motion_history_offset(t, i);
1596a46c0ec8Sopenharmony_ci		p->time = time - jumping_interval - normal_interval * i;
1597a46c0ec8Sopenharmony_ci	}
1598a46c0ec8Sopenharmony_ci}
1599a46c0ec8Sopenharmony_ci
1600a46c0ec8Sopenharmony_cistatic void
1601a46c0ec8Sopenharmony_citp_process_msc_timestamp(struct tp_dispatch *tp, uint64_t time)
1602a46c0ec8Sopenharmony_ci{
1603a46c0ec8Sopenharmony_ci	struct msc_timestamp *m = &tp->quirks.msc_timestamp;
1604a46c0ec8Sopenharmony_ci
1605a46c0ec8Sopenharmony_ci	/* Pointer jump detection based on MSC_TIMESTAMP.
1606a46c0ec8Sopenharmony_ci
1607a46c0ec8Sopenharmony_ci	   MSC_TIMESTAMP gets reset after a kernel timeout (1s) and on some
1608a46c0ec8Sopenharmony_ci	   devices (Dell XPS) the i2c controller sleeps after a timeout. On
1609a46c0ec8Sopenharmony_ci	   wakeup, some events are swallowed, triggering a cursor jump. The
1610a46c0ec8Sopenharmony_ci	   event sequence after a sleep is always:
1611a46c0ec8Sopenharmony_ci
1612a46c0ec8Sopenharmony_ci	   initial finger down:
1613a46c0ec8Sopenharmony_ci		   ABS_X/Y	x/y
1614a46c0ec8Sopenharmony_ci		   MSC_TIMESTAMP 0
1615a46c0ec8Sopenharmony_ci		   SYN_REPORT +2500ms
1616a46c0ec8Sopenharmony_ci	   second event:
1617a46c0ec8Sopenharmony_ci		   ABS_X/Y	x+n/y+n        # normal movement
1618a46c0ec8Sopenharmony_ci		   MSC_TIMESTAMP 7300          # the hw interval
1619a46c0ec8Sopenharmony_ci		   SYN_REPORT +2ms
1620a46c0ec8Sopenharmony_ci	   third event:
1621a46c0ec8Sopenharmony_ci		   ABS_X/Y	x+lots/y+lots  # pointer jump!
1622a46c0ec8Sopenharmony_ci		   MSC_TIMESTAMP 123456        # well above the hw interval
1623a46c0ec8Sopenharmony_ci		   SYN_REPORT +2ms
1624a46c0ec8Sopenharmony_ci	   fourth event:
1625a46c0ec8Sopenharmony_ci		   ABS_X/Y	x+lots+n/y+lots+n  # all normal again
1626a46c0ec8Sopenharmony_ci		   MSC_TIMESTAMP 123456 + 7300
1627a46c0ec8Sopenharmony_ci		   SYN_REPORT +8ms
1628a46c0ec8Sopenharmony_ci
1629a46c0ec8Sopenharmony_ci	   Our approach is to detect the 0 timestamp, check the interval on
1630a46c0ec8Sopenharmony_ci	   the next event and then calculate the movement for one fictitious
1631a46c0ec8Sopenharmony_ci	   event instead, swallowing all other movements. So if the time
1632a46c0ec8Sopenharmony_ci	   delta is equivalent to 10 events and the movement is x, we
1633a46c0ec8Sopenharmony_ci	   instead pretend there was movement of x/10.
1634a46c0ec8Sopenharmony_ci	 */
1635a46c0ec8Sopenharmony_ci	if (m->now == 0) {
1636a46c0ec8Sopenharmony_ci		m->state = JUMP_STATE_EXPECT_FIRST;
1637a46c0ec8Sopenharmony_ci		m->interval = 0;
1638a46c0ec8Sopenharmony_ci		return;
1639a46c0ec8Sopenharmony_ci	}
1640a46c0ec8Sopenharmony_ci
1641a46c0ec8Sopenharmony_ci	switch(m->state) {
1642a46c0ec8Sopenharmony_ci	case JUMP_STATE_EXPECT_FIRST:
1643a46c0ec8Sopenharmony_ci		if (m->now > ms2us(20)) {
1644a46c0ec8Sopenharmony_ci			m->state = JUMP_STATE_IGNORE;
1645a46c0ec8Sopenharmony_ci		} else {
1646a46c0ec8Sopenharmony_ci			m->state = JUMP_STATE_EXPECT_DELAY;
1647a46c0ec8Sopenharmony_ci			m->interval = m->now;
1648a46c0ec8Sopenharmony_ci		}
1649a46c0ec8Sopenharmony_ci		break;
1650a46c0ec8Sopenharmony_ci	case JUMP_STATE_EXPECT_DELAY:
1651a46c0ec8Sopenharmony_ci		if (m->now > m->interval * 2) {
1652a46c0ec8Sopenharmony_ci			uint32_t tdelta; /* µs */
1653a46c0ec8Sopenharmony_ci			struct tp_touch *t;
1654a46c0ec8Sopenharmony_ci
1655a46c0ec8Sopenharmony_ci			/* The current time is > 2 times the interval so we
1656a46c0ec8Sopenharmony_ci			 * have a jump. Fix the motion history */
1657a46c0ec8Sopenharmony_ci			tdelta = m->now - m->interval;
1658a46c0ec8Sopenharmony_ci
1659a46c0ec8Sopenharmony_ci			tp_for_each_touch(tp, t) {
1660a46c0ec8Sopenharmony_ci				tp_motion_history_fix_last(tp,
1661a46c0ec8Sopenharmony_ci							   t,
1662a46c0ec8Sopenharmony_ci							   tdelta,
1663a46c0ec8Sopenharmony_ci							   m->interval,
1664a46c0ec8Sopenharmony_ci							   time);
1665a46c0ec8Sopenharmony_ci			}
1666a46c0ec8Sopenharmony_ci			m->state = JUMP_STATE_IGNORE;
1667a46c0ec8Sopenharmony_ci
1668a46c0ec8Sopenharmony_ci			/* We need to restart the acceleration filter to forget its history.
1669a46c0ec8Sopenharmony_ci			 * The current point becomes the first point in the history there
1670a46c0ec8Sopenharmony_ci			 * (including timestamp) and that accelerates correctly.
1671a46c0ec8Sopenharmony_ci			 * This has a potential to be incorrect but since we only ever see
1672a46c0ec8Sopenharmony_ci			 * those jumps over the first three events it doesn't matter.
1673a46c0ec8Sopenharmony_ci			 */
1674a46c0ec8Sopenharmony_ci			filter_restart(tp->device->pointer.filter, tp, time - tdelta);
1675a46c0ec8Sopenharmony_ci		}
1676a46c0ec8Sopenharmony_ci		break;
1677a46c0ec8Sopenharmony_ci	case JUMP_STATE_IGNORE:
1678a46c0ec8Sopenharmony_ci		break;
1679a46c0ec8Sopenharmony_ci	}
1680a46c0ec8Sopenharmony_ci}
1681a46c0ec8Sopenharmony_ci
1682a46c0ec8Sopenharmony_cistatic void
1683a46c0ec8Sopenharmony_citp_pre_process_state(struct tp_dispatch *tp, uint64_t time)
1684a46c0ec8Sopenharmony_ci{
1685a46c0ec8Sopenharmony_ci	struct tp_touch *t;
1686a46c0ec8Sopenharmony_ci
1687a46c0ec8Sopenharmony_ci	if (tp->queued & TOUCHPAD_EVENT_TIMESTAMP)
1688a46c0ec8Sopenharmony_ci		tp_process_msc_timestamp(tp, time);
1689a46c0ec8Sopenharmony_ci
1690a46c0ec8Sopenharmony_ci	tp_process_fake_touches(tp, time);
1691a46c0ec8Sopenharmony_ci	tp_unhover_touches(tp, time);
1692a46c0ec8Sopenharmony_ci
1693a46c0ec8Sopenharmony_ci	tp_for_each_touch(tp, t) {
1694a46c0ec8Sopenharmony_ci		if (t->state == TOUCH_MAYBE_END)
1695a46c0ec8Sopenharmony_ci			tp_end_touch(tp, t, time);
1696a46c0ec8Sopenharmony_ci
1697a46c0ec8Sopenharmony_ci		/* Ignore motion when pressure/touch size fell below the
1698a46c0ec8Sopenharmony_ci		 * threshold, thus ending the touch */
1699a46c0ec8Sopenharmony_ci		if (t->state == TOUCH_END && t->history.count > 0)
1700a46c0ec8Sopenharmony_ci			t->point = tp_motion_history_offset(t, 0)->point;
1701a46c0ec8Sopenharmony_ci	}
1702a46c0ec8Sopenharmony_ci
1703a46c0ec8Sopenharmony_ci}
1704a46c0ec8Sopenharmony_ci
1705a46c0ec8Sopenharmony_cistatic void
1706a46c0ec8Sopenharmony_citp_process_state(struct tp_dispatch *tp, uint64_t time)
1707a46c0ec8Sopenharmony_ci{
1708a46c0ec8Sopenharmony_ci	struct tp_touch *t;
1709a46c0ec8Sopenharmony_ci	bool restart_filter = false;
1710a46c0ec8Sopenharmony_ci	bool want_motion_reset;
1711a46c0ec8Sopenharmony_ci	bool have_new_touch = false;
1712a46c0ec8Sopenharmony_ci	unsigned int speed_exceeded_count = 0;
1713a46c0ec8Sopenharmony_ci
1714a46c0ec8Sopenharmony_ci	tp_position_fake_touches(tp);
1715a46c0ec8Sopenharmony_ci
1716a46c0ec8Sopenharmony_ci	want_motion_reset = tp_need_motion_history_reset(tp);
1717a46c0ec8Sopenharmony_ci
1718a46c0ec8Sopenharmony_ci	tp_for_each_touch(tp, t) {
1719a46c0ec8Sopenharmony_ci		if (t->state == TOUCH_NONE)
1720a46c0ec8Sopenharmony_ci			continue;
1721a46c0ec8Sopenharmony_ci
1722a46c0ec8Sopenharmony_ci		if (want_motion_reset) {
1723a46c0ec8Sopenharmony_ci			tp_motion_history_reset(t);
1724a46c0ec8Sopenharmony_ci			t->quirks.reset_motion_history = true;
1725a46c0ec8Sopenharmony_ci		} else if (t->quirks.reset_motion_history) {
1726a46c0ec8Sopenharmony_ci			tp_motion_history_reset(t);
1727a46c0ec8Sopenharmony_ci			t->quirks.reset_motion_history = false;
1728a46c0ec8Sopenharmony_ci		}
1729a46c0ec8Sopenharmony_ci
1730a46c0ec8Sopenharmony_ci		if (!t->dirty) {
1731a46c0ec8Sopenharmony_ci			/* A non-dirty touch must be below the speed limit */
1732a46c0ec8Sopenharmony_ci			if (t->speed.exceeded_count > 0)
1733a46c0ec8Sopenharmony_ci				t->speed.exceeded_count--;
1734a46c0ec8Sopenharmony_ci
1735a46c0ec8Sopenharmony_ci			speed_exceeded_count = max(speed_exceeded_count,
1736a46c0ec8Sopenharmony_ci						   t->speed.exceeded_count);
1737a46c0ec8Sopenharmony_ci
1738a46c0ec8Sopenharmony_ci			/* A touch that hasn't moved must be in the same
1739a46c0ec8Sopenharmony_ci			 * position, so let's add this to the motion
1740a46c0ec8Sopenharmony_ci			 * history.
1741a46c0ec8Sopenharmony_ci			 */
1742a46c0ec8Sopenharmony_ci			tp_motion_history_push(t, time);
1743a46c0ec8Sopenharmony_ci			continue;
1744a46c0ec8Sopenharmony_ci		}
1745a46c0ec8Sopenharmony_ci
1746a46c0ec8Sopenharmony_ci		if (tp_detect_jumps(tp, t, time)) {
1747a46c0ec8Sopenharmony_ci			if (!tp->semi_mt)
1748a46c0ec8Sopenharmony_ci				evdev_log_bug_kernel_ratelimit(tp->device,
1749a46c0ec8Sopenharmony_ci						&tp->jump.warning,
1750a46c0ec8Sopenharmony_ci					        "Touch jump detected and discarded.\n"
1751a46c0ec8Sopenharmony_ci					        "See %s/touchpad-jumping-cursors.html for details\n",
1752a46c0ec8Sopenharmony_ci					        HTTP_DOC_LINK);
1753a46c0ec8Sopenharmony_ci			tp_motion_history_reset(t);
1754a46c0ec8Sopenharmony_ci		}
1755a46c0ec8Sopenharmony_ci
1756a46c0ec8Sopenharmony_ci		tp_thumb_update_touch(tp, t, time);
1757a46c0ec8Sopenharmony_ci		tp_palm_detect(tp, t, time);
1758a46c0ec8Sopenharmony_ci		tp_detect_wobbling(tp, t, time);
1759a46c0ec8Sopenharmony_ci		tp_motion_hysteresis(tp, t);
1760a46c0ec8Sopenharmony_ci		tp_motion_history_push(t, time);
1761a46c0ec8Sopenharmony_ci
1762a46c0ec8Sopenharmony_ci		/* Touch speed handling: if we'are above the threshold,
1763a46c0ec8Sopenharmony_ci		 * count each event that we're over the threshold up to 10
1764a46c0ec8Sopenharmony_ci		 * events. Count down when we are below the speed.
1765a46c0ec8Sopenharmony_ci		 *
1766a46c0ec8Sopenharmony_ci		 * Take the touch with the highest speed excess, if it is
1767a46c0ec8Sopenharmony_ci		 * above a certain threshold (5, see below), assume a
1768a46c0ec8Sopenharmony_ci		 * dropped finger is a thumb.
1769a46c0ec8Sopenharmony_ci		 *
1770a46c0ec8Sopenharmony_ci		 * Yes, this relies on the touchpad to keep sending us
1771a46c0ec8Sopenharmony_ci		 * events even if the finger doesn't move, otherwise we
1772a46c0ec8Sopenharmony_ci		 * never count down. Let's see how far we get with that.
1773a46c0ec8Sopenharmony_ci		 */
1774a46c0ec8Sopenharmony_ci		if (t->speed.last_speed > THUMB_IGNORE_SPEED_THRESHOLD) {
1775a46c0ec8Sopenharmony_ci			if (t->speed.exceeded_count < 15)
1776a46c0ec8Sopenharmony_ci				t->speed.exceeded_count++;
1777a46c0ec8Sopenharmony_ci		} else if (t->speed.exceeded_count > 0) {
1778a46c0ec8Sopenharmony_ci				t->speed.exceeded_count--;
1779a46c0ec8Sopenharmony_ci		}
1780a46c0ec8Sopenharmony_ci
1781a46c0ec8Sopenharmony_ci		speed_exceeded_count = max(speed_exceeded_count,
1782a46c0ec8Sopenharmony_ci					   t->speed.exceeded_count);
1783a46c0ec8Sopenharmony_ci
1784a46c0ec8Sopenharmony_ci		tp_calculate_motion_speed(tp, t, time);
1785a46c0ec8Sopenharmony_ci
1786a46c0ec8Sopenharmony_ci		tp_unpin_finger(tp, t);
1787a46c0ec8Sopenharmony_ci
1788a46c0ec8Sopenharmony_ci		if (t->state == TOUCH_BEGIN) {
1789a46c0ec8Sopenharmony_ci			have_new_touch = true;
1790a46c0ec8Sopenharmony_ci			restart_filter = true;
1791a46c0ec8Sopenharmony_ci		}
1792a46c0ec8Sopenharmony_ci	}
1793a46c0ec8Sopenharmony_ci
1794a46c0ec8Sopenharmony_ci	if (tp->thumb.detect_thumbs &&
1795a46c0ec8Sopenharmony_ci	    have_new_touch &&
1796a46c0ec8Sopenharmony_ci	    tp->nfingers_down >= 2)
1797a46c0ec8Sopenharmony_ci		tp_thumb_update_multifinger(tp);
1798a46c0ec8Sopenharmony_ci
1799a46c0ec8Sopenharmony_ci	if (restart_filter)
1800a46c0ec8Sopenharmony_ci		filter_restart(tp->device->pointer.filter, tp, time);
1801a46c0ec8Sopenharmony_ci
1802a46c0ec8Sopenharmony_ci	tp_button_handle_state(tp, time);
1803a46c0ec8Sopenharmony_ci	tp_edge_scroll_handle_state(tp, time);
1804a46c0ec8Sopenharmony_ci
1805a46c0ec8Sopenharmony_ci	/*
1806a46c0ec8Sopenharmony_ci	 * We have a physical button down event on a clickpad. To avoid
1807a46c0ec8Sopenharmony_ci	 * spurious pointer moves by the clicking finger we pin all fingers.
1808a46c0ec8Sopenharmony_ci	 * We unpin fingers when they move more then a certain threshold to
1809a46c0ec8Sopenharmony_ci	 * to allow drag and drop.
1810a46c0ec8Sopenharmony_ci	 */
1811a46c0ec8Sopenharmony_ci	if ((tp->queued & TOUCHPAD_EVENT_BUTTON_PRESS) &&
1812a46c0ec8Sopenharmony_ci	    tp->buttons.is_clickpad)
1813a46c0ec8Sopenharmony_ci		tp_pin_fingers(tp);
1814a46c0ec8Sopenharmony_ci
1815a46c0ec8Sopenharmony_ci	tp_gesture_handle_state(tp, time);
1816a46c0ec8Sopenharmony_ci}
1817a46c0ec8Sopenharmony_ci
1818a46c0ec8Sopenharmony_cistatic void
1819a46c0ec8Sopenharmony_citp_post_process_state(struct tp_dispatch *tp, uint64_t time)
1820a46c0ec8Sopenharmony_ci{
1821a46c0ec8Sopenharmony_ci	struct tp_touch *t;
1822a46c0ec8Sopenharmony_ci
1823a46c0ec8Sopenharmony_ci	tp_for_each_touch(tp, t) {
1824a46c0ec8Sopenharmony_ci
1825a46c0ec8Sopenharmony_ci		if (!t->dirty)
1826a46c0ec8Sopenharmony_ci			continue;
1827a46c0ec8Sopenharmony_ci
1828a46c0ec8Sopenharmony_ci		if (t->state == TOUCH_END) {
1829a46c0ec8Sopenharmony_ci			if (t->has_ended)
1830a46c0ec8Sopenharmony_ci				t->state = TOUCH_NONE;
1831a46c0ec8Sopenharmony_ci			else
1832a46c0ec8Sopenharmony_ci				t->state = TOUCH_HOVERING;
1833a46c0ec8Sopenharmony_ci		} else if (t->state == TOUCH_BEGIN) {
1834a46c0ec8Sopenharmony_ci			t->state = TOUCH_UPDATE;
1835a46c0ec8Sopenharmony_ci		}
1836a46c0ec8Sopenharmony_ci
1837a46c0ec8Sopenharmony_ci		t->dirty = false;
1838a46c0ec8Sopenharmony_ci	}
1839a46c0ec8Sopenharmony_ci
1840a46c0ec8Sopenharmony_ci	tp->old_nfingers_down = tp->nfingers_down;
1841a46c0ec8Sopenharmony_ci	tp->buttons.old_state = tp->buttons.state;
1842a46c0ec8Sopenharmony_ci
1843a46c0ec8Sopenharmony_ci	tp->queued = TOUCHPAD_EVENT_NONE;
1844a46c0ec8Sopenharmony_ci
1845a46c0ec8Sopenharmony_ci	if (tp->nfingers_down == 0)
1846a46c0ec8Sopenharmony_ci		tp_thumb_reset(tp);
1847a46c0ec8Sopenharmony_ci
1848a46c0ec8Sopenharmony_ci	tp_tap_post_process_state(tp);
1849a46c0ec8Sopenharmony_ci}
1850a46c0ec8Sopenharmony_ci
1851a46c0ec8Sopenharmony_cistatic void
1852a46c0ec8Sopenharmony_citp_post_events(struct tp_dispatch *tp, uint64_t time)
1853a46c0ec8Sopenharmony_ci{
1854a46c0ec8Sopenharmony_ci	bool ignore_motion = false;
1855a46c0ec8Sopenharmony_ci
1856a46c0ec8Sopenharmony_ci	/* Only post (top) button events while suspended */
1857a46c0ec8Sopenharmony_ci	if (tp->device->is_suspended) {
1858a46c0ec8Sopenharmony_ci		tp_post_button_events(tp, time);
1859a46c0ec8Sopenharmony_ci		return;
1860a46c0ec8Sopenharmony_ci	}
1861a46c0ec8Sopenharmony_ci
1862a46c0ec8Sopenharmony_ci	ignore_motion |= tp_tap_handle_state(tp, time);
1863a46c0ec8Sopenharmony_ci	ignore_motion |= tp_post_button_events(tp, time);
1864a46c0ec8Sopenharmony_ci
1865a46c0ec8Sopenharmony_ci	if (tp->palm.trackpoint_active || tp->dwt.keyboard_active) {
1866a46c0ec8Sopenharmony_ci		tp_edge_scroll_stop_events(tp, time);
1867a46c0ec8Sopenharmony_ci		tp_gesture_cancel(tp, time);
1868a46c0ec8Sopenharmony_ci		return;
1869a46c0ec8Sopenharmony_ci	}
1870a46c0ec8Sopenharmony_ci
1871a46c0ec8Sopenharmony_ci	if (ignore_motion) {
1872a46c0ec8Sopenharmony_ci		tp_edge_scroll_stop_events(tp, time);
1873a46c0ec8Sopenharmony_ci		tp_gesture_cancel_motion_gestures(tp, time);
1874a46c0ec8Sopenharmony_ci		tp_gesture_post_events(tp, time, true);
1875a46c0ec8Sopenharmony_ci		return;
1876a46c0ec8Sopenharmony_ci	}
1877a46c0ec8Sopenharmony_ci
1878a46c0ec8Sopenharmony_ci	if (tp_edge_scroll_post_events(tp, time) != 0)
1879a46c0ec8Sopenharmony_ci		return;
1880a46c0ec8Sopenharmony_ci
1881a46c0ec8Sopenharmony_ci	tp_gesture_post_events(tp, time, false);
1882a46c0ec8Sopenharmony_ci}
1883a46c0ec8Sopenharmony_ci
1884a46c0ec8Sopenharmony_cistatic void
1885a46c0ec8Sopenharmony_citp_apply_rotation(struct evdev_device *device)
1886a46c0ec8Sopenharmony_ci{
1887a46c0ec8Sopenharmony_ci	struct tp_dispatch *tp = (struct tp_dispatch *)device->dispatch;
1888a46c0ec8Sopenharmony_ci
1889a46c0ec8Sopenharmony_ci	if (tp->left_handed.want_rotate == tp->left_handed.rotate)
1890a46c0ec8Sopenharmony_ci		return;
1891a46c0ec8Sopenharmony_ci
1892a46c0ec8Sopenharmony_ci	if (tp->nfingers_down)
1893a46c0ec8Sopenharmony_ci		return;
1894a46c0ec8Sopenharmony_ci
1895a46c0ec8Sopenharmony_ci	tp->left_handed.rotate = tp->left_handed.want_rotate;
1896a46c0ec8Sopenharmony_ci
1897a46c0ec8Sopenharmony_ci	evdev_log_debug(device,
1898a46c0ec8Sopenharmony_ci			"touchpad-rotation: rotation is %s\n",
1899a46c0ec8Sopenharmony_ci			tp->left_handed.rotate ? "on" : "off");
1900a46c0ec8Sopenharmony_ci}
1901a46c0ec8Sopenharmony_ci
1902a46c0ec8Sopenharmony_cistatic void
1903a46c0ec8Sopenharmony_citp_handle_state(struct tp_dispatch *tp,
1904a46c0ec8Sopenharmony_ci		uint64_t time)
1905a46c0ec8Sopenharmony_ci{
1906a46c0ec8Sopenharmony_ci	tp_pre_process_state(tp, time);
1907a46c0ec8Sopenharmony_ci	tp_process_state(tp, time);
1908a46c0ec8Sopenharmony_ci	tp_post_events(tp, time);
1909a46c0ec8Sopenharmony_ci	tp_post_process_state(tp, time);
1910a46c0ec8Sopenharmony_ci
1911a46c0ec8Sopenharmony_ci	tp_clickpad_middlebutton_apply_config(tp->device);
1912a46c0ec8Sopenharmony_ci	tp_apply_rotation(tp->device);
1913a46c0ec8Sopenharmony_ci}
1914a46c0ec8Sopenharmony_ci
1915a46c0ec8Sopenharmony_ciLIBINPUT_UNUSED
1916a46c0ec8Sopenharmony_cistatic inline void
1917a46c0ec8Sopenharmony_citp_debug_touch_state(struct tp_dispatch *tp,
1918a46c0ec8Sopenharmony_ci		     struct evdev_device *device)
1919a46c0ec8Sopenharmony_ci{
1920a46c0ec8Sopenharmony_ci	char buf[1024] = {0};
1921a46c0ec8Sopenharmony_ci	struct tp_touch *t;
1922a46c0ec8Sopenharmony_ci	size_t i = 0;
1923a46c0ec8Sopenharmony_ci
1924a46c0ec8Sopenharmony_ci	tp_for_each_touch(tp, t) {
1925a46c0ec8Sopenharmony_ci		if (i >= tp->nfingers_down)
1926a46c0ec8Sopenharmony_ci			break;
1927a46c0ec8Sopenharmony_ci		sprintf(&buf[strlen(buf)],
1928a46c0ec8Sopenharmony_ci			"slot %zd: %04d/%04d p%03d %s |",
1929a46c0ec8Sopenharmony_ci			i++,
1930a46c0ec8Sopenharmony_ci			t->point.x,
1931a46c0ec8Sopenharmony_ci			t->point.y,
1932a46c0ec8Sopenharmony_ci			t->pressure,
1933a46c0ec8Sopenharmony_ci			tp_touch_active(tp, t) ? "" : "inactive");
1934a46c0ec8Sopenharmony_ci	}
1935a46c0ec8Sopenharmony_ci	if (buf[0] != '\0')
1936a46c0ec8Sopenharmony_ci		evdev_log_debug(device, "touch state: %s\n", buf);
1937a46c0ec8Sopenharmony_ci}
1938a46c0ec8Sopenharmony_ci
1939a46c0ec8Sopenharmony_cistatic void
1940a46c0ec8Sopenharmony_citp_interface_process(struct evdev_dispatch *dispatch,
1941a46c0ec8Sopenharmony_ci		     struct evdev_device *device,
1942a46c0ec8Sopenharmony_ci		     struct input_event *e,
1943a46c0ec8Sopenharmony_ci		     uint64_t time)
1944a46c0ec8Sopenharmony_ci{
1945a46c0ec8Sopenharmony_ci	struct tp_dispatch *tp = tp_dispatch(dispatch);
1946a46c0ec8Sopenharmony_ci
1947a46c0ec8Sopenharmony_ci	switch (e->type) {
1948a46c0ec8Sopenharmony_ci	case EV_ABS:
1949a46c0ec8Sopenharmony_ci		if (tp->has_mt)
1950a46c0ec8Sopenharmony_ci			tp_process_absolute(tp, e, time);
1951a46c0ec8Sopenharmony_ci		else
1952a46c0ec8Sopenharmony_ci			tp_process_absolute_st(tp, e, time);
1953a46c0ec8Sopenharmony_ci		break;
1954a46c0ec8Sopenharmony_ci	case EV_KEY:
1955a46c0ec8Sopenharmony_ci		tp_process_key(tp, e, time);
1956a46c0ec8Sopenharmony_ci		break;
1957a46c0ec8Sopenharmony_ci	case EV_MSC:
1958a46c0ec8Sopenharmony_ci		tp_process_msc(tp, e, time);
1959a46c0ec8Sopenharmony_ci		break;
1960a46c0ec8Sopenharmony_ci	case EV_SYN:
1961a46c0ec8Sopenharmony_ci		tp_handle_state(tp, time);
1962a46c0ec8Sopenharmony_ci#if 0
1963a46c0ec8Sopenharmony_ci		tp_debug_touch_state(tp, device);
1964a46c0ec8Sopenharmony_ci#endif
1965a46c0ec8Sopenharmony_ci		break;
1966a46c0ec8Sopenharmony_ci	}
1967a46c0ec8Sopenharmony_ci}
1968a46c0ec8Sopenharmony_ci
1969a46c0ec8Sopenharmony_cistatic void
1970a46c0ec8Sopenharmony_citp_remove_sendevents(struct tp_dispatch *tp)
1971a46c0ec8Sopenharmony_ci{
1972a46c0ec8Sopenharmony_ci	struct evdev_paired_keyboard *kbd;
1973a46c0ec8Sopenharmony_ci
1974a46c0ec8Sopenharmony_ci	libinput_timer_cancel(&tp->palm.trackpoint_timer);
1975a46c0ec8Sopenharmony_ci	libinput_timer_cancel(&tp->dwt.keyboard_timer);
1976a46c0ec8Sopenharmony_ci
1977a46c0ec8Sopenharmony_ci	if (tp->buttons.trackpoint &&
1978a46c0ec8Sopenharmony_ci	    tp->palm.monitor_trackpoint)
1979a46c0ec8Sopenharmony_ci		libinput_device_remove_event_listener(
1980a46c0ec8Sopenharmony_ci					&tp->palm.trackpoint_listener);
1981a46c0ec8Sopenharmony_ci
1982a46c0ec8Sopenharmony_ci	list_for_each(kbd, &tp->dwt.paired_keyboard_list, link) {
1983a46c0ec8Sopenharmony_ci		libinput_device_remove_event_listener(&kbd->listener);
1984a46c0ec8Sopenharmony_ci	}
1985a46c0ec8Sopenharmony_ci
1986a46c0ec8Sopenharmony_ci	if (tp->lid_switch.lid_switch)
1987a46c0ec8Sopenharmony_ci		libinput_device_remove_event_listener(
1988a46c0ec8Sopenharmony_ci					&tp->lid_switch.listener);
1989a46c0ec8Sopenharmony_ci
1990a46c0ec8Sopenharmony_ci	if (tp->tablet_mode_switch.tablet_mode_switch)
1991a46c0ec8Sopenharmony_ci		libinput_device_remove_event_listener(
1992a46c0ec8Sopenharmony_ci					&tp->tablet_mode_switch.listener);
1993a46c0ec8Sopenharmony_ci}
1994a46c0ec8Sopenharmony_ci
1995a46c0ec8Sopenharmony_cistatic void
1996a46c0ec8Sopenharmony_citp_interface_remove(struct evdev_dispatch *dispatch)
1997a46c0ec8Sopenharmony_ci{
1998a46c0ec8Sopenharmony_ci	struct tp_dispatch *tp = tp_dispatch(dispatch);
1999a46c0ec8Sopenharmony_ci	struct evdev_paired_keyboard *kbd;
2000a46c0ec8Sopenharmony_ci
2001a46c0ec8Sopenharmony_ci	libinput_timer_cancel(&tp->arbitration.arbitration_timer);
2002a46c0ec8Sopenharmony_ci
2003a46c0ec8Sopenharmony_ci	list_for_each_safe(kbd, &tp->dwt.paired_keyboard_list, link) {
2004a46c0ec8Sopenharmony_ci		evdev_paired_keyboard_destroy(kbd);
2005a46c0ec8Sopenharmony_ci	}
2006a46c0ec8Sopenharmony_ci	tp->dwt.keyboard_active = false;
2007a46c0ec8Sopenharmony_ci
2008a46c0ec8Sopenharmony_ci	tp_remove_tap(tp);
2009a46c0ec8Sopenharmony_ci	tp_remove_buttons(tp);
2010a46c0ec8Sopenharmony_ci	tp_remove_sendevents(tp);
2011a46c0ec8Sopenharmony_ci	tp_remove_edge_scroll(tp);
2012a46c0ec8Sopenharmony_ci	tp_remove_gesture(tp);
2013a46c0ec8Sopenharmony_ci}
2014a46c0ec8Sopenharmony_ci
2015a46c0ec8Sopenharmony_cistatic void
2016a46c0ec8Sopenharmony_citp_interface_destroy(struct evdev_dispatch *dispatch)
2017a46c0ec8Sopenharmony_ci{
2018a46c0ec8Sopenharmony_ci	struct tp_dispatch *tp = tp_dispatch(dispatch);
2019a46c0ec8Sopenharmony_ci
2020a46c0ec8Sopenharmony_ci	libinput_timer_destroy(&tp->arbitration.arbitration_timer);
2021a46c0ec8Sopenharmony_ci	libinput_timer_destroy(&tp->palm.trackpoint_timer);
2022a46c0ec8Sopenharmony_ci	libinput_timer_destroy(&tp->dwt.keyboard_timer);
2023a46c0ec8Sopenharmony_ci	libinput_timer_destroy(&tp->tap.timer);
2024a46c0ec8Sopenharmony_ci	libinput_timer_destroy(&tp->gesture.finger_count_switch_timer);
2025a46c0ec8Sopenharmony_ci	libinput_timer_destroy(&tp->gesture.hold_timer);
2026a46c0ec8Sopenharmony_ci	free(tp->touches);
2027a46c0ec8Sopenharmony_ci	free(tp);
2028a46c0ec8Sopenharmony_ci}
2029a46c0ec8Sopenharmony_ci
2030a46c0ec8Sopenharmony_cistatic void
2031a46c0ec8Sopenharmony_citp_release_fake_touches(struct tp_dispatch *tp)
2032a46c0ec8Sopenharmony_ci{
2033a46c0ec8Sopenharmony_ci	tp->fake_touches = 0;
2034a46c0ec8Sopenharmony_ci}
2035a46c0ec8Sopenharmony_ci
2036a46c0ec8Sopenharmony_cistatic void
2037a46c0ec8Sopenharmony_citp_clear_state(struct tp_dispatch *tp)
2038a46c0ec8Sopenharmony_ci{
2039a46c0ec8Sopenharmony_ci	uint64_t now = libinput_now(tp_libinput_context(tp));
2040a46c0ec8Sopenharmony_ci	struct tp_touch *t;
2041a46c0ec8Sopenharmony_ci
2042a46c0ec8Sopenharmony_ci	/* Unroll the touchpad state.
2043a46c0ec8Sopenharmony_ci	 * Release buttons first. If tp is a clickpad, the button event
2044a46c0ec8Sopenharmony_ci	 * must come before the touch up. If it isn't, the order doesn't
2045a46c0ec8Sopenharmony_ci	 * matter anyway
2046a46c0ec8Sopenharmony_ci	 *
2047a46c0ec8Sopenharmony_ci	 * Then cancel all timeouts on the taps, triggering the last set
2048a46c0ec8Sopenharmony_ci	 * of events.
2049a46c0ec8Sopenharmony_ci	 *
2050a46c0ec8Sopenharmony_ci	 * Then lift all touches so the touchpad is in a neutral state.
2051a46c0ec8Sopenharmony_ci	 *
2052a46c0ec8Sopenharmony_ci	 * Then reset thumb state.
2053a46c0ec8Sopenharmony_ci	 *
2054a46c0ec8Sopenharmony_ci	 */
2055a46c0ec8Sopenharmony_ci	tp_release_all_buttons(tp, now);
2056a46c0ec8Sopenharmony_ci	tp_release_all_taps(tp, now);
2057a46c0ec8Sopenharmony_ci
2058a46c0ec8Sopenharmony_ci	tp_for_each_touch(tp, t) {
2059a46c0ec8Sopenharmony_ci		tp_end_sequence(tp, t, now);
2060a46c0ec8Sopenharmony_ci	}
2061a46c0ec8Sopenharmony_ci	tp_release_fake_touches(tp);
2062a46c0ec8Sopenharmony_ci
2063a46c0ec8Sopenharmony_ci	tp_thumb_reset(tp);
2064a46c0ec8Sopenharmony_ci
2065a46c0ec8Sopenharmony_ci	tp_handle_state(tp, now);
2066a46c0ec8Sopenharmony_ci}
2067a46c0ec8Sopenharmony_ci
2068a46c0ec8Sopenharmony_cistatic void
2069a46c0ec8Sopenharmony_citp_suspend(struct tp_dispatch *tp,
2070a46c0ec8Sopenharmony_ci	   struct evdev_device *device,
2071a46c0ec8Sopenharmony_ci	   enum suspend_trigger trigger)
2072a46c0ec8Sopenharmony_ci{
2073a46c0ec8Sopenharmony_ci	if (tp->suspend_reason & trigger)
2074a46c0ec8Sopenharmony_ci		return;
2075a46c0ec8Sopenharmony_ci
2076a46c0ec8Sopenharmony_ci	if (tp->suspend_reason != 0)
2077a46c0ec8Sopenharmony_ci		goto out;
2078a46c0ec8Sopenharmony_ci
2079a46c0ec8Sopenharmony_ci	tp_clear_state(tp);
2080a46c0ec8Sopenharmony_ci
2081a46c0ec8Sopenharmony_ci	/* On devices with top softwarebuttons we don't actually suspend the
2082a46c0ec8Sopenharmony_ci	 * device, to keep the "trackpoint" buttons working. tp_post_events()
2083a46c0ec8Sopenharmony_ci	 * will only send events for the trackpoint while suspended.
2084a46c0ec8Sopenharmony_ci	 */
2085a46c0ec8Sopenharmony_ci	if (tp->buttons.has_topbuttons) {
2086a46c0ec8Sopenharmony_ci		evdev_notify_suspended_device(device);
2087a46c0ec8Sopenharmony_ci		/* Enlarge topbutton area while suspended */
2088a46c0ec8Sopenharmony_ci		tp_init_top_softbuttons(tp, device, 3.0);
2089a46c0ec8Sopenharmony_ci	} else {
2090a46c0ec8Sopenharmony_ci		evdev_device_suspend(device);
2091a46c0ec8Sopenharmony_ci	}
2092a46c0ec8Sopenharmony_ci
2093a46c0ec8Sopenharmony_ciout:
2094a46c0ec8Sopenharmony_ci	tp->suspend_reason |= trigger;
2095a46c0ec8Sopenharmony_ci}
2096a46c0ec8Sopenharmony_ci
2097a46c0ec8Sopenharmony_cistatic void
2098a46c0ec8Sopenharmony_citp_interface_suspend(struct evdev_dispatch *dispatch,
2099a46c0ec8Sopenharmony_ci		     struct evdev_device *device)
2100a46c0ec8Sopenharmony_ci{
2101a46c0ec8Sopenharmony_ci	struct tp_dispatch *tp = tp_dispatch(dispatch);
2102a46c0ec8Sopenharmony_ci
2103a46c0ec8Sopenharmony_ci	tp_clear_state(tp);
2104a46c0ec8Sopenharmony_ci}
2105a46c0ec8Sopenharmony_ci
2106a46c0ec8Sopenharmony_cistatic inline void
2107a46c0ec8Sopenharmony_citp_sync_touch(struct tp_dispatch *tp,
2108a46c0ec8Sopenharmony_ci	      struct evdev_device *device,
2109a46c0ec8Sopenharmony_ci	      struct tp_touch *t,
2110a46c0ec8Sopenharmony_ci	      int slot)
2111a46c0ec8Sopenharmony_ci{
2112a46c0ec8Sopenharmony_ci	struct libevdev *evdev = device->evdev;
2113a46c0ec8Sopenharmony_ci	int tracking_id;
2114a46c0ec8Sopenharmony_ci
2115a46c0ec8Sopenharmony_ci	if (!libevdev_fetch_slot_value(evdev,
2116a46c0ec8Sopenharmony_ci				       slot,
2117a46c0ec8Sopenharmony_ci				       ABS_MT_POSITION_X,
2118a46c0ec8Sopenharmony_ci				       &t->point.x))
2119a46c0ec8Sopenharmony_ci		t->point.x = libevdev_get_event_value(evdev, EV_ABS, ABS_X);
2120a46c0ec8Sopenharmony_ci	if (!libevdev_fetch_slot_value(evdev,
2121a46c0ec8Sopenharmony_ci				       slot,
2122a46c0ec8Sopenharmony_ci				       ABS_MT_POSITION_Y,
2123a46c0ec8Sopenharmony_ci				       &t->point.y))
2124a46c0ec8Sopenharmony_ci		t->point.y = libevdev_get_event_value(evdev, EV_ABS, ABS_Y);
2125a46c0ec8Sopenharmony_ci
2126a46c0ec8Sopenharmony_ci	if (!libevdev_fetch_slot_value(evdev,
2127a46c0ec8Sopenharmony_ci				       slot,
2128a46c0ec8Sopenharmony_ci				       ABS_MT_PRESSURE,
2129a46c0ec8Sopenharmony_ci				       &t->pressure))
2130a46c0ec8Sopenharmony_ci		t->pressure = libevdev_get_event_value(evdev,
2131a46c0ec8Sopenharmony_ci						       EV_ABS,
2132a46c0ec8Sopenharmony_ci						       ABS_PRESSURE);
2133a46c0ec8Sopenharmony_ci
2134a46c0ec8Sopenharmony_ci	libevdev_fetch_slot_value(evdev,
2135a46c0ec8Sopenharmony_ci				  slot,
2136a46c0ec8Sopenharmony_ci				  ABS_MT_TOUCH_MAJOR,
2137a46c0ec8Sopenharmony_ci				  &t->major);
2138a46c0ec8Sopenharmony_ci	libevdev_fetch_slot_value(evdev,
2139a46c0ec8Sopenharmony_ci				  slot,
2140a46c0ec8Sopenharmony_ci				  ABS_MT_TOUCH_MINOR,
2141a46c0ec8Sopenharmony_ci				  &t->minor);
2142a46c0ec8Sopenharmony_ci
2143a46c0ec8Sopenharmony_ci	if (libevdev_fetch_slot_value(evdev,
2144a46c0ec8Sopenharmony_ci				      slot,
2145a46c0ec8Sopenharmony_ci				      ABS_MT_TRACKING_ID,
2146a46c0ec8Sopenharmony_ci				      &tracking_id) &&
2147a46c0ec8Sopenharmony_ci	    tracking_id != -1)
2148a46c0ec8Sopenharmony_ci		tp->nactive_slots++;
2149a46c0ec8Sopenharmony_ci}
2150a46c0ec8Sopenharmony_ci
2151a46c0ec8Sopenharmony_cistatic void
2152a46c0ec8Sopenharmony_citp_sync_slots(struct tp_dispatch *tp,
2153a46c0ec8Sopenharmony_ci	      struct evdev_device *device)
2154a46c0ec8Sopenharmony_ci{
2155a46c0ec8Sopenharmony_ci	/* Always sync the first touch so we get ABS_X/Y synced on
2156a46c0ec8Sopenharmony_ci	 * single-touch touchpads */
2157a46c0ec8Sopenharmony_ci	tp_sync_touch(tp, device, &tp->touches[0], 0);
2158a46c0ec8Sopenharmony_ci	for (unsigned int i = 1; i < tp->num_slots; i++)
2159a46c0ec8Sopenharmony_ci		tp_sync_touch(tp, device, &tp->touches[i], i);
2160a46c0ec8Sopenharmony_ci}
2161a46c0ec8Sopenharmony_ci
2162a46c0ec8Sopenharmony_cistatic void
2163a46c0ec8Sopenharmony_citp_resume(struct tp_dispatch *tp,
2164a46c0ec8Sopenharmony_ci	  struct evdev_device *device,
2165a46c0ec8Sopenharmony_ci	  enum suspend_trigger trigger)
2166a46c0ec8Sopenharmony_ci{
2167a46c0ec8Sopenharmony_ci	tp->suspend_reason &= ~trigger;
2168a46c0ec8Sopenharmony_ci	if (tp->suspend_reason != 0)
2169a46c0ec8Sopenharmony_ci		return;
2170a46c0ec8Sopenharmony_ci
2171a46c0ec8Sopenharmony_ci	if (tp->buttons.has_topbuttons) {
2172a46c0ec8Sopenharmony_ci		/* tap state-machine is offline while suspended, reset state */
2173a46c0ec8Sopenharmony_ci		tp_clear_state(tp);
2174a46c0ec8Sopenharmony_ci		/* restore original topbutton area size */
2175a46c0ec8Sopenharmony_ci		tp_init_top_softbuttons(tp, device, 1.0);
2176a46c0ec8Sopenharmony_ci		evdev_notify_resumed_device(device);
2177a46c0ec8Sopenharmony_ci	} else {
2178a46c0ec8Sopenharmony_ci		evdev_device_resume(device);
2179a46c0ec8Sopenharmony_ci	}
2180a46c0ec8Sopenharmony_ci
2181a46c0ec8Sopenharmony_ci	tp_sync_slots(tp, device);
2182a46c0ec8Sopenharmony_ci}
2183a46c0ec8Sopenharmony_ci
2184a46c0ec8Sopenharmony_cistatic void
2185a46c0ec8Sopenharmony_citp_trackpoint_timeout(uint64_t now, void *data)
2186a46c0ec8Sopenharmony_ci{
2187a46c0ec8Sopenharmony_ci	struct tp_dispatch *tp = data;
2188a46c0ec8Sopenharmony_ci
2189a46c0ec8Sopenharmony_ci	if (tp->palm.trackpoint_active) {
2190a46c0ec8Sopenharmony_ci		tp_tap_resume(tp, now);
2191a46c0ec8Sopenharmony_ci		tp->palm.trackpoint_active = false;
2192a46c0ec8Sopenharmony_ci	}
2193a46c0ec8Sopenharmony_ci	tp->palm.trackpoint_event_count = 0;
2194a46c0ec8Sopenharmony_ci}
2195a46c0ec8Sopenharmony_ci
2196a46c0ec8Sopenharmony_cistatic void
2197a46c0ec8Sopenharmony_citp_trackpoint_event(uint64_t time, struct libinput_event *event, void *data)
2198a46c0ec8Sopenharmony_ci{
2199a46c0ec8Sopenharmony_ci	struct tp_dispatch *tp = data;
2200a46c0ec8Sopenharmony_ci
2201a46c0ec8Sopenharmony_ci	if (!tp->palm.dwtp_enabled)
2202a46c0ec8Sopenharmony_ci		return;
2203a46c0ec8Sopenharmony_ci
2204a46c0ec8Sopenharmony_ci	/* Buttons do not count as trackpad activity, as people may use
2205a46c0ec8Sopenharmony_ci	   the trackpoint buttons in combination with the touchpad. */
2206a46c0ec8Sopenharmony_ci	if (event->type == LIBINPUT_EVENT_POINTER_BUTTON)
2207a46c0ec8Sopenharmony_ci		return;
2208a46c0ec8Sopenharmony_ci
2209a46c0ec8Sopenharmony_ci	tp->palm.trackpoint_last_event_time = time;
2210a46c0ec8Sopenharmony_ci	tp->palm.trackpoint_event_count++;
2211a46c0ec8Sopenharmony_ci
2212a46c0ec8Sopenharmony_ci	/* Require at least three events before enabling palm detection */
2213a46c0ec8Sopenharmony_ci	if (tp->palm.trackpoint_event_count < 3) {
2214a46c0ec8Sopenharmony_ci		libinput_timer_set(&tp->palm.trackpoint_timer,
2215a46c0ec8Sopenharmony_ci				   time + DEFAULT_TRACKPOINT_EVENT_TIMEOUT);
2216a46c0ec8Sopenharmony_ci		return;
2217a46c0ec8Sopenharmony_ci	}
2218a46c0ec8Sopenharmony_ci
2219a46c0ec8Sopenharmony_ci	if (!tp->palm.trackpoint_active) {
2220a46c0ec8Sopenharmony_ci		tp_stop_actions(tp, time);
2221a46c0ec8Sopenharmony_ci		tp->palm.trackpoint_active = true;
2222a46c0ec8Sopenharmony_ci	}
2223a46c0ec8Sopenharmony_ci
2224a46c0ec8Sopenharmony_ci	libinput_timer_set(&tp->palm.trackpoint_timer,
2225a46c0ec8Sopenharmony_ci			   time + DEFAULT_TRACKPOINT_ACTIVITY_TIMEOUT);
2226a46c0ec8Sopenharmony_ci}
2227a46c0ec8Sopenharmony_ci
2228a46c0ec8Sopenharmony_cistatic void
2229a46c0ec8Sopenharmony_citp_keyboard_timeout(uint64_t now, void *data)
2230a46c0ec8Sopenharmony_ci{
2231a46c0ec8Sopenharmony_ci	struct tp_dispatch *tp = data;
2232a46c0ec8Sopenharmony_ci
2233a46c0ec8Sopenharmony_ci	if (tp->dwt.dwt_enabled &&
2234a46c0ec8Sopenharmony_ci	    long_any_bit_set(tp->dwt.key_mask,
2235a46c0ec8Sopenharmony_ci			     ARRAY_LENGTH(tp->dwt.key_mask))) {
2236a46c0ec8Sopenharmony_ci		libinput_timer_set(&tp->dwt.keyboard_timer,
2237a46c0ec8Sopenharmony_ci				   now + DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_2);
2238a46c0ec8Sopenharmony_ci		tp->dwt.keyboard_last_press_time = now;
2239a46c0ec8Sopenharmony_ci		evdev_log_debug(tp->device, "palm: keyboard timeout refresh\n");
2240a46c0ec8Sopenharmony_ci		return;
2241a46c0ec8Sopenharmony_ci	}
2242a46c0ec8Sopenharmony_ci
2243a46c0ec8Sopenharmony_ci	tp_tap_resume(tp, now);
2244a46c0ec8Sopenharmony_ci
2245a46c0ec8Sopenharmony_ci	tp->dwt.keyboard_active = false;
2246a46c0ec8Sopenharmony_ci
2247a46c0ec8Sopenharmony_ci	evdev_log_debug(tp->device, "palm: keyboard timeout\n");
2248a46c0ec8Sopenharmony_ci}
2249a46c0ec8Sopenharmony_ci
2250a46c0ec8Sopenharmony_cistatic inline bool
2251a46c0ec8Sopenharmony_citp_key_is_modifier(unsigned int keycode)
2252a46c0ec8Sopenharmony_ci{
2253a46c0ec8Sopenharmony_ci	switch (keycode) {
2254a46c0ec8Sopenharmony_ci	/* Ignore modifiers to be responsive to ctrl-click, alt-tab, etc. */
2255a46c0ec8Sopenharmony_ci	case KEY_LEFTCTRL:
2256a46c0ec8Sopenharmony_ci	case KEY_RIGHTCTRL:
2257a46c0ec8Sopenharmony_ci	case KEY_LEFTALT:
2258a46c0ec8Sopenharmony_ci	case KEY_RIGHTALT:
2259a46c0ec8Sopenharmony_ci	case KEY_LEFTSHIFT:
2260a46c0ec8Sopenharmony_ci	case KEY_RIGHTSHIFT:
2261a46c0ec8Sopenharmony_ci	case KEY_FN:
2262a46c0ec8Sopenharmony_ci	case KEY_CAPSLOCK:
2263a46c0ec8Sopenharmony_ci	case KEY_TAB:
2264a46c0ec8Sopenharmony_ci	case KEY_COMPOSE:
2265a46c0ec8Sopenharmony_ci	case KEY_RIGHTMETA:
2266a46c0ec8Sopenharmony_ci	case KEY_LEFTMETA:
2267a46c0ec8Sopenharmony_ci		return true;
2268a46c0ec8Sopenharmony_ci	default:
2269a46c0ec8Sopenharmony_ci		return false;
2270a46c0ec8Sopenharmony_ci	}
2271a46c0ec8Sopenharmony_ci}
2272a46c0ec8Sopenharmony_ci
2273a46c0ec8Sopenharmony_cistatic inline bool
2274a46c0ec8Sopenharmony_citp_key_ignore_for_dwt(unsigned int keycode)
2275a46c0ec8Sopenharmony_ci{
2276a46c0ec8Sopenharmony_ci	/* Ignore keys not part of the "typewriter set", i.e. F-keys,
2277a46c0ec8Sopenharmony_ci	 * multimedia keys, numpad, etc.
2278a46c0ec8Sopenharmony_ci	 */
2279a46c0ec8Sopenharmony_ci
2280a46c0ec8Sopenharmony_ci	if (tp_key_is_modifier(keycode))
2281a46c0ec8Sopenharmony_ci		return false;
2282a46c0ec8Sopenharmony_ci
2283a46c0ec8Sopenharmony_ci	switch (keycode) {
2284a46c0ec8Sopenharmony_ci	case KEY_ESC:
2285a46c0ec8Sopenharmony_ci	case KEY_KPASTERISK:
2286a46c0ec8Sopenharmony_ci		return true;
2287a46c0ec8Sopenharmony_ci	default:
2288a46c0ec8Sopenharmony_ci		return keycode >= KEY_F1;
2289a46c0ec8Sopenharmony_ci	}
2290a46c0ec8Sopenharmony_ci}
2291a46c0ec8Sopenharmony_ci
2292a46c0ec8Sopenharmony_cistatic void
2293a46c0ec8Sopenharmony_citp_keyboard_event(uint64_t time, struct libinput_event *event, void *data)
2294a46c0ec8Sopenharmony_ci{
2295a46c0ec8Sopenharmony_ci	struct tp_dispatch *tp = data;
2296a46c0ec8Sopenharmony_ci	struct libinput_event_keyboard *kbdev;
2297a46c0ec8Sopenharmony_ci	unsigned int timeout;
2298a46c0ec8Sopenharmony_ci	unsigned int key;
2299a46c0ec8Sopenharmony_ci	bool is_modifier;
2300a46c0ec8Sopenharmony_ci
2301a46c0ec8Sopenharmony_ci	if (event->type != LIBINPUT_EVENT_KEYBOARD_KEY)
2302a46c0ec8Sopenharmony_ci		return;
2303a46c0ec8Sopenharmony_ci
2304a46c0ec8Sopenharmony_ci	kbdev = libinput_event_get_keyboard_event(event);
2305a46c0ec8Sopenharmony_ci	key = libinput_event_keyboard_get_key(kbdev);
2306a46c0ec8Sopenharmony_ci
2307a46c0ec8Sopenharmony_ci	/* Only trigger the timer on key down. */
2308a46c0ec8Sopenharmony_ci	if (libinput_event_keyboard_get_key_state(kbdev) !=
2309a46c0ec8Sopenharmony_ci	    LIBINPUT_KEY_STATE_PRESSED) {
2310a46c0ec8Sopenharmony_ci		long_clear_bit(tp->dwt.key_mask, key);
2311a46c0ec8Sopenharmony_ci		long_clear_bit(tp->dwt.mod_mask, key);
2312a46c0ec8Sopenharmony_ci		return;
2313a46c0ec8Sopenharmony_ci	}
2314a46c0ec8Sopenharmony_ci
2315a46c0ec8Sopenharmony_ci	if (!tp->dwt.dwt_enabled)
2316a46c0ec8Sopenharmony_ci		return;
2317a46c0ec8Sopenharmony_ci
2318a46c0ec8Sopenharmony_ci	if (tp_key_ignore_for_dwt(key))
2319a46c0ec8Sopenharmony_ci		return;
2320a46c0ec8Sopenharmony_ci
2321a46c0ec8Sopenharmony_ci	/* modifier keys don't trigger disable-while-typing so things like
2322a46c0ec8Sopenharmony_ci	 * ctrl+zoom or ctrl+click are possible */
2323a46c0ec8Sopenharmony_ci	is_modifier = tp_key_is_modifier(key);
2324a46c0ec8Sopenharmony_ci	if (is_modifier) {
2325a46c0ec8Sopenharmony_ci		long_set_bit(tp->dwt.mod_mask, key);
2326a46c0ec8Sopenharmony_ci		return;
2327a46c0ec8Sopenharmony_ci	}
2328a46c0ec8Sopenharmony_ci
2329a46c0ec8Sopenharmony_ci	if (!tp->dwt.keyboard_active) {
2330a46c0ec8Sopenharmony_ci		/* This is the first non-modifier key press. Check if the
2331a46c0ec8Sopenharmony_ci		 * modifier mask is set. If any modifier is down we don't
2332a46c0ec8Sopenharmony_ci		 * trigger dwt because it's likely to be combination like
2333a46c0ec8Sopenharmony_ci		 * Ctrl+S or similar */
2334a46c0ec8Sopenharmony_ci
2335a46c0ec8Sopenharmony_ci		if (long_any_bit_set(tp->dwt.mod_mask,
2336a46c0ec8Sopenharmony_ci				     ARRAY_LENGTH(tp->dwt.mod_mask)))
2337a46c0ec8Sopenharmony_ci		    return;
2338a46c0ec8Sopenharmony_ci
2339a46c0ec8Sopenharmony_ci		tp_stop_actions(tp, time);
2340a46c0ec8Sopenharmony_ci		tp->dwt.keyboard_active = true;
2341a46c0ec8Sopenharmony_ci		timeout = DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_1;
2342a46c0ec8Sopenharmony_ci	} else {
2343a46c0ec8Sopenharmony_ci		timeout = DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_2;
2344a46c0ec8Sopenharmony_ci	}
2345a46c0ec8Sopenharmony_ci
2346a46c0ec8Sopenharmony_ci	tp->dwt.keyboard_last_press_time = time;
2347a46c0ec8Sopenharmony_ci	long_set_bit(tp->dwt.key_mask, key);
2348a46c0ec8Sopenharmony_ci	libinput_timer_set(&tp->dwt.keyboard_timer,
2349a46c0ec8Sopenharmony_ci			   time + timeout);
2350a46c0ec8Sopenharmony_ci}
2351a46c0ec8Sopenharmony_ci
2352a46c0ec8Sopenharmony_cistatic bool
2353a46c0ec8Sopenharmony_citp_want_dwt(struct evdev_device *touchpad,
2354a46c0ec8Sopenharmony_ci	    struct evdev_device *keyboard)
2355a46c0ec8Sopenharmony_ci{
2356a46c0ec8Sopenharmony_ci	unsigned int vendor_tp = evdev_device_get_id_vendor(touchpad);
2357a46c0ec8Sopenharmony_ci	unsigned int vendor_kbd = evdev_device_get_id_vendor(keyboard);
2358a46c0ec8Sopenharmony_ci	unsigned int product_tp = evdev_device_get_id_product(touchpad);
2359a46c0ec8Sopenharmony_ci	unsigned int product_kbd = evdev_device_get_id_product(keyboard);
2360a46c0ec8Sopenharmony_ci
2361a46c0ec8Sopenharmony_ci	/* External touchpads with the same vid/pid as the keyboard are
2362a46c0ec8Sopenharmony_ci	   considered a happy couple */
2363a46c0ec8Sopenharmony_ci	if (touchpad->tags & EVDEV_TAG_EXTERNAL_TOUCHPAD)
2364a46c0ec8Sopenharmony_ci		return vendor_tp == vendor_kbd && product_tp == product_kbd;
2365a46c0ec8Sopenharmony_ci
2366a46c0ec8Sopenharmony_ci	if (keyboard->tags & EVDEV_TAG_INTERNAL_KEYBOARD)
2367a46c0ec8Sopenharmony_ci		return true;
2368a46c0ec8Sopenharmony_ci
2369a46c0ec8Sopenharmony_ci	/* keyboard is not tagged as internal keyboard and it's not part of
2370a46c0ec8Sopenharmony_ci	 * a combo */
2371a46c0ec8Sopenharmony_ci	return false;
2372a46c0ec8Sopenharmony_ci}
2373a46c0ec8Sopenharmony_ci
2374a46c0ec8Sopenharmony_cistatic void
2375a46c0ec8Sopenharmony_citp_dwt_pair_keyboard(struct evdev_device *touchpad,
2376a46c0ec8Sopenharmony_ci		     struct evdev_device *keyboard)
2377a46c0ec8Sopenharmony_ci{
2378a46c0ec8Sopenharmony_ci	struct tp_dispatch *tp = (struct tp_dispatch*)touchpad->dispatch;
2379a46c0ec8Sopenharmony_ci	struct evdev_paired_keyboard *kbd;
2380a46c0ec8Sopenharmony_ci	size_t count = 0;
2381a46c0ec8Sopenharmony_ci
2382a46c0ec8Sopenharmony_ci	if ((keyboard->tags & EVDEV_TAG_KEYBOARD) == 0)
2383a46c0ec8Sopenharmony_ci		return;
2384a46c0ec8Sopenharmony_ci
2385a46c0ec8Sopenharmony_ci	if (!tp_want_dwt(touchpad, keyboard))
2386a46c0ec8Sopenharmony_ci		return;
2387a46c0ec8Sopenharmony_ci
2388a46c0ec8Sopenharmony_ci	list_for_each(kbd, &tp->dwt.paired_keyboard_list, link) {
2389a46c0ec8Sopenharmony_ci		count++;
2390a46c0ec8Sopenharmony_ci		if (count > 3) {
2391a46c0ec8Sopenharmony_ci			evdev_log_info(touchpad,
2392a46c0ec8Sopenharmony_ci				       "too many internal keyboards for dwt\n");
2393a46c0ec8Sopenharmony_ci			break;
2394a46c0ec8Sopenharmony_ci		}
2395a46c0ec8Sopenharmony_ci	}
2396a46c0ec8Sopenharmony_ci
2397a46c0ec8Sopenharmony_ci	kbd = zalloc(sizeof(*kbd));
2398a46c0ec8Sopenharmony_ci	kbd->device = keyboard;
2399a46c0ec8Sopenharmony_ci	libinput_device_add_event_listener(&keyboard->base,
2400a46c0ec8Sopenharmony_ci					   &kbd->listener,
2401a46c0ec8Sopenharmony_ci					   tp_keyboard_event, tp);
2402a46c0ec8Sopenharmony_ci	list_insert(&tp->dwt.paired_keyboard_list, &kbd->link);
2403a46c0ec8Sopenharmony_ci	evdev_log_debug(touchpad,
2404a46c0ec8Sopenharmony_ci			"palm: dwt activated with %s<->%s\n",
2405a46c0ec8Sopenharmony_ci			touchpad->devname,
2406a46c0ec8Sopenharmony_ci			keyboard->devname);
2407a46c0ec8Sopenharmony_ci}
2408a46c0ec8Sopenharmony_ci
2409a46c0ec8Sopenharmony_cistatic void
2410a46c0ec8Sopenharmony_citp_pair_trackpoint(struct evdev_device *touchpad,
2411a46c0ec8Sopenharmony_ci			struct evdev_device *trackpoint)
2412a46c0ec8Sopenharmony_ci{
2413a46c0ec8Sopenharmony_ci	struct tp_dispatch *tp = (struct tp_dispatch*)touchpad->dispatch;
2414a46c0ec8Sopenharmony_ci	unsigned int bus_trp = libevdev_get_id_bustype(trackpoint->evdev);
2415a46c0ec8Sopenharmony_ci	bool tp_is_internal, trp_is_internal;
2416a46c0ec8Sopenharmony_ci
2417a46c0ec8Sopenharmony_ci	if ((trackpoint->tags & EVDEV_TAG_TRACKPOINT) == 0)
2418a46c0ec8Sopenharmony_ci		return;
2419a46c0ec8Sopenharmony_ci
2420a46c0ec8Sopenharmony_ci	tp_is_internal = !!(touchpad->tags & EVDEV_TAG_INTERNAL_TOUCHPAD);
2421a46c0ec8Sopenharmony_ci	trp_is_internal = bus_trp != BUS_USB && bus_trp != BUS_BLUETOOTH;
2422a46c0ec8Sopenharmony_ci
2423a46c0ec8Sopenharmony_ci	if (tp->buttons.trackpoint == NULL &&
2424a46c0ec8Sopenharmony_ci	    tp_is_internal && trp_is_internal) {
2425a46c0ec8Sopenharmony_ci		/* Don't send any pending releases to the new trackpoint */
2426a46c0ec8Sopenharmony_ci		tp->buttons.active_is_topbutton = false;
2427a46c0ec8Sopenharmony_ci		tp->buttons.trackpoint = trackpoint;
2428a46c0ec8Sopenharmony_ci		if (tp->palm.monitor_trackpoint)
2429a46c0ec8Sopenharmony_ci			libinput_device_add_event_listener(&trackpoint->base,
2430a46c0ec8Sopenharmony_ci						&tp->palm.trackpoint_listener,
2431a46c0ec8Sopenharmony_ci						tp_trackpoint_event, tp);
2432a46c0ec8Sopenharmony_ci	}
2433a46c0ec8Sopenharmony_ci}
2434a46c0ec8Sopenharmony_ci
2435a46c0ec8Sopenharmony_cistatic void
2436a46c0ec8Sopenharmony_citp_lid_switch_event(uint64_t time, struct libinput_event *event, void *data)
2437a46c0ec8Sopenharmony_ci{
2438a46c0ec8Sopenharmony_ci	struct tp_dispatch *tp = data;
2439a46c0ec8Sopenharmony_ci	struct libinput_event_switch *swev;
2440a46c0ec8Sopenharmony_ci
2441a46c0ec8Sopenharmony_ci	if (libinput_event_get_type(event) != LIBINPUT_EVENT_SWITCH_TOGGLE)
2442a46c0ec8Sopenharmony_ci		return;
2443a46c0ec8Sopenharmony_ci
2444a46c0ec8Sopenharmony_ci	swev = libinput_event_get_switch_event(event);
2445a46c0ec8Sopenharmony_ci	if (libinput_event_switch_get_switch(swev) != LIBINPUT_SWITCH_LID)
2446a46c0ec8Sopenharmony_ci		return;
2447a46c0ec8Sopenharmony_ci
2448a46c0ec8Sopenharmony_ci	switch (libinput_event_switch_get_switch_state(swev)) {
2449a46c0ec8Sopenharmony_ci	case LIBINPUT_SWITCH_STATE_OFF:
2450a46c0ec8Sopenharmony_ci		tp_resume(tp, tp->device, SUSPEND_LID);
2451a46c0ec8Sopenharmony_ci		evdev_log_debug(tp->device, "lid: resume touchpad\n");
2452a46c0ec8Sopenharmony_ci		break;
2453a46c0ec8Sopenharmony_ci	case LIBINPUT_SWITCH_STATE_ON:
2454a46c0ec8Sopenharmony_ci		tp_suspend(tp, tp->device, SUSPEND_LID);
2455a46c0ec8Sopenharmony_ci		evdev_log_debug(tp->device, "lid: suspending touchpad\n");
2456a46c0ec8Sopenharmony_ci		break;
2457a46c0ec8Sopenharmony_ci	}
2458a46c0ec8Sopenharmony_ci}
2459a46c0ec8Sopenharmony_ci
2460a46c0ec8Sopenharmony_cistatic void
2461a46c0ec8Sopenharmony_citp_tablet_mode_switch_event(uint64_t time,
2462a46c0ec8Sopenharmony_ci			    struct libinput_event *event,
2463a46c0ec8Sopenharmony_ci			    void *data)
2464a46c0ec8Sopenharmony_ci{
2465a46c0ec8Sopenharmony_ci	struct tp_dispatch *tp = data;
2466a46c0ec8Sopenharmony_ci	struct libinput_event_switch *swev;
2467a46c0ec8Sopenharmony_ci
2468a46c0ec8Sopenharmony_ci	if (libinput_event_get_type(event) != LIBINPUT_EVENT_SWITCH_TOGGLE)
2469a46c0ec8Sopenharmony_ci		return;
2470a46c0ec8Sopenharmony_ci
2471a46c0ec8Sopenharmony_ci	swev = libinput_event_get_switch_event(event);
2472a46c0ec8Sopenharmony_ci	if (libinput_event_switch_get_switch(swev) !=
2473a46c0ec8Sopenharmony_ci	    LIBINPUT_SWITCH_TABLET_MODE)
2474a46c0ec8Sopenharmony_ci		return;
2475a46c0ec8Sopenharmony_ci
2476a46c0ec8Sopenharmony_ci	switch (libinput_event_switch_get_switch_state(swev)) {
2477a46c0ec8Sopenharmony_ci	case LIBINPUT_SWITCH_STATE_OFF:
2478a46c0ec8Sopenharmony_ci		tp_resume(tp, tp->device, SUSPEND_TABLET_MODE);
2479a46c0ec8Sopenharmony_ci		evdev_log_debug(tp->device, "tablet-mode: resume touchpad\n");
2480a46c0ec8Sopenharmony_ci		break;
2481a46c0ec8Sopenharmony_ci	case LIBINPUT_SWITCH_STATE_ON:
2482a46c0ec8Sopenharmony_ci		tp_suspend(tp, tp->device, SUSPEND_TABLET_MODE);
2483a46c0ec8Sopenharmony_ci		evdev_log_debug(tp->device, "tablet-mode: suspending touchpad\n");
2484a46c0ec8Sopenharmony_ci		break;
2485a46c0ec8Sopenharmony_ci	}
2486a46c0ec8Sopenharmony_ci}
2487a46c0ec8Sopenharmony_ci
2488a46c0ec8Sopenharmony_cistatic void
2489a46c0ec8Sopenharmony_citp_pair_lid_switch(struct evdev_device *touchpad,
2490a46c0ec8Sopenharmony_ci		   struct evdev_device *lid_switch)
2491a46c0ec8Sopenharmony_ci{
2492a46c0ec8Sopenharmony_ci	struct tp_dispatch *tp = (struct tp_dispatch*)touchpad->dispatch;
2493a46c0ec8Sopenharmony_ci
2494a46c0ec8Sopenharmony_ci	if ((lid_switch->tags & EVDEV_TAG_LID_SWITCH) == 0)
2495a46c0ec8Sopenharmony_ci		return;
2496a46c0ec8Sopenharmony_ci
2497a46c0ec8Sopenharmony_ci	if (touchpad->tags & EVDEV_TAG_EXTERNAL_TOUCHPAD)
2498a46c0ec8Sopenharmony_ci		return;
2499a46c0ec8Sopenharmony_ci
2500a46c0ec8Sopenharmony_ci	if (tp->lid_switch.lid_switch == NULL) {
2501a46c0ec8Sopenharmony_ci		evdev_log_debug(touchpad,
2502a46c0ec8Sopenharmony_ci				"lid: activated for %s<->%s\n",
2503a46c0ec8Sopenharmony_ci				touchpad->devname,
2504a46c0ec8Sopenharmony_ci				lid_switch->devname);
2505a46c0ec8Sopenharmony_ci
2506a46c0ec8Sopenharmony_ci		libinput_device_add_event_listener(&lid_switch->base,
2507a46c0ec8Sopenharmony_ci						   &tp->lid_switch.listener,
2508a46c0ec8Sopenharmony_ci						   tp_lid_switch_event, tp);
2509a46c0ec8Sopenharmony_ci		tp->lid_switch.lid_switch = lid_switch;
2510a46c0ec8Sopenharmony_ci	}
2511a46c0ec8Sopenharmony_ci}
2512a46c0ec8Sopenharmony_ci
2513a46c0ec8Sopenharmony_cistatic void
2514a46c0ec8Sopenharmony_citp_pair_tablet_mode_switch(struct evdev_device *touchpad,
2515a46c0ec8Sopenharmony_ci			   struct evdev_device *tablet_mode_switch)
2516a46c0ec8Sopenharmony_ci{
2517a46c0ec8Sopenharmony_ci	struct tp_dispatch *tp = (struct tp_dispatch*)touchpad->dispatch;
2518a46c0ec8Sopenharmony_ci
2519a46c0ec8Sopenharmony_ci	if ((tablet_mode_switch->tags & EVDEV_TAG_TABLET_MODE_SWITCH) == 0)
2520a46c0ec8Sopenharmony_ci		return;
2521a46c0ec8Sopenharmony_ci
2522a46c0ec8Sopenharmony_ci	if (tp->tablet_mode_switch.tablet_mode_switch)
2523a46c0ec8Sopenharmony_ci		return;
2524a46c0ec8Sopenharmony_ci
2525a46c0ec8Sopenharmony_ci	if (touchpad->tags & EVDEV_TAG_EXTERNAL_TOUCHPAD)
2526a46c0ec8Sopenharmony_ci		return;
2527a46c0ec8Sopenharmony_ci
2528a46c0ec8Sopenharmony_ci	if (evdev_device_has_model_quirk(touchpad,
2529a46c0ec8Sopenharmony_ci					 QUIRK_MODEL_TABLET_MODE_NO_SUSPEND))
2530a46c0ec8Sopenharmony_ci		return;
2531a46c0ec8Sopenharmony_ci
2532a46c0ec8Sopenharmony_ci	evdev_log_debug(touchpad,
2533a46c0ec8Sopenharmony_ci			"tablet-mode: activated for %s<->%s\n",
2534a46c0ec8Sopenharmony_ci			touchpad->devname,
2535a46c0ec8Sopenharmony_ci			tablet_mode_switch->devname);
2536a46c0ec8Sopenharmony_ci
2537a46c0ec8Sopenharmony_ci	libinput_device_add_event_listener(&tablet_mode_switch->base,
2538a46c0ec8Sopenharmony_ci				&tp->tablet_mode_switch.listener,
2539a46c0ec8Sopenharmony_ci				tp_tablet_mode_switch_event, tp);
2540a46c0ec8Sopenharmony_ci	tp->tablet_mode_switch.tablet_mode_switch = tablet_mode_switch;
2541a46c0ec8Sopenharmony_ci
2542a46c0ec8Sopenharmony_ci	if (evdev_device_switch_get_state(tablet_mode_switch,
2543a46c0ec8Sopenharmony_ci					  LIBINPUT_SWITCH_TABLET_MODE)
2544a46c0ec8Sopenharmony_ci		    == LIBINPUT_SWITCH_STATE_ON) {
2545a46c0ec8Sopenharmony_ci		tp_suspend(tp, touchpad, SUSPEND_TABLET_MODE);
2546a46c0ec8Sopenharmony_ci	}
2547a46c0ec8Sopenharmony_ci}
2548a46c0ec8Sopenharmony_ci
2549a46c0ec8Sopenharmony_cistatic void
2550a46c0ec8Sopenharmony_citp_change_rotation(struct evdev_device *device, enum notify notify)
2551a46c0ec8Sopenharmony_ci{
2552a46c0ec8Sopenharmony_ci	struct tp_dispatch *tp = (struct tp_dispatch *)device->dispatch;
2553a46c0ec8Sopenharmony_ci	struct evdev_device *tablet_device = tp->left_handed.tablet_device;
2554a46c0ec8Sopenharmony_ci	bool tablet_is_left, touchpad_is_left;
2555a46c0ec8Sopenharmony_ci
2556a46c0ec8Sopenharmony_ci	if (!tp->left_handed.must_rotate)
2557a46c0ec8Sopenharmony_ci		return;
2558a46c0ec8Sopenharmony_ci
2559a46c0ec8Sopenharmony_ci	touchpad_is_left = device->left_handed.enabled;
2560a46c0ec8Sopenharmony_ci	tablet_is_left = tp->left_handed.tablet_left_handed_state;
2561a46c0ec8Sopenharmony_ci
2562a46c0ec8Sopenharmony_ci	tp->left_handed.want_rotate = touchpad_is_left || tablet_is_left;
2563a46c0ec8Sopenharmony_ci
2564a46c0ec8Sopenharmony_ci	tp_apply_rotation(device);
2565a46c0ec8Sopenharmony_ci
2566a46c0ec8Sopenharmony_ci	if (notify == DO_NOTIFY && tablet_device) {
2567a46c0ec8Sopenharmony_ci		struct evdev_dispatch *dispatch = tablet_device->dispatch;
2568a46c0ec8Sopenharmony_ci
2569a46c0ec8Sopenharmony_ci		if (dispatch->interface->left_handed_toggle)
2570a46c0ec8Sopenharmony_ci			dispatch->interface->left_handed_toggle(dispatch,
2571a46c0ec8Sopenharmony_ci								tablet_device,
2572a46c0ec8Sopenharmony_ci								tp->left_handed.want_rotate);
2573a46c0ec8Sopenharmony_ci	}
2574a46c0ec8Sopenharmony_ci}
2575a46c0ec8Sopenharmony_ci
2576a46c0ec8Sopenharmony_cistatic void
2577a46c0ec8Sopenharmony_citp_pair_tablet(struct evdev_device *touchpad,
2578a46c0ec8Sopenharmony_ci	       struct evdev_device *tablet)
2579a46c0ec8Sopenharmony_ci{
2580a46c0ec8Sopenharmony_ci	struct tp_dispatch *tp = (struct tp_dispatch*)touchpad->dispatch;
2581a46c0ec8Sopenharmony_ci
2582a46c0ec8Sopenharmony_ci	if (!tp->left_handed.must_rotate)
2583a46c0ec8Sopenharmony_ci		return;
2584a46c0ec8Sopenharmony_ci
2585a46c0ec8Sopenharmony_ci	if ((tablet->seat_caps & EVDEV_DEVICE_TABLET) == 0)
2586a46c0ec8Sopenharmony_ci		return;
2587a46c0ec8Sopenharmony_ci
2588a46c0ec8Sopenharmony_ci	if (libinput_device_get_device_group(&touchpad->base) !=
2589a46c0ec8Sopenharmony_ci	    libinput_device_get_device_group(&tablet->base))
2590a46c0ec8Sopenharmony_ci		return;
2591a46c0ec8Sopenharmony_ci
2592a46c0ec8Sopenharmony_ci	tp->left_handed.tablet_device = tablet;
2593a46c0ec8Sopenharmony_ci
2594a46c0ec8Sopenharmony_ci	evdev_log_debug(touchpad,
2595a46c0ec8Sopenharmony_ci			"touchpad-rotation: %s will rotate %s\n",
2596a46c0ec8Sopenharmony_ci			touchpad->devname,
2597a46c0ec8Sopenharmony_ci			tablet->devname);
2598a46c0ec8Sopenharmony_ci
2599a46c0ec8Sopenharmony_ci	if (libinput_device_config_left_handed_get(&tablet->base)) {
2600a46c0ec8Sopenharmony_ci		tp->left_handed.want_rotate = true;
2601a46c0ec8Sopenharmony_ci		tp->left_handed.tablet_left_handed_state = true;
2602a46c0ec8Sopenharmony_ci		tp_change_rotation(touchpad, DONT_NOTIFY);
2603a46c0ec8Sopenharmony_ci	}
2604a46c0ec8Sopenharmony_ci}
2605a46c0ec8Sopenharmony_ci
2606a46c0ec8Sopenharmony_cistatic void
2607a46c0ec8Sopenharmony_citp_interface_device_added(struct evdev_device *device,
2608a46c0ec8Sopenharmony_ci			  struct evdev_device *added_device)
2609a46c0ec8Sopenharmony_ci{
2610a46c0ec8Sopenharmony_ci	struct tp_dispatch *tp = (struct tp_dispatch*)device->dispatch;
2611a46c0ec8Sopenharmony_ci
2612a46c0ec8Sopenharmony_ci	tp_pair_trackpoint(device, added_device);
2613a46c0ec8Sopenharmony_ci	tp_dwt_pair_keyboard(device, added_device);
2614a46c0ec8Sopenharmony_ci	tp_pair_lid_switch(device, added_device);
2615a46c0ec8Sopenharmony_ci	tp_pair_tablet_mode_switch(device, added_device);
2616a46c0ec8Sopenharmony_ci	tp_pair_tablet(device, added_device);
2617a46c0ec8Sopenharmony_ci
2618a46c0ec8Sopenharmony_ci	if (tp->sendevents.current_mode !=
2619a46c0ec8Sopenharmony_ci	    LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE)
2620a46c0ec8Sopenharmony_ci		return;
2621a46c0ec8Sopenharmony_ci
2622a46c0ec8Sopenharmony_ci	if (added_device->tags & EVDEV_TAG_EXTERNAL_MOUSE)
2623a46c0ec8Sopenharmony_ci		tp_suspend(tp, device, SUSPEND_EXTERNAL_MOUSE);
2624a46c0ec8Sopenharmony_ci}
2625a46c0ec8Sopenharmony_ci
2626a46c0ec8Sopenharmony_cistatic void
2627a46c0ec8Sopenharmony_citp_interface_device_removed(struct evdev_device *device,
2628a46c0ec8Sopenharmony_ci			    struct evdev_device *removed_device)
2629a46c0ec8Sopenharmony_ci{
2630a46c0ec8Sopenharmony_ci	struct tp_dispatch *tp = (struct tp_dispatch*)device->dispatch;
2631a46c0ec8Sopenharmony_ci	struct evdev_paired_keyboard *kbd;
2632a46c0ec8Sopenharmony_ci
2633a46c0ec8Sopenharmony_ci	if (removed_device == tp->buttons.trackpoint) {
2634a46c0ec8Sopenharmony_ci		/* Clear any pending releases for the trackpoint */
2635a46c0ec8Sopenharmony_ci		if (tp->buttons.active && tp->buttons.active_is_topbutton) {
2636a46c0ec8Sopenharmony_ci			tp->buttons.active = 0;
2637a46c0ec8Sopenharmony_ci			tp->buttons.active_is_topbutton = false;
2638a46c0ec8Sopenharmony_ci		}
2639a46c0ec8Sopenharmony_ci		if (tp->palm.monitor_trackpoint)
2640a46c0ec8Sopenharmony_ci			libinput_device_remove_event_listener(
2641a46c0ec8Sopenharmony_ci						&tp->palm.trackpoint_listener);
2642a46c0ec8Sopenharmony_ci		tp->buttons.trackpoint = NULL;
2643a46c0ec8Sopenharmony_ci	}
2644a46c0ec8Sopenharmony_ci
2645a46c0ec8Sopenharmony_ci	list_for_each_safe(kbd, &tp->dwt.paired_keyboard_list, link) {
2646a46c0ec8Sopenharmony_ci		if (kbd->device == removed_device) {
2647a46c0ec8Sopenharmony_ci			evdev_paired_keyboard_destroy(kbd);
2648a46c0ec8Sopenharmony_ci			tp->dwt.keyboard_active = false;
2649a46c0ec8Sopenharmony_ci		}
2650a46c0ec8Sopenharmony_ci	}
2651a46c0ec8Sopenharmony_ci
2652a46c0ec8Sopenharmony_ci	if (removed_device == tp->lid_switch.lid_switch) {
2653a46c0ec8Sopenharmony_ci		libinput_device_remove_event_listener(
2654a46c0ec8Sopenharmony_ci					&tp->lid_switch.listener);
2655a46c0ec8Sopenharmony_ci		tp->lid_switch.lid_switch = NULL;
2656a46c0ec8Sopenharmony_ci		tp_resume(tp, device, SUSPEND_LID);
2657a46c0ec8Sopenharmony_ci	}
2658a46c0ec8Sopenharmony_ci
2659a46c0ec8Sopenharmony_ci	if (removed_device == tp->tablet_mode_switch.tablet_mode_switch) {
2660a46c0ec8Sopenharmony_ci		libinput_device_remove_event_listener(
2661a46c0ec8Sopenharmony_ci					&tp->tablet_mode_switch.listener);
2662a46c0ec8Sopenharmony_ci		tp->tablet_mode_switch.tablet_mode_switch = NULL;
2663a46c0ec8Sopenharmony_ci		tp_resume(tp, device, SUSPEND_TABLET_MODE);
2664a46c0ec8Sopenharmony_ci	}
2665a46c0ec8Sopenharmony_ci
2666a46c0ec8Sopenharmony_ci	if (tp->sendevents.current_mode ==
2667a46c0ec8Sopenharmony_ci		    LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE) {
2668a46c0ec8Sopenharmony_ci		struct libinput_device *dev;
2669a46c0ec8Sopenharmony_ci		bool found = false;
2670a46c0ec8Sopenharmony_ci
2671a46c0ec8Sopenharmony_ci		list_for_each(dev, &device->base.seat->devices_list, link) {
2672a46c0ec8Sopenharmony_ci			struct evdev_device *d = evdev_device(dev);
2673a46c0ec8Sopenharmony_ci			if (d != removed_device &&
2674a46c0ec8Sopenharmony_ci			    (d->tags & EVDEV_TAG_EXTERNAL_MOUSE)) {
2675a46c0ec8Sopenharmony_ci				found = true;
2676a46c0ec8Sopenharmony_ci				break;
2677a46c0ec8Sopenharmony_ci			}
2678a46c0ec8Sopenharmony_ci		}
2679a46c0ec8Sopenharmony_ci		if (!found)
2680a46c0ec8Sopenharmony_ci			tp_resume(tp, device, SUSPEND_EXTERNAL_MOUSE);
2681a46c0ec8Sopenharmony_ci	}
2682a46c0ec8Sopenharmony_ci
2683a46c0ec8Sopenharmony_ci	if (removed_device == tp->left_handed.tablet_device) {
2684a46c0ec8Sopenharmony_ci		tp->left_handed.tablet_device = NULL;
2685a46c0ec8Sopenharmony_ci		tp->left_handed.tablet_left_handed_state = false;
2686a46c0ec8Sopenharmony_ci
2687a46c0ec8Sopenharmony_ci		/* Slight awkwardness: removing the tablet causes the
2688a46c0ec8Sopenharmony_ci		 * touchpad to rotate back to normal if only the tablet was
2689a46c0ec8Sopenharmony_ci		 * set to left-handed. Niche case, nothing to worry about
2690a46c0ec8Sopenharmony_ci		 */
2691a46c0ec8Sopenharmony_ci		tp_change_rotation(device, DO_NOTIFY);
2692a46c0ec8Sopenharmony_ci	}
2693a46c0ec8Sopenharmony_ci}
2694a46c0ec8Sopenharmony_ci
2695a46c0ec8Sopenharmony_cistatic inline void
2696a46c0ec8Sopenharmony_cievdev_tag_touchpad_internal(struct evdev_device *device)
2697a46c0ec8Sopenharmony_ci{
2698a46c0ec8Sopenharmony_ci	device->tags |= EVDEV_TAG_INTERNAL_TOUCHPAD;
2699a46c0ec8Sopenharmony_ci	device->tags &= ~EVDEV_TAG_EXTERNAL_TOUCHPAD;
2700a46c0ec8Sopenharmony_ci}
2701a46c0ec8Sopenharmony_ci
2702a46c0ec8Sopenharmony_cistatic inline void
2703a46c0ec8Sopenharmony_cievdev_tag_touchpad_external(struct evdev_device *device)
2704a46c0ec8Sopenharmony_ci{
2705a46c0ec8Sopenharmony_ci	device->tags |= EVDEV_TAG_EXTERNAL_TOUCHPAD;
2706a46c0ec8Sopenharmony_ci	device->tags &= ~EVDEV_TAG_INTERNAL_TOUCHPAD;
2707a46c0ec8Sopenharmony_ci}
2708a46c0ec8Sopenharmony_ci
2709a46c0ec8Sopenharmony_cistatic void
2710a46c0ec8Sopenharmony_cievdev_tag_touchpad(struct evdev_device *device,
2711a46c0ec8Sopenharmony_ci		   struct udev_device *udev_device)
2712a46c0ec8Sopenharmony_ci{
2713a46c0ec8Sopenharmony_ci	int bustype, vendor;
2714a46c0ec8Sopenharmony_ci	const char *prop;
2715a46c0ec8Sopenharmony_ci
2716a46c0ec8Sopenharmony_ci	prop = udev_device_get_property_value(udev_device,
2717a46c0ec8Sopenharmony_ci					      "ID_INPUT_TOUCHPAD_INTEGRATION");
2718a46c0ec8Sopenharmony_ci	if (prop) {
2719a46c0ec8Sopenharmony_ci		if (streq(prop, "internal")) {
2720a46c0ec8Sopenharmony_ci			evdev_tag_touchpad_internal(device);
2721a46c0ec8Sopenharmony_ci			return;
2722a46c0ec8Sopenharmony_ci		}
2723a46c0ec8Sopenharmony_ci
2724a46c0ec8Sopenharmony_ci		if (streq(prop, "external")) {
2725a46c0ec8Sopenharmony_ci			evdev_tag_touchpad_external(device);
2726a46c0ec8Sopenharmony_ci			return;
2727a46c0ec8Sopenharmony_ci		}
2728a46c0ec8Sopenharmony_ci
2729a46c0ec8Sopenharmony_ci		evdev_log_info(device,
2730a46c0ec8Sopenharmony_ci			       "tagged with unknown value %s\n",
2731a46c0ec8Sopenharmony_ci			       prop);
2732a46c0ec8Sopenharmony_ci	}
2733a46c0ec8Sopenharmony_ci
2734a46c0ec8Sopenharmony_ci	/* The hwdb is the authority on integration, these heuristics are
2735a46c0ec8Sopenharmony_ci	 * the fallback only (they precede the hwdb too).
2736a46c0ec8Sopenharmony_ci	 *
2737a46c0ec8Sopenharmony_ci	 * Simple approach:
2738a46c0ec8Sopenharmony_ci	 * Bluetooth touchpads are considered external, anything else is
2739a46c0ec8Sopenharmony_ci	 * internal. Except the ones from some vendors that only make external
2740a46c0ec8Sopenharmony_ci	 * touchpads.
2741a46c0ec8Sopenharmony_ci	 */
2742a46c0ec8Sopenharmony_ci	bustype = libevdev_get_id_bustype(device->evdev);
2743a46c0ec8Sopenharmony_ci	vendor = libevdev_get_id_vendor(device->evdev);
2744a46c0ec8Sopenharmony_ci
2745a46c0ec8Sopenharmony_ci	switch (bustype) {
2746a46c0ec8Sopenharmony_ci	case BUS_BLUETOOTH:
2747a46c0ec8Sopenharmony_ci		evdev_tag_touchpad_external(device);
2748a46c0ec8Sopenharmony_ci		break;
2749a46c0ec8Sopenharmony_ci	default:
2750a46c0ec8Sopenharmony_ci		evdev_tag_touchpad_internal(device);
2751a46c0ec8Sopenharmony_ci		break;
2752a46c0ec8Sopenharmony_ci	}
2753a46c0ec8Sopenharmony_ci
2754a46c0ec8Sopenharmony_ci	switch (vendor) {
2755a46c0ec8Sopenharmony_ci	/* Logitech does not have internal touchpads */
2756a46c0ec8Sopenharmony_ci	case VENDOR_ID_LOGITECH:
2757a46c0ec8Sopenharmony_ci		evdev_tag_touchpad_external(device);
2758a46c0ec8Sopenharmony_ci		break;
2759a46c0ec8Sopenharmony_ci	}
2760a46c0ec8Sopenharmony_ci
2761a46c0ec8Sopenharmony_ci	/* Wacom makes touchpads, but not internal ones */
2762a46c0ec8Sopenharmony_ci	if (device->model_flags & EVDEV_MODEL_WACOM_TOUCHPAD)
2763a46c0ec8Sopenharmony_ci		evdev_tag_touchpad_external(device);
2764a46c0ec8Sopenharmony_ci
2765a46c0ec8Sopenharmony_ci	if ((device->tags &
2766a46c0ec8Sopenharmony_ci	    (EVDEV_TAG_EXTERNAL_TOUCHPAD|EVDEV_TAG_INTERNAL_TOUCHPAD)) == 0) {
2767a46c0ec8Sopenharmony_ci		evdev_log_bug_libinput(device,
2768a46c0ec8Sopenharmony_ci				       "Internal or external? Please file a bug.\n");
2769a46c0ec8Sopenharmony_ci		evdev_tag_touchpad_external(device);
2770a46c0ec8Sopenharmony_ci	}
2771a46c0ec8Sopenharmony_ci}
2772a46c0ec8Sopenharmony_ci
2773a46c0ec8Sopenharmony_cistatic void
2774a46c0ec8Sopenharmony_citp_arbitration_timeout(uint64_t now, void *data)
2775a46c0ec8Sopenharmony_ci{
2776a46c0ec8Sopenharmony_ci	struct tp_dispatch *tp = data;
2777a46c0ec8Sopenharmony_ci
2778a46c0ec8Sopenharmony_ci	if (tp->arbitration.state != ARBITRATION_NOT_ACTIVE)
2779a46c0ec8Sopenharmony_ci		tp->arbitration.state = ARBITRATION_NOT_ACTIVE;
2780a46c0ec8Sopenharmony_ci}
2781a46c0ec8Sopenharmony_ci
2782a46c0ec8Sopenharmony_cistatic void
2783a46c0ec8Sopenharmony_citp_interface_toggle_touch(struct evdev_dispatch *dispatch,
2784a46c0ec8Sopenharmony_ci			  struct evdev_device *device,
2785a46c0ec8Sopenharmony_ci			  enum evdev_arbitration_state which,
2786a46c0ec8Sopenharmony_ci			  const struct phys_rect *rect,
2787a46c0ec8Sopenharmony_ci			  uint64_t time)
2788a46c0ec8Sopenharmony_ci{
2789a46c0ec8Sopenharmony_ci	struct tp_dispatch *tp = tp_dispatch(dispatch);
2790a46c0ec8Sopenharmony_ci
2791a46c0ec8Sopenharmony_ci	if (which == tp->arbitration.state)
2792a46c0ec8Sopenharmony_ci		return;
2793a46c0ec8Sopenharmony_ci
2794a46c0ec8Sopenharmony_ci	switch (which) {
2795a46c0ec8Sopenharmony_ci	case ARBITRATION_IGNORE_ALL:
2796a46c0ec8Sopenharmony_ci	case ARBITRATION_IGNORE_RECT:
2797a46c0ec8Sopenharmony_ci		libinput_timer_cancel(&tp->arbitration.arbitration_timer);
2798a46c0ec8Sopenharmony_ci		tp_clear_state(tp);
2799a46c0ec8Sopenharmony_ci		tp->arbitration.state = which;
2800a46c0ec8Sopenharmony_ci		break;
2801a46c0ec8Sopenharmony_ci	case ARBITRATION_NOT_ACTIVE:
2802a46c0ec8Sopenharmony_ci		/* if in-kernel arbitration is in use and there is a touch
2803a46c0ec8Sopenharmony_ci		 * and a pen in proximity, lifting the pen out of proximity
2804a46c0ec8Sopenharmony_ci		 * causes a touch begin for the touch. On a hand-lift the
2805a46c0ec8Sopenharmony_ci		 * proximity out precedes the touch up by a few ms, so we
2806a46c0ec8Sopenharmony_ci		 * get what looks like a tap. Fix this by delaying
2807a46c0ec8Sopenharmony_ci		 * arbitration by just a little bit so that any touch in
2808a46c0ec8Sopenharmony_ci		 * event is caught as palm touch. */
2809a46c0ec8Sopenharmony_ci		libinput_timer_set(&tp->arbitration.arbitration_timer,
2810a46c0ec8Sopenharmony_ci				   time + ms2us(90));
2811a46c0ec8Sopenharmony_ci		break;
2812a46c0ec8Sopenharmony_ci	}
2813a46c0ec8Sopenharmony_ci}
2814a46c0ec8Sopenharmony_ci
2815a46c0ec8Sopenharmony_ci/* Called when the tablet toggles to left-handed */
2816a46c0ec8Sopenharmony_cistatic void
2817a46c0ec8Sopenharmony_citouchpad_left_handed_toggled(struct evdev_dispatch *dispatch,
2818a46c0ec8Sopenharmony_ci			     struct evdev_device *device,
2819a46c0ec8Sopenharmony_ci			     bool left_handed_enabled)
2820a46c0ec8Sopenharmony_ci{
2821a46c0ec8Sopenharmony_ci	struct tp_dispatch *tp = tp_dispatch(dispatch);
2822a46c0ec8Sopenharmony_ci
2823a46c0ec8Sopenharmony_ci	if (!tp->left_handed.tablet_device)
2824a46c0ec8Sopenharmony_ci		return;
2825a46c0ec8Sopenharmony_ci
2826a46c0ec8Sopenharmony_ci	evdev_log_debug(device,
2827a46c0ec8Sopenharmony_ci			"touchpad-rotation: tablet is %s\n",
2828a46c0ec8Sopenharmony_ci			left_handed_enabled ? "left-handed" : "right-handed");
2829a46c0ec8Sopenharmony_ci
2830a46c0ec8Sopenharmony_ci	/* Our left-handed config is independent even though rotation is
2831a46c0ec8Sopenharmony_ci	 * locked. So we rotate when either device is left-handed. But it
2832a46c0ec8Sopenharmony_ci	 * can only be actually changed when the device is in a neutral
2833a46c0ec8Sopenharmony_ci	 * state, hence the want_rotate.
2834a46c0ec8Sopenharmony_ci	 */
2835a46c0ec8Sopenharmony_ci	tp->left_handed.tablet_left_handed_state = left_handed_enabled;
2836a46c0ec8Sopenharmony_ci	tp_change_rotation(device, DONT_NOTIFY);
2837a46c0ec8Sopenharmony_ci}
2838a46c0ec8Sopenharmony_ci
2839a46c0ec8Sopenharmony_cistatic struct evdev_dispatch_interface tp_interface = {
2840a46c0ec8Sopenharmony_ci	.process = tp_interface_process,
2841a46c0ec8Sopenharmony_ci	.suspend = tp_interface_suspend,
2842a46c0ec8Sopenharmony_ci	.remove = tp_interface_remove,
2843a46c0ec8Sopenharmony_ci	.destroy = tp_interface_destroy,
2844a46c0ec8Sopenharmony_ci	.device_added = tp_interface_device_added,
2845a46c0ec8Sopenharmony_ci	.device_removed = tp_interface_device_removed,
2846a46c0ec8Sopenharmony_ci	.device_suspended = tp_interface_device_removed, /* treat as remove */
2847a46c0ec8Sopenharmony_ci	.device_resumed = tp_interface_device_added,   /* treat as add */
2848a46c0ec8Sopenharmony_ci	.post_added = NULL,
2849a46c0ec8Sopenharmony_ci	.touch_arbitration_toggle = tp_interface_toggle_touch,
2850a46c0ec8Sopenharmony_ci	.touch_arbitration_update_rect = NULL,
2851a46c0ec8Sopenharmony_ci	.get_switch_state = NULL,
2852a46c0ec8Sopenharmony_ci	.left_handed_toggle = touchpad_left_handed_toggled,
2853a46c0ec8Sopenharmony_ci};
2854a46c0ec8Sopenharmony_ci
2855a46c0ec8Sopenharmony_cistatic void
2856a46c0ec8Sopenharmony_citp_init_touch(struct tp_dispatch *tp,
2857a46c0ec8Sopenharmony_ci	      struct tp_touch *t,
2858a46c0ec8Sopenharmony_ci	      unsigned int index)
2859a46c0ec8Sopenharmony_ci{
2860a46c0ec8Sopenharmony_ci	t->tp = tp;
2861a46c0ec8Sopenharmony_ci	t->has_ended = true;
2862a46c0ec8Sopenharmony_ci	t->index = index;
2863a46c0ec8Sopenharmony_ci}
2864a46c0ec8Sopenharmony_ci
2865a46c0ec8Sopenharmony_cistatic inline void
2866a46c0ec8Sopenharmony_citp_disable_abs_mt(struct evdev_device *device)
2867a46c0ec8Sopenharmony_ci{
2868a46c0ec8Sopenharmony_ci	struct libevdev *evdev = device->evdev;
2869a46c0ec8Sopenharmony_ci	unsigned int code;
2870a46c0ec8Sopenharmony_ci
2871a46c0ec8Sopenharmony_ci	for (code = ABS_MT_SLOT; code <= ABS_MAX; code++)
2872a46c0ec8Sopenharmony_ci		libevdev_disable_event_code(evdev, EV_ABS, code);
2873a46c0ec8Sopenharmony_ci}
2874a46c0ec8Sopenharmony_ci
2875a46c0ec8Sopenharmony_cistatic bool
2876a46c0ec8Sopenharmony_citp_init_slots(struct tp_dispatch *tp,
2877a46c0ec8Sopenharmony_ci	      struct evdev_device *device)
2878a46c0ec8Sopenharmony_ci{
2879a46c0ec8Sopenharmony_ci	const struct input_absinfo *absinfo;
2880a46c0ec8Sopenharmony_ci	struct map {
2881a46c0ec8Sopenharmony_ci		unsigned int code;
2882a46c0ec8Sopenharmony_ci		int ntouches;
2883a46c0ec8Sopenharmony_ci	} max_touches[] = {
2884a46c0ec8Sopenharmony_ci		{ BTN_TOOL_QUINTTAP, 5 },
2885a46c0ec8Sopenharmony_ci		{ BTN_TOOL_QUADTAP, 4 },
2886a46c0ec8Sopenharmony_ci		{ BTN_TOOL_TRIPLETAP, 3 },
2887a46c0ec8Sopenharmony_ci		{ BTN_TOOL_DOUBLETAP, 2 },
2888a46c0ec8Sopenharmony_ci	};
2889a46c0ec8Sopenharmony_ci	unsigned int i, n_btn_tool_touches = 1;
2890a46c0ec8Sopenharmony_ci
2891a46c0ec8Sopenharmony_ci	absinfo = libevdev_get_abs_info(device->evdev, ABS_MT_SLOT);
2892a46c0ec8Sopenharmony_ci	if (absinfo) {
2893a46c0ec8Sopenharmony_ci		tp->num_slots = absinfo->maximum + 1;
2894a46c0ec8Sopenharmony_ci		tp->slot = absinfo->value;
2895a46c0ec8Sopenharmony_ci		tp->has_mt = true;
2896a46c0ec8Sopenharmony_ci	} else {
2897a46c0ec8Sopenharmony_ci		tp->num_slots = 1;
2898a46c0ec8Sopenharmony_ci		tp->slot = 0;
2899a46c0ec8Sopenharmony_ci		tp->has_mt = false;
2900a46c0ec8Sopenharmony_ci	}
2901a46c0ec8Sopenharmony_ci
2902a46c0ec8Sopenharmony_ci	tp->semi_mt = libevdev_has_property(device->evdev, INPUT_PROP_SEMI_MT);
2903a46c0ec8Sopenharmony_ci
2904a46c0ec8Sopenharmony_ci	/* Semi-mt devices are not reliable for true multitouch data, so we
2905a46c0ec8Sopenharmony_ci	 * simply pretend they're single touch touchpads with BTN_TOOL bits.
2906a46c0ec8Sopenharmony_ci	 * Synaptics:
2907a46c0ec8Sopenharmony_ci	 * Terrible resolution when two fingers are down,
2908a46c0ec8Sopenharmony_ci	 * causing scroll jumps. The single-touch emulation ABS_X/Y is
2909a46c0ec8Sopenharmony_ci	 * accurate but the ABS_MT_POSITION touchpoints report the bounding
2910a46c0ec8Sopenharmony_ci	 * box and that causes jumps. See https://bugzilla.redhat.com/1235175
2911a46c0ec8Sopenharmony_ci	 * Elantech:
2912a46c0ec8Sopenharmony_ci	 * On three-finger taps/clicks, one slot doesn't get a coordinate
2913a46c0ec8Sopenharmony_ci	 * assigned. See https://bugs.freedesktop.org/show_bug.cgi?id=93583
2914a46c0ec8Sopenharmony_ci	 * Alps:
2915a46c0ec8Sopenharmony_ci	 * If three fingers are set down in the same frame, one slot has the
2916a46c0ec8Sopenharmony_ci	 * coordinates 0/0 and may not get updated for several frames.
2917a46c0ec8Sopenharmony_ci	 * See https://bugzilla.redhat.com/show_bug.cgi?id=1295073
2918a46c0ec8Sopenharmony_ci	 *
2919a46c0ec8Sopenharmony_ci	 * The HP Pavilion DM4 touchpad has random jumps in slots, including
2920a46c0ec8Sopenharmony_ci	 * for single-finger movement. See fdo bug 91135
2921a46c0ec8Sopenharmony_ci	 */
2922a46c0ec8Sopenharmony_ci	if (tp->semi_mt ||
2923a46c0ec8Sopenharmony_ci	    evdev_device_has_model_quirk(tp->device,
2924a46c0ec8Sopenharmony_ci					 QUIRK_MODEL_HP_PAVILION_DM4_TOUCHPAD)) {
2925a46c0ec8Sopenharmony_ci		tp->num_slots = 1;
2926a46c0ec8Sopenharmony_ci		tp->slot = 0;
2927a46c0ec8Sopenharmony_ci		tp->has_mt = false;
2928a46c0ec8Sopenharmony_ci	}
2929a46c0ec8Sopenharmony_ci
2930a46c0ec8Sopenharmony_ci	if (!tp->has_mt)
2931a46c0ec8Sopenharmony_ci		tp_disable_abs_mt(device);
2932a46c0ec8Sopenharmony_ci
2933a46c0ec8Sopenharmony_ci	ARRAY_FOR_EACH(max_touches, m) {
2934a46c0ec8Sopenharmony_ci		if (libevdev_has_event_code(device->evdev,
2935a46c0ec8Sopenharmony_ci					    EV_KEY,
2936a46c0ec8Sopenharmony_ci					    m->code)) {
2937a46c0ec8Sopenharmony_ci			n_btn_tool_touches = m->ntouches;
2938a46c0ec8Sopenharmony_ci			break;
2939a46c0ec8Sopenharmony_ci		}
2940a46c0ec8Sopenharmony_ci	}
2941a46c0ec8Sopenharmony_ci
2942a46c0ec8Sopenharmony_ci	tp->ntouches = max(tp->num_slots, n_btn_tool_touches);
2943a46c0ec8Sopenharmony_ci	tp->touches = zalloc(tp->ntouches * sizeof(struct tp_touch));
2944a46c0ec8Sopenharmony_ci
2945a46c0ec8Sopenharmony_ci	for (i = 0; i < tp->ntouches; i++)
2946a46c0ec8Sopenharmony_ci		tp_init_touch(tp, &tp->touches[i], i);
2947a46c0ec8Sopenharmony_ci
2948a46c0ec8Sopenharmony_ci	tp_sync_slots(tp, device);
2949a46c0ec8Sopenharmony_ci
2950a46c0ec8Sopenharmony_ci	/* Some touchpads don't reset BTN_TOOL_FINGER on touch up and only
2951a46c0ec8Sopenharmony_ci	 * change to/from it when BTN_TOOL_DOUBLETAP is set. This causes us
2952a46c0ec8Sopenharmony_ci	 * to ignore the first touches events until a two-finger gesture is
2953a46c0ec8Sopenharmony_ci	 * performed.
2954a46c0ec8Sopenharmony_ci	 */
2955a46c0ec8Sopenharmony_ci	if (libevdev_get_event_value(device->evdev, EV_KEY, BTN_TOOL_FINGER))
2956a46c0ec8Sopenharmony_ci		tp_fake_finger_set(tp, BTN_TOOL_FINGER, 1);
2957a46c0ec8Sopenharmony_ci
2958a46c0ec8Sopenharmony_ci	return true;
2959a46c0ec8Sopenharmony_ci}
2960a46c0ec8Sopenharmony_ci
2961a46c0ec8Sopenharmony_cistatic enum libinput_config_status
2962a46c0ec8Sopenharmony_citp_accel_config_set_profile(struct libinput_device *libinput_device,
2963a46c0ec8Sopenharmony_ci			    enum libinput_config_accel_profile profile);
2964a46c0ec8Sopenharmony_ci
2965a46c0ec8Sopenharmony_cistatic bool
2966a46c0ec8Sopenharmony_citp_init_accel(struct tp_dispatch *tp, enum libinput_config_accel_profile which)
2967a46c0ec8Sopenharmony_ci{
2968a46c0ec8Sopenharmony_ci	struct evdev_device *device = tp->device;
2969a46c0ec8Sopenharmony_ci	int res_x, res_y;
2970a46c0ec8Sopenharmony_ci	struct motion_filter *filter;
2971a46c0ec8Sopenharmony_ci	int dpi = device->dpi;
2972a46c0ec8Sopenharmony_ci	bool use_v_avg = device->use_velocity_averaging;
2973a46c0ec8Sopenharmony_ci
2974a46c0ec8Sopenharmony_ci	res_x = tp->device->abs.absinfo_x->resolution;
2975a46c0ec8Sopenharmony_ci	res_y = tp->device->abs.absinfo_y->resolution;
2976a46c0ec8Sopenharmony_ci
2977a46c0ec8Sopenharmony_ci	/*
2978a46c0ec8Sopenharmony_ci	 * Not all touchpads report the same amount of units/mm (resolution).
2979a46c0ec8Sopenharmony_ci	 * Normalize motion events to the default mouse DPI as base
2980a46c0ec8Sopenharmony_ci	 * (unaccelerated) speed. This also evens out any differences in x
2981a46c0ec8Sopenharmony_ci	 * and y resolution, so that a circle on the
2982a46c0ec8Sopenharmony_ci	 * touchpad does not turn into an ellipse on the screen.
2983a46c0ec8Sopenharmony_ci	 */
2984a46c0ec8Sopenharmony_ci	tp->accel.x_scale_coeff = (DEFAULT_MOUSE_DPI/25.4) / res_x;
2985a46c0ec8Sopenharmony_ci	tp->accel.y_scale_coeff = (DEFAULT_MOUSE_DPI/25.4) / res_y;
2986a46c0ec8Sopenharmony_ci	tp->accel.xy_scale_coeff = 1.0 * res_x/res_y;
2987a46c0ec8Sopenharmony_ci
2988a46c0ec8Sopenharmony_ci	if (which == LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT) {
2989a46c0ec8Sopenharmony_ci		filter = create_pointer_accelerator_filter_touchpad_flat(dpi);
2990a46c0ec8Sopenharmony_ci	} else if (which == LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM) {
2991a46c0ec8Sopenharmony_ci		filter = create_custom_accelerator_filter();
2992a46c0ec8Sopenharmony_ci	} else if (evdev_device_has_model_quirk(device, QUIRK_MODEL_LENOVO_X230) ||
2993a46c0ec8Sopenharmony_ci		 tp->device->model_flags & EVDEV_MODEL_LENOVO_X220_TOUCHPAD_FW81) {
2994a46c0ec8Sopenharmony_ci		filter = create_pointer_accelerator_filter_lenovo_x230(dpi, use_v_avg);
2995a46c0ec8Sopenharmony_ci	} else {
2996a46c0ec8Sopenharmony_ci		uint64_t eds_threshold = 0;
2997a46c0ec8Sopenharmony_ci		uint64_t eds_value = 0;
2998a46c0ec8Sopenharmony_ci
2999a46c0ec8Sopenharmony_ci		if (libevdev_get_id_bustype(device->evdev) == BUS_BLUETOOTH) {
3000a46c0ec8Sopenharmony_ci			eds_threshold = ms2us(50);
3001a46c0ec8Sopenharmony_ci			eds_value = ms2us(10);
3002a46c0ec8Sopenharmony_ci		}
3003a46c0ec8Sopenharmony_ci		filter = create_pointer_accelerator_filter_touchpad(dpi,
3004a46c0ec8Sopenharmony_ci								    eds_threshold,
3005a46c0ec8Sopenharmony_ci								    eds_value,
3006a46c0ec8Sopenharmony_ci								    use_v_avg);
3007a46c0ec8Sopenharmony_ci	}
3008a46c0ec8Sopenharmony_ci
3009a46c0ec8Sopenharmony_ci	if (!filter)
3010a46c0ec8Sopenharmony_ci		return false;
3011a46c0ec8Sopenharmony_ci
3012a46c0ec8Sopenharmony_ci	evdev_device_init_pointer_acceleration(tp->device, filter);
3013a46c0ec8Sopenharmony_ci
3014a46c0ec8Sopenharmony_ci	device->pointer.config.set_profile = tp_accel_config_set_profile;
3015a46c0ec8Sopenharmony_ci
3016a46c0ec8Sopenharmony_ci	return true;
3017a46c0ec8Sopenharmony_ci}
3018a46c0ec8Sopenharmony_ci
3019a46c0ec8Sopenharmony_cistatic enum libinput_config_status
3020a46c0ec8Sopenharmony_citp_accel_config_set_speed(struct libinput_device *device, double speed)
3021a46c0ec8Sopenharmony_ci{
3022a46c0ec8Sopenharmony_ci	struct evdev_device *dev = evdev_device(device);
3023a46c0ec8Sopenharmony_ci
3024a46c0ec8Sopenharmony_ci	if (!filter_set_speed(dev->pointer.filter, speed))
3025a46c0ec8Sopenharmony_ci		return LIBINPUT_CONFIG_STATUS_INVALID;
3026a46c0ec8Sopenharmony_ci
3027a46c0ec8Sopenharmony_ci	return LIBINPUT_CONFIG_STATUS_SUCCESS;
3028a46c0ec8Sopenharmony_ci}
3029a46c0ec8Sopenharmony_ci
3030a46c0ec8Sopenharmony_cistatic enum libinput_config_status
3031a46c0ec8Sopenharmony_citp_accel_config_set_profile(struct libinput_device *libinput_device,
3032a46c0ec8Sopenharmony_ci			    enum libinput_config_accel_profile profile)
3033a46c0ec8Sopenharmony_ci{
3034a46c0ec8Sopenharmony_ci	struct evdev_device *device = evdev_device(libinput_device);
3035a46c0ec8Sopenharmony_ci	struct tp_dispatch *tp = tp_dispatch(device->dispatch);
3036a46c0ec8Sopenharmony_ci	struct motion_filter *filter;
3037a46c0ec8Sopenharmony_ci	double speed;
3038a46c0ec8Sopenharmony_ci
3039a46c0ec8Sopenharmony_ci	filter = device->pointer.filter;
3040a46c0ec8Sopenharmony_ci	if (filter_get_type(filter) == profile)
3041a46c0ec8Sopenharmony_ci		return LIBINPUT_CONFIG_STATUS_SUCCESS;
3042a46c0ec8Sopenharmony_ci
3043a46c0ec8Sopenharmony_ci	speed = filter_get_speed(filter);
3044a46c0ec8Sopenharmony_ci	device->pointer.filter = NULL;
3045a46c0ec8Sopenharmony_ci
3046a46c0ec8Sopenharmony_ci	if (tp_init_accel(tp, profile)) {
3047a46c0ec8Sopenharmony_ci		tp_accel_config_set_speed(libinput_device, speed);
3048a46c0ec8Sopenharmony_ci		filter_destroy(filter);
3049a46c0ec8Sopenharmony_ci	} else {
3050a46c0ec8Sopenharmony_ci		device->pointer.filter = filter;
3051a46c0ec8Sopenharmony_ci		return LIBINPUT_CONFIG_STATUS_UNSUPPORTED;
3052a46c0ec8Sopenharmony_ci	}
3053a46c0ec8Sopenharmony_ci
3054a46c0ec8Sopenharmony_ci	return LIBINPUT_CONFIG_STATUS_SUCCESS;
3055a46c0ec8Sopenharmony_ci}
3056a46c0ec8Sopenharmony_ci
3057a46c0ec8Sopenharmony_cistatic uint32_t
3058a46c0ec8Sopenharmony_citp_scroll_get_methods(struct tp_dispatch *tp)
3059a46c0ec8Sopenharmony_ci{
3060a46c0ec8Sopenharmony_ci	uint32_t methods = LIBINPUT_CONFIG_SCROLL_EDGE;
3061a46c0ec8Sopenharmony_ci
3062a46c0ec8Sopenharmony_ci	/* Any movement with more than one finger has random cursor
3063a46c0ec8Sopenharmony_ci	 * jumps. Don't allow for 2fg scrolling on this device, see
3064a46c0ec8Sopenharmony_ci	 * fdo bug 91135 */
3065a46c0ec8Sopenharmony_ci	if (evdev_device_has_model_quirk(tp->device,
3066a46c0ec8Sopenharmony_ci					 QUIRK_MODEL_HP_PAVILION_DM4_TOUCHPAD))
3067a46c0ec8Sopenharmony_ci		return LIBINPUT_CONFIG_SCROLL_EDGE;
3068a46c0ec8Sopenharmony_ci
3069a46c0ec8Sopenharmony_ci	if (tp->ntouches >= 2)
3070a46c0ec8Sopenharmony_ci		methods |= LIBINPUT_CONFIG_SCROLL_2FG;
3071a46c0ec8Sopenharmony_ci
3072a46c0ec8Sopenharmony_ci	return methods;
3073a46c0ec8Sopenharmony_ci}
3074a46c0ec8Sopenharmony_ci
3075a46c0ec8Sopenharmony_cistatic uint32_t
3076a46c0ec8Sopenharmony_citp_scroll_config_scroll_method_get_methods(struct libinput_device *device)
3077a46c0ec8Sopenharmony_ci{
3078a46c0ec8Sopenharmony_ci	struct evdev_device *evdev = evdev_device(device);
3079a46c0ec8Sopenharmony_ci	struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
3080a46c0ec8Sopenharmony_ci
3081a46c0ec8Sopenharmony_ci	return tp_scroll_get_methods(tp);
3082a46c0ec8Sopenharmony_ci}
3083a46c0ec8Sopenharmony_ci
3084a46c0ec8Sopenharmony_cistatic enum libinput_config_status
3085a46c0ec8Sopenharmony_citp_scroll_config_scroll_method_set_method(struct libinput_device *device,
3086a46c0ec8Sopenharmony_ci		        enum libinput_config_scroll_method method)
3087a46c0ec8Sopenharmony_ci{
3088a46c0ec8Sopenharmony_ci	struct evdev_device *evdev = evdev_device(device);
3089a46c0ec8Sopenharmony_ci	struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
3090a46c0ec8Sopenharmony_ci	uint64_t time = libinput_now(tp_libinput_context(tp));
3091a46c0ec8Sopenharmony_ci
3092a46c0ec8Sopenharmony_ci	if (method == tp->scroll.method)
3093a46c0ec8Sopenharmony_ci		return LIBINPUT_CONFIG_STATUS_SUCCESS;
3094a46c0ec8Sopenharmony_ci
3095a46c0ec8Sopenharmony_ci	tp_edge_scroll_stop_events(tp, time);
3096a46c0ec8Sopenharmony_ci	tp_gesture_stop_twofinger_scroll(tp, time);
3097a46c0ec8Sopenharmony_ci
3098a46c0ec8Sopenharmony_ci	tp->scroll.method = method;
3099a46c0ec8Sopenharmony_ci
3100a46c0ec8Sopenharmony_ci	return LIBINPUT_CONFIG_STATUS_SUCCESS;
3101a46c0ec8Sopenharmony_ci}
3102a46c0ec8Sopenharmony_ci
3103a46c0ec8Sopenharmony_cistatic enum libinput_config_scroll_method
3104a46c0ec8Sopenharmony_citp_scroll_config_scroll_method_get_method(struct libinput_device *device)
3105a46c0ec8Sopenharmony_ci{
3106a46c0ec8Sopenharmony_ci	struct evdev_device *evdev = evdev_device(device);
3107a46c0ec8Sopenharmony_ci	struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
3108a46c0ec8Sopenharmony_ci
3109a46c0ec8Sopenharmony_ci	return tp->scroll.method;
3110a46c0ec8Sopenharmony_ci}
3111a46c0ec8Sopenharmony_ci
3112a46c0ec8Sopenharmony_cistatic enum libinput_config_scroll_method
3113a46c0ec8Sopenharmony_citp_scroll_get_default_method(struct tp_dispatch *tp)
3114a46c0ec8Sopenharmony_ci{
3115a46c0ec8Sopenharmony_ci	uint32_t methods;
3116a46c0ec8Sopenharmony_ci	enum libinput_config_scroll_method method;
3117a46c0ec8Sopenharmony_ci
3118a46c0ec8Sopenharmony_ci	methods = tp_scroll_get_methods(tp);
3119a46c0ec8Sopenharmony_ci
3120a46c0ec8Sopenharmony_ci	if (methods & LIBINPUT_CONFIG_SCROLL_2FG)
3121a46c0ec8Sopenharmony_ci		method = LIBINPUT_CONFIG_SCROLL_2FG;
3122a46c0ec8Sopenharmony_ci	else
3123a46c0ec8Sopenharmony_ci		method = LIBINPUT_CONFIG_SCROLL_EDGE;
3124a46c0ec8Sopenharmony_ci
3125a46c0ec8Sopenharmony_ci	if ((methods & method) == 0)
3126a46c0ec8Sopenharmony_ci		evdev_log_bug_libinput(tp->device,
3127a46c0ec8Sopenharmony_ci				       "invalid default scroll method %d\n",
3128a46c0ec8Sopenharmony_ci				       method);
3129a46c0ec8Sopenharmony_ci	return method;
3130a46c0ec8Sopenharmony_ci}
3131a46c0ec8Sopenharmony_ci
3132a46c0ec8Sopenharmony_cistatic enum libinput_config_scroll_method
3133a46c0ec8Sopenharmony_citp_scroll_config_scroll_method_get_default_method(struct libinput_device *device)
3134a46c0ec8Sopenharmony_ci{
3135a46c0ec8Sopenharmony_ci	struct evdev_device *evdev = evdev_device(device);
3136a46c0ec8Sopenharmony_ci	struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
3137a46c0ec8Sopenharmony_ci
3138a46c0ec8Sopenharmony_ci	return tp_scroll_get_default_method(tp);
3139a46c0ec8Sopenharmony_ci}
3140a46c0ec8Sopenharmony_ci
3141a46c0ec8Sopenharmony_cistatic int
3142a46c0ec8Sopenharmony_citp_scroll_config_natural_get_default(struct libinput_device *device)
3143a46c0ec8Sopenharmony_ci{
3144a46c0ec8Sopenharmony_ci	struct evdev_device *dev = evdev_device(device);
3145a46c0ec8Sopenharmony_ci
3146a46c0ec8Sopenharmony_ci	return (evdev_device_has_model_quirk(dev, QUIRK_MODEL_APPLE_TOUCHPAD) ||
3147a46c0ec8Sopenharmony_ci		evdev_device_has_model_quirk(dev, QUIRK_MODEL_APPLE_TOUCHPAD_ONEBUTTON));
3148a46c0ec8Sopenharmony_ci}
3149a46c0ec8Sopenharmony_ci
3150a46c0ec8Sopenharmony_cistatic void
3151a46c0ec8Sopenharmony_citp_init_scroll(struct tp_dispatch *tp, struct evdev_device *device)
3152a46c0ec8Sopenharmony_ci{
3153a46c0ec8Sopenharmony_ci	tp_edge_scroll_init(tp, device);
3154a46c0ec8Sopenharmony_ci
3155a46c0ec8Sopenharmony_ci	evdev_init_natural_scroll(device);
3156a46c0ec8Sopenharmony_ci	/* Override natural scroll config for Apple touchpads */
3157a46c0ec8Sopenharmony_ci	device->scroll.config_natural.get_default_enabled = tp_scroll_config_natural_get_default;
3158a46c0ec8Sopenharmony_ci	device->scroll.natural_scrolling_enabled = tp_scroll_config_natural_get_default(&device->base);
3159a46c0ec8Sopenharmony_ci
3160a46c0ec8Sopenharmony_ci	tp->scroll.config_method.get_methods = tp_scroll_config_scroll_method_get_methods;
3161a46c0ec8Sopenharmony_ci	tp->scroll.config_method.set_method = tp_scroll_config_scroll_method_set_method;
3162a46c0ec8Sopenharmony_ci	tp->scroll.config_method.get_method = tp_scroll_config_scroll_method_get_method;
3163a46c0ec8Sopenharmony_ci	tp->scroll.config_method.get_default_method = tp_scroll_config_scroll_method_get_default_method;
3164a46c0ec8Sopenharmony_ci	tp->scroll.method = tp_scroll_get_default_method(tp);
3165a46c0ec8Sopenharmony_ci	tp->device->base.config.scroll_method = &tp->scroll.config_method;
3166a46c0ec8Sopenharmony_ci
3167a46c0ec8Sopenharmony_ci	 /* In mm for touchpads with valid resolution, see tp_init_accel() */
3168a46c0ec8Sopenharmony_ci	tp->device->scroll.threshold = 0.0;
3169a46c0ec8Sopenharmony_ci	tp->device->scroll.direction_lock_threshold = 5.0;
3170a46c0ec8Sopenharmony_ci}
3171a46c0ec8Sopenharmony_ci
3172a46c0ec8Sopenharmony_cistatic int
3173a46c0ec8Sopenharmony_citp_dwt_config_is_available(struct libinput_device *device)
3174a46c0ec8Sopenharmony_ci{
3175a46c0ec8Sopenharmony_ci	return 1;
3176a46c0ec8Sopenharmony_ci}
3177a46c0ec8Sopenharmony_ci
3178a46c0ec8Sopenharmony_cistatic enum libinput_config_status
3179a46c0ec8Sopenharmony_citp_dwt_config_set(struct libinput_device *device,
3180a46c0ec8Sopenharmony_ci	   enum libinput_config_dwt_state enable)
3181a46c0ec8Sopenharmony_ci{
3182a46c0ec8Sopenharmony_ci	struct evdev_device *evdev = evdev_device(device);
3183a46c0ec8Sopenharmony_ci	struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
3184a46c0ec8Sopenharmony_ci
3185a46c0ec8Sopenharmony_ci	switch(enable) {
3186a46c0ec8Sopenharmony_ci	case LIBINPUT_CONFIG_DWT_ENABLED:
3187a46c0ec8Sopenharmony_ci	case LIBINPUT_CONFIG_DWT_DISABLED:
3188a46c0ec8Sopenharmony_ci		break;
3189a46c0ec8Sopenharmony_ci	default:
3190a46c0ec8Sopenharmony_ci		return LIBINPUT_CONFIG_STATUS_INVALID;
3191a46c0ec8Sopenharmony_ci	}
3192a46c0ec8Sopenharmony_ci
3193a46c0ec8Sopenharmony_ci	tp->dwt.dwt_enabled = (enable == LIBINPUT_CONFIG_DWT_ENABLED);
3194a46c0ec8Sopenharmony_ci
3195a46c0ec8Sopenharmony_ci	return LIBINPUT_CONFIG_STATUS_SUCCESS;
3196a46c0ec8Sopenharmony_ci}
3197a46c0ec8Sopenharmony_ci
3198a46c0ec8Sopenharmony_cistatic enum libinput_config_dwt_state
3199a46c0ec8Sopenharmony_citp_dwt_config_get(struct libinput_device *device)
3200a46c0ec8Sopenharmony_ci{
3201a46c0ec8Sopenharmony_ci	struct evdev_device *evdev = evdev_device(device);
3202a46c0ec8Sopenharmony_ci	struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
3203a46c0ec8Sopenharmony_ci
3204a46c0ec8Sopenharmony_ci	return tp->dwt.dwt_enabled ?
3205a46c0ec8Sopenharmony_ci		LIBINPUT_CONFIG_DWT_ENABLED :
3206a46c0ec8Sopenharmony_ci		LIBINPUT_CONFIG_DWT_DISABLED;
3207a46c0ec8Sopenharmony_ci}
3208a46c0ec8Sopenharmony_ci
3209a46c0ec8Sopenharmony_cistatic bool
3210a46c0ec8Sopenharmony_citp_dwt_default_enabled(struct tp_dispatch *tp)
3211a46c0ec8Sopenharmony_ci{
3212a46c0ec8Sopenharmony_ci	return true;
3213a46c0ec8Sopenharmony_ci}
3214a46c0ec8Sopenharmony_ci
3215a46c0ec8Sopenharmony_cistatic enum libinput_config_dwt_state
3216a46c0ec8Sopenharmony_citp_dwt_config_get_default(struct libinput_device *device)
3217a46c0ec8Sopenharmony_ci{
3218a46c0ec8Sopenharmony_ci	struct evdev_device *evdev = evdev_device(device);
3219a46c0ec8Sopenharmony_ci	struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
3220a46c0ec8Sopenharmony_ci
3221a46c0ec8Sopenharmony_ci	return tp_dwt_default_enabled(tp) ?
3222a46c0ec8Sopenharmony_ci		LIBINPUT_CONFIG_DWT_ENABLED :
3223a46c0ec8Sopenharmony_ci		LIBINPUT_CONFIG_DWT_DISABLED;
3224a46c0ec8Sopenharmony_ci}
3225a46c0ec8Sopenharmony_ci
3226a46c0ec8Sopenharmony_cistatic int
3227a46c0ec8Sopenharmony_citp_dwtp_config_is_available(struct libinput_device *device)
3228a46c0ec8Sopenharmony_ci{
3229a46c0ec8Sopenharmony_ci	return 1;
3230a46c0ec8Sopenharmony_ci}
3231a46c0ec8Sopenharmony_ci
3232a46c0ec8Sopenharmony_cistatic enum libinput_config_status
3233a46c0ec8Sopenharmony_citp_dwtp_config_set(struct libinput_device *device,
3234a46c0ec8Sopenharmony_ci	   enum libinput_config_dwtp_state enable)
3235a46c0ec8Sopenharmony_ci{
3236a46c0ec8Sopenharmony_ci	struct evdev_device *evdev = evdev_device(device);
3237a46c0ec8Sopenharmony_ci	struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
3238a46c0ec8Sopenharmony_ci
3239a46c0ec8Sopenharmony_ci	switch(enable) {
3240a46c0ec8Sopenharmony_ci	case LIBINPUT_CONFIG_DWTP_ENABLED:
3241a46c0ec8Sopenharmony_ci	case LIBINPUT_CONFIG_DWTP_DISABLED:
3242a46c0ec8Sopenharmony_ci		break;
3243a46c0ec8Sopenharmony_ci	default:
3244a46c0ec8Sopenharmony_ci		return LIBINPUT_CONFIG_STATUS_INVALID;
3245a46c0ec8Sopenharmony_ci	}
3246a46c0ec8Sopenharmony_ci
3247a46c0ec8Sopenharmony_ci	tp->palm.dwtp_enabled = (enable == LIBINPUT_CONFIG_DWTP_ENABLED);
3248a46c0ec8Sopenharmony_ci
3249a46c0ec8Sopenharmony_ci	return LIBINPUT_CONFIG_STATUS_SUCCESS;
3250a46c0ec8Sopenharmony_ci}
3251a46c0ec8Sopenharmony_ci
3252a46c0ec8Sopenharmony_cistatic enum libinput_config_dwtp_state
3253a46c0ec8Sopenharmony_citp_dwtp_config_get(struct libinput_device *device)
3254a46c0ec8Sopenharmony_ci{
3255a46c0ec8Sopenharmony_ci	struct evdev_device *evdev = evdev_device(device);
3256a46c0ec8Sopenharmony_ci	struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
3257a46c0ec8Sopenharmony_ci
3258a46c0ec8Sopenharmony_ci	return tp->palm.dwtp_enabled ?
3259a46c0ec8Sopenharmony_ci		LIBINPUT_CONFIG_DWTP_ENABLED :
3260a46c0ec8Sopenharmony_ci		LIBINPUT_CONFIG_DWTP_DISABLED;
3261a46c0ec8Sopenharmony_ci}
3262a46c0ec8Sopenharmony_ci
3263a46c0ec8Sopenharmony_cistatic bool
3264a46c0ec8Sopenharmony_citp_dwtp_default_enabled(struct tp_dispatch *tp)
3265a46c0ec8Sopenharmony_ci{
3266a46c0ec8Sopenharmony_ci	return true;
3267a46c0ec8Sopenharmony_ci}
3268a46c0ec8Sopenharmony_ci
3269a46c0ec8Sopenharmony_cistatic enum libinput_config_dwtp_state
3270a46c0ec8Sopenharmony_citp_dwtp_config_get_default(struct libinput_device *device)
3271a46c0ec8Sopenharmony_ci{
3272a46c0ec8Sopenharmony_ci	struct evdev_device *evdev = evdev_device(device);
3273a46c0ec8Sopenharmony_ci	struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
3274a46c0ec8Sopenharmony_ci
3275a46c0ec8Sopenharmony_ci	return tp_dwtp_default_enabled(tp) ?
3276a46c0ec8Sopenharmony_ci		LIBINPUT_CONFIG_DWTP_ENABLED :
3277a46c0ec8Sopenharmony_ci		LIBINPUT_CONFIG_DWTP_DISABLED;
3278a46c0ec8Sopenharmony_ci}
3279a46c0ec8Sopenharmony_ci
3280a46c0ec8Sopenharmony_cistatic inline bool
3281a46c0ec8Sopenharmony_citp_is_tpkb_combo_below(struct evdev_device *device)
3282a46c0ec8Sopenharmony_ci{
3283a46c0ec8Sopenharmony_ci	struct quirks_context *quirks;
3284a46c0ec8Sopenharmony_ci	struct quirks *q;
3285a46c0ec8Sopenharmony_ci	char *prop;
3286a46c0ec8Sopenharmony_ci	enum tpkbcombo_layout layout = TPKBCOMBO_LAYOUT_UNKNOWN;
3287a46c0ec8Sopenharmony_ci	int rc = false;
3288a46c0ec8Sopenharmony_ci
3289a46c0ec8Sopenharmony_ci	quirks = evdev_libinput_context(device)->quirks;
3290a46c0ec8Sopenharmony_ci	q = quirks_fetch_for_device(quirks, device->udev_device);
3291a46c0ec8Sopenharmony_ci	if (!q)
3292a46c0ec8Sopenharmony_ci		return false;
3293a46c0ec8Sopenharmony_ci
3294a46c0ec8Sopenharmony_ci	if (quirks_get_string(q, QUIRK_ATTR_TPKBCOMBO_LAYOUT, &prop)) {
3295a46c0ec8Sopenharmony_ci		rc = parse_tpkbcombo_layout_poperty(prop, &layout) &&
3296a46c0ec8Sopenharmony_ci			layout == TPKBCOMBO_LAYOUT_BELOW;
3297a46c0ec8Sopenharmony_ci	}
3298a46c0ec8Sopenharmony_ci
3299a46c0ec8Sopenharmony_ci	quirks_unref(q);
3300a46c0ec8Sopenharmony_ci
3301a46c0ec8Sopenharmony_ci	return rc;
3302a46c0ec8Sopenharmony_ci}
3303a46c0ec8Sopenharmony_ci
3304a46c0ec8Sopenharmony_cistatic inline bool
3305a46c0ec8Sopenharmony_citp_is_tablet(struct evdev_device *device)
3306a46c0ec8Sopenharmony_ci{
3307a46c0ec8Sopenharmony_ci	return device->tags & EVDEV_TAG_TABLET_TOUCHPAD;
3308a46c0ec8Sopenharmony_ci}
3309a46c0ec8Sopenharmony_ci
3310a46c0ec8Sopenharmony_cistatic void
3311a46c0ec8Sopenharmony_citp_init_dwt(struct tp_dispatch *tp,
3312a46c0ec8Sopenharmony_ci	    struct evdev_device *device)
3313a46c0ec8Sopenharmony_ci{
3314a46c0ec8Sopenharmony_ci	if (device->tags & EVDEV_TAG_EXTERNAL_TOUCHPAD &&
3315a46c0ec8Sopenharmony_ci	    !tp_is_tpkb_combo_below(device))
3316a46c0ec8Sopenharmony_ci		return;
3317a46c0ec8Sopenharmony_ci
3318a46c0ec8Sopenharmony_ci	tp->dwt.config.is_available = tp_dwt_config_is_available;
3319a46c0ec8Sopenharmony_ci	tp->dwt.config.set_enabled = tp_dwt_config_set;
3320a46c0ec8Sopenharmony_ci	tp->dwt.config.get_enabled = tp_dwt_config_get;
3321a46c0ec8Sopenharmony_ci	tp->dwt.config.get_default_enabled = tp_dwt_config_get_default;
3322a46c0ec8Sopenharmony_ci	tp->dwt.dwt_enabled = tp_dwt_default_enabled(tp);
3323a46c0ec8Sopenharmony_ci	device->base.config.dwt = &tp->dwt.config;
3324a46c0ec8Sopenharmony_ci}
3325a46c0ec8Sopenharmony_ci
3326a46c0ec8Sopenharmony_cistatic void
3327a46c0ec8Sopenharmony_citp_init_dwtp(struct tp_dispatch *tp,
3328a46c0ec8Sopenharmony_ci	    struct evdev_device *device)
3329a46c0ec8Sopenharmony_ci{
3330a46c0ec8Sopenharmony_ci	tp->palm.dwtp_enabled = tp_dwtp_default_enabled(tp);
3331a46c0ec8Sopenharmony_ci
3332a46c0ec8Sopenharmony_ci	if (device->tags & EVDEV_TAG_EXTERNAL_TOUCHPAD)
3333a46c0ec8Sopenharmony_ci		return;
3334a46c0ec8Sopenharmony_ci
3335a46c0ec8Sopenharmony_ci	tp->palm.config.is_available = tp_dwtp_config_is_available;
3336a46c0ec8Sopenharmony_ci	tp->palm.config.set_enabled = tp_dwtp_config_set;
3337a46c0ec8Sopenharmony_ci	tp->palm.config.get_enabled = tp_dwtp_config_get;
3338a46c0ec8Sopenharmony_ci	tp->palm.config.get_default_enabled = tp_dwtp_config_get_default;
3339a46c0ec8Sopenharmony_ci	device->base.config.dwtp = &tp->palm.config;
3340a46c0ec8Sopenharmony_ci}
3341a46c0ec8Sopenharmony_ci
3342a46c0ec8Sopenharmony_cistatic inline void
3343a46c0ec8Sopenharmony_citp_init_palmdetect_edge(struct tp_dispatch *tp,
3344a46c0ec8Sopenharmony_ci			struct evdev_device *device)
3345a46c0ec8Sopenharmony_ci{
3346a46c0ec8Sopenharmony_ci	double width, height;
3347a46c0ec8Sopenharmony_ci	struct phys_coords mm = { 0.0, 0.0 };
3348a46c0ec8Sopenharmony_ci	struct device_coords edges;
3349a46c0ec8Sopenharmony_ci
3350a46c0ec8Sopenharmony_ci	if (device->tags & EVDEV_TAG_EXTERNAL_TOUCHPAD &&
3351a46c0ec8Sopenharmony_ci	    !tp_is_tpkb_combo_below(device))
3352a46c0ec8Sopenharmony_ci		return;
3353a46c0ec8Sopenharmony_ci
3354a46c0ec8Sopenharmony_ci	/* Edge palm detection hurts more than it helps on Apple touchpads. */
3355a46c0ec8Sopenharmony_ci	if (evdev_device_has_model_quirk(device, QUIRK_MODEL_APPLE_TOUCHPAD))
3356a46c0ec8Sopenharmony_ci		return;
3357a46c0ec8Sopenharmony_ci
3358a46c0ec8Sopenharmony_ci	evdev_device_get_size(device, &width, &height);
3359a46c0ec8Sopenharmony_ci
3360a46c0ec8Sopenharmony_ci	/* Enable edge palm detection on touchpads >= 70 mm. Anything
3361a46c0ec8Sopenharmony_ci	   smaller probably won't need it, until we find out it does */
3362a46c0ec8Sopenharmony_ci	if (width < 70.0)
3363a46c0ec8Sopenharmony_ci		return;
3364a46c0ec8Sopenharmony_ci
3365a46c0ec8Sopenharmony_ci	/* palm edges are 8% of the width on each side */
3366a46c0ec8Sopenharmony_ci	mm.x = min(8, width * 0.08);
3367a46c0ec8Sopenharmony_ci	edges = evdev_device_mm_to_units(device, &mm);
3368a46c0ec8Sopenharmony_ci	tp->palm.left_edge = edges.x;
3369a46c0ec8Sopenharmony_ci
3370a46c0ec8Sopenharmony_ci	mm.x = width - min(8, width * 0.08);
3371a46c0ec8Sopenharmony_ci	edges = evdev_device_mm_to_units(device, &mm);
3372a46c0ec8Sopenharmony_ci	tp->palm.right_edge = edges.x;
3373a46c0ec8Sopenharmony_ci
3374a46c0ec8Sopenharmony_ci	if (!tp->buttons.has_topbuttons && height > 55) {
3375a46c0ec8Sopenharmony_ci		/* top edge is 5% of the height */
3376a46c0ec8Sopenharmony_ci		mm.y = height * 0.05;
3377a46c0ec8Sopenharmony_ci		edges = evdev_device_mm_to_units(device, &mm);
3378a46c0ec8Sopenharmony_ci		tp->palm.upper_edge = edges.y;
3379a46c0ec8Sopenharmony_ci	}
3380a46c0ec8Sopenharmony_ci}
3381a46c0ec8Sopenharmony_ci
3382a46c0ec8Sopenharmony_cistatic int
3383a46c0ec8Sopenharmony_citp_read_palm_pressure_prop(struct tp_dispatch *tp,
3384a46c0ec8Sopenharmony_ci			   const struct evdev_device *device)
3385a46c0ec8Sopenharmony_ci{
3386a46c0ec8Sopenharmony_ci	const int default_palm_threshold = 130;
3387a46c0ec8Sopenharmony_ci	uint32_t threshold = default_palm_threshold;
3388a46c0ec8Sopenharmony_ci	struct quirks_context *quirks;
3389a46c0ec8Sopenharmony_ci	struct quirks *q;
3390a46c0ec8Sopenharmony_ci
3391a46c0ec8Sopenharmony_ci	quirks = evdev_libinput_context(device)->quirks;
3392a46c0ec8Sopenharmony_ci	q = quirks_fetch_for_device(quirks, device->udev_device);
3393a46c0ec8Sopenharmony_ci	if (!q)
3394a46c0ec8Sopenharmony_ci		return threshold;
3395a46c0ec8Sopenharmony_ci
3396a46c0ec8Sopenharmony_ci	quirks_get_uint32(q, QUIRK_ATTR_PALM_PRESSURE_THRESHOLD, &threshold);
3397a46c0ec8Sopenharmony_ci	quirks_unref(q);
3398a46c0ec8Sopenharmony_ci
3399a46c0ec8Sopenharmony_ci	return threshold;
3400a46c0ec8Sopenharmony_ci}
3401a46c0ec8Sopenharmony_ci
3402a46c0ec8Sopenharmony_cistatic inline void
3403a46c0ec8Sopenharmony_citp_init_palmdetect_pressure(struct tp_dispatch *tp,
3404a46c0ec8Sopenharmony_ci			    struct evdev_device *device)
3405a46c0ec8Sopenharmony_ci{
3406a46c0ec8Sopenharmony_ci	if (!libevdev_has_event_code(device->evdev, EV_ABS, ABS_MT_PRESSURE)) {
3407a46c0ec8Sopenharmony_ci		tp->palm.use_pressure = false;
3408a46c0ec8Sopenharmony_ci		return;
3409a46c0ec8Sopenharmony_ci	}
3410a46c0ec8Sopenharmony_ci
3411a46c0ec8Sopenharmony_ci	tp->palm.pressure_threshold = tp_read_palm_pressure_prop(tp, device);
3412a46c0ec8Sopenharmony_ci	if (tp->palm.pressure_threshold != 0) {
3413a46c0ec8Sopenharmony_ci		tp->palm.use_pressure = true;
3414a46c0ec8Sopenharmony_ci
3415a46c0ec8Sopenharmony_ci		evdev_log_debug(device,
3416a46c0ec8Sopenharmony_ci				"palm: pressure threshold is %d\n",
3417a46c0ec8Sopenharmony_ci				tp->palm.pressure_threshold);
3418a46c0ec8Sopenharmony_ci	}
3419a46c0ec8Sopenharmony_ci}
3420a46c0ec8Sopenharmony_ci
3421a46c0ec8Sopenharmony_cistatic inline void
3422a46c0ec8Sopenharmony_citp_init_palmdetect_size(struct tp_dispatch *tp,
3423a46c0ec8Sopenharmony_ci			struct evdev_device *device)
3424a46c0ec8Sopenharmony_ci{
3425a46c0ec8Sopenharmony_ci	struct quirks_context *quirks;
3426a46c0ec8Sopenharmony_ci	struct quirks *q;
3427a46c0ec8Sopenharmony_ci	uint32_t threshold;
3428a46c0ec8Sopenharmony_ci
3429a46c0ec8Sopenharmony_ci	quirks = evdev_libinput_context(device)->quirks;
3430a46c0ec8Sopenharmony_ci	q = quirks_fetch_for_device(quirks, device->udev_device);
3431a46c0ec8Sopenharmony_ci	if (!q)
3432a46c0ec8Sopenharmony_ci		return;
3433a46c0ec8Sopenharmony_ci
3434a46c0ec8Sopenharmony_ci	if (quirks_get_uint32(q, QUIRK_ATTR_PALM_SIZE_THRESHOLD, &threshold)) {
3435a46c0ec8Sopenharmony_ci		if (threshold != 0) {
3436a46c0ec8Sopenharmony_ci			tp->palm.use_size = true;
3437a46c0ec8Sopenharmony_ci			tp->palm.size_threshold = threshold;
3438a46c0ec8Sopenharmony_ci		}
3439a46c0ec8Sopenharmony_ci	}
3440a46c0ec8Sopenharmony_ci	quirks_unref(q);
3441a46c0ec8Sopenharmony_ci}
3442a46c0ec8Sopenharmony_ci
3443a46c0ec8Sopenharmony_cistatic inline void
3444a46c0ec8Sopenharmony_citp_init_palmdetect_arbitration(struct tp_dispatch *tp,
3445a46c0ec8Sopenharmony_ci			       struct evdev_device *device)
3446a46c0ec8Sopenharmony_ci{
3447a46c0ec8Sopenharmony_ci	char timer_name[64];
3448a46c0ec8Sopenharmony_ci
3449a46c0ec8Sopenharmony_ci	snprintf(timer_name,
3450a46c0ec8Sopenharmony_ci		 sizeof(timer_name),
3451a46c0ec8Sopenharmony_ci		  "%s arbitration",
3452a46c0ec8Sopenharmony_ci		  evdev_device_get_sysname(device));
3453a46c0ec8Sopenharmony_ci	libinput_timer_init(&tp->arbitration.arbitration_timer,
3454a46c0ec8Sopenharmony_ci			    tp_libinput_context(tp),
3455a46c0ec8Sopenharmony_ci			    timer_name,
3456a46c0ec8Sopenharmony_ci			    tp_arbitration_timeout, tp);
3457a46c0ec8Sopenharmony_ci	tp->arbitration.state = ARBITRATION_NOT_ACTIVE;
3458a46c0ec8Sopenharmony_ci}
3459a46c0ec8Sopenharmony_ci
3460a46c0ec8Sopenharmony_cistatic void
3461a46c0ec8Sopenharmony_citp_init_palmdetect(struct tp_dispatch *tp,
3462a46c0ec8Sopenharmony_ci		   struct evdev_device *device)
3463a46c0ec8Sopenharmony_ci{
3464a46c0ec8Sopenharmony_ci
3465a46c0ec8Sopenharmony_ci	tp->palm.right_edge = INT_MAX;
3466a46c0ec8Sopenharmony_ci	tp->palm.left_edge = INT_MIN;
3467a46c0ec8Sopenharmony_ci	tp->palm.upper_edge = INT_MIN;
3468a46c0ec8Sopenharmony_ci
3469a46c0ec8Sopenharmony_ci	tp_init_palmdetect_arbitration(tp, device);
3470a46c0ec8Sopenharmony_ci
3471a46c0ec8Sopenharmony_ci	if (device->tags & EVDEV_TAG_EXTERNAL_TOUCHPAD &&
3472a46c0ec8Sopenharmony_ci	    !tp_is_tpkb_combo_below(device) &&
3473a46c0ec8Sopenharmony_ci	    !tp_is_tablet(device))
3474a46c0ec8Sopenharmony_ci		return;
3475a46c0ec8Sopenharmony_ci
3476a46c0ec8Sopenharmony_ci	if (!tp_is_tablet(device))
3477a46c0ec8Sopenharmony_ci		tp->palm.monitor_trackpoint = true;
3478a46c0ec8Sopenharmony_ci
3479a46c0ec8Sopenharmony_ci	if (libevdev_has_event_code(device->evdev,
3480a46c0ec8Sopenharmony_ci				    EV_ABS,
3481a46c0ec8Sopenharmony_ci				    ABS_MT_TOOL_TYPE))
3482a46c0ec8Sopenharmony_ci		tp->palm.use_mt_tool = true;
3483a46c0ec8Sopenharmony_ci
3484a46c0ec8Sopenharmony_ci	if (!tp_is_tablet(device))
3485a46c0ec8Sopenharmony_ci		tp_init_palmdetect_edge(tp, device);
3486a46c0ec8Sopenharmony_ci	tp_init_palmdetect_pressure(tp, device);
3487a46c0ec8Sopenharmony_ci	tp_init_palmdetect_size(tp, device);
3488a46c0ec8Sopenharmony_ci}
3489a46c0ec8Sopenharmony_ci
3490a46c0ec8Sopenharmony_cistatic void
3491a46c0ec8Sopenharmony_citp_init_sendevents(struct tp_dispatch *tp,
3492a46c0ec8Sopenharmony_ci		   struct evdev_device *device)
3493a46c0ec8Sopenharmony_ci{
3494a46c0ec8Sopenharmony_ci	char timer_name[64];
3495a46c0ec8Sopenharmony_ci
3496a46c0ec8Sopenharmony_ci	snprintf(timer_name,
3497a46c0ec8Sopenharmony_ci		 sizeof(timer_name),
3498a46c0ec8Sopenharmony_ci		  "%s trackpoint",
3499a46c0ec8Sopenharmony_ci		  evdev_device_get_sysname(device));
3500a46c0ec8Sopenharmony_ci	libinput_timer_init(&tp->palm.trackpoint_timer,
3501a46c0ec8Sopenharmony_ci			    tp_libinput_context(tp),
3502a46c0ec8Sopenharmony_ci			    timer_name,
3503a46c0ec8Sopenharmony_ci			    tp_trackpoint_timeout, tp);
3504a46c0ec8Sopenharmony_ci
3505a46c0ec8Sopenharmony_ci	snprintf(timer_name,
3506a46c0ec8Sopenharmony_ci		 sizeof(timer_name),
3507a46c0ec8Sopenharmony_ci		 "%s keyboard",
3508a46c0ec8Sopenharmony_ci		 evdev_device_get_sysname(device));
3509a46c0ec8Sopenharmony_ci	libinput_timer_init(&tp->dwt.keyboard_timer,
3510a46c0ec8Sopenharmony_ci			    tp_libinput_context(tp),
3511a46c0ec8Sopenharmony_ci			    timer_name,
3512a46c0ec8Sopenharmony_ci			    tp_keyboard_timeout, tp);
3513a46c0ec8Sopenharmony_ci}
3514a46c0ec8Sopenharmony_ci
3515a46c0ec8Sopenharmony_cistatic bool
3516a46c0ec8Sopenharmony_citp_pass_sanity_check(struct tp_dispatch *tp,
3517a46c0ec8Sopenharmony_ci		     struct evdev_device *device)
3518a46c0ec8Sopenharmony_ci{
3519a46c0ec8Sopenharmony_ci	struct libevdev *evdev = device->evdev;
3520a46c0ec8Sopenharmony_ci
3521a46c0ec8Sopenharmony_ci	if (!libevdev_has_event_code(evdev, EV_ABS, ABS_X))
3522a46c0ec8Sopenharmony_ci		goto error;
3523a46c0ec8Sopenharmony_ci
3524a46c0ec8Sopenharmony_ci	if (!libevdev_has_event_code(evdev, EV_KEY, BTN_TOUCH))
3525a46c0ec8Sopenharmony_ci		goto error;
3526a46c0ec8Sopenharmony_ci
3527a46c0ec8Sopenharmony_ci	if (!libevdev_has_event_code(evdev, EV_KEY, BTN_TOOL_FINGER))
3528a46c0ec8Sopenharmony_ci		goto error;
3529a46c0ec8Sopenharmony_ci
3530a46c0ec8Sopenharmony_ci	return true;
3531a46c0ec8Sopenharmony_ci
3532a46c0ec8Sopenharmony_cierror:
3533a46c0ec8Sopenharmony_ci	evdev_log_bug_kernel(device,
3534a46c0ec8Sopenharmony_ci			     "device failed touchpad sanity checks\n");
3535a46c0ec8Sopenharmony_ci	return false;
3536a46c0ec8Sopenharmony_ci}
3537a46c0ec8Sopenharmony_ci
3538a46c0ec8Sopenharmony_cistatic void
3539a46c0ec8Sopenharmony_citp_init_default_resolution(struct tp_dispatch *tp,
3540a46c0ec8Sopenharmony_ci			   struct evdev_device *device)
3541a46c0ec8Sopenharmony_ci{
3542a46c0ec8Sopenharmony_ci	const int touchpad_width_mm = 69, /* 1 under palm detection */
3543a46c0ec8Sopenharmony_ci		  touchpad_height_mm = 50;
3544a46c0ec8Sopenharmony_ci	int xres, yres;
3545a46c0ec8Sopenharmony_ci
3546a46c0ec8Sopenharmony_ci	if (!device->abs.is_fake_resolution)
3547a46c0ec8Sopenharmony_ci		return;
3548a46c0ec8Sopenharmony_ci
3549a46c0ec8Sopenharmony_ci	/* we only get here if
3550a46c0ec8Sopenharmony_ci	 * - the touchpad provides no resolution
3551a46c0ec8Sopenharmony_ci	 * - the udev hwdb didn't override the resolution
3552a46c0ec8Sopenharmony_ci	 * - no ATTR_SIZE_HINT is set
3553a46c0ec8Sopenharmony_ci	 *
3554a46c0ec8Sopenharmony_ci	 * The majority of touchpads that triggers all these conditions
3555a46c0ec8Sopenharmony_ci	 * are old ones, so let's assume a small touchpad size and assume
3556a46c0ec8Sopenharmony_ci	 * that.
3557a46c0ec8Sopenharmony_ci	 */
3558a46c0ec8Sopenharmony_ci	evdev_log_info(device,
3559a46c0ec8Sopenharmony_ci		       "no resolution or size hints, assuming a size of %dx%dmm\n",
3560a46c0ec8Sopenharmony_ci		       touchpad_width_mm,
3561a46c0ec8Sopenharmony_ci		       touchpad_height_mm);
3562a46c0ec8Sopenharmony_ci
3563a46c0ec8Sopenharmony_ci	xres = device->abs.dimensions.x/touchpad_width_mm;
3564a46c0ec8Sopenharmony_ci	yres = device->abs.dimensions.y/touchpad_height_mm;
3565a46c0ec8Sopenharmony_ci	libevdev_set_abs_resolution(device->evdev, ABS_X, xres);
3566a46c0ec8Sopenharmony_ci	libevdev_set_abs_resolution(device->evdev, ABS_Y, yres);
3567a46c0ec8Sopenharmony_ci	libevdev_set_abs_resolution(device->evdev, ABS_MT_POSITION_X, xres);
3568a46c0ec8Sopenharmony_ci	libevdev_set_abs_resolution(device->evdev, ABS_MT_POSITION_Y, yres);
3569a46c0ec8Sopenharmony_ci	device->abs.is_fake_resolution = false;
3570a46c0ec8Sopenharmony_ci}
3571a46c0ec8Sopenharmony_ci
3572a46c0ec8Sopenharmony_cistatic inline void
3573a46c0ec8Sopenharmony_citp_init_hysteresis(struct tp_dispatch *tp)
3574a46c0ec8Sopenharmony_ci{
3575a46c0ec8Sopenharmony_ci	int xmargin, ymargin;
3576a46c0ec8Sopenharmony_ci	const struct input_absinfo *ax = tp->device->abs.absinfo_x,
3577a46c0ec8Sopenharmony_ci				   *ay = tp->device->abs.absinfo_y;
3578a46c0ec8Sopenharmony_ci
3579a46c0ec8Sopenharmony_ci	if (ax->fuzz)
3580a46c0ec8Sopenharmony_ci		xmargin = ax->fuzz;
3581a46c0ec8Sopenharmony_ci	else
3582a46c0ec8Sopenharmony_ci		xmargin = ax->resolution/4;
3583a46c0ec8Sopenharmony_ci
3584a46c0ec8Sopenharmony_ci	if (ay->fuzz)
3585a46c0ec8Sopenharmony_ci		ymargin = ay->fuzz;
3586a46c0ec8Sopenharmony_ci	else
3587a46c0ec8Sopenharmony_ci		ymargin = ay->resolution/4;
3588a46c0ec8Sopenharmony_ci
3589a46c0ec8Sopenharmony_ci	tp->hysteresis.margin.x = xmargin;
3590a46c0ec8Sopenharmony_ci	tp->hysteresis.margin.y = ymargin;
3591a46c0ec8Sopenharmony_ci	tp->hysteresis.enabled = (ax->fuzz || ay->fuzz);
3592a46c0ec8Sopenharmony_ci	if (tp->hysteresis.enabled)
3593a46c0ec8Sopenharmony_ci		evdev_log_debug(tp->device,
3594a46c0ec8Sopenharmony_ci				"hysteresis enabled. "
3595a46c0ec8Sopenharmony_ci				"See %s/touchpad-jitter.html for details\n",
3596a46c0ec8Sopenharmony_ci				HTTP_DOC_LINK);
3597a46c0ec8Sopenharmony_ci}
3598a46c0ec8Sopenharmony_ci
3599a46c0ec8Sopenharmony_cistatic void
3600a46c0ec8Sopenharmony_citp_init_pressure(struct tp_dispatch *tp,
3601a46c0ec8Sopenharmony_ci		 struct evdev_device *device)
3602a46c0ec8Sopenharmony_ci{
3603a46c0ec8Sopenharmony_ci	const struct input_absinfo *abs;
3604a46c0ec8Sopenharmony_ci	unsigned int code;
3605a46c0ec8Sopenharmony_ci	struct quirks_context *quirks;
3606a46c0ec8Sopenharmony_ci	struct quirks *q;
3607a46c0ec8Sopenharmony_ci	struct quirk_range r;
3608a46c0ec8Sopenharmony_ci	int hi, lo;
3609a46c0ec8Sopenharmony_ci
3610a46c0ec8Sopenharmony_ci	code = tp->has_mt ? ABS_MT_PRESSURE : ABS_PRESSURE;
3611a46c0ec8Sopenharmony_ci	if (!libevdev_has_event_code(device->evdev, EV_ABS, code)) {
3612a46c0ec8Sopenharmony_ci		tp->pressure.use_pressure = false;
3613a46c0ec8Sopenharmony_ci		return;
3614a46c0ec8Sopenharmony_ci	}
3615a46c0ec8Sopenharmony_ci
3616a46c0ec8Sopenharmony_ci	abs = libevdev_get_abs_info(device->evdev, code);
3617a46c0ec8Sopenharmony_ci	assert(abs);
3618a46c0ec8Sopenharmony_ci
3619a46c0ec8Sopenharmony_ci	quirks = evdev_libinput_context(device)->quirks;
3620a46c0ec8Sopenharmony_ci	q = quirks_fetch_for_device(quirks, device->udev_device);
3621a46c0ec8Sopenharmony_ci	if (q && quirks_get_range(q, QUIRK_ATTR_PRESSURE_RANGE, &r)) {
3622a46c0ec8Sopenharmony_ci		hi = r.upper;
3623a46c0ec8Sopenharmony_ci		lo = r.lower;
3624a46c0ec8Sopenharmony_ci
3625a46c0ec8Sopenharmony_ci		if (hi == 0 && lo == 0) {
3626a46c0ec8Sopenharmony_ci			evdev_log_info(device,
3627a46c0ec8Sopenharmony_ci			       "pressure-based touch detection disabled\n");
3628a46c0ec8Sopenharmony_ci			goto out;
3629a46c0ec8Sopenharmony_ci		}
3630a46c0ec8Sopenharmony_ci	} else {
3631a46c0ec8Sopenharmony_ci		double range = absinfo_range(abs);
3632a46c0ec8Sopenharmony_ci
3633a46c0ec8Sopenharmony_ci		/* Approximately the synaptics defaults */
3634a46c0ec8Sopenharmony_ci		hi = abs->minimum + 0.12 * range;
3635a46c0ec8Sopenharmony_ci		lo = abs->minimum + 0.10 * range;
3636a46c0ec8Sopenharmony_ci	}
3637a46c0ec8Sopenharmony_ci
3638a46c0ec8Sopenharmony_ci	if (hi > abs->maximum || hi < abs->minimum ||
3639a46c0ec8Sopenharmony_ci	    lo > abs->maximum || lo < abs->minimum) {
3640a46c0ec8Sopenharmony_ci		evdev_log_bug_libinput(device,
3641a46c0ec8Sopenharmony_ci			       "discarding out-of-bounds pressure range %d:%d\n",
3642a46c0ec8Sopenharmony_ci			       hi, lo);
3643a46c0ec8Sopenharmony_ci		goto out;
3644a46c0ec8Sopenharmony_ci	}
3645a46c0ec8Sopenharmony_ci
3646a46c0ec8Sopenharmony_ci	tp->pressure.use_pressure = true;
3647a46c0ec8Sopenharmony_ci	tp->pressure.high = hi;
3648a46c0ec8Sopenharmony_ci	tp->pressure.low = lo;
3649a46c0ec8Sopenharmony_ci
3650a46c0ec8Sopenharmony_ci	evdev_log_debug(device,
3651a46c0ec8Sopenharmony_ci			"using pressure-based touch detection (%d:%d)\n",
3652a46c0ec8Sopenharmony_ci			lo,
3653a46c0ec8Sopenharmony_ci			hi);
3654a46c0ec8Sopenharmony_ciout:
3655a46c0ec8Sopenharmony_ci	quirks_unref(q);
3656a46c0ec8Sopenharmony_ci}
3657a46c0ec8Sopenharmony_ci
3658a46c0ec8Sopenharmony_cistatic bool
3659a46c0ec8Sopenharmony_citp_init_touch_size(struct tp_dispatch *tp,
3660a46c0ec8Sopenharmony_ci		   struct evdev_device *device)
3661a46c0ec8Sopenharmony_ci{
3662a46c0ec8Sopenharmony_ci	struct quirks_context *quirks;
3663a46c0ec8Sopenharmony_ci	struct quirks *q;
3664a46c0ec8Sopenharmony_ci	struct quirk_range r;
3665a46c0ec8Sopenharmony_ci	int lo, hi;
3666a46c0ec8Sopenharmony_ci	int rc = false;
3667a46c0ec8Sopenharmony_ci
3668a46c0ec8Sopenharmony_ci	if (!libevdev_has_event_code(device->evdev,
3669a46c0ec8Sopenharmony_ci				     EV_ABS,
3670a46c0ec8Sopenharmony_ci				     ABS_MT_TOUCH_MAJOR)) {
3671a46c0ec8Sopenharmony_ci		return false;
3672a46c0ec8Sopenharmony_ci	}
3673a46c0ec8Sopenharmony_ci
3674a46c0ec8Sopenharmony_ci	quirks = evdev_libinput_context(device)->quirks;
3675a46c0ec8Sopenharmony_ci	q = quirks_fetch_for_device(quirks, device->udev_device);
3676a46c0ec8Sopenharmony_ci	if (q && quirks_get_range(q, QUIRK_ATTR_TOUCH_SIZE_RANGE, &r)) {
3677a46c0ec8Sopenharmony_ci		hi = r.upper;
3678a46c0ec8Sopenharmony_ci		lo = r.lower;
3679a46c0ec8Sopenharmony_ci	} else {
3680a46c0ec8Sopenharmony_ci		goto out;
3681a46c0ec8Sopenharmony_ci	}
3682a46c0ec8Sopenharmony_ci
3683a46c0ec8Sopenharmony_ci	if (libevdev_get_num_slots(device->evdev) < 5) {
3684a46c0ec8Sopenharmony_ci		evdev_log_bug_libinput(device,
3685a46c0ec8Sopenharmony_ci			       "Expected 5+ slots for touch size detection\n");
3686a46c0ec8Sopenharmony_ci		goto out;
3687a46c0ec8Sopenharmony_ci	}
3688a46c0ec8Sopenharmony_ci
3689a46c0ec8Sopenharmony_ci	if (hi == 0 && lo == 0) {
3690a46c0ec8Sopenharmony_ci		evdev_log_info(device,
3691a46c0ec8Sopenharmony_ci			       "touch size based touch detection disabled\n");
3692a46c0ec8Sopenharmony_ci		goto out;
3693a46c0ec8Sopenharmony_ci	}
3694a46c0ec8Sopenharmony_ci
3695a46c0ec8Sopenharmony_ci	/* Thresholds apply for both major or minor */
3696a46c0ec8Sopenharmony_ci	tp->touch_size.low = lo;
3697a46c0ec8Sopenharmony_ci	tp->touch_size.high = hi;
3698a46c0ec8Sopenharmony_ci	tp->touch_size.use_touch_size = true;
3699a46c0ec8Sopenharmony_ci
3700a46c0ec8Sopenharmony_ci	evdev_log_debug(device,
3701a46c0ec8Sopenharmony_ci			"using size-based touch detection (%d:%d)\n",
3702a46c0ec8Sopenharmony_ci			hi, lo);
3703a46c0ec8Sopenharmony_ci
3704a46c0ec8Sopenharmony_ci	rc = true;
3705a46c0ec8Sopenharmony_ciout:
3706a46c0ec8Sopenharmony_ci	quirks_unref(q);
3707a46c0ec8Sopenharmony_ci	return rc;
3708a46c0ec8Sopenharmony_ci}
3709a46c0ec8Sopenharmony_ci
3710a46c0ec8Sopenharmony_cistatic void
3711a46c0ec8Sopenharmony_citp_init_pressurepad(struct tp_dispatch *tp,
3712a46c0ec8Sopenharmony_ci		    struct evdev_device *device)
3713a46c0ec8Sopenharmony_ci{
3714a46c0ec8Sopenharmony_ci	/* On traditional touchpads, the pressure value equals contact
3715a46c0ec8Sopenharmony_ci	 * size. On PressurePads, pressure is a real physical axis for the
3716a46c0ec8Sopenharmony_ci	 * force down. So we disable it here because we don't do anything
3717a46c0ec8Sopenharmony_ci	 * with it anyway and using it for touch size messes things up.
3718a46c0ec8Sopenharmony_ci	 *
3719a46c0ec8Sopenharmony_ci	 * The kernel/udev set the resolution to non-zero on those devices
3720a46c0ec8Sopenharmony_ci	 * to indicate that the value is in a known axis space.
3721a46c0ec8Sopenharmony_ci	 *
3722a46c0ec8Sopenharmony_ci	 * See also #562
3723a46c0ec8Sopenharmony_ci	 */
3724a46c0ec8Sopenharmony_ci	if (libevdev_get_abs_resolution(device->evdev, ABS_MT_PRESSURE) != 0 ||
3725a46c0ec8Sopenharmony_ci	    evdev_device_has_model_quirk(device, QUIRK_MODEL_PRESSURE_PAD)) {
3726a46c0ec8Sopenharmony_ci		libevdev_disable_event_code(device->evdev, EV_ABS, ABS_MT_PRESSURE);
3727a46c0ec8Sopenharmony_ci		libevdev_disable_event_code(device->evdev, EV_ABS, ABS_PRESSURE);
3728a46c0ec8Sopenharmony_ci	}
3729a46c0ec8Sopenharmony_ci}
3730a46c0ec8Sopenharmony_ci
3731a46c0ec8Sopenharmony_cistatic int
3732a46c0ec8Sopenharmony_citp_init(struct tp_dispatch *tp,
3733a46c0ec8Sopenharmony_ci	struct evdev_device *device)
3734a46c0ec8Sopenharmony_ci{
3735a46c0ec8Sopenharmony_ci	bool use_touch_size = false;
3736a46c0ec8Sopenharmony_ci
3737a46c0ec8Sopenharmony_ci	tp->base.dispatch_type = DISPATCH_TOUCHPAD;
3738a46c0ec8Sopenharmony_ci	tp->base.interface = &tp_interface;
3739a46c0ec8Sopenharmony_ci	tp->device = device;
3740a46c0ec8Sopenharmony_ci	list_init(&tp->dwt.paired_keyboard_list);
3741a46c0ec8Sopenharmony_ci
3742a46c0ec8Sopenharmony_ci	if (!tp_pass_sanity_check(tp, device))
3743a46c0ec8Sopenharmony_ci		return false;
3744a46c0ec8Sopenharmony_ci
3745a46c0ec8Sopenharmony_ci	tp_init_default_resolution(tp, device);
3746a46c0ec8Sopenharmony_ci	tp_init_pressurepad(tp, device);
3747a46c0ec8Sopenharmony_ci
3748a46c0ec8Sopenharmony_ci	if (!tp_init_slots(tp, device))
3749a46c0ec8Sopenharmony_ci		return false;
3750a46c0ec8Sopenharmony_ci
3751a46c0ec8Sopenharmony_ci	evdev_device_init_abs_range_warnings(device);
3752a46c0ec8Sopenharmony_ci	use_touch_size = tp_init_touch_size(tp, device);
3753a46c0ec8Sopenharmony_ci
3754a46c0ec8Sopenharmony_ci	if (!use_touch_size)
3755a46c0ec8Sopenharmony_ci		tp_init_pressure(tp, device);
3756a46c0ec8Sopenharmony_ci
3757a46c0ec8Sopenharmony_ci	/* 5 warnings per 24 hours should be enough */
3758a46c0ec8Sopenharmony_ci	ratelimit_init(&tp->jump.warning, h2us(24), 5);
3759a46c0ec8Sopenharmony_ci
3760a46c0ec8Sopenharmony_ci	/* Set the dpi to that of the x axis, because that's what we normalize
3761a46c0ec8Sopenharmony_ci	   to when needed*/
3762a46c0ec8Sopenharmony_ci	device->dpi = device->abs.absinfo_x->resolution * 25.4;
3763a46c0ec8Sopenharmony_ci
3764a46c0ec8Sopenharmony_ci	tp_init_hysteresis(tp);
3765a46c0ec8Sopenharmony_ci
3766a46c0ec8Sopenharmony_ci	if (!tp_init_accel(tp, LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE))
3767a46c0ec8Sopenharmony_ci		return false;
3768a46c0ec8Sopenharmony_ci
3769a46c0ec8Sopenharmony_ci	tp_init_tap(tp);
3770a46c0ec8Sopenharmony_ci	tp_init_buttons(tp, device);
3771a46c0ec8Sopenharmony_ci	tp_init_dwt(tp, device);
3772a46c0ec8Sopenharmony_ci	tp_init_dwtp(tp, device);
3773a46c0ec8Sopenharmony_ci	tp_init_palmdetect(tp, device);
3774a46c0ec8Sopenharmony_ci	tp_init_sendevents(tp, device);
3775a46c0ec8Sopenharmony_ci	tp_init_scroll(tp, device);
3776a46c0ec8Sopenharmony_ci	tp_init_gesture(tp);
3777a46c0ec8Sopenharmony_ci	tp_init_thumb(tp);
3778a46c0ec8Sopenharmony_ci
3779a46c0ec8Sopenharmony_ci	/* Lenovo X1 Gen6 buffers the events in a weird way, making jump
3780a46c0ec8Sopenharmony_ci	 * detection impossible. See
3781a46c0ec8Sopenharmony_ci	 * https://gitlab.freedesktop.org/libinput/libinput/-/issues/506
3782a46c0ec8Sopenharmony_ci	 */
3783a46c0ec8Sopenharmony_ci	if (evdev_device_has_model_quirk(device,
3784a46c0ec8Sopenharmony_ci					 QUIRK_MODEL_LENOVO_X1GEN6_TOUCHPAD))
3785a46c0ec8Sopenharmony_ci		tp->jump.detection_disabled = true;
3786a46c0ec8Sopenharmony_ci
3787a46c0ec8Sopenharmony_ci	device->seat_caps |= EVDEV_DEVICE_POINTER;
3788a46c0ec8Sopenharmony_ci	if (tp->gesture.enabled)
3789a46c0ec8Sopenharmony_ci		device->seat_caps |= EVDEV_DEVICE_GESTURE;
3790a46c0ec8Sopenharmony_ci
3791a46c0ec8Sopenharmony_ci	return true;
3792a46c0ec8Sopenharmony_ci}
3793a46c0ec8Sopenharmony_ci
3794a46c0ec8Sopenharmony_cistatic uint32_t
3795a46c0ec8Sopenharmony_citp_sendevents_get_modes(struct libinput_device *device)
3796a46c0ec8Sopenharmony_ci{
3797a46c0ec8Sopenharmony_ci	struct evdev_device *evdev = evdev_device(device);
3798a46c0ec8Sopenharmony_ci	uint32_t modes = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED;
3799a46c0ec8Sopenharmony_ci
3800a46c0ec8Sopenharmony_ci	if (evdev->tags & EVDEV_TAG_INTERNAL_TOUCHPAD)
3801a46c0ec8Sopenharmony_ci		modes |= LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
3802a46c0ec8Sopenharmony_ci
3803a46c0ec8Sopenharmony_ci	return modes;
3804a46c0ec8Sopenharmony_ci}
3805a46c0ec8Sopenharmony_ci
3806a46c0ec8Sopenharmony_cistatic void
3807a46c0ec8Sopenharmony_citp_suspend_conditional(struct tp_dispatch *tp,
3808a46c0ec8Sopenharmony_ci		       struct evdev_device *device)
3809a46c0ec8Sopenharmony_ci{
3810a46c0ec8Sopenharmony_ci	struct libinput_device *dev;
3811a46c0ec8Sopenharmony_ci
3812a46c0ec8Sopenharmony_ci	list_for_each(dev, &device->base.seat->devices_list, link) {
3813a46c0ec8Sopenharmony_ci		struct evdev_device *d = evdev_device(dev);
3814a46c0ec8Sopenharmony_ci		if (d->tags & EVDEV_TAG_EXTERNAL_MOUSE) {
3815a46c0ec8Sopenharmony_ci			tp_suspend(tp, device, SUSPEND_EXTERNAL_MOUSE);
3816a46c0ec8Sopenharmony_ci			break;
3817a46c0ec8Sopenharmony_ci		}
3818a46c0ec8Sopenharmony_ci	}
3819a46c0ec8Sopenharmony_ci}
3820a46c0ec8Sopenharmony_ci
3821a46c0ec8Sopenharmony_cistatic enum libinput_config_status
3822a46c0ec8Sopenharmony_citp_sendevents_set_mode(struct libinput_device *device,
3823a46c0ec8Sopenharmony_ci		       enum libinput_config_send_events_mode mode)
3824a46c0ec8Sopenharmony_ci{
3825a46c0ec8Sopenharmony_ci	struct evdev_device *evdev = evdev_device(device);
3826a46c0ec8Sopenharmony_ci	struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
3827a46c0ec8Sopenharmony_ci
3828a46c0ec8Sopenharmony_ci	/* DISABLED overrides any DISABLED_ON_ */
3829a46c0ec8Sopenharmony_ci	if ((mode & LIBINPUT_CONFIG_SEND_EVENTS_DISABLED) &&
3830a46c0ec8Sopenharmony_ci	    (mode & LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE))
3831a46c0ec8Sopenharmony_ci	    mode &= ~LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
3832a46c0ec8Sopenharmony_ci
3833a46c0ec8Sopenharmony_ci	if (mode == tp->sendevents.current_mode)
3834a46c0ec8Sopenharmony_ci		return LIBINPUT_CONFIG_STATUS_SUCCESS;
3835a46c0ec8Sopenharmony_ci
3836a46c0ec8Sopenharmony_ci	switch(mode) {
3837a46c0ec8Sopenharmony_ci	case LIBINPUT_CONFIG_SEND_EVENTS_ENABLED:
3838a46c0ec8Sopenharmony_ci		tp_resume(tp, evdev, SUSPEND_SENDEVENTS);
3839a46c0ec8Sopenharmony_ci		tp_resume(tp, evdev, SUSPEND_EXTERNAL_MOUSE);
3840a46c0ec8Sopenharmony_ci		break;
3841a46c0ec8Sopenharmony_ci	case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED:
3842a46c0ec8Sopenharmony_ci		tp_suspend(tp, evdev, SUSPEND_SENDEVENTS);
3843a46c0ec8Sopenharmony_ci		tp_resume(tp, evdev, SUSPEND_EXTERNAL_MOUSE);
3844a46c0ec8Sopenharmony_ci		break;
3845a46c0ec8Sopenharmony_ci	case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE:
3846a46c0ec8Sopenharmony_ci		tp_suspend_conditional(tp, evdev);
3847a46c0ec8Sopenharmony_ci		tp_resume(tp, evdev, SUSPEND_SENDEVENTS);
3848a46c0ec8Sopenharmony_ci		break;
3849a46c0ec8Sopenharmony_ci	default:
3850a46c0ec8Sopenharmony_ci		return LIBINPUT_CONFIG_STATUS_UNSUPPORTED;
3851a46c0ec8Sopenharmony_ci	}
3852a46c0ec8Sopenharmony_ci
3853a46c0ec8Sopenharmony_ci	tp->sendevents.current_mode = mode;
3854a46c0ec8Sopenharmony_ci
3855a46c0ec8Sopenharmony_ci	return LIBINPUT_CONFIG_STATUS_SUCCESS;
3856a46c0ec8Sopenharmony_ci}
3857a46c0ec8Sopenharmony_ci
3858a46c0ec8Sopenharmony_cistatic enum libinput_config_send_events_mode
3859a46c0ec8Sopenharmony_citp_sendevents_get_mode(struct libinput_device *device)
3860a46c0ec8Sopenharmony_ci{
3861a46c0ec8Sopenharmony_ci	struct evdev_device *evdev = evdev_device(device);
3862a46c0ec8Sopenharmony_ci	struct tp_dispatch *dispatch = (struct tp_dispatch*)evdev->dispatch;
3863a46c0ec8Sopenharmony_ci
3864a46c0ec8Sopenharmony_ci	return dispatch->sendevents.current_mode;
3865a46c0ec8Sopenharmony_ci}
3866a46c0ec8Sopenharmony_ci
3867a46c0ec8Sopenharmony_cistatic enum libinput_config_send_events_mode
3868a46c0ec8Sopenharmony_citp_sendevents_get_default_mode(struct libinput_device *device)
3869a46c0ec8Sopenharmony_ci{
3870a46c0ec8Sopenharmony_ci	return LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
3871a46c0ec8Sopenharmony_ci}
3872a46c0ec8Sopenharmony_ci
3873a46c0ec8Sopenharmony_cistatic void
3874a46c0ec8Sopenharmony_citp_change_to_left_handed(struct evdev_device *device)
3875a46c0ec8Sopenharmony_ci{
3876a46c0ec8Sopenharmony_ci	struct tp_dispatch *tp = (struct tp_dispatch *)device->dispatch;
3877a46c0ec8Sopenharmony_ci
3878a46c0ec8Sopenharmony_ci	if (device->left_handed.want_enabled == device->left_handed.enabled)
3879a46c0ec8Sopenharmony_ci		return;
3880a46c0ec8Sopenharmony_ci
3881a46c0ec8Sopenharmony_ci	if (tp->buttons.state & 0x3) /* BTN_LEFT|BTN_RIGHT */
3882a46c0ec8Sopenharmony_ci		return;
3883a46c0ec8Sopenharmony_ci
3884a46c0ec8Sopenharmony_ci	/* tapping and clickfinger aren't affected by left-handed config,
3885a46c0ec8Sopenharmony_ci	 * so checking physical buttons is enough */
3886a46c0ec8Sopenharmony_ci
3887a46c0ec8Sopenharmony_ci	device->left_handed.enabled = device->left_handed.want_enabled;
3888a46c0ec8Sopenharmony_ci	tp_change_rotation(device, DO_NOTIFY);
3889a46c0ec8Sopenharmony_ci}
3890a46c0ec8Sopenharmony_ci
3891a46c0ec8Sopenharmony_cistatic bool
3892a46c0ec8Sopenharmony_citp_requires_rotation(struct tp_dispatch *tp, struct evdev_device *device)
3893a46c0ec8Sopenharmony_ci{
3894a46c0ec8Sopenharmony_ci	bool rotate = false;
3895a46c0ec8Sopenharmony_ci#if HAVE_LIBWACOM
3896a46c0ec8Sopenharmony_ci	struct libinput *li = tp_libinput_context(tp);
3897a46c0ec8Sopenharmony_ci	WacomDeviceDatabase *db = NULL;
3898a46c0ec8Sopenharmony_ci	WacomDevice **devices = NULL,
3899a46c0ec8Sopenharmony_ci		    **d;
3900a46c0ec8Sopenharmony_ci	WacomDevice *dev;
3901a46c0ec8Sopenharmony_ci	uint32_t vid = evdev_device_get_id_vendor(device),
3902a46c0ec8Sopenharmony_ci		 pid = evdev_device_get_id_product(device);
3903a46c0ec8Sopenharmony_ci
3904a46c0ec8Sopenharmony_ci	if ((device->tags & EVDEV_TAG_TABLET_TOUCHPAD) == 0)
3905a46c0ec8Sopenharmony_ci		goto out;
3906a46c0ec8Sopenharmony_ci
3907a46c0ec8Sopenharmony_ci	db = libinput_libwacom_ref(li);
3908a46c0ec8Sopenharmony_ci	if (!db)
3909a46c0ec8Sopenharmony_ci		goto out;
3910a46c0ec8Sopenharmony_ci
3911a46c0ec8Sopenharmony_ci	/* Check if we have a device with the same vid/pid. If not,
3912a46c0ec8Sopenharmony_ci	   we need to loop through all devices and check their paired
3913a46c0ec8Sopenharmony_ci	   device. */
3914a46c0ec8Sopenharmony_ci	dev = libwacom_new_from_usbid(db, vid, pid, NULL);
3915a46c0ec8Sopenharmony_ci	if (dev) {
3916a46c0ec8Sopenharmony_ci		rotate = libwacom_is_reversible(dev);
3917a46c0ec8Sopenharmony_ci		libwacom_destroy(dev);
3918a46c0ec8Sopenharmony_ci		goto out;
3919a46c0ec8Sopenharmony_ci	}
3920a46c0ec8Sopenharmony_ci
3921a46c0ec8Sopenharmony_ci	devices = libwacom_list_devices_from_database(db, NULL);
3922a46c0ec8Sopenharmony_ci	if (!devices)
3923a46c0ec8Sopenharmony_ci		goto out;
3924a46c0ec8Sopenharmony_ci	d = devices;
3925a46c0ec8Sopenharmony_ci	while(*d) {
3926a46c0ec8Sopenharmony_ci		const WacomMatch *paired;
3927a46c0ec8Sopenharmony_ci
3928a46c0ec8Sopenharmony_ci		paired = libwacom_get_paired_device(*d);
3929a46c0ec8Sopenharmony_ci		if (paired &&
3930a46c0ec8Sopenharmony_ci		    libwacom_match_get_vendor_id(paired) == vid &&
3931a46c0ec8Sopenharmony_ci		    libwacom_match_get_product_id(paired) == pid) {
3932a46c0ec8Sopenharmony_ci			rotate = libwacom_is_reversible(dev);
3933a46c0ec8Sopenharmony_ci			break;
3934a46c0ec8Sopenharmony_ci		}
3935a46c0ec8Sopenharmony_ci		d++;
3936a46c0ec8Sopenharmony_ci	}
3937a46c0ec8Sopenharmony_ci
3938a46c0ec8Sopenharmony_ci	free(devices);
3939a46c0ec8Sopenharmony_ci
3940a46c0ec8Sopenharmony_ciout:
3941a46c0ec8Sopenharmony_ci	/* We don't need to keep it around for the touchpad, we're done with
3942a46c0ec8Sopenharmony_ci	 * it until the device dies. */
3943a46c0ec8Sopenharmony_ci	if (db)
3944a46c0ec8Sopenharmony_ci		libinput_libwacom_unref(li);
3945a46c0ec8Sopenharmony_ci#endif
3946a46c0ec8Sopenharmony_ci
3947a46c0ec8Sopenharmony_ci	return rotate;
3948a46c0ec8Sopenharmony_ci}
3949a46c0ec8Sopenharmony_ci
3950a46c0ec8Sopenharmony_cistatic void
3951a46c0ec8Sopenharmony_citp_init_left_handed(struct tp_dispatch *tp,
3952a46c0ec8Sopenharmony_ci		    struct evdev_device *device)
3953a46c0ec8Sopenharmony_ci{
3954a46c0ec8Sopenharmony_ci	bool want_left_handed = true;
3955a46c0ec8Sopenharmony_ci
3956a46c0ec8Sopenharmony_ci	tp->left_handed.must_rotate = tp_requires_rotation(tp, device);
3957a46c0ec8Sopenharmony_ci
3958a46c0ec8Sopenharmony_ci	if (device->model_flags & EVDEV_MODEL_APPLE_TOUCHPAD_ONEBUTTON)
3959a46c0ec8Sopenharmony_ci		want_left_handed = false;
3960a46c0ec8Sopenharmony_ci	if (want_left_handed)
3961a46c0ec8Sopenharmony_ci		evdev_init_left_handed(device, tp_change_to_left_handed);
3962a46c0ec8Sopenharmony_ci
3963a46c0ec8Sopenharmony_ci}
3964a46c0ec8Sopenharmony_ci
3965a46c0ec8Sopenharmony_cistruct evdev_dispatch *
3966a46c0ec8Sopenharmony_cievdev_mt_touchpad_create(struct evdev_device *device)
3967a46c0ec8Sopenharmony_ci{
3968a46c0ec8Sopenharmony_ci	struct tp_dispatch *tp;
3969a46c0ec8Sopenharmony_ci
3970a46c0ec8Sopenharmony_ci	evdev_tag_touchpad(device, device->udev_device);
3971a46c0ec8Sopenharmony_ci
3972a46c0ec8Sopenharmony_ci	tp = zalloc(sizeof *tp);
3973a46c0ec8Sopenharmony_ci
3974a46c0ec8Sopenharmony_ci	if (!tp_init(tp, device)) {
3975a46c0ec8Sopenharmony_ci		tp_interface_destroy(&tp->base);
3976a46c0ec8Sopenharmony_ci		return NULL;
3977a46c0ec8Sopenharmony_ci	}
3978a46c0ec8Sopenharmony_ci
3979a46c0ec8Sopenharmony_ci	device->base.config.sendevents = &tp->sendevents.config;
3980a46c0ec8Sopenharmony_ci
3981a46c0ec8Sopenharmony_ci	tp->sendevents.current_mode = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
3982a46c0ec8Sopenharmony_ci	tp->sendevents.config.get_modes = tp_sendevents_get_modes;
3983a46c0ec8Sopenharmony_ci	tp->sendevents.config.set_mode = tp_sendevents_set_mode;
3984a46c0ec8Sopenharmony_ci	tp->sendevents.config.get_mode = tp_sendevents_get_mode;
3985a46c0ec8Sopenharmony_ci	tp->sendevents.config.get_default_mode = tp_sendevents_get_default_mode;
3986a46c0ec8Sopenharmony_ci
3987a46c0ec8Sopenharmony_ci	tp_init_left_handed(tp, device);
3988a46c0ec8Sopenharmony_ci
3989a46c0ec8Sopenharmony_ci	return &tp->base;
3990a46c0ec8Sopenharmony_ci}
3991