1a46c0ec8Sopenharmony_ci/*
2a46c0ec8Sopenharmony_ci * Copyright © 2014 Red Hat, Inc.
3a46c0ec8Sopenharmony_ci * Copyright © 2014 Lyude Paul
4a46c0ec8Sopenharmony_ci *
5a46c0ec8Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
6a46c0ec8Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
7a46c0ec8Sopenharmony_ci * to deal in the Software without restriction, including without limitation
8a46c0ec8Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9a46c0ec8Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
10a46c0ec8Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
11a46c0ec8Sopenharmony_ci *
12a46c0ec8Sopenharmony_ci * The above copyright notice and this permission notice (including the next
13a46c0ec8Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
14a46c0ec8Sopenharmony_ci * Software.
15a46c0ec8Sopenharmony_ci *
16a46c0ec8Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17a46c0ec8Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18a46c0ec8Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19a46c0ec8Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20a46c0ec8Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21a46c0ec8Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22a46c0ec8Sopenharmony_ci * DEALINGS IN THE SOFTWARE.
23a46c0ec8Sopenharmony_ci */
24a46c0ec8Sopenharmony_ci#include "config.h"
25a46c0ec8Sopenharmony_ci#include "evdev-tablet.h"
26a46c0ec8Sopenharmony_ci#include "util-input-event.h"
27a46c0ec8Sopenharmony_ci
28a46c0ec8Sopenharmony_ci#include <assert.h>
29a46c0ec8Sopenharmony_ci#include <stdbool.h>
30a46c0ec8Sopenharmony_ci#include <string.h>
31a46c0ec8Sopenharmony_ci
32a46c0ec8Sopenharmony_ci#if HAVE_LIBWACOM
33a46c0ec8Sopenharmony_ci#include <libwacom/libwacom.h>
34a46c0ec8Sopenharmony_ci#endif
35a46c0ec8Sopenharmony_ci
36a46c0ec8Sopenharmony_cienum notify {
37a46c0ec8Sopenharmony_ci	DONT_NOTIFY,
38a46c0ec8Sopenharmony_ci	DO_NOTIFY,
39a46c0ec8Sopenharmony_ci};
40a46c0ec8Sopenharmony_ci
41a46c0ec8Sopenharmony_ci/* The tablet sends events every ~2ms , 50ms should be plenty enough to
42a46c0ec8Sopenharmony_ci   detect out-of-range.
43a46c0ec8Sopenharmony_ci   This value is higher during test suite runs */
44a46c0ec8Sopenharmony_cistatic int FORCED_PROXOUT_TIMEOUT = 50 * 1000; /* µs */
45a46c0ec8Sopenharmony_ci
46a46c0ec8Sopenharmony_ci#define tablet_set_status(tablet_,s_) (tablet_)->status |= (s_)
47a46c0ec8Sopenharmony_ci#define tablet_unset_status(tablet_,s_) (tablet_)->status &= ~(s_)
48a46c0ec8Sopenharmony_ci#define tablet_has_status(tablet_,s_) (!!((tablet_)->status & (s_)))
49a46c0ec8Sopenharmony_ci
50a46c0ec8Sopenharmony_cistatic inline void
51a46c0ec8Sopenharmony_citablet_get_pressed_buttons(struct tablet_dispatch *tablet,
52a46c0ec8Sopenharmony_ci			   struct button_state *buttons)
53a46c0ec8Sopenharmony_ci{
54a46c0ec8Sopenharmony_ci	size_t i;
55a46c0ec8Sopenharmony_ci	const struct button_state *state = &tablet->button_state,
56a46c0ec8Sopenharmony_ci			          *prev_state = &tablet->prev_button_state;
57a46c0ec8Sopenharmony_ci
58a46c0ec8Sopenharmony_ci	for (i = 0; i < sizeof(buttons->bits); i++)
59a46c0ec8Sopenharmony_ci		buttons->bits[i] = state->bits[i] & ~(prev_state->bits[i]);
60a46c0ec8Sopenharmony_ci}
61a46c0ec8Sopenharmony_ci
62a46c0ec8Sopenharmony_cistatic inline void
63a46c0ec8Sopenharmony_citablet_get_released_buttons(struct tablet_dispatch *tablet,
64a46c0ec8Sopenharmony_ci			    struct button_state *buttons)
65a46c0ec8Sopenharmony_ci{
66a46c0ec8Sopenharmony_ci	size_t i;
67a46c0ec8Sopenharmony_ci	const struct button_state *state = &tablet->button_state,
68a46c0ec8Sopenharmony_ci			          *prev_state = &tablet->prev_button_state;
69a46c0ec8Sopenharmony_ci
70a46c0ec8Sopenharmony_ci	for (i = 0; i < sizeof(buttons->bits); i++)
71a46c0ec8Sopenharmony_ci		buttons->bits[i] = prev_state->bits[i] &
72a46c0ec8Sopenharmony_ci					~(state->bits[i]);
73a46c0ec8Sopenharmony_ci}
74a46c0ec8Sopenharmony_ci
75a46c0ec8Sopenharmony_ci/* Merge the previous state with the current one so all buttons look like
76a46c0ec8Sopenharmony_ci * they just got pressed in this frame */
77a46c0ec8Sopenharmony_cistatic inline void
78a46c0ec8Sopenharmony_citablet_force_button_presses(struct tablet_dispatch *tablet)
79a46c0ec8Sopenharmony_ci{
80a46c0ec8Sopenharmony_ci	struct button_state *state = &tablet->button_state,
81a46c0ec8Sopenharmony_ci			    *prev_state = &tablet->prev_button_state;
82a46c0ec8Sopenharmony_ci	size_t i;
83a46c0ec8Sopenharmony_ci
84a46c0ec8Sopenharmony_ci	for (i = 0; i < sizeof(state->bits); i++) {
85a46c0ec8Sopenharmony_ci		state->bits[i] = state->bits[i] | prev_state->bits[i];
86a46c0ec8Sopenharmony_ci		prev_state->bits[i] = 0;
87a46c0ec8Sopenharmony_ci	}
88a46c0ec8Sopenharmony_ci}
89a46c0ec8Sopenharmony_ci
90a46c0ec8Sopenharmony_cistatic inline size_t
91a46c0ec8Sopenharmony_citablet_history_size(const struct tablet_dispatch *tablet)
92a46c0ec8Sopenharmony_ci{
93a46c0ec8Sopenharmony_ci	return tablet->history.size;
94a46c0ec8Sopenharmony_ci}
95a46c0ec8Sopenharmony_ci
96a46c0ec8Sopenharmony_cistatic inline void
97a46c0ec8Sopenharmony_citablet_history_reset(struct tablet_dispatch *tablet)
98a46c0ec8Sopenharmony_ci{
99a46c0ec8Sopenharmony_ci	tablet->history.count = 0;
100a46c0ec8Sopenharmony_ci}
101a46c0ec8Sopenharmony_ci
102a46c0ec8Sopenharmony_cistatic inline void
103a46c0ec8Sopenharmony_citablet_history_push(struct tablet_dispatch *tablet,
104a46c0ec8Sopenharmony_ci		    const struct tablet_axes *axes)
105a46c0ec8Sopenharmony_ci{
106a46c0ec8Sopenharmony_ci	unsigned int index = (tablet->history.index + 1) %
107a46c0ec8Sopenharmony_ci				tablet_history_size(tablet);
108a46c0ec8Sopenharmony_ci
109a46c0ec8Sopenharmony_ci	tablet->history.samples[index] = *axes;
110a46c0ec8Sopenharmony_ci	tablet->history.index = index;
111a46c0ec8Sopenharmony_ci	tablet->history.count = min(tablet->history.count + 1,
112a46c0ec8Sopenharmony_ci				    tablet_history_size(tablet));
113a46c0ec8Sopenharmony_ci
114a46c0ec8Sopenharmony_ci	if (tablet->history.count < tablet_history_size(tablet))
115a46c0ec8Sopenharmony_ci		tablet_history_push(tablet, axes);
116a46c0ec8Sopenharmony_ci}
117a46c0ec8Sopenharmony_ci
118a46c0ec8Sopenharmony_ci/**
119a46c0ec8Sopenharmony_ci * Return a previous axis state, where index of 0 means "most recent", 1 is
120a46c0ec8Sopenharmony_ci * "one before most recent", etc.
121a46c0ec8Sopenharmony_ci */
122a46c0ec8Sopenharmony_cistatic inline const struct tablet_axes*
123a46c0ec8Sopenharmony_citablet_history_get(const struct tablet_dispatch *tablet, unsigned int index)
124a46c0ec8Sopenharmony_ci{
125a46c0ec8Sopenharmony_ci	size_t sz = tablet_history_size(tablet);
126a46c0ec8Sopenharmony_ci
127a46c0ec8Sopenharmony_ci	assert(index < sz);
128a46c0ec8Sopenharmony_ci	assert(index < tablet->history.count);
129a46c0ec8Sopenharmony_ci
130a46c0ec8Sopenharmony_ci	index = (tablet->history.index + sz - index) % sz;
131a46c0ec8Sopenharmony_ci	return &tablet->history.samples[index];
132a46c0ec8Sopenharmony_ci}
133a46c0ec8Sopenharmony_ci
134a46c0ec8Sopenharmony_cistatic inline void
135a46c0ec8Sopenharmony_citablet_reset_changed_axes(struct tablet_dispatch *tablet)
136a46c0ec8Sopenharmony_ci{
137a46c0ec8Sopenharmony_ci	memset(tablet->changed_axes, 0, sizeof(tablet->changed_axes));
138a46c0ec8Sopenharmony_ci}
139a46c0ec8Sopenharmony_ci
140a46c0ec8Sopenharmony_cistatic bool
141a46c0ec8Sopenharmony_citablet_device_has_axis(struct tablet_dispatch *tablet,
142a46c0ec8Sopenharmony_ci		       enum libinput_tablet_tool_axis axis)
143a46c0ec8Sopenharmony_ci{
144a46c0ec8Sopenharmony_ci	struct libevdev *evdev = tablet->device->evdev;
145a46c0ec8Sopenharmony_ci	bool has_axis = false;
146a46c0ec8Sopenharmony_ci	unsigned int code;
147a46c0ec8Sopenharmony_ci
148a46c0ec8Sopenharmony_ci	if (axis == LIBINPUT_TABLET_TOOL_AXIS_ROTATION_Z) {
149a46c0ec8Sopenharmony_ci		has_axis = (libevdev_has_event_code(evdev,
150a46c0ec8Sopenharmony_ci						    EV_KEY,
151a46c0ec8Sopenharmony_ci						    BTN_TOOL_MOUSE) &&
152a46c0ec8Sopenharmony_ci			    libevdev_has_event_code(evdev,
153a46c0ec8Sopenharmony_ci						    EV_ABS,
154a46c0ec8Sopenharmony_ci						    ABS_TILT_X) &&
155a46c0ec8Sopenharmony_ci			    libevdev_has_event_code(evdev,
156a46c0ec8Sopenharmony_ci						    EV_ABS,
157a46c0ec8Sopenharmony_ci						    ABS_TILT_Y));
158a46c0ec8Sopenharmony_ci		code = axis_to_evcode(axis);
159a46c0ec8Sopenharmony_ci		has_axis |= libevdev_has_event_code(evdev,
160a46c0ec8Sopenharmony_ci						    EV_ABS,
161a46c0ec8Sopenharmony_ci						    code);
162a46c0ec8Sopenharmony_ci	} else if (axis == LIBINPUT_TABLET_TOOL_AXIS_REL_WHEEL) {
163a46c0ec8Sopenharmony_ci		has_axis = libevdev_has_event_code(evdev,
164a46c0ec8Sopenharmony_ci						   EV_REL,
165a46c0ec8Sopenharmony_ci						   REL_WHEEL);
166a46c0ec8Sopenharmony_ci	} else {
167a46c0ec8Sopenharmony_ci		code = axis_to_evcode(axis);
168a46c0ec8Sopenharmony_ci		has_axis = libevdev_has_event_code(evdev,
169a46c0ec8Sopenharmony_ci						   EV_ABS,
170a46c0ec8Sopenharmony_ci						   code);
171a46c0ec8Sopenharmony_ci	}
172a46c0ec8Sopenharmony_ci
173a46c0ec8Sopenharmony_ci	return has_axis;
174a46c0ec8Sopenharmony_ci}
175a46c0ec8Sopenharmony_ci
176a46c0ec8Sopenharmony_cistatic inline bool
177a46c0ec8Sopenharmony_citablet_filter_axis_fuzz(const struct tablet_dispatch *tablet,
178a46c0ec8Sopenharmony_ci			const struct evdev_device *device,
179a46c0ec8Sopenharmony_ci			const struct input_event *e,
180a46c0ec8Sopenharmony_ci			enum libinput_tablet_tool_axis axis)
181a46c0ec8Sopenharmony_ci{
182a46c0ec8Sopenharmony_ci	int delta, fuzz;
183a46c0ec8Sopenharmony_ci	int current, previous;
184a46c0ec8Sopenharmony_ci
185a46c0ec8Sopenharmony_ci	previous = tablet->prev_value[axis];
186a46c0ec8Sopenharmony_ci	current = e->value;
187a46c0ec8Sopenharmony_ci	delta = previous - current;
188a46c0ec8Sopenharmony_ci
189a46c0ec8Sopenharmony_ci	fuzz = libevdev_get_abs_fuzz(device->evdev, e->code);
190a46c0ec8Sopenharmony_ci
191a46c0ec8Sopenharmony_ci	/* ABS_DISTANCE doesn't have have fuzz set and causes continuous
192a46c0ec8Sopenharmony_ci	 * updates for the cursor/lens tools. Add a minimum fuzz of 2, same
193a46c0ec8Sopenharmony_ci	 * as the xf86-input-wacom driver
194a46c0ec8Sopenharmony_ci	 */
195a46c0ec8Sopenharmony_ci	switch (e->code) {
196a46c0ec8Sopenharmony_ci	case ABS_DISTANCE:
197a46c0ec8Sopenharmony_ci		fuzz = max(2, fuzz);
198a46c0ec8Sopenharmony_ci		break;
199a46c0ec8Sopenharmony_ci	default:
200a46c0ec8Sopenharmony_ci		break;
201a46c0ec8Sopenharmony_ci	}
202a46c0ec8Sopenharmony_ci
203a46c0ec8Sopenharmony_ci	return abs(delta) <= fuzz;
204a46c0ec8Sopenharmony_ci}
205a46c0ec8Sopenharmony_ci
206a46c0ec8Sopenharmony_cistatic void
207a46c0ec8Sopenharmony_citablet_process_absolute(struct tablet_dispatch *tablet,
208a46c0ec8Sopenharmony_ci			struct evdev_device *device,
209a46c0ec8Sopenharmony_ci			struct input_event *e,
210a46c0ec8Sopenharmony_ci			uint64_t time)
211a46c0ec8Sopenharmony_ci{
212a46c0ec8Sopenharmony_ci	enum libinput_tablet_tool_axis axis;
213a46c0ec8Sopenharmony_ci
214a46c0ec8Sopenharmony_ci	switch (e->code) {
215a46c0ec8Sopenharmony_ci	case ABS_X:
216a46c0ec8Sopenharmony_ci	case ABS_Y:
217a46c0ec8Sopenharmony_ci	case ABS_Z:
218a46c0ec8Sopenharmony_ci	case ABS_PRESSURE:
219a46c0ec8Sopenharmony_ci	case ABS_TILT_X:
220a46c0ec8Sopenharmony_ci	case ABS_TILT_Y:
221a46c0ec8Sopenharmony_ci	case ABS_DISTANCE:
222a46c0ec8Sopenharmony_ci	case ABS_WHEEL:
223a46c0ec8Sopenharmony_ci		axis = evcode_to_axis(e->code);
224a46c0ec8Sopenharmony_ci		if (axis == LIBINPUT_TABLET_TOOL_AXIS_NONE) {
225a46c0ec8Sopenharmony_ci			evdev_log_bug_libinput(device,
226a46c0ec8Sopenharmony_ci					       "Invalid ABS event code %#x\n",
227a46c0ec8Sopenharmony_ci					       e->code);
228a46c0ec8Sopenharmony_ci			break;
229a46c0ec8Sopenharmony_ci		}
230a46c0ec8Sopenharmony_ci
231a46c0ec8Sopenharmony_ci		tablet->prev_value[axis] = tablet->current_value[axis];
232a46c0ec8Sopenharmony_ci		if (tablet_filter_axis_fuzz(tablet, device, e, axis))
233a46c0ec8Sopenharmony_ci			break;
234a46c0ec8Sopenharmony_ci
235a46c0ec8Sopenharmony_ci		tablet->current_value[axis] = e->value;
236a46c0ec8Sopenharmony_ci		set_bit(tablet->changed_axes, axis);
237a46c0ec8Sopenharmony_ci		tablet_set_status(tablet, TABLET_AXES_UPDATED);
238a46c0ec8Sopenharmony_ci		break;
239a46c0ec8Sopenharmony_ci	/* tool_id is the identifier for the tool we can use in libwacom
240a46c0ec8Sopenharmony_ci	 * to identify it (if we have one anyway) */
241a46c0ec8Sopenharmony_ci	case ABS_MISC:
242a46c0ec8Sopenharmony_ci		tablet->current_tool.id = e->value;
243a46c0ec8Sopenharmony_ci		break;
244a46c0ec8Sopenharmony_ci	/* Intuos 3 strip data. Should only happen on the Pad device, not on
245a46c0ec8Sopenharmony_ci	   the Pen device. */
246a46c0ec8Sopenharmony_ci	case ABS_RX:
247a46c0ec8Sopenharmony_ci	case ABS_RY:
248a46c0ec8Sopenharmony_ci	/* Only on the 4D mouse (Intuos2), obsolete */
249a46c0ec8Sopenharmony_ci	case ABS_RZ:
250a46c0ec8Sopenharmony_ci	/* Only on the 4D mouse (Intuos2), obsolete.
251a46c0ec8Sopenharmony_ci	   The 24HD sends ABS_THROTTLE on the Pad device for the second
252a46c0ec8Sopenharmony_ci	   wheel but we shouldn't get here on kernel >= 3.17.
253a46c0ec8Sopenharmony_ci	   */
254a46c0ec8Sopenharmony_ci	case ABS_THROTTLE:
255a46c0ec8Sopenharmony_ci	default:
256a46c0ec8Sopenharmony_ci		evdev_log_info(device,
257a46c0ec8Sopenharmony_ci			       "Unhandled ABS event code %#x\n",
258a46c0ec8Sopenharmony_ci			       e->code);
259a46c0ec8Sopenharmony_ci		break;
260a46c0ec8Sopenharmony_ci	}
261a46c0ec8Sopenharmony_ci}
262a46c0ec8Sopenharmony_ci
263a46c0ec8Sopenharmony_cistatic void
264a46c0ec8Sopenharmony_citablet_apply_rotation(struct evdev_device *device)
265a46c0ec8Sopenharmony_ci{
266a46c0ec8Sopenharmony_ci	struct tablet_dispatch *tablet = tablet_dispatch(device->dispatch);
267a46c0ec8Sopenharmony_ci
268a46c0ec8Sopenharmony_ci	if (tablet->rotation.rotate == tablet->rotation.want_rotate)
269a46c0ec8Sopenharmony_ci		return;
270a46c0ec8Sopenharmony_ci
271a46c0ec8Sopenharmony_ci	if (!tablet_has_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY))
272a46c0ec8Sopenharmony_ci		return;
273a46c0ec8Sopenharmony_ci
274a46c0ec8Sopenharmony_ci	tablet->rotation.rotate = tablet->rotation.want_rotate;
275a46c0ec8Sopenharmony_ci
276a46c0ec8Sopenharmony_ci	evdev_log_debug(device,
277a46c0ec8Sopenharmony_ci			"tablet-rotation: rotation is %s\n",
278a46c0ec8Sopenharmony_ci			tablet->rotation.rotate ? "on" : "off");
279a46c0ec8Sopenharmony_ci}
280a46c0ec8Sopenharmony_ci
281a46c0ec8Sopenharmony_cistatic void
282a46c0ec8Sopenharmony_citablet_change_rotation(struct evdev_device *device, enum notify notify)
283a46c0ec8Sopenharmony_ci{
284a46c0ec8Sopenharmony_ci	struct tablet_dispatch *tablet = tablet_dispatch(device->dispatch);
285a46c0ec8Sopenharmony_ci	struct evdev_device *touch_device = tablet->touch_device;
286a46c0ec8Sopenharmony_ci	struct evdev_dispatch *dispatch;
287a46c0ec8Sopenharmony_ci	bool tablet_is_left, touchpad_is_left;
288a46c0ec8Sopenharmony_ci
289a46c0ec8Sopenharmony_ci	tablet_is_left = tablet->device->left_handed.enabled;
290a46c0ec8Sopenharmony_ci	touchpad_is_left = tablet->rotation.touch_device_left_handed_state;
291a46c0ec8Sopenharmony_ci
292a46c0ec8Sopenharmony_ci	tablet->rotation.want_rotate = tablet_is_left || touchpad_is_left;
293a46c0ec8Sopenharmony_ci	tablet_apply_rotation(device);
294a46c0ec8Sopenharmony_ci
295a46c0ec8Sopenharmony_ci	if (notify == DO_NOTIFY && touch_device) {
296a46c0ec8Sopenharmony_ci		bool enable = device->left_handed.want_enabled;
297a46c0ec8Sopenharmony_ci
298a46c0ec8Sopenharmony_ci		dispatch = touch_device->dispatch;
299a46c0ec8Sopenharmony_ci		if (dispatch->interface->left_handed_toggle)
300a46c0ec8Sopenharmony_ci			dispatch->interface->left_handed_toggle(dispatch,
301a46c0ec8Sopenharmony_ci								touch_device,
302a46c0ec8Sopenharmony_ci								enable);
303a46c0ec8Sopenharmony_ci	}
304a46c0ec8Sopenharmony_ci}
305a46c0ec8Sopenharmony_ci
306a46c0ec8Sopenharmony_cistatic void
307a46c0ec8Sopenharmony_citablet_change_to_left_handed(struct evdev_device *device)
308a46c0ec8Sopenharmony_ci{
309a46c0ec8Sopenharmony_ci	if (device->left_handed.enabled == device->left_handed.want_enabled)
310a46c0ec8Sopenharmony_ci		return;
311a46c0ec8Sopenharmony_ci
312a46c0ec8Sopenharmony_ci	device->left_handed.enabled = device->left_handed.want_enabled;
313a46c0ec8Sopenharmony_ci
314a46c0ec8Sopenharmony_ci	tablet_change_rotation(device, DO_NOTIFY);
315a46c0ec8Sopenharmony_ci}
316a46c0ec8Sopenharmony_ci
317a46c0ec8Sopenharmony_cistatic void
318a46c0ec8Sopenharmony_citablet_update_tool(struct tablet_dispatch *tablet,
319a46c0ec8Sopenharmony_ci		   struct evdev_device *device,
320a46c0ec8Sopenharmony_ci		   enum libinput_tablet_tool_type tool,
321a46c0ec8Sopenharmony_ci		   bool enabled)
322a46c0ec8Sopenharmony_ci{
323a46c0ec8Sopenharmony_ci	assert(tool != LIBINPUT_TOOL_NONE);
324a46c0ec8Sopenharmony_ci
325a46c0ec8Sopenharmony_ci	if (enabled) {
326a46c0ec8Sopenharmony_ci		tablet->current_tool.type = tool;
327a46c0ec8Sopenharmony_ci		tablet_set_status(tablet, TABLET_TOOL_ENTERING_PROXIMITY);
328a46c0ec8Sopenharmony_ci		tablet_unset_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY);
329a46c0ec8Sopenharmony_ci	}
330a46c0ec8Sopenharmony_ci	else if (!tablet_has_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY)) {
331a46c0ec8Sopenharmony_ci		tablet_set_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY);
332a46c0ec8Sopenharmony_ci	}
333a46c0ec8Sopenharmony_ci}
334a46c0ec8Sopenharmony_ci
335a46c0ec8Sopenharmony_cistatic inline double
336a46c0ec8Sopenharmony_cinormalize_slider(const struct input_absinfo *absinfo)
337a46c0ec8Sopenharmony_ci{
338a46c0ec8Sopenharmony_ci	double value = (absinfo->value - absinfo->minimum) / absinfo_range(absinfo);
339a46c0ec8Sopenharmony_ci
340a46c0ec8Sopenharmony_ci	return value * 2 - 1;
341a46c0ec8Sopenharmony_ci}
342a46c0ec8Sopenharmony_ci
343a46c0ec8Sopenharmony_cistatic inline double
344a46c0ec8Sopenharmony_cinormalize_distance(const struct input_absinfo *absinfo)
345a46c0ec8Sopenharmony_ci{
346a46c0ec8Sopenharmony_ci	double value = (absinfo->value - absinfo->minimum) / absinfo_range(absinfo);
347a46c0ec8Sopenharmony_ci
348a46c0ec8Sopenharmony_ci	return value;
349a46c0ec8Sopenharmony_ci}
350a46c0ec8Sopenharmony_ci
351a46c0ec8Sopenharmony_cistatic inline double
352a46c0ec8Sopenharmony_cinormalize_pressure(const struct input_absinfo *absinfo,
353a46c0ec8Sopenharmony_ci		   struct libinput_tablet_tool *tool)
354a46c0ec8Sopenharmony_ci{
355a46c0ec8Sopenharmony_ci	/**
356a46c0ec8Sopenharmony_ci	 * Note: the upper threshold takes the offset into account so that
357a46c0ec8Sopenharmony_ci	 *            |- 4% -|
358a46c0ec8Sopenharmony_ci	 * min |------X------X-------------------------| max
359a46c0ec8Sopenharmony_ci	 *            |      |
360a46c0ec8Sopenharmony_ci	 *            |      + upper threshold / tip trigger
361a46c0ec8Sopenharmony_ci	 *            +- offset and lower threshold
362a46c0ec8Sopenharmony_ci	 *
363a46c0ec8Sopenharmony_ci	 * The axis is scaled into the range [lower, max] so that the lower
364a46c0ec8Sopenharmony_ci	 * threshold is 0 pressure.
365a46c0ec8Sopenharmony_ci	 */
366a46c0ec8Sopenharmony_ci	int base = tool->pressure.threshold.lower;
367a46c0ec8Sopenharmony_ci	double range = absinfo->maximum - base + 1;
368a46c0ec8Sopenharmony_ci	double value = (absinfo->value - base) / range;
369a46c0ec8Sopenharmony_ci
370a46c0ec8Sopenharmony_ci	return max(0.0, value);
371a46c0ec8Sopenharmony_ci}
372a46c0ec8Sopenharmony_ci
373a46c0ec8Sopenharmony_cistatic inline double
374a46c0ec8Sopenharmony_ciadjust_tilt(const struct input_absinfo *absinfo)
375a46c0ec8Sopenharmony_ci{
376a46c0ec8Sopenharmony_ci	double value = (absinfo->value - absinfo->minimum) / absinfo_range(absinfo);
377a46c0ec8Sopenharmony_ci	const int WACOM_MAX_DEGREES = 64;
378a46c0ec8Sopenharmony_ci
379a46c0ec8Sopenharmony_ci	/* If resolution is nonzero, it's in units/radian. But require
380a46c0ec8Sopenharmony_ci	 * a min/max less/greater than zero so we can assume 0 is the
381a46c0ec8Sopenharmony_ci	 * center */
382a46c0ec8Sopenharmony_ci	if (absinfo->resolution != 0 &&
383a46c0ec8Sopenharmony_ci	    absinfo->maximum > 0 &&
384a46c0ec8Sopenharmony_ci	    absinfo->minimum < 0) {
385a46c0ec8Sopenharmony_ci		value = 180.0/M_PI * absinfo->value/absinfo->resolution;
386a46c0ec8Sopenharmony_ci	} else {
387a46c0ec8Sopenharmony_ci		/* Wacom supports physical [-64, 64] degrees, so map to that by
388a46c0ec8Sopenharmony_ci		 * default. If other tablets have a different physical range or
389a46c0ec8Sopenharmony_ci		 * nonzero physical offsets, they need extra treatment
390a46c0ec8Sopenharmony_ci		 * here.
391a46c0ec8Sopenharmony_ci		 */
392a46c0ec8Sopenharmony_ci		/* Map to the (-1, 1) range */
393a46c0ec8Sopenharmony_ci		value = (value * 2) - 1;
394a46c0ec8Sopenharmony_ci		value *= WACOM_MAX_DEGREES;
395a46c0ec8Sopenharmony_ci	}
396a46c0ec8Sopenharmony_ci
397a46c0ec8Sopenharmony_ci	return value;
398a46c0ec8Sopenharmony_ci}
399a46c0ec8Sopenharmony_ci
400a46c0ec8Sopenharmony_cistatic inline int32_t
401a46c0ec8Sopenharmony_ciinvert_axis(const struct input_absinfo *absinfo)
402a46c0ec8Sopenharmony_ci{
403a46c0ec8Sopenharmony_ci	return absinfo->maximum - (absinfo->value - absinfo->minimum);
404a46c0ec8Sopenharmony_ci}
405a46c0ec8Sopenharmony_ci
406a46c0ec8Sopenharmony_cistatic void
407a46c0ec8Sopenharmony_ciconvert_tilt_to_rotation(struct tablet_dispatch *tablet)
408a46c0ec8Sopenharmony_ci{
409a46c0ec8Sopenharmony_ci	const int offset = 5;
410a46c0ec8Sopenharmony_ci	double x, y;
411a46c0ec8Sopenharmony_ci	double angle = 0.0;
412a46c0ec8Sopenharmony_ci
413a46c0ec8Sopenharmony_ci	/* Wacom Intuos 4, 5, Pro mouse calculates rotation from the x/y tilt
414a46c0ec8Sopenharmony_ci	   values. The device has a 175 degree CCW hardware offset but since we use
415a46c0ec8Sopenharmony_ci	   atan2 the effective offset is just 5 degrees.
416a46c0ec8Sopenharmony_ci	   */
417a46c0ec8Sopenharmony_ci	x = tablet->axes.tilt.x;
418a46c0ec8Sopenharmony_ci	y = tablet->axes.tilt.y;
419a46c0ec8Sopenharmony_ci
420a46c0ec8Sopenharmony_ci	/* atan2 is CCW, we want CW -> negate x */
421a46c0ec8Sopenharmony_ci	if (x || y)
422a46c0ec8Sopenharmony_ci		angle = ((180.0 * atan2(-x, y)) / M_PI);
423a46c0ec8Sopenharmony_ci
424a46c0ec8Sopenharmony_ci	angle = fmod(360 + angle - offset, 360);
425a46c0ec8Sopenharmony_ci
426a46c0ec8Sopenharmony_ci	tablet->axes.rotation = angle;
427a46c0ec8Sopenharmony_ci	set_bit(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_ROTATION_Z);
428a46c0ec8Sopenharmony_ci}
429a46c0ec8Sopenharmony_ci
430a46c0ec8Sopenharmony_cistatic double
431a46c0ec8Sopenharmony_ciconvert_to_degrees(const struct input_absinfo *absinfo, double offset)
432a46c0ec8Sopenharmony_ci{
433a46c0ec8Sopenharmony_ci	/* range is [0, 360[, i.e. range + 1 */
434a46c0ec8Sopenharmony_ci	double value = (absinfo->value - absinfo->minimum) / absinfo_range(absinfo);
435a46c0ec8Sopenharmony_ci
436a46c0ec8Sopenharmony_ci	return fmod(value * 360.0 + offset, 360.0);
437a46c0ec8Sopenharmony_ci}
438a46c0ec8Sopenharmony_ci
439a46c0ec8Sopenharmony_cistatic inline double
440a46c0ec8Sopenharmony_cinormalize_wheel(struct tablet_dispatch *tablet,
441a46c0ec8Sopenharmony_ci		int value)
442a46c0ec8Sopenharmony_ci{
443a46c0ec8Sopenharmony_ci	struct evdev_device *device = tablet->device;
444a46c0ec8Sopenharmony_ci
445a46c0ec8Sopenharmony_ci	return value * device->scroll.wheel_click_angle.x;
446a46c0ec8Sopenharmony_ci}
447a46c0ec8Sopenharmony_ci
448a46c0ec8Sopenharmony_cistatic inline void
449a46c0ec8Sopenharmony_citablet_update_xy(struct tablet_dispatch *tablet,
450a46c0ec8Sopenharmony_ci		 struct evdev_device *device)
451a46c0ec8Sopenharmony_ci{
452a46c0ec8Sopenharmony_ci	const struct input_absinfo *absinfo;
453a46c0ec8Sopenharmony_ci	int value;
454a46c0ec8Sopenharmony_ci
455a46c0ec8Sopenharmony_ci	if (!libevdev_has_event_code(device->evdev, EV_ABS, ABS_X) ||
456a46c0ec8Sopenharmony_ci	    !libevdev_has_event_code(device->evdev, EV_ABS, ABS_Y))
457a46c0ec8Sopenharmony_ci		return;
458a46c0ec8Sopenharmony_ci
459a46c0ec8Sopenharmony_ci	if (bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_X) ||
460a46c0ec8Sopenharmony_ci	    bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_Y)) {
461a46c0ec8Sopenharmony_ci		absinfo = libevdev_get_abs_info(device->evdev, ABS_X);
462a46c0ec8Sopenharmony_ci
463a46c0ec8Sopenharmony_ci		if (tablet->rotation.rotate)
464a46c0ec8Sopenharmony_ci			value = invert_axis(absinfo);
465a46c0ec8Sopenharmony_ci		else
466a46c0ec8Sopenharmony_ci			value = absinfo->value;
467a46c0ec8Sopenharmony_ci
468a46c0ec8Sopenharmony_ci		tablet->axes.point.x = value;
469a46c0ec8Sopenharmony_ci
470a46c0ec8Sopenharmony_ci		absinfo = libevdev_get_abs_info(device->evdev, ABS_Y);
471a46c0ec8Sopenharmony_ci
472a46c0ec8Sopenharmony_ci		if (tablet->rotation.rotate)
473a46c0ec8Sopenharmony_ci			value = invert_axis(absinfo);
474a46c0ec8Sopenharmony_ci		else
475a46c0ec8Sopenharmony_ci			value = absinfo->value;
476a46c0ec8Sopenharmony_ci
477a46c0ec8Sopenharmony_ci		tablet->axes.point.y = value;
478a46c0ec8Sopenharmony_ci
479a46c0ec8Sopenharmony_ci		evdev_transform_absolute(device, &tablet->axes.point);
480a46c0ec8Sopenharmony_ci	}
481a46c0ec8Sopenharmony_ci}
482a46c0ec8Sopenharmony_ci
483a46c0ec8Sopenharmony_cistatic inline struct normalized_coords
484a46c0ec8Sopenharmony_citablet_tool_process_delta(struct tablet_dispatch *tablet,
485a46c0ec8Sopenharmony_ci			  struct libinput_tablet_tool *tool,
486a46c0ec8Sopenharmony_ci			  const struct evdev_device *device,
487a46c0ec8Sopenharmony_ci			  struct tablet_axes *axes,
488a46c0ec8Sopenharmony_ci			  uint64_t time)
489a46c0ec8Sopenharmony_ci{
490a46c0ec8Sopenharmony_ci	const struct normalized_coords zero = { 0.0, 0.0 };
491a46c0ec8Sopenharmony_ci	struct device_coords delta = { 0, 0 };
492a46c0ec8Sopenharmony_ci	struct device_float_coords accel;
493a46c0ec8Sopenharmony_ci
494a46c0ec8Sopenharmony_ci	/* When tool contact changes, we probably got a cursor jump. Don't
495a46c0ec8Sopenharmony_ci	   try to calculate a delta for that event */
496a46c0ec8Sopenharmony_ci	if (!tablet_has_status(tablet,
497a46c0ec8Sopenharmony_ci			       TABLET_TOOL_ENTERING_PROXIMITY) &&
498a46c0ec8Sopenharmony_ci	    !tablet_has_status(tablet, TABLET_TOOL_ENTERING_CONTACT) &&
499a46c0ec8Sopenharmony_ci	    !tablet_has_status(tablet, TABLET_TOOL_LEAVING_CONTACT) &&
500a46c0ec8Sopenharmony_ci	    (bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_X) ||
501a46c0ec8Sopenharmony_ci	     bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_Y))) {
502a46c0ec8Sopenharmony_ci		delta.x = axes->point.x - tablet->last_smooth_point.x;
503a46c0ec8Sopenharmony_ci		delta.y = axes->point.y - tablet->last_smooth_point.y;
504a46c0ec8Sopenharmony_ci	}
505a46c0ec8Sopenharmony_ci
506a46c0ec8Sopenharmony_ci	if (axes->point.x != tablet->last_smooth_point.x)
507a46c0ec8Sopenharmony_ci		set_bit(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_X);
508a46c0ec8Sopenharmony_ci	if (axes->point.y != tablet->last_smooth_point.y)
509a46c0ec8Sopenharmony_ci		set_bit(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_Y);
510a46c0ec8Sopenharmony_ci
511a46c0ec8Sopenharmony_ci	tablet->last_smooth_point = axes->point;
512a46c0ec8Sopenharmony_ci
513a46c0ec8Sopenharmony_ci	accel.x = 1.0 * delta.x;
514a46c0ec8Sopenharmony_ci	accel.y = 1.0 * delta.y;
515a46c0ec8Sopenharmony_ci
516a46c0ec8Sopenharmony_ci	if (device_float_is_zero(accel))
517a46c0ec8Sopenharmony_ci		return zero;
518a46c0ec8Sopenharmony_ci
519a46c0ec8Sopenharmony_ci	return filter_dispatch(device->pointer.filter,
520a46c0ec8Sopenharmony_ci			       &accel,
521a46c0ec8Sopenharmony_ci			       tool,
522a46c0ec8Sopenharmony_ci			       time);
523a46c0ec8Sopenharmony_ci}
524a46c0ec8Sopenharmony_ci
525a46c0ec8Sopenharmony_cistatic inline void
526a46c0ec8Sopenharmony_citablet_update_pressure(struct tablet_dispatch *tablet,
527a46c0ec8Sopenharmony_ci		       struct evdev_device *device,
528a46c0ec8Sopenharmony_ci		       struct libinput_tablet_tool *tool)
529a46c0ec8Sopenharmony_ci{
530a46c0ec8Sopenharmony_ci	const struct input_absinfo *absinfo;
531a46c0ec8Sopenharmony_ci
532a46c0ec8Sopenharmony_ci	if (!libevdev_has_event_code(device->evdev, EV_ABS, ABS_PRESSURE))
533a46c0ec8Sopenharmony_ci		return;
534a46c0ec8Sopenharmony_ci
535a46c0ec8Sopenharmony_ci	if (bit_is_set(tablet->changed_axes,
536a46c0ec8Sopenharmony_ci		       LIBINPUT_TABLET_TOOL_AXIS_PRESSURE)) {
537a46c0ec8Sopenharmony_ci		absinfo = libevdev_get_abs_info(device->evdev, ABS_PRESSURE);
538a46c0ec8Sopenharmony_ci		tablet->axes.pressure = normalize_pressure(absinfo, tool);
539a46c0ec8Sopenharmony_ci	}
540a46c0ec8Sopenharmony_ci}
541a46c0ec8Sopenharmony_ci
542a46c0ec8Sopenharmony_cistatic inline void
543a46c0ec8Sopenharmony_citablet_update_distance(struct tablet_dispatch *tablet,
544a46c0ec8Sopenharmony_ci		       struct evdev_device *device)
545a46c0ec8Sopenharmony_ci{
546a46c0ec8Sopenharmony_ci	const struct input_absinfo *absinfo;
547a46c0ec8Sopenharmony_ci
548a46c0ec8Sopenharmony_ci	if (!libevdev_has_event_code(device->evdev, EV_ABS, ABS_DISTANCE))
549a46c0ec8Sopenharmony_ci		return;
550a46c0ec8Sopenharmony_ci
551a46c0ec8Sopenharmony_ci	if (bit_is_set(tablet->changed_axes,
552a46c0ec8Sopenharmony_ci		       LIBINPUT_TABLET_TOOL_AXIS_DISTANCE)) {
553a46c0ec8Sopenharmony_ci		absinfo = libevdev_get_abs_info(device->evdev, ABS_DISTANCE);
554a46c0ec8Sopenharmony_ci		tablet->axes.distance = normalize_distance(absinfo);
555a46c0ec8Sopenharmony_ci	}
556a46c0ec8Sopenharmony_ci}
557a46c0ec8Sopenharmony_ci
558a46c0ec8Sopenharmony_cistatic inline void
559a46c0ec8Sopenharmony_citablet_update_slider(struct tablet_dispatch *tablet,
560a46c0ec8Sopenharmony_ci		     struct evdev_device *device)
561a46c0ec8Sopenharmony_ci{
562a46c0ec8Sopenharmony_ci	const struct input_absinfo *absinfo;
563a46c0ec8Sopenharmony_ci
564a46c0ec8Sopenharmony_ci	if (!libevdev_has_event_code(device->evdev, EV_ABS, ABS_WHEEL))
565a46c0ec8Sopenharmony_ci		return;
566a46c0ec8Sopenharmony_ci
567a46c0ec8Sopenharmony_ci	if (bit_is_set(tablet->changed_axes,
568a46c0ec8Sopenharmony_ci		       LIBINPUT_TABLET_TOOL_AXIS_SLIDER)) {
569a46c0ec8Sopenharmony_ci		absinfo = libevdev_get_abs_info(device->evdev, ABS_WHEEL);
570a46c0ec8Sopenharmony_ci		tablet->axes.slider = normalize_slider(absinfo);
571a46c0ec8Sopenharmony_ci	}
572a46c0ec8Sopenharmony_ci}
573a46c0ec8Sopenharmony_ci
574a46c0ec8Sopenharmony_cistatic inline void
575a46c0ec8Sopenharmony_citablet_update_tilt(struct tablet_dispatch *tablet,
576a46c0ec8Sopenharmony_ci		   struct evdev_device *device)
577a46c0ec8Sopenharmony_ci{
578a46c0ec8Sopenharmony_ci	const struct input_absinfo *absinfo;
579a46c0ec8Sopenharmony_ci
580a46c0ec8Sopenharmony_ci	if (!libevdev_has_event_code(device->evdev, EV_ABS, ABS_TILT_X) ||
581a46c0ec8Sopenharmony_ci	    !libevdev_has_event_code(device->evdev, EV_ABS, ABS_TILT_Y))
582a46c0ec8Sopenharmony_ci		return;
583a46c0ec8Sopenharmony_ci
584a46c0ec8Sopenharmony_ci	/* mouse rotation resets tilt to 0 so always fetch both axes if
585a46c0ec8Sopenharmony_ci	 * either has changed */
586a46c0ec8Sopenharmony_ci	if (bit_is_set(tablet->changed_axes,
587a46c0ec8Sopenharmony_ci		       LIBINPUT_TABLET_TOOL_AXIS_TILT_X) ||
588a46c0ec8Sopenharmony_ci	    bit_is_set(tablet->changed_axes,
589a46c0ec8Sopenharmony_ci		       LIBINPUT_TABLET_TOOL_AXIS_TILT_Y)) {
590a46c0ec8Sopenharmony_ci
591a46c0ec8Sopenharmony_ci		absinfo = libevdev_get_abs_info(device->evdev, ABS_TILT_X);
592a46c0ec8Sopenharmony_ci		tablet->axes.tilt.x = adjust_tilt(absinfo);
593a46c0ec8Sopenharmony_ci
594a46c0ec8Sopenharmony_ci		absinfo = libevdev_get_abs_info(device->evdev, ABS_TILT_Y);
595a46c0ec8Sopenharmony_ci		tablet->axes.tilt.y = adjust_tilt(absinfo);
596a46c0ec8Sopenharmony_ci
597a46c0ec8Sopenharmony_ci		if (device->left_handed.enabled) {
598a46c0ec8Sopenharmony_ci			tablet->axes.tilt.x *= -1;
599a46c0ec8Sopenharmony_ci			tablet->axes.tilt.y *= -1;
600a46c0ec8Sopenharmony_ci		}
601a46c0ec8Sopenharmony_ci	}
602a46c0ec8Sopenharmony_ci}
603a46c0ec8Sopenharmony_ci
604a46c0ec8Sopenharmony_cistatic inline void
605a46c0ec8Sopenharmony_citablet_update_artpen_rotation(struct tablet_dispatch *tablet,
606a46c0ec8Sopenharmony_ci			      struct evdev_device *device)
607a46c0ec8Sopenharmony_ci{
608a46c0ec8Sopenharmony_ci	const struct input_absinfo *absinfo;
609a46c0ec8Sopenharmony_ci
610a46c0ec8Sopenharmony_ci	if (!libevdev_has_event_code(device->evdev, EV_ABS, ABS_Z))
611a46c0ec8Sopenharmony_ci		return;
612a46c0ec8Sopenharmony_ci
613a46c0ec8Sopenharmony_ci	if (bit_is_set(tablet->changed_axes,
614a46c0ec8Sopenharmony_ci		       LIBINPUT_TABLET_TOOL_AXIS_ROTATION_Z)) {
615a46c0ec8Sopenharmony_ci		absinfo = libevdev_get_abs_info(device->evdev,
616a46c0ec8Sopenharmony_ci						ABS_Z);
617a46c0ec8Sopenharmony_ci		/* artpen has 0 with buttons pointing east */
618a46c0ec8Sopenharmony_ci		tablet->axes.rotation = convert_to_degrees(absinfo, 90);
619a46c0ec8Sopenharmony_ci	}
620a46c0ec8Sopenharmony_ci}
621a46c0ec8Sopenharmony_ci
622a46c0ec8Sopenharmony_cistatic inline void
623a46c0ec8Sopenharmony_citablet_update_mouse_rotation(struct tablet_dispatch *tablet,
624a46c0ec8Sopenharmony_ci			     struct evdev_device *device)
625a46c0ec8Sopenharmony_ci{
626a46c0ec8Sopenharmony_ci	if (bit_is_set(tablet->changed_axes,
627a46c0ec8Sopenharmony_ci		       LIBINPUT_TABLET_TOOL_AXIS_TILT_X) ||
628a46c0ec8Sopenharmony_ci	    bit_is_set(tablet->changed_axes,
629a46c0ec8Sopenharmony_ci		       LIBINPUT_TABLET_TOOL_AXIS_TILT_Y)) {
630a46c0ec8Sopenharmony_ci		convert_tilt_to_rotation(tablet);
631a46c0ec8Sopenharmony_ci	}
632a46c0ec8Sopenharmony_ci}
633a46c0ec8Sopenharmony_ci
634a46c0ec8Sopenharmony_cistatic inline void
635a46c0ec8Sopenharmony_citablet_update_rotation(struct tablet_dispatch *tablet,
636a46c0ec8Sopenharmony_ci		       struct evdev_device *device)
637a46c0ec8Sopenharmony_ci{
638a46c0ec8Sopenharmony_ci	/* We must check ROTATION_Z after TILT_X/Y so that the tilt axes are
639a46c0ec8Sopenharmony_ci	 * already normalized and set if we have the mouse/lens tool */
640a46c0ec8Sopenharmony_ci	if (tablet->current_tool.type == LIBINPUT_TABLET_TOOL_TYPE_MOUSE ||
641a46c0ec8Sopenharmony_ci	    tablet->current_tool.type == LIBINPUT_TABLET_TOOL_TYPE_LENS) {
642a46c0ec8Sopenharmony_ci		tablet_update_mouse_rotation(tablet, device);
643a46c0ec8Sopenharmony_ci		clear_bit(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_TILT_X);
644a46c0ec8Sopenharmony_ci		clear_bit(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_TILT_Y);
645a46c0ec8Sopenharmony_ci		tablet->axes.tilt.x = 0;
646a46c0ec8Sopenharmony_ci		tablet->axes.tilt.y = 0;
647a46c0ec8Sopenharmony_ci
648a46c0ec8Sopenharmony_ci		/* tilt is already converted to left-handed, so mouse
649a46c0ec8Sopenharmony_ci		 * rotation is converted to left-handed automatically */
650a46c0ec8Sopenharmony_ci	} else {
651a46c0ec8Sopenharmony_ci
652a46c0ec8Sopenharmony_ci		tablet_update_artpen_rotation(tablet, device);
653a46c0ec8Sopenharmony_ci
654a46c0ec8Sopenharmony_ci		if (device->left_handed.enabled) {
655a46c0ec8Sopenharmony_ci			double r = tablet->axes.rotation;
656a46c0ec8Sopenharmony_ci			tablet->axes.rotation = fmod(180 + r, 360);
657a46c0ec8Sopenharmony_ci		}
658a46c0ec8Sopenharmony_ci	}
659a46c0ec8Sopenharmony_ci}
660a46c0ec8Sopenharmony_ci
661a46c0ec8Sopenharmony_cistatic inline void
662a46c0ec8Sopenharmony_citablet_update_wheel(struct tablet_dispatch *tablet,
663a46c0ec8Sopenharmony_ci		    struct evdev_device *device)
664a46c0ec8Sopenharmony_ci{
665a46c0ec8Sopenharmony_ci	int a;
666a46c0ec8Sopenharmony_ci
667a46c0ec8Sopenharmony_ci	a = LIBINPUT_TABLET_TOOL_AXIS_REL_WHEEL;
668a46c0ec8Sopenharmony_ci	if (bit_is_set(tablet->changed_axes, a)) {
669a46c0ec8Sopenharmony_ci		/* tablet->axes.wheel_discrete is already set */
670a46c0ec8Sopenharmony_ci		tablet->axes.wheel = normalize_wheel(tablet,
671a46c0ec8Sopenharmony_ci						     tablet->axes.wheel_discrete);
672a46c0ec8Sopenharmony_ci	} else {
673a46c0ec8Sopenharmony_ci		tablet->axes.wheel = 0;
674a46c0ec8Sopenharmony_ci		tablet->axes.wheel_discrete = 0;
675a46c0ec8Sopenharmony_ci	}
676a46c0ec8Sopenharmony_ci}
677a46c0ec8Sopenharmony_ci
678a46c0ec8Sopenharmony_cistatic void
679a46c0ec8Sopenharmony_citablet_smoothen_axes(const struct tablet_dispatch *tablet,
680a46c0ec8Sopenharmony_ci		     struct tablet_axes *axes)
681a46c0ec8Sopenharmony_ci{
682a46c0ec8Sopenharmony_ci	size_t i;
683a46c0ec8Sopenharmony_ci	size_t count = tablet_history_size(tablet);
684a46c0ec8Sopenharmony_ci	struct tablet_axes smooth = { 0 };
685a46c0ec8Sopenharmony_ci
686a46c0ec8Sopenharmony_ci	for (i = 0; i < count; i++) {
687a46c0ec8Sopenharmony_ci		const struct tablet_axes *a = tablet_history_get(tablet, i);
688a46c0ec8Sopenharmony_ci
689a46c0ec8Sopenharmony_ci		smooth.point.x += a->point.x;
690a46c0ec8Sopenharmony_ci		smooth.point.y += a->point.y;
691a46c0ec8Sopenharmony_ci
692a46c0ec8Sopenharmony_ci		smooth.tilt.x += a->tilt.x;
693a46c0ec8Sopenharmony_ci		smooth.tilt.y += a->tilt.y;
694a46c0ec8Sopenharmony_ci	}
695a46c0ec8Sopenharmony_ci
696a46c0ec8Sopenharmony_ci	axes->point.x = smooth.point.x/count;
697a46c0ec8Sopenharmony_ci	axes->point.y = smooth.point.y/count;
698a46c0ec8Sopenharmony_ci
699a46c0ec8Sopenharmony_ci	axes->tilt.x = smooth.tilt.x/count;
700a46c0ec8Sopenharmony_ci	axes->tilt.y = smooth.tilt.y/count;
701a46c0ec8Sopenharmony_ci}
702a46c0ec8Sopenharmony_ci
703a46c0ec8Sopenharmony_cistatic bool
704a46c0ec8Sopenharmony_citablet_check_notify_axes(struct tablet_dispatch *tablet,
705a46c0ec8Sopenharmony_ci			 struct evdev_device *device,
706a46c0ec8Sopenharmony_ci			 struct libinput_tablet_tool *tool,
707a46c0ec8Sopenharmony_ci			 struct tablet_axes *axes_out,
708a46c0ec8Sopenharmony_ci			 uint64_t time)
709a46c0ec8Sopenharmony_ci{
710a46c0ec8Sopenharmony_ci	struct tablet_axes axes = {0};
711a46c0ec8Sopenharmony_ci	const char tmp[sizeof(tablet->changed_axes)] = {0};
712a46c0ec8Sopenharmony_ci	bool rc = false;
713a46c0ec8Sopenharmony_ci
714a46c0ec8Sopenharmony_ci	if (memcmp(tmp, tablet->changed_axes, sizeof(tmp)) == 0) {
715a46c0ec8Sopenharmony_ci		axes = tablet->axes;
716a46c0ec8Sopenharmony_ci		goto out;
717a46c0ec8Sopenharmony_ci	}
718a46c0ec8Sopenharmony_ci
719a46c0ec8Sopenharmony_ci	tablet_update_xy(tablet, device);
720a46c0ec8Sopenharmony_ci	tablet_update_pressure(tablet, device, tool);
721a46c0ec8Sopenharmony_ci	tablet_update_distance(tablet, device);
722a46c0ec8Sopenharmony_ci	tablet_update_slider(tablet, device);
723a46c0ec8Sopenharmony_ci	tablet_update_tilt(tablet, device);
724a46c0ec8Sopenharmony_ci	tablet_update_wheel(tablet, device);
725a46c0ec8Sopenharmony_ci	/* We must check ROTATION_Z after TILT_X/Y so that the tilt axes are
726a46c0ec8Sopenharmony_ci	 * already normalized and set if we have the mouse/lens tool */
727a46c0ec8Sopenharmony_ci	tablet_update_rotation(tablet, device);
728a46c0ec8Sopenharmony_ci
729a46c0ec8Sopenharmony_ci	axes.point = tablet->axes.point;
730a46c0ec8Sopenharmony_ci	axes.pressure = tablet->axes.pressure;
731a46c0ec8Sopenharmony_ci	axes.distance = tablet->axes.distance;
732a46c0ec8Sopenharmony_ci	axes.slider = tablet->axes.slider;
733a46c0ec8Sopenharmony_ci	axes.tilt = tablet->axes.tilt;
734a46c0ec8Sopenharmony_ci	axes.wheel = tablet->axes.wheel;
735a46c0ec8Sopenharmony_ci	axes.wheel_discrete = tablet->axes.wheel_discrete;
736a46c0ec8Sopenharmony_ci	axes.rotation = tablet->axes.rotation;
737a46c0ec8Sopenharmony_ci
738a46c0ec8Sopenharmony_ci	rc = true;
739a46c0ec8Sopenharmony_ci
740a46c0ec8Sopenharmony_ciout:
741a46c0ec8Sopenharmony_ci	/* The tool position often jumps to a different spot when contact changes.
742a46c0ec8Sopenharmony_ci	 * If tool contact changes, clear the history to prevent axis smoothing
743a46c0ec8Sopenharmony_ci	 * from trying to average over the spatial discontinuity. */
744a46c0ec8Sopenharmony_ci	if (tablet_has_status(tablet, TABLET_TOOL_ENTERING_CONTACT) ||
745a46c0ec8Sopenharmony_ci	    tablet_has_status(tablet, TABLET_TOOL_LEAVING_CONTACT)) {
746a46c0ec8Sopenharmony_ci		tablet_history_reset(tablet);
747a46c0ec8Sopenharmony_ci	}
748a46c0ec8Sopenharmony_ci
749a46c0ec8Sopenharmony_ci	tablet_history_push(tablet, &tablet->axes);
750a46c0ec8Sopenharmony_ci	tablet_smoothen_axes(tablet, &axes);
751a46c0ec8Sopenharmony_ci
752a46c0ec8Sopenharmony_ci	/* The delta relies on the last *smooth* point, so we do it last */
753a46c0ec8Sopenharmony_ci	axes.delta = tablet_tool_process_delta(tablet, tool, device, &axes, time);
754a46c0ec8Sopenharmony_ci
755a46c0ec8Sopenharmony_ci	*axes_out = axes;
756a46c0ec8Sopenharmony_ci
757a46c0ec8Sopenharmony_ci	return rc;
758a46c0ec8Sopenharmony_ci}
759a46c0ec8Sopenharmony_ci
760a46c0ec8Sopenharmony_cistatic void
761a46c0ec8Sopenharmony_citablet_update_button(struct tablet_dispatch *tablet,
762a46c0ec8Sopenharmony_ci		     uint32_t evcode,
763a46c0ec8Sopenharmony_ci		     uint32_t enable)
764a46c0ec8Sopenharmony_ci{
765a46c0ec8Sopenharmony_ci	switch (evcode) {
766a46c0ec8Sopenharmony_ci	case BTN_LEFT:
767a46c0ec8Sopenharmony_ci	case BTN_RIGHT:
768a46c0ec8Sopenharmony_ci	case BTN_MIDDLE:
769a46c0ec8Sopenharmony_ci	case BTN_SIDE:
770a46c0ec8Sopenharmony_ci	case BTN_EXTRA:
771a46c0ec8Sopenharmony_ci	case BTN_FORWARD:
772a46c0ec8Sopenharmony_ci	case BTN_BACK:
773a46c0ec8Sopenharmony_ci	case BTN_TASK:
774a46c0ec8Sopenharmony_ci	case BTN_STYLUS:
775a46c0ec8Sopenharmony_ci	case BTN_STYLUS2:
776a46c0ec8Sopenharmony_ci		break;
777a46c0ec8Sopenharmony_ci	default:
778a46c0ec8Sopenharmony_ci		evdev_log_info(tablet->device,
779a46c0ec8Sopenharmony_ci			       "Unhandled button %s (%#x)\n",
780a46c0ec8Sopenharmony_ci			       libevdev_event_code_get_name(EV_KEY, evcode),
781a46c0ec8Sopenharmony_ci			       evcode);
782a46c0ec8Sopenharmony_ci		return;
783a46c0ec8Sopenharmony_ci	}
784a46c0ec8Sopenharmony_ci
785a46c0ec8Sopenharmony_ci	if (enable) {
786a46c0ec8Sopenharmony_ci		set_bit(tablet->button_state.bits, evcode);
787a46c0ec8Sopenharmony_ci		tablet_set_status(tablet, TABLET_BUTTONS_PRESSED);
788a46c0ec8Sopenharmony_ci	} else {
789a46c0ec8Sopenharmony_ci		clear_bit(tablet->button_state.bits, evcode);
790a46c0ec8Sopenharmony_ci		tablet_set_status(tablet, TABLET_BUTTONS_RELEASED);
791a46c0ec8Sopenharmony_ci	}
792a46c0ec8Sopenharmony_ci}
793a46c0ec8Sopenharmony_ci
794a46c0ec8Sopenharmony_cistatic inline enum libinput_tablet_tool_type
795a46c0ec8Sopenharmony_citablet_evcode_to_tool(int code)
796a46c0ec8Sopenharmony_ci{
797a46c0ec8Sopenharmony_ci	enum libinput_tablet_tool_type type;
798a46c0ec8Sopenharmony_ci
799a46c0ec8Sopenharmony_ci	switch (code) {
800a46c0ec8Sopenharmony_ci	case BTN_TOOL_PEN:	type = LIBINPUT_TABLET_TOOL_TYPE_PEN;		break;
801a46c0ec8Sopenharmony_ci	case BTN_TOOL_RUBBER:	type = LIBINPUT_TABLET_TOOL_TYPE_ERASER;	break;
802a46c0ec8Sopenharmony_ci	case BTN_TOOL_BRUSH:	type = LIBINPUT_TABLET_TOOL_TYPE_BRUSH;		break;
803a46c0ec8Sopenharmony_ci	case BTN_TOOL_PENCIL:	type = LIBINPUT_TABLET_TOOL_TYPE_PENCIL;	break;
804a46c0ec8Sopenharmony_ci	case BTN_TOOL_AIRBRUSH:	type = LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH;	break;
805a46c0ec8Sopenharmony_ci	case BTN_TOOL_MOUSE:	type = LIBINPUT_TABLET_TOOL_TYPE_MOUSE;		break;
806a46c0ec8Sopenharmony_ci	case BTN_TOOL_LENS:	type = LIBINPUT_TABLET_TOOL_TYPE_LENS;		break;
807a46c0ec8Sopenharmony_ci	default:
808a46c0ec8Sopenharmony_ci		abort();
809a46c0ec8Sopenharmony_ci	}
810a46c0ec8Sopenharmony_ci
811a46c0ec8Sopenharmony_ci	return type;
812a46c0ec8Sopenharmony_ci}
813a46c0ec8Sopenharmony_ci
814a46c0ec8Sopenharmony_cistatic void
815a46c0ec8Sopenharmony_citablet_process_key(struct tablet_dispatch *tablet,
816a46c0ec8Sopenharmony_ci		   struct evdev_device *device,
817a46c0ec8Sopenharmony_ci		   struct input_event *e,
818a46c0ec8Sopenharmony_ci		   uint64_t time)
819a46c0ec8Sopenharmony_ci{
820a46c0ec8Sopenharmony_ci	enum libinput_tablet_tool_type type;
821a46c0ec8Sopenharmony_ci
822a46c0ec8Sopenharmony_ci	/* ignore kernel key repeat */
823a46c0ec8Sopenharmony_ci	if (e->value == 2)
824a46c0ec8Sopenharmony_ci		return;
825a46c0ec8Sopenharmony_ci
826a46c0ec8Sopenharmony_ci	switch (e->code) {
827a46c0ec8Sopenharmony_ci	case BTN_TOOL_FINGER:
828a46c0ec8Sopenharmony_ci		evdev_log_bug_libinput(device,
829a46c0ec8Sopenharmony_ci			       "Invalid tool 'finger' on tablet interface\n");
830a46c0ec8Sopenharmony_ci		break;
831a46c0ec8Sopenharmony_ci	case BTN_TOOL_PEN:
832a46c0ec8Sopenharmony_ci	case BTN_TOOL_RUBBER:
833a46c0ec8Sopenharmony_ci	case BTN_TOOL_BRUSH:
834a46c0ec8Sopenharmony_ci	case BTN_TOOL_PENCIL:
835a46c0ec8Sopenharmony_ci	case BTN_TOOL_AIRBRUSH:
836a46c0ec8Sopenharmony_ci	case BTN_TOOL_MOUSE:
837a46c0ec8Sopenharmony_ci	case BTN_TOOL_LENS:
838a46c0ec8Sopenharmony_ci		type = tablet_evcode_to_tool(e->code);
839a46c0ec8Sopenharmony_ci		tablet_set_status(tablet, TABLET_TOOL_UPDATED);
840a46c0ec8Sopenharmony_ci		if (e->value)
841a46c0ec8Sopenharmony_ci			tablet->tool_state |= bit(type);
842a46c0ec8Sopenharmony_ci		else
843a46c0ec8Sopenharmony_ci			tablet->tool_state &= ~bit(type);
844a46c0ec8Sopenharmony_ci		break;
845a46c0ec8Sopenharmony_ci	case BTN_TOUCH:
846a46c0ec8Sopenharmony_ci		if (!bit_is_set(tablet->axis_caps,
847a46c0ec8Sopenharmony_ci				LIBINPUT_TABLET_TOOL_AXIS_PRESSURE)) {
848a46c0ec8Sopenharmony_ci			if (e->value)
849a46c0ec8Sopenharmony_ci				tablet_set_status(tablet,
850a46c0ec8Sopenharmony_ci						  TABLET_TOOL_ENTERING_CONTACT);
851a46c0ec8Sopenharmony_ci			else
852a46c0ec8Sopenharmony_ci				tablet_set_status(tablet,
853a46c0ec8Sopenharmony_ci						  TABLET_TOOL_LEAVING_CONTACT);
854a46c0ec8Sopenharmony_ci		}
855a46c0ec8Sopenharmony_ci		break;
856a46c0ec8Sopenharmony_ci	default:
857a46c0ec8Sopenharmony_ci		tablet_update_button(tablet, e->code, e->value);
858a46c0ec8Sopenharmony_ci		break;
859a46c0ec8Sopenharmony_ci	}
860a46c0ec8Sopenharmony_ci}
861a46c0ec8Sopenharmony_ci
862a46c0ec8Sopenharmony_cistatic void
863a46c0ec8Sopenharmony_citablet_process_relative(struct tablet_dispatch *tablet,
864a46c0ec8Sopenharmony_ci			struct evdev_device *device,
865a46c0ec8Sopenharmony_ci			struct input_event *e,
866a46c0ec8Sopenharmony_ci			uint64_t time)
867a46c0ec8Sopenharmony_ci{
868a46c0ec8Sopenharmony_ci	enum libinput_tablet_tool_axis axis;
869a46c0ec8Sopenharmony_ci
870a46c0ec8Sopenharmony_ci	switch (e->code) {
871a46c0ec8Sopenharmony_ci	case REL_WHEEL:
872a46c0ec8Sopenharmony_ci		axis = rel_evcode_to_axis(e->code);
873a46c0ec8Sopenharmony_ci		if (axis == LIBINPUT_TABLET_TOOL_AXIS_NONE) {
874a46c0ec8Sopenharmony_ci			evdev_log_bug_libinput(device,
875a46c0ec8Sopenharmony_ci					       "Invalid ABS event code %#x\n",
876a46c0ec8Sopenharmony_ci					       e->code);
877a46c0ec8Sopenharmony_ci			break;
878a46c0ec8Sopenharmony_ci		}
879a46c0ec8Sopenharmony_ci		set_bit(tablet->changed_axes, axis);
880a46c0ec8Sopenharmony_ci		tablet->axes.wheel_discrete = -1 * e->value;
881a46c0ec8Sopenharmony_ci		tablet_set_status(tablet, TABLET_AXES_UPDATED);
882a46c0ec8Sopenharmony_ci		break;
883a46c0ec8Sopenharmony_ci	default:
884a46c0ec8Sopenharmony_ci		evdev_log_info(device,
885a46c0ec8Sopenharmony_ci			       "Unhandled relative axis %s (%#x)\n",
886a46c0ec8Sopenharmony_ci			       libevdev_event_code_get_name(EV_REL, e->code),
887a46c0ec8Sopenharmony_ci			       e->code);
888a46c0ec8Sopenharmony_ci		return;
889a46c0ec8Sopenharmony_ci	}
890a46c0ec8Sopenharmony_ci}
891a46c0ec8Sopenharmony_ci
892a46c0ec8Sopenharmony_cistatic void
893a46c0ec8Sopenharmony_citablet_process_misc(struct tablet_dispatch *tablet,
894a46c0ec8Sopenharmony_ci		    struct evdev_device *device,
895a46c0ec8Sopenharmony_ci		    struct input_event *e,
896a46c0ec8Sopenharmony_ci		    uint64_t time)
897a46c0ec8Sopenharmony_ci{
898a46c0ec8Sopenharmony_ci	switch (e->code) {
899a46c0ec8Sopenharmony_ci	case MSC_SERIAL:
900a46c0ec8Sopenharmony_ci		if (e->value != -1)
901a46c0ec8Sopenharmony_ci			tablet->current_tool.serial = e->value;
902a46c0ec8Sopenharmony_ci
903a46c0ec8Sopenharmony_ci		break;
904a46c0ec8Sopenharmony_ci	case MSC_SCAN:
905a46c0ec8Sopenharmony_ci		break;
906a46c0ec8Sopenharmony_ci	default:
907a46c0ec8Sopenharmony_ci		evdev_log_info(device,
908a46c0ec8Sopenharmony_ci			       "Unhandled MSC event code %s (%#x)\n",
909a46c0ec8Sopenharmony_ci			       libevdev_event_code_get_name(EV_MSC, e->code),
910a46c0ec8Sopenharmony_ci			       e->code);
911a46c0ec8Sopenharmony_ci		break;
912a46c0ec8Sopenharmony_ci	}
913a46c0ec8Sopenharmony_ci}
914a46c0ec8Sopenharmony_ci
915a46c0ec8Sopenharmony_cistatic inline void
916a46c0ec8Sopenharmony_cicopy_axis_cap(const struct tablet_dispatch *tablet,
917a46c0ec8Sopenharmony_ci	      struct libinput_tablet_tool *tool,
918a46c0ec8Sopenharmony_ci	      enum libinput_tablet_tool_axis axis)
919a46c0ec8Sopenharmony_ci{
920a46c0ec8Sopenharmony_ci	if (bit_is_set(tablet->axis_caps, axis))
921a46c0ec8Sopenharmony_ci		set_bit(tool->axis_caps, axis);
922a46c0ec8Sopenharmony_ci}
923a46c0ec8Sopenharmony_ci
924a46c0ec8Sopenharmony_cistatic inline void
925a46c0ec8Sopenharmony_cicopy_button_cap(const struct tablet_dispatch *tablet,
926a46c0ec8Sopenharmony_ci		struct libinput_tablet_tool *tool,
927a46c0ec8Sopenharmony_ci		uint32_t button)
928a46c0ec8Sopenharmony_ci{
929a46c0ec8Sopenharmony_ci	struct libevdev *evdev = tablet->device->evdev;
930a46c0ec8Sopenharmony_ci	if (libevdev_has_event_code(evdev, EV_KEY, button))
931a46c0ec8Sopenharmony_ci		set_bit(tool->buttons, button);
932a46c0ec8Sopenharmony_ci}
933a46c0ec8Sopenharmony_ci
934a46c0ec8Sopenharmony_ci#if HAVE_LIBWACOM
935a46c0ec8Sopenharmony_cistatic inline int
936a46c0ec8Sopenharmony_citool_set_bits_from_libwacom(const struct tablet_dispatch *tablet,
937a46c0ec8Sopenharmony_ci			    struct libinput_tablet_tool *tool)
938a46c0ec8Sopenharmony_ci{
939a46c0ec8Sopenharmony_ci	int rc = 1;
940a46c0ec8Sopenharmony_ci	WacomDeviceDatabase *db;
941a46c0ec8Sopenharmony_ci	const WacomStylus *s = NULL;
942a46c0ec8Sopenharmony_ci	int code;
943a46c0ec8Sopenharmony_ci	WacomStylusType type;
944a46c0ec8Sopenharmony_ci	WacomAxisTypeFlags axes;
945a46c0ec8Sopenharmony_ci
946a46c0ec8Sopenharmony_ci	db = tablet_libinput_context(tablet)->libwacom.db;
947a46c0ec8Sopenharmony_ci	if (!db)
948a46c0ec8Sopenharmony_ci		return rc;
949a46c0ec8Sopenharmony_ci
950a46c0ec8Sopenharmony_ci	s = libwacom_stylus_get_for_id(db, tool->tool_id);
951a46c0ec8Sopenharmony_ci	if (!s)
952a46c0ec8Sopenharmony_ci		return rc;
953a46c0ec8Sopenharmony_ci
954a46c0ec8Sopenharmony_ci	type = libwacom_stylus_get_type(s);
955a46c0ec8Sopenharmony_ci	if (type == WSTYLUS_PUCK) {
956a46c0ec8Sopenharmony_ci		for (code = BTN_LEFT;
957a46c0ec8Sopenharmony_ci		     code < BTN_LEFT + libwacom_stylus_get_num_buttons(s);
958a46c0ec8Sopenharmony_ci		     code++)
959a46c0ec8Sopenharmony_ci			copy_button_cap(tablet, tool, code);
960a46c0ec8Sopenharmony_ci	} else {
961a46c0ec8Sopenharmony_ci		if (libwacom_stylus_get_num_buttons(s) >= 2)
962a46c0ec8Sopenharmony_ci			copy_button_cap(tablet, tool, BTN_STYLUS2);
963a46c0ec8Sopenharmony_ci		if (libwacom_stylus_get_num_buttons(s) >= 1)
964a46c0ec8Sopenharmony_ci			copy_button_cap(tablet, tool, BTN_STYLUS);
965a46c0ec8Sopenharmony_ci	}
966a46c0ec8Sopenharmony_ci
967a46c0ec8Sopenharmony_ci	if (libwacom_stylus_has_wheel(s))
968a46c0ec8Sopenharmony_ci		copy_axis_cap(tablet, tool, LIBINPUT_TABLET_TOOL_AXIS_REL_WHEEL);
969a46c0ec8Sopenharmony_ci
970a46c0ec8Sopenharmony_ci	axes = libwacom_stylus_get_axes(s);
971a46c0ec8Sopenharmony_ci
972a46c0ec8Sopenharmony_ci	if (axes & WACOM_AXIS_TYPE_TILT) {
973a46c0ec8Sopenharmony_ci		/* tilt on the puck is converted to rotation */
974a46c0ec8Sopenharmony_ci		if (type == WSTYLUS_PUCK) {
975a46c0ec8Sopenharmony_ci			set_bit(tool->axis_caps,
976a46c0ec8Sopenharmony_ci				LIBINPUT_TABLET_TOOL_AXIS_ROTATION_Z);
977a46c0ec8Sopenharmony_ci		} else {
978a46c0ec8Sopenharmony_ci			copy_axis_cap(tablet,
979a46c0ec8Sopenharmony_ci				      tool,
980a46c0ec8Sopenharmony_ci				      LIBINPUT_TABLET_TOOL_AXIS_TILT_X);
981a46c0ec8Sopenharmony_ci			copy_axis_cap(tablet,
982a46c0ec8Sopenharmony_ci				      tool,
983a46c0ec8Sopenharmony_ci				      LIBINPUT_TABLET_TOOL_AXIS_TILT_Y);
984a46c0ec8Sopenharmony_ci		}
985a46c0ec8Sopenharmony_ci	}
986a46c0ec8Sopenharmony_ci	if (axes & WACOM_AXIS_TYPE_ROTATION_Z)
987a46c0ec8Sopenharmony_ci		copy_axis_cap(tablet, tool, LIBINPUT_TABLET_TOOL_AXIS_ROTATION_Z);
988a46c0ec8Sopenharmony_ci	if (axes & WACOM_AXIS_TYPE_DISTANCE)
989a46c0ec8Sopenharmony_ci		copy_axis_cap(tablet, tool, LIBINPUT_TABLET_TOOL_AXIS_DISTANCE);
990a46c0ec8Sopenharmony_ci	if (axes & WACOM_AXIS_TYPE_SLIDER)
991a46c0ec8Sopenharmony_ci		copy_axis_cap(tablet, tool, LIBINPUT_TABLET_TOOL_AXIS_SLIDER);
992a46c0ec8Sopenharmony_ci	if (axes & WACOM_AXIS_TYPE_PRESSURE)
993a46c0ec8Sopenharmony_ci		copy_axis_cap(tablet, tool, LIBINPUT_TABLET_TOOL_AXIS_PRESSURE);
994a46c0ec8Sopenharmony_ci
995a46c0ec8Sopenharmony_ci	rc = 0;
996a46c0ec8Sopenharmony_ci
997a46c0ec8Sopenharmony_ci	return rc;
998a46c0ec8Sopenharmony_ci}
999a46c0ec8Sopenharmony_ci#endif
1000a46c0ec8Sopenharmony_ci
1001a46c0ec8Sopenharmony_cistatic void
1002a46c0ec8Sopenharmony_citool_set_bits(const struct tablet_dispatch *tablet,
1003a46c0ec8Sopenharmony_ci	      struct libinput_tablet_tool *tool)
1004a46c0ec8Sopenharmony_ci{
1005a46c0ec8Sopenharmony_ci	enum libinput_tablet_tool_type type = tool->type;
1006a46c0ec8Sopenharmony_ci
1007a46c0ec8Sopenharmony_ci	copy_axis_cap(tablet, tool, LIBINPUT_TABLET_TOOL_AXIS_X);
1008a46c0ec8Sopenharmony_ci	copy_axis_cap(tablet, tool, LIBINPUT_TABLET_TOOL_AXIS_Y);
1009a46c0ec8Sopenharmony_ci
1010a46c0ec8Sopenharmony_ci#if HAVE_LIBWACOM
1011a46c0ec8Sopenharmony_ci	if (tool_set_bits_from_libwacom(tablet, tool) == 0)
1012a46c0ec8Sopenharmony_ci		return;
1013a46c0ec8Sopenharmony_ci#endif
1014a46c0ec8Sopenharmony_ci	/* If we don't have libwacom, we simply copy any axis we have on the
1015a46c0ec8Sopenharmony_ci	   tablet onto the tool. Except we know that mice only have rotation
1016a46c0ec8Sopenharmony_ci	   anyway.
1017a46c0ec8Sopenharmony_ci	 */
1018a46c0ec8Sopenharmony_ci	switch (type) {
1019a46c0ec8Sopenharmony_ci	case LIBINPUT_TABLET_TOOL_TYPE_PEN:
1020a46c0ec8Sopenharmony_ci	case LIBINPUT_TABLET_TOOL_TYPE_ERASER:
1021a46c0ec8Sopenharmony_ci	case LIBINPUT_TABLET_TOOL_TYPE_PENCIL:
1022a46c0ec8Sopenharmony_ci	case LIBINPUT_TABLET_TOOL_TYPE_BRUSH:
1023a46c0ec8Sopenharmony_ci	case LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH:
1024a46c0ec8Sopenharmony_ci		copy_axis_cap(tablet, tool, LIBINPUT_TABLET_TOOL_AXIS_PRESSURE);
1025a46c0ec8Sopenharmony_ci		copy_axis_cap(tablet, tool, LIBINPUT_TABLET_TOOL_AXIS_DISTANCE);
1026a46c0ec8Sopenharmony_ci		copy_axis_cap(tablet, tool, LIBINPUT_TABLET_TOOL_AXIS_TILT_X);
1027a46c0ec8Sopenharmony_ci		copy_axis_cap(tablet, tool, LIBINPUT_TABLET_TOOL_AXIS_TILT_Y);
1028a46c0ec8Sopenharmony_ci		copy_axis_cap(tablet, tool, LIBINPUT_TABLET_TOOL_AXIS_SLIDER);
1029a46c0ec8Sopenharmony_ci
1030a46c0ec8Sopenharmony_ci		/* Rotation is special, it can be either ABS_Z or
1031a46c0ec8Sopenharmony_ci		 * BTN_TOOL_MOUSE+ABS_TILT_X/Y. Aiptek tablets have
1032a46c0ec8Sopenharmony_ci		 * mouse+tilt (and thus rotation), but they do not have
1033a46c0ec8Sopenharmony_ci		 * ABS_Z. So let's not copy the axis bit if we don't have
1034a46c0ec8Sopenharmony_ci		 * ABS_Z, otherwise we try to get the value from it later on
1035a46c0ec8Sopenharmony_ci		 * proximity in and go boom because the absinfo isn't there.
1036a46c0ec8Sopenharmony_ci		 */
1037a46c0ec8Sopenharmony_ci		if (libevdev_has_event_code(tablet->device->evdev, EV_ABS,
1038a46c0ec8Sopenharmony_ci					    ABS_Z))
1039a46c0ec8Sopenharmony_ci			copy_axis_cap(tablet, tool, LIBINPUT_TABLET_TOOL_AXIS_ROTATION_Z);
1040a46c0ec8Sopenharmony_ci		break;
1041a46c0ec8Sopenharmony_ci	case LIBINPUT_TABLET_TOOL_TYPE_MOUSE:
1042a46c0ec8Sopenharmony_ci	case LIBINPUT_TABLET_TOOL_TYPE_LENS:
1043a46c0ec8Sopenharmony_ci		copy_axis_cap(tablet, tool, LIBINPUT_TABLET_TOOL_AXIS_ROTATION_Z);
1044a46c0ec8Sopenharmony_ci		copy_axis_cap(tablet, tool, LIBINPUT_TABLET_TOOL_AXIS_REL_WHEEL);
1045a46c0ec8Sopenharmony_ci		break;
1046a46c0ec8Sopenharmony_ci	default:
1047a46c0ec8Sopenharmony_ci		break;
1048a46c0ec8Sopenharmony_ci	}
1049a46c0ec8Sopenharmony_ci
1050a46c0ec8Sopenharmony_ci	/* If we don't have libwacom, copy all pen-related buttons from the
1051a46c0ec8Sopenharmony_ci	   tablet vs all mouse-related buttons */
1052a46c0ec8Sopenharmony_ci	switch (type) {
1053a46c0ec8Sopenharmony_ci	case LIBINPUT_TABLET_TOOL_TYPE_PEN:
1054a46c0ec8Sopenharmony_ci	case LIBINPUT_TABLET_TOOL_TYPE_BRUSH:
1055a46c0ec8Sopenharmony_ci	case LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH:
1056a46c0ec8Sopenharmony_ci	case LIBINPUT_TABLET_TOOL_TYPE_PENCIL:
1057a46c0ec8Sopenharmony_ci	case LIBINPUT_TABLET_TOOL_TYPE_ERASER:
1058a46c0ec8Sopenharmony_ci		copy_button_cap(tablet, tool, BTN_STYLUS);
1059a46c0ec8Sopenharmony_ci		copy_button_cap(tablet, tool, BTN_STYLUS2);
1060a46c0ec8Sopenharmony_ci		break;
1061a46c0ec8Sopenharmony_ci	case LIBINPUT_TABLET_TOOL_TYPE_MOUSE:
1062a46c0ec8Sopenharmony_ci	case LIBINPUT_TABLET_TOOL_TYPE_LENS:
1063a46c0ec8Sopenharmony_ci		copy_button_cap(tablet, tool, BTN_LEFT);
1064a46c0ec8Sopenharmony_ci		copy_button_cap(tablet, tool, BTN_MIDDLE);
1065a46c0ec8Sopenharmony_ci		copy_button_cap(tablet, tool, BTN_RIGHT);
1066a46c0ec8Sopenharmony_ci		copy_button_cap(tablet, tool, BTN_SIDE);
1067a46c0ec8Sopenharmony_ci		copy_button_cap(tablet, tool, BTN_EXTRA);
1068a46c0ec8Sopenharmony_ci		break;
1069a46c0ec8Sopenharmony_ci	default:
1070a46c0ec8Sopenharmony_ci		break;
1071a46c0ec8Sopenharmony_ci	}
1072a46c0ec8Sopenharmony_ci}
1073a46c0ec8Sopenharmony_ci
1074a46c0ec8Sopenharmony_cistatic inline int
1075a46c0ec8Sopenharmony_ciaxis_range_percentage(const struct input_absinfo *a, double percent)
1076a46c0ec8Sopenharmony_ci{
1077a46c0ec8Sopenharmony_ci	return absinfo_range(a) * percent/100.0 + a->minimum;
1078a46c0ec8Sopenharmony_ci}
1079a46c0ec8Sopenharmony_ci
1080a46c0ec8Sopenharmony_cistatic inline void
1081a46c0ec8Sopenharmony_citool_set_pressure_thresholds(struct tablet_dispatch *tablet,
1082a46c0ec8Sopenharmony_ci			     struct libinput_tablet_tool *tool)
1083a46c0ec8Sopenharmony_ci{
1084a46c0ec8Sopenharmony_ci	struct evdev_device *device = tablet->device;
1085a46c0ec8Sopenharmony_ci	const struct input_absinfo *pressure, *distance;
1086a46c0ec8Sopenharmony_ci	struct quirks_context *quirks = NULL;
1087a46c0ec8Sopenharmony_ci	struct quirks *q = NULL;
1088a46c0ec8Sopenharmony_ci	struct quirk_range r;
1089a46c0ec8Sopenharmony_ci	int lo = 0, hi = 1;
1090a46c0ec8Sopenharmony_ci
1091a46c0ec8Sopenharmony_ci	tool->pressure.offset = 0;
1092a46c0ec8Sopenharmony_ci	tool->pressure.has_offset = false;
1093a46c0ec8Sopenharmony_ci
1094a46c0ec8Sopenharmony_ci	pressure = libevdev_get_abs_info(device->evdev, ABS_PRESSURE);
1095a46c0ec8Sopenharmony_ci	if (!pressure)
1096a46c0ec8Sopenharmony_ci		goto out;
1097a46c0ec8Sopenharmony_ci
1098a46c0ec8Sopenharmony_ci	quirks = evdev_libinput_context(device)->quirks;
1099a46c0ec8Sopenharmony_ci	q = quirks_fetch_for_device(quirks, device->udev_device);
1100a46c0ec8Sopenharmony_ci
1101a46c0ec8Sopenharmony_ci	distance = libevdev_get_abs_info(device->evdev, ABS_DISTANCE);
1102a46c0ec8Sopenharmony_ci	if (distance) {
1103a46c0ec8Sopenharmony_ci		tool->pressure.offset = pressure->minimum;
1104a46c0ec8Sopenharmony_ci		tool->pressure.heuristic_state = PRESSURE_HEURISTIC_STATE_DONE;
1105a46c0ec8Sopenharmony_ci	} else {
1106a46c0ec8Sopenharmony_ci		tool->pressure.offset = pressure->maximum;
1107a46c0ec8Sopenharmony_ci		tool->pressure.heuristic_state = PRESSURE_HEURISTIC_STATE_PROXIN1;
1108a46c0ec8Sopenharmony_ci	}
1109a46c0ec8Sopenharmony_ci
1110a46c0ec8Sopenharmony_ci	/* 5 and 1% of the pressure range */
1111a46c0ec8Sopenharmony_ci	hi = axis_range_percentage(pressure, 5);
1112a46c0ec8Sopenharmony_ci	lo = axis_range_percentage(pressure, 1);
1113a46c0ec8Sopenharmony_ci
1114a46c0ec8Sopenharmony_ci	if (q && quirks_get_range(q, QUIRK_ATTR_PRESSURE_RANGE, &r)) {
1115a46c0ec8Sopenharmony_ci		if (r.lower >= r.upper) {
1116a46c0ec8Sopenharmony_ci			evdev_log_info(device,
1117a46c0ec8Sopenharmony_ci				       "Invalid pressure range, using defaults\n");
1118a46c0ec8Sopenharmony_ci		} else {
1119a46c0ec8Sopenharmony_ci			hi = r.upper;
1120a46c0ec8Sopenharmony_ci			lo = r.lower;
1121a46c0ec8Sopenharmony_ci		}
1122a46c0ec8Sopenharmony_ci	}
1123a46c0ec8Sopenharmony_ciout:
1124a46c0ec8Sopenharmony_ci	tool->pressure.threshold.upper = hi;
1125a46c0ec8Sopenharmony_ci	tool->pressure.threshold.lower = lo;
1126a46c0ec8Sopenharmony_ci
1127a46c0ec8Sopenharmony_ci	quirks_unref(q);
1128a46c0ec8Sopenharmony_ci}
1129a46c0ec8Sopenharmony_ci
1130a46c0ec8Sopenharmony_cistatic struct libinput_tablet_tool *
1131a46c0ec8Sopenharmony_citablet_get_tool(struct tablet_dispatch *tablet,
1132a46c0ec8Sopenharmony_ci		enum libinput_tablet_tool_type type,
1133a46c0ec8Sopenharmony_ci		uint32_t tool_id,
1134a46c0ec8Sopenharmony_ci		uint32_t serial)
1135a46c0ec8Sopenharmony_ci{
1136a46c0ec8Sopenharmony_ci	struct libinput *libinput = tablet_libinput_context(tablet);
1137a46c0ec8Sopenharmony_ci	struct libinput_tablet_tool *tool = NULL, *t;
1138a46c0ec8Sopenharmony_ci	struct list *tool_list;
1139a46c0ec8Sopenharmony_ci
1140a46c0ec8Sopenharmony_ci	if (serial) {
1141a46c0ec8Sopenharmony_ci		tool_list = &libinput->tool_list;
1142a46c0ec8Sopenharmony_ci		/* Check if we already have the tool in our list of tools */
1143a46c0ec8Sopenharmony_ci		list_for_each(t, tool_list, link) {
1144a46c0ec8Sopenharmony_ci			if (type == t->type && serial == t->serial) {
1145a46c0ec8Sopenharmony_ci				tool = t;
1146a46c0ec8Sopenharmony_ci				break;
1147a46c0ec8Sopenharmony_ci			}
1148a46c0ec8Sopenharmony_ci		}
1149a46c0ec8Sopenharmony_ci	}
1150a46c0ec8Sopenharmony_ci
1151a46c0ec8Sopenharmony_ci	/* If we get a tool with a delayed serial number, we already created
1152a46c0ec8Sopenharmony_ci	 * a 0-serial number tool for it earlier. Re-use that, even though
1153a46c0ec8Sopenharmony_ci	 * it means we can't distinguish this tool from others.
1154a46c0ec8Sopenharmony_ci	 * https://bugs.freedesktop.org/show_bug.cgi?id=97526
1155a46c0ec8Sopenharmony_ci	 */
1156a46c0ec8Sopenharmony_ci	if (!tool) {
1157a46c0ec8Sopenharmony_ci		tool_list = &tablet->tool_list;
1158a46c0ec8Sopenharmony_ci		/* We can't guarantee that tools without serial numbers are
1159a46c0ec8Sopenharmony_ci		 * unique, so we keep them local to the tablet that they come
1160a46c0ec8Sopenharmony_ci		 * into proximity of instead of storing them in the global tool
1161a46c0ec8Sopenharmony_ci		 * list
1162a46c0ec8Sopenharmony_ci		 * Same as above, but don't bother checking the serial number
1163a46c0ec8Sopenharmony_ci		 */
1164a46c0ec8Sopenharmony_ci		list_for_each(t, tool_list, link) {
1165a46c0ec8Sopenharmony_ci			if (type == t->type) {
1166a46c0ec8Sopenharmony_ci				tool = t;
1167a46c0ec8Sopenharmony_ci				break;
1168a46c0ec8Sopenharmony_ci			}
1169a46c0ec8Sopenharmony_ci		}
1170a46c0ec8Sopenharmony_ci
1171a46c0ec8Sopenharmony_ci		/* Didn't find the tool but we have a serial. Switch
1172a46c0ec8Sopenharmony_ci		 * tool_list back so we create in the correct list */
1173a46c0ec8Sopenharmony_ci		if (!tool && serial)
1174a46c0ec8Sopenharmony_ci			tool_list = &libinput->tool_list;
1175a46c0ec8Sopenharmony_ci	}
1176a46c0ec8Sopenharmony_ci
1177a46c0ec8Sopenharmony_ci	/* If we didn't already have the new_tool in our list of tools,
1178a46c0ec8Sopenharmony_ci	 * add it */
1179a46c0ec8Sopenharmony_ci	if (!tool) {
1180a46c0ec8Sopenharmony_ci		tool = zalloc(sizeof *tool);
1181a46c0ec8Sopenharmony_ci
1182a46c0ec8Sopenharmony_ci		*tool = (struct libinput_tablet_tool) {
1183a46c0ec8Sopenharmony_ci			.type = type,
1184a46c0ec8Sopenharmony_ci			.serial = serial,
1185a46c0ec8Sopenharmony_ci			.tool_id = tool_id,
1186a46c0ec8Sopenharmony_ci			.refcount = 1,
1187a46c0ec8Sopenharmony_ci		};
1188a46c0ec8Sopenharmony_ci
1189a46c0ec8Sopenharmony_ci		tool_set_pressure_thresholds(tablet, tool);
1190a46c0ec8Sopenharmony_ci		tool_set_bits(tablet, tool);
1191a46c0ec8Sopenharmony_ci
1192a46c0ec8Sopenharmony_ci		list_insert(tool_list, &tool->link);
1193a46c0ec8Sopenharmony_ci	}
1194a46c0ec8Sopenharmony_ci
1195a46c0ec8Sopenharmony_ci	return tool;
1196a46c0ec8Sopenharmony_ci}
1197a46c0ec8Sopenharmony_ci
1198a46c0ec8Sopenharmony_cistatic void
1199a46c0ec8Sopenharmony_citablet_notify_button_mask(struct tablet_dispatch *tablet,
1200a46c0ec8Sopenharmony_ci			  struct evdev_device *device,
1201a46c0ec8Sopenharmony_ci			  uint64_t time,
1202a46c0ec8Sopenharmony_ci			  struct libinput_tablet_tool *tool,
1203a46c0ec8Sopenharmony_ci			  const struct button_state *buttons,
1204a46c0ec8Sopenharmony_ci			  enum libinput_button_state state)
1205a46c0ec8Sopenharmony_ci{
1206a46c0ec8Sopenharmony_ci	struct libinput_device *base = &device->base;
1207a46c0ec8Sopenharmony_ci	size_t i;
1208a46c0ec8Sopenharmony_ci	size_t nbits = 8 * sizeof(buttons->bits);
1209a46c0ec8Sopenharmony_ci	enum libinput_tablet_tool_tip_state tip_state;
1210a46c0ec8Sopenharmony_ci
1211a46c0ec8Sopenharmony_ci	if (tablet_has_status(tablet, TABLET_TOOL_IN_CONTACT))
1212a46c0ec8Sopenharmony_ci		tip_state = LIBINPUT_TABLET_TOOL_TIP_DOWN;
1213a46c0ec8Sopenharmony_ci	else
1214a46c0ec8Sopenharmony_ci		tip_state = LIBINPUT_TABLET_TOOL_TIP_UP;
1215a46c0ec8Sopenharmony_ci
1216a46c0ec8Sopenharmony_ci	for (i = 0; i < nbits; i++) {
1217a46c0ec8Sopenharmony_ci		if (!bit_is_set(buttons->bits, i))
1218a46c0ec8Sopenharmony_ci			continue;
1219a46c0ec8Sopenharmony_ci
1220a46c0ec8Sopenharmony_ci		tablet_notify_button(base,
1221a46c0ec8Sopenharmony_ci				     time,
1222a46c0ec8Sopenharmony_ci				     tool,
1223a46c0ec8Sopenharmony_ci				     tip_state,
1224a46c0ec8Sopenharmony_ci				     &tablet->axes,
1225a46c0ec8Sopenharmony_ci				     i,
1226a46c0ec8Sopenharmony_ci				     state);
1227a46c0ec8Sopenharmony_ci	}
1228a46c0ec8Sopenharmony_ci}
1229a46c0ec8Sopenharmony_ci
1230a46c0ec8Sopenharmony_cistatic void
1231a46c0ec8Sopenharmony_citablet_notify_buttons(struct tablet_dispatch *tablet,
1232a46c0ec8Sopenharmony_ci		      struct evdev_device *device,
1233a46c0ec8Sopenharmony_ci		      uint64_t time,
1234a46c0ec8Sopenharmony_ci		      struct libinput_tablet_tool *tool,
1235a46c0ec8Sopenharmony_ci		      enum libinput_button_state state)
1236a46c0ec8Sopenharmony_ci{
1237a46c0ec8Sopenharmony_ci	struct button_state buttons;
1238a46c0ec8Sopenharmony_ci
1239a46c0ec8Sopenharmony_ci	if (state == LIBINPUT_BUTTON_STATE_PRESSED)
1240a46c0ec8Sopenharmony_ci		tablet_get_pressed_buttons(tablet, &buttons);
1241a46c0ec8Sopenharmony_ci	else
1242a46c0ec8Sopenharmony_ci		tablet_get_released_buttons(tablet, &buttons);
1243a46c0ec8Sopenharmony_ci
1244a46c0ec8Sopenharmony_ci	tablet_notify_button_mask(tablet,
1245a46c0ec8Sopenharmony_ci				  device,
1246a46c0ec8Sopenharmony_ci				  time,
1247a46c0ec8Sopenharmony_ci				  tool,
1248a46c0ec8Sopenharmony_ci				  &buttons,
1249a46c0ec8Sopenharmony_ci				  state);
1250a46c0ec8Sopenharmony_ci}
1251a46c0ec8Sopenharmony_ci
1252a46c0ec8Sopenharmony_cistatic void
1253a46c0ec8Sopenharmony_cisanitize_pressure_distance(struct tablet_dispatch *tablet,
1254a46c0ec8Sopenharmony_ci			   struct libinput_tablet_tool *tool)
1255a46c0ec8Sopenharmony_ci{
1256a46c0ec8Sopenharmony_ci	bool tool_in_contact;
1257a46c0ec8Sopenharmony_ci	const struct input_absinfo *distance,
1258a46c0ec8Sopenharmony_ci	                           *pressure;
1259a46c0ec8Sopenharmony_ci
1260a46c0ec8Sopenharmony_ci	distance = libevdev_get_abs_info(tablet->device->evdev, ABS_DISTANCE);
1261a46c0ec8Sopenharmony_ci	pressure = libevdev_get_abs_info(tablet->device->evdev, ABS_PRESSURE);
1262a46c0ec8Sopenharmony_ci
1263a46c0ec8Sopenharmony_ci	if (!pressure || !distance)
1264a46c0ec8Sopenharmony_ci		return;
1265a46c0ec8Sopenharmony_ci
1266a46c0ec8Sopenharmony_ci	bool pressure_changed = bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_PRESSURE);
1267a46c0ec8Sopenharmony_ci	bool distance_changed = bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_DISTANCE);
1268a46c0ec8Sopenharmony_ci
1269a46c0ec8Sopenharmony_ci	if (!pressure_changed && !distance_changed)
1270a46c0ec8Sopenharmony_ci		return;
1271a46c0ec8Sopenharmony_ci
1272a46c0ec8Sopenharmony_ci	/* Note: this is an arbitrary "in contact" decision rather than "tip
1273a46c0ec8Sopenharmony_ci	 * down". We use the lower threshold as minimum pressure value,
1274a46c0ec8Sopenharmony_ci	 * anything less than that gets filtered away */
1275a46c0ec8Sopenharmony_ci	tool_in_contact = (pressure->value > tool->pressure.threshold.lower);
1276a46c0ec8Sopenharmony_ci
1277a46c0ec8Sopenharmony_ci	/* Keep distance and pressure mutually exclusive */
1278a46c0ec8Sopenharmony_ci	if (distance &&
1279a46c0ec8Sopenharmony_ci	    distance->value > distance->minimum &&
1280a46c0ec8Sopenharmony_ci	    pressure->value > pressure->minimum) {
1281a46c0ec8Sopenharmony_ci		if (tool_in_contact) {
1282a46c0ec8Sopenharmony_ci			clear_bit(tablet->changed_axes,
1283a46c0ec8Sopenharmony_ci				  LIBINPUT_TABLET_TOOL_AXIS_DISTANCE);
1284a46c0ec8Sopenharmony_ci			tablet->axes.distance = 0;
1285a46c0ec8Sopenharmony_ci		} else {
1286a46c0ec8Sopenharmony_ci			clear_bit(tablet->changed_axes,
1287a46c0ec8Sopenharmony_ci				  LIBINPUT_TABLET_TOOL_AXIS_PRESSURE);
1288a46c0ec8Sopenharmony_ci			tablet->axes.pressure = 0;
1289a46c0ec8Sopenharmony_ci		}
1290a46c0ec8Sopenharmony_ci	} else if (pressure_changed && !tool_in_contact) {
1291a46c0ec8Sopenharmony_ci		/* Make sure that the last axis value sent to the caller is a 0 */
1292a46c0ec8Sopenharmony_ci		if (tablet->axes.pressure == 0)
1293a46c0ec8Sopenharmony_ci			clear_bit(tablet->changed_axes,
1294a46c0ec8Sopenharmony_ci				  LIBINPUT_TABLET_TOOL_AXIS_PRESSURE);
1295a46c0ec8Sopenharmony_ci		else
1296a46c0ec8Sopenharmony_ci			tablet->axes.pressure = 0;
1297a46c0ec8Sopenharmony_ci	}
1298a46c0ec8Sopenharmony_ci}
1299a46c0ec8Sopenharmony_ci
1300a46c0ec8Sopenharmony_cistatic inline void
1301a46c0ec8Sopenharmony_cisanitize_mouse_lens_rotation(struct tablet_dispatch *tablet)
1302a46c0ec8Sopenharmony_ci{
1303a46c0ec8Sopenharmony_ci	/* If we have a mouse/lens cursor and the tilt changed, the rotation
1304a46c0ec8Sopenharmony_ci	   changed. Mark this, calculate the angle later */
1305a46c0ec8Sopenharmony_ci	if ((tablet->current_tool.type == LIBINPUT_TABLET_TOOL_TYPE_MOUSE ||
1306a46c0ec8Sopenharmony_ci	    tablet->current_tool.type == LIBINPUT_TABLET_TOOL_TYPE_LENS) &&
1307a46c0ec8Sopenharmony_ci	    (bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_TILT_X) ||
1308a46c0ec8Sopenharmony_ci	     bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_TILT_Y)))
1309a46c0ec8Sopenharmony_ci		set_bit(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_ROTATION_Z);
1310a46c0ec8Sopenharmony_ci}
1311a46c0ec8Sopenharmony_ci
1312a46c0ec8Sopenharmony_cistatic void
1313a46c0ec8Sopenharmony_cisanitize_tablet_axes(struct tablet_dispatch *tablet,
1314a46c0ec8Sopenharmony_ci		     struct libinput_tablet_tool *tool)
1315a46c0ec8Sopenharmony_ci{
1316a46c0ec8Sopenharmony_ci	sanitize_pressure_distance(tablet, tool);
1317a46c0ec8Sopenharmony_ci	sanitize_mouse_lens_rotation(tablet);
1318a46c0ec8Sopenharmony_ci}
1319a46c0ec8Sopenharmony_ci
1320a46c0ec8Sopenharmony_cistatic void
1321a46c0ec8Sopenharmony_ciset_pressure_offset(struct libinput_tablet_tool *tool, int offset)
1322a46c0ec8Sopenharmony_ci{
1323a46c0ec8Sopenharmony_ci	tool->pressure.offset = offset;
1324a46c0ec8Sopenharmony_ci	tool->pressure.has_offset = true;
1325a46c0ec8Sopenharmony_ci
1326a46c0ec8Sopenharmony_ci	/* Adjust the tresholds accordingly - we use the same gap (4% in
1327a46c0ec8Sopenharmony_ci	 * device coordinates) between upper and lower as before which isn't
1328a46c0ec8Sopenharmony_ci	 * technically correct (our range shrunk) but it's easy to calculate.
1329a46c0ec8Sopenharmony_ci	 */
1330a46c0ec8Sopenharmony_ci	int gap = tool->pressure.threshold.upper - tool->pressure.threshold.lower;
1331a46c0ec8Sopenharmony_ci	tool->pressure.threshold.lower = offset;
1332a46c0ec8Sopenharmony_ci	tool->pressure.threshold.upper = offset + gap;
1333a46c0ec8Sopenharmony_ci}
1334a46c0ec8Sopenharmony_ci
1335a46c0ec8Sopenharmony_cistatic void
1336a46c0ec8Sopenharmony_ciupdate_pressure_offset(struct tablet_dispatch *tablet,
1337a46c0ec8Sopenharmony_ci		       struct evdev_device *device,
1338a46c0ec8Sopenharmony_ci		       struct libinput_tablet_tool *tool)
1339a46c0ec8Sopenharmony_ci{
1340a46c0ec8Sopenharmony_ci	const struct input_absinfo *pressure =
1341a46c0ec8Sopenharmony_ci		libevdev_get_abs_info(device->evdev, ABS_PRESSURE);
1342a46c0ec8Sopenharmony_ci
1343a46c0ec8Sopenharmony_ci	if (!pressure ||
1344a46c0ec8Sopenharmony_ci	    !bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_PRESSURE))
1345a46c0ec8Sopenharmony_ci		return;
1346a46c0ec8Sopenharmony_ci
1347a46c0ec8Sopenharmony_ci	/* If we have an event that falls below the current offset, adjust
1348a46c0ec8Sopenharmony_ci	 * the offset downwards. A fast contact can start with a
1349a46c0ec8Sopenharmony_ci	 * higher-than-needed pressure offset and then we'd be tied into a
1350a46c0ec8Sopenharmony_ci	 * high pressure offset for the rest of the session.
1351a46c0ec8Sopenharmony_ci	 *
1352a46c0ec8Sopenharmony_ci	 * If we are still pending the offset decision, only update the observed
1353a46c0ec8Sopenharmony_ci	 * offset value, don't actually set it to have an offset.
1354a46c0ec8Sopenharmony_ci	 */
1355a46c0ec8Sopenharmony_ci	int offset = pressure->value;
1356a46c0ec8Sopenharmony_ci	if (tool->pressure.has_offset) {
1357a46c0ec8Sopenharmony_ci		if (offset < tool->pressure.offset)
1358a46c0ec8Sopenharmony_ci			set_pressure_offset(tool, offset);
1359a46c0ec8Sopenharmony_ci	} else if (tool->pressure.heuristic_state != PRESSURE_HEURISTIC_STATE_DONE) {
1360a46c0ec8Sopenharmony_ci		tool->pressure.offset = min(offset, tool->pressure.offset);
1361a46c0ec8Sopenharmony_ci	}
1362a46c0ec8Sopenharmony_ci}
1363a46c0ec8Sopenharmony_ci
1364a46c0ec8Sopenharmony_cistatic void
1365a46c0ec8Sopenharmony_cidetect_pressure_offset(struct tablet_dispatch *tablet,
1366a46c0ec8Sopenharmony_ci		       struct evdev_device *device,
1367a46c0ec8Sopenharmony_ci		       struct libinput_tablet_tool *tool)
1368a46c0ec8Sopenharmony_ci{
1369a46c0ec8Sopenharmony_ci	const struct input_absinfo *pressure, *distance;
1370a46c0ec8Sopenharmony_ci	int offset;
1371a46c0ec8Sopenharmony_ci
1372a46c0ec8Sopenharmony_ci	if (tool->pressure.has_offset ||
1373a46c0ec8Sopenharmony_ci	    !bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_PRESSURE))
1374a46c0ec8Sopenharmony_ci		return;
1375a46c0ec8Sopenharmony_ci
1376a46c0ec8Sopenharmony_ci	pressure = libevdev_get_abs_info(device->evdev, ABS_PRESSURE);
1377a46c0ec8Sopenharmony_ci	distance = libevdev_get_abs_info(device->evdev, ABS_DISTANCE);
1378a46c0ec8Sopenharmony_ci
1379a46c0ec8Sopenharmony_ci	if (!pressure)
1380a46c0ec8Sopenharmony_ci		return;
1381a46c0ec8Sopenharmony_ci
1382a46c0ec8Sopenharmony_ci	offset = pressure->value;
1383a46c0ec8Sopenharmony_ci	if (offset <= pressure->minimum)
1384a46c0ec8Sopenharmony_ci		return;
1385a46c0ec8Sopenharmony_ci
1386a46c0ec8Sopenharmony_ci	if (distance) {
1387a46c0ec8Sopenharmony_ci		/* If we're closer than 50% of the distance axis, skip pressure
1388a46c0ec8Sopenharmony_ci		 * offset detection, too likely to be wrong */
1389a46c0ec8Sopenharmony_ci		if (distance->value < axis_range_percentage(distance, 50))
1390a46c0ec8Sopenharmony_ci			return;
1391a46c0ec8Sopenharmony_ci	} else {
1392a46c0ec8Sopenharmony_ci                /* A device without distance will always have some pressure on
1393a46c0ec8Sopenharmony_ci                 * contact. Offset detection is delayed for a few proximity ins
1394a46c0ec8Sopenharmony_ci                 * in the hope we'll find the minimum value until then. That
1395a46c0ec8Sopenharmony_ci                 * offset is updated during motion events so by the time the
1396a46c0ec8Sopenharmony_ci                 * deciding prox-in arrives we should know the minimum offset.
1397a46c0ec8Sopenharmony_ci                 */
1398a46c0ec8Sopenharmony_ci                if (offset > pressure->minimum)
1399a46c0ec8Sopenharmony_ci			tool->pressure.offset = min(offset, tool->pressure.offset);
1400a46c0ec8Sopenharmony_ci
1401a46c0ec8Sopenharmony_ci		switch (tool->pressure.heuristic_state) {
1402a46c0ec8Sopenharmony_ci		case PRESSURE_HEURISTIC_STATE_PROXIN1:
1403a46c0ec8Sopenharmony_ci		case PRESSURE_HEURISTIC_STATE_PROXIN2:
1404a46c0ec8Sopenharmony_ci			tool->pressure.heuristic_state++;
1405a46c0ec8Sopenharmony_ci			return;
1406a46c0ec8Sopenharmony_ci		case PRESSURE_HEURISTIC_STATE_DECIDE:
1407a46c0ec8Sopenharmony_ci			tool->pressure.heuristic_state++;
1408a46c0ec8Sopenharmony_ci			offset = tool->pressure.offset;
1409a46c0ec8Sopenharmony_ci			break;
1410a46c0ec8Sopenharmony_ci		case PRESSURE_HEURISTIC_STATE_DONE:
1411a46c0ec8Sopenharmony_ci			return;
1412a46c0ec8Sopenharmony_ci		}
1413a46c0ec8Sopenharmony_ci	}
1414a46c0ec8Sopenharmony_ci
1415a46c0ec8Sopenharmony_ci	if (offset <= pressure->minimum)
1416a46c0ec8Sopenharmony_ci		return;
1417a46c0ec8Sopenharmony_ci
1418a46c0ec8Sopenharmony_ci	if (offset > axis_range_percentage(pressure, 50)) {
1419a46c0ec8Sopenharmony_ci		evdev_log_error(device,
1420a46c0ec8Sopenharmony_ci			 "Ignoring pressure offset greater than 50%% detected on tool %s (serial %#x). "
1421a46c0ec8Sopenharmony_ci			 "See %s/tablet-support.html\n",
1422a46c0ec8Sopenharmony_ci			 tablet_tool_type_to_string(tool->type),
1423a46c0ec8Sopenharmony_ci			 tool->serial,
1424a46c0ec8Sopenharmony_ci			 HTTP_DOC_LINK);
1425a46c0ec8Sopenharmony_ci		return;
1426a46c0ec8Sopenharmony_ci	}
1427a46c0ec8Sopenharmony_ci
1428a46c0ec8Sopenharmony_ci	evdev_log_info(device,
1429a46c0ec8Sopenharmony_ci		 "Pressure offset detected on tool %s (serial %#x).  "
1430a46c0ec8Sopenharmony_ci		 "See %s/tablet-support.html\n",
1431a46c0ec8Sopenharmony_ci		 tablet_tool_type_to_string(tool->type),
1432a46c0ec8Sopenharmony_ci		 tool->serial,
1433a46c0ec8Sopenharmony_ci		 HTTP_DOC_LINK);
1434a46c0ec8Sopenharmony_ci
1435a46c0ec8Sopenharmony_ci	set_pressure_offset(tool, offset);
1436a46c0ec8Sopenharmony_ci}
1437a46c0ec8Sopenharmony_ci
1438a46c0ec8Sopenharmony_cistatic void
1439a46c0ec8Sopenharmony_cidetect_tool_contact(struct tablet_dispatch *tablet,
1440a46c0ec8Sopenharmony_ci		    struct evdev_device *device,
1441a46c0ec8Sopenharmony_ci		    struct libinput_tablet_tool *tool)
1442a46c0ec8Sopenharmony_ci{
1443a46c0ec8Sopenharmony_ci	const struct input_absinfo *p;
1444a46c0ec8Sopenharmony_ci	int pressure;
1445a46c0ec8Sopenharmony_ci
1446a46c0ec8Sopenharmony_ci	if (!bit_is_set(tool->axis_caps, LIBINPUT_TABLET_TOOL_AXIS_PRESSURE))
1447a46c0ec8Sopenharmony_ci		return;
1448a46c0ec8Sopenharmony_ci
1449a46c0ec8Sopenharmony_ci	/* if we have pressure, always use that for contact, not BTN_TOUCH */
1450a46c0ec8Sopenharmony_ci	if (tablet_has_status(tablet, TABLET_TOOL_ENTERING_CONTACT))
1451a46c0ec8Sopenharmony_ci		evdev_log_bug_libinput(device,
1452a46c0ec8Sopenharmony_ci				       "Invalid status: entering contact\n");
1453a46c0ec8Sopenharmony_ci	if (tablet_has_status(tablet, TABLET_TOOL_LEAVING_CONTACT) &&
1454a46c0ec8Sopenharmony_ci	    !tablet_has_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY))
1455a46c0ec8Sopenharmony_ci		evdev_log_bug_libinput(device,
1456a46c0ec8Sopenharmony_ci				       "Invalid status: leaving contact\n");
1457a46c0ec8Sopenharmony_ci
1458a46c0ec8Sopenharmony_ci	p = libevdev_get_abs_info(tablet->device->evdev, ABS_PRESSURE);
1459a46c0ec8Sopenharmony_ci	if (!p) {
1460a46c0ec8Sopenharmony_ci		evdev_log_bug_libinput(device,
1461a46c0ec8Sopenharmony_ci				       "Missing pressure axis\n");
1462a46c0ec8Sopenharmony_ci		return;
1463a46c0ec8Sopenharmony_ci	}
1464a46c0ec8Sopenharmony_ci	pressure = p->value;
1465a46c0ec8Sopenharmony_ci
1466a46c0ec8Sopenharmony_ci	if (pressure <= tool->pressure.threshold.lower &&
1467a46c0ec8Sopenharmony_ci	    tablet_has_status(tablet, TABLET_TOOL_IN_CONTACT)) {
1468a46c0ec8Sopenharmony_ci		tablet_set_status(tablet, TABLET_TOOL_LEAVING_CONTACT);
1469a46c0ec8Sopenharmony_ci	} else if (pressure >= tool->pressure.threshold.upper &&
1470a46c0ec8Sopenharmony_ci		   !tablet_has_status(tablet, TABLET_TOOL_IN_CONTACT)) {
1471a46c0ec8Sopenharmony_ci		tablet_set_status(tablet, TABLET_TOOL_ENTERING_CONTACT);
1472a46c0ec8Sopenharmony_ci	}
1473a46c0ec8Sopenharmony_ci}
1474a46c0ec8Sopenharmony_ci
1475a46c0ec8Sopenharmony_cistatic void
1476a46c0ec8Sopenharmony_citablet_mark_all_axes_changed(struct tablet_dispatch *tablet,
1477a46c0ec8Sopenharmony_ci			     struct libinput_tablet_tool *tool)
1478a46c0ec8Sopenharmony_ci{
1479a46c0ec8Sopenharmony_ci	static_assert(sizeof(tablet->changed_axes) ==
1480a46c0ec8Sopenharmony_ci			      sizeof(tool->axis_caps),
1481a46c0ec8Sopenharmony_ci		      "Mismatching array sizes");
1482a46c0ec8Sopenharmony_ci
1483a46c0ec8Sopenharmony_ci	memcpy(tablet->changed_axes,
1484a46c0ec8Sopenharmony_ci	       tool->axis_caps,
1485a46c0ec8Sopenharmony_ci	       sizeof(tablet->changed_axes));
1486a46c0ec8Sopenharmony_ci}
1487a46c0ec8Sopenharmony_ci
1488a46c0ec8Sopenharmony_cistatic void
1489a46c0ec8Sopenharmony_citablet_update_proximity_state(struct tablet_dispatch *tablet,
1490a46c0ec8Sopenharmony_ci			      struct evdev_device *device,
1491a46c0ec8Sopenharmony_ci			      struct libinput_tablet_tool *tool)
1492a46c0ec8Sopenharmony_ci{
1493a46c0ec8Sopenharmony_ci	const struct input_absinfo *distance;
1494a46c0ec8Sopenharmony_ci	int dist_max = tablet->cursor_proximity_threshold;
1495a46c0ec8Sopenharmony_ci	int dist;
1496a46c0ec8Sopenharmony_ci
1497a46c0ec8Sopenharmony_ci	distance = libevdev_get_abs_info(tablet->device->evdev, ABS_DISTANCE);
1498a46c0ec8Sopenharmony_ci	if (!distance)
1499a46c0ec8Sopenharmony_ci		return;
1500a46c0ec8Sopenharmony_ci
1501a46c0ec8Sopenharmony_ci	dist = distance->value;
1502a46c0ec8Sopenharmony_ci	if (dist == 0)
1503a46c0ec8Sopenharmony_ci		return;
1504a46c0ec8Sopenharmony_ci
1505a46c0ec8Sopenharmony_ci	/* Tool got into permitted range */
1506a46c0ec8Sopenharmony_ci	if (dist < dist_max &&
1507a46c0ec8Sopenharmony_ci	    (tablet_has_status(tablet, TABLET_TOOL_OUT_OF_RANGE) ||
1508a46c0ec8Sopenharmony_ci	     tablet_has_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY))) {
1509a46c0ec8Sopenharmony_ci		tablet_unset_status(tablet,
1510a46c0ec8Sopenharmony_ci				    TABLET_TOOL_OUT_OF_RANGE);
1511a46c0ec8Sopenharmony_ci		tablet_unset_status(tablet,
1512a46c0ec8Sopenharmony_ci				    TABLET_TOOL_OUT_OF_PROXIMITY);
1513a46c0ec8Sopenharmony_ci		tablet_set_status(tablet, TABLET_TOOL_ENTERING_PROXIMITY);
1514a46c0ec8Sopenharmony_ci		tablet_mark_all_axes_changed(tablet, tool);
1515a46c0ec8Sopenharmony_ci
1516a46c0ec8Sopenharmony_ci		tablet_set_status(tablet, TABLET_BUTTONS_PRESSED);
1517a46c0ec8Sopenharmony_ci		tablet_force_button_presses(tablet);
1518a46c0ec8Sopenharmony_ci		return;
1519a46c0ec8Sopenharmony_ci	}
1520a46c0ec8Sopenharmony_ci
1521a46c0ec8Sopenharmony_ci	if (dist < dist_max)
1522a46c0ec8Sopenharmony_ci		return;
1523a46c0ec8Sopenharmony_ci
1524a46c0ec8Sopenharmony_ci	/* Still out of range/proximity */
1525a46c0ec8Sopenharmony_ci	if (tablet_has_status(tablet, TABLET_TOOL_OUT_OF_RANGE) ||
1526a46c0ec8Sopenharmony_ci	    tablet_has_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY))
1527a46c0ec8Sopenharmony_ci	    return;
1528a46c0ec8Sopenharmony_ci
1529a46c0ec8Sopenharmony_ci	/* Tool entered prox but is outside of permitted range */
1530a46c0ec8Sopenharmony_ci	if (tablet_has_status(tablet,
1531a46c0ec8Sopenharmony_ci			      TABLET_TOOL_ENTERING_PROXIMITY)) {
1532a46c0ec8Sopenharmony_ci		tablet_set_status(tablet,
1533a46c0ec8Sopenharmony_ci				  TABLET_TOOL_OUT_OF_RANGE);
1534a46c0ec8Sopenharmony_ci		tablet_unset_status(tablet,
1535a46c0ec8Sopenharmony_ci				    TABLET_TOOL_ENTERING_PROXIMITY);
1536a46c0ec8Sopenharmony_ci		return;
1537a46c0ec8Sopenharmony_ci	}
1538a46c0ec8Sopenharmony_ci
1539a46c0ec8Sopenharmony_ci	/* Tool was in prox and is now outside of range. Set leaving
1540a46c0ec8Sopenharmony_ci	 * proximity, on the next event it will be OUT_OF_PROXIMITY and thus
1541a46c0ec8Sopenharmony_ci	 * caught by the above conditions */
1542a46c0ec8Sopenharmony_ci	tablet_set_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY);
1543a46c0ec8Sopenharmony_ci}
1544a46c0ec8Sopenharmony_ci
1545a46c0ec8Sopenharmony_cistatic struct phys_rect
1546a46c0ec8Sopenharmony_citablet_calculate_arbitration_rect(struct tablet_dispatch *tablet)
1547a46c0ec8Sopenharmony_ci{
1548a46c0ec8Sopenharmony_ci	struct evdev_device *device = tablet->device;
1549a46c0ec8Sopenharmony_ci	struct phys_rect r = {0};
1550a46c0ec8Sopenharmony_ci	struct phys_coords mm;
1551a46c0ec8Sopenharmony_ci
1552a46c0ec8Sopenharmony_ci	mm = evdev_device_units_to_mm(device, &tablet->axes.point);
1553a46c0ec8Sopenharmony_ci
1554a46c0ec8Sopenharmony_ci	/* The rect we disable is 20mm left of the tip, 100mm north of the
1555a46c0ec8Sopenharmony_ci	 * tip, and 200x250mm large.
1556a46c0ec8Sopenharmony_ci	 * If the stylus is tilted left (tip further right than the eraser
1557a46c0ec8Sopenharmony_ci	 * end) assume left-handed mode.
1558a46c0ec8Sopenharmony_ci	 *
1559a46c0ec8Sopenharmony_ci	 * Obviously if we'd run out of the boundaries, we clip the rect
1560a46c0ec8Sopenharmony_ci	 * accordingly.
1561a46c0ec8Sopenharmony_ci	 */
1562a46c0ec8Sopenharmony_ci	if (tablet->axes.tilt.x > 0) {
1563a46c0ec8Sopenharmony_ci		r.x = mm.x - 20;
1564a46c0ec8Sopenharmony_ci		r.w = 200;
1565a46c0ec8Sopenharmony_ci	} else {
1566a46c0ec8Sopenharmony_ci		r.x = mm.x + 20;
1567a46c0ec8Sopenharmony_ci		r.w = 200;
1568a46c0ec8Sopenharmony_ci		r.x -= r.w;
1569a46c0ec8Sopenharmony_ci	}
1570a46c0ec8Sopenharmony_ci
1571a46c0ec8Sopenharmony_ci	if (r.x < 0) {
1572a46c0ec8Sopenharmony_ci		r.w += r.x;
1573a46c0ec8Sopenharmony_ci		r.x = 0;
1574a46c0ec8Sopenharmony_ci	}
1575a46c0ec8Sopenharmony_ci
1576a46c0ec8Sopenharmony_ci	r.y = mm.y - 100;
1577a46c0ec8Sopenharmony_ci	r.h = 250;
1578a46c0ec8Sopenharmony_ci	if (r.y < 0) {
1579a46c0ec8Sopenharmony_ci		r.h += r.y;
1580a46c0ec8Sopenharmony_ci		r.y = 0;
1581a46c0ec8Sopenharmony_ci	}
1582a46c0ec8Sopenharmony_ci
1583a46c0ec8Sopenharmony_ci	return r;
1584a46c0ec8Sopenharmony_ci}
1585a46c0ec8Sopenharmony_ci
1586a46c0ec8Sopenharmony_cistatic inline void
1587a46c0ec8Sopenharmony_citablet_update_touch_device_rect(struct tablet_dispatch *tablet,
1588a46c0ec8Sopenharmony_ci				const struct tablet_axes *axes,
1589a46c0ec8Sopenharmony_ci				uint64_t time)
1590a46c0ec8Sopenharmony_ci{
1591a46c0ec8Sopenharmony_ci	struct evdev_dispatch *dispatch;
1592a46c0ec8Sopenharmony_ci	struct phys_rect rect = {0};
1593a46c0ec8Sopenharmony_ci
1594a46c0ec8Sopenharmony_ci	if (tablet->touch_device == NULL ||
1595a46c0ec8Sopenharmony_ci	    tablet->arbitration != ARBITRATION_IGNORE_RECT)
1596a46c0ec8Sopenharmony_ci		return;
1597a46c0ec8Sopenharmony_ci
1598a46c0ec8Sopenharmony_ci	rect = tablet_calculate_arbitration_rect(tablet);
1599a46c0ec8Sopenharmony_ci
1600a46c0ec8Sopenharmony_ci	dispatch = tablet->touch_device->dispatch;
1601a46c0ec8Sopenharmony_ci	if (dispatch->interface->touch_arbitration_update_rect)
1602a46c0ec8Sopenharmony_ci		dispatch->interface->touch_arbitration_update_rect(dispatch,
1603a46c0ec8Sopenharmony_ci								   tablet->touch_device,
1604a46c0ec8Sopenharmony_ci								   &rect,
1605a46c0ec8Sopenharmony_ci								   time);
1606a46c0ec8Sopenharmony_ci}
1607a46c0ec8Sopenharmony_ci
1608a46c0ec8Sopenharmony_cistatic inline bool
1609a46c0ec8Sopenharmony_citablet_send_proximity_in(struct tablet_dispatch *tablet,
1610a46c0ec8Sopenharmony_ci			 struct libinput_tablet_tool *tool,
1611a46c0ec8Sopenharmony_ci			 struct evdev_device *device,
1612a46c0ec8Sopenharmony_ci			 struct tablet_axes *axes,
1613a46c0ec8Sopenharmony_ci			 uint64_t time)
1614a46c0ec8Sopenharmony_ci{
1615a46c0ec8Sopenharmony_ci	if (!tablet_has_status(tablet, TABLET_TOOL_ENTERING_PROXIMITY))
1616a46c0ec8Sopenharmony_ci		return false;
1617a46c0ec8Sopenharmony_ci
1618a46c0ec8Sopenharmony_ci	tablet_notify_proximity(&device->base,
1619a46c0ec8Sopenharmony_ci				time,
1620a46c0ec8Sopenharmony_ci				tool,
1621a46c0ec8Sopenharmony_ci				LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN,
1622a46c0ec8Sopenharmony_ci				tablet->changed_axes,
1623a46c0ec8Sopenharmony_ci				axes);
1624a46c0ec8Sopenharmony_ci	tablet_unset_status(tablet, TABLET_TOOL_ENTERING_PROXIMITY);
1625a46c0ec8Sopenharmony_ci	tablet_unset_status(tablet, TABLET_AXES_UPDATED);
1626a46c0ec8Sopenharmony_ci
1627a46c0ec8Sopenharmony_ci	tablet_reset_changed_axes(tablet);
1628a46c0ec8Sopenharmony_ci	axes->delta.x = 0;
1629a46c0ec8Sopenharmony_ci	axes->delta.y = 0;
1630a46c0ec8Sopenharmony_ci
1631a46c0ec8Sopenharmony_ci	return true;
1632a46c0ec8Sopenharmony_ci}
1633a46c0ec8Sopenharmony_ci
1634a46c0ec8Sopenharmony_cistatic inline bool
1635a46c0ec8Sopenharmony_citablet_send_proximity_out(struct tablet_dispatch *tablet,
1636a46c0ec8Sopenharmony_ci			 struct libinput_tablet_tool *tool,
1637a46c0ec8Sopenharmony_ci			 struct evdev_device *device,
1638a46c0ec8Sopenharmony_ci			 struct tablet_axes *axes,
1639a46c0ec8Sopenharmony_ci			 uint64_t time)
1640a46c0ec8Sopenharmony_ci{
1641a46c0ec8Sopenharmony_ci	if (!tablet_has_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY))
1642a46c0ec8Sopenharmony_ci		return false;
1643a46c0ec8Sopenharmony_ci
1644a46c0ec8Sopenharmony_ci	tablet_notify_proximity(&device->base,
1645a46c0ec8Sopenharmony_ci				time,
1646a46c0ec8Sopenharmony_ci				tool,
1647a46c0ec8Sopenharmony_ci				LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT,
1648a46c0ec8Sopenharmony_ci				tablet->changed_axes,
1649a46c0ec8Sopenharmony_ci				axes);
1650a46c0ec8Sopenharmony_ci
1651a46c0ec8Sopenharmony_ci	tablet_set_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY);
1652a46c0ec8Sopenharmony_ci	tablet_unset_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY);
1653a46c0ec8Sopenharmony_ci
1654a46c0ec8Sopenharmony_ci	tablet_reset_changed_axes(tablet);
1655a46c0ec8Sopenharmony_ci	axes->delta.x = 0;
1656a46c0ec8Sopenharmony_ci	axes->delta.y = 0;
1657a46c0ec8Sopenharmony_ci
1658a46c0ec8Sopenharmony_ci	return true;
1659a46c0ec8Sopenharmony_ci}
1660a46c0ec8Sopenharmony_ci
1661a46c0ec8Sopenharmony_cistatic inline bool
1662a46c0ec8Sopenharmony_citablet_send_tip(struct tablet_dispatch *tablet,
1663a46c0ec8Sopenharmony_ci		struct libinput_tablet_tool *tool,
1664a46c0ec8Sopenharmony_ci		struct evdev_device *device,
1665a46c0ec8Sopenharmony_ci		struct tablet_axes *axes,
1666a46c0ec8Sopenharmony_ci		uint64_t time)
1667a46c0ec8Sopenharmony_ci{
1668a46c0ec8Sopenharmony_ci	if (tablet_has_status(tablet, TABLET_TOOL_ENTERING_CONTACT)) {
1669a46c0ec8Sopenharmony_ci		tablet_notify_tip(&device->base,
1670a46c0ec8Sopenharmony_ci				  time,
1671a46c0ec8Sopenharmony_ci				  tool,
1672a46c0ec8Sopenharmony_ci				  LIBINPUT_TABLET_TOOL_TIP_DOWN,
1673a46c0ec8Sopenharmony_ci				  tablet->changed_axes,
1674a46c0ec8Sopenharmony_ci				  axes);
1675a46c0ec8Sopenharmony_ci		tablet_unset_status(tablet, TABLET_AXES_UPDATED);
1676a46c0ec8Sopenharmony_ci		tablet_unset_status(tablet, TABLET_TOOL_ENTERING_CONTACT);
1677a46c0ec8Sopenharmony_ci		tablet_set_status(tablet, TABLET_TOOL_IN_CONTACT);
1678a46c0ec8Sopenharmony_ci
1679a46c0ec8Sopenharmony_ci		tablet_reset_changed_axes(tablet);
1680a46c0ec8Sopenharmony_ci		axes->delta.x = 0;
1681a46c0ec8Sopenharmony_ci		axes->delta.y = 0;
1682a46c0ec8Sopenharmony_ci
1683a46c0ec8Sopenharmony_ci		return true;
1684a46c0ec8Sopenharmony_ci	}
1685a46c0ec8Sopenharmony_ci
1686a46c0ec8Sopenharmony_ci	if (tablet_has_status(tablet, TABLET_TOOL_LEAVING_CONTACT)) {
1687a46c0ec8Sopenharmony_ci		tablet_notify_tip(&device->base,
1688a46c0ec8Sopenharmony_ci				  time,
1689a46c0ec8Sopenharmony_ci				  tool,
1690a46c0ec8Sopenharmony_ci				  LIBINPUT_TABLET_TOOL_TIP_UP,
1691a46c0ec8Sopenharmony_ci				  tablet->changed_axes,
1692a46c0ec8Sopenharmony_ci				  axes);
1693a46c0ec8Sopenharmony_ci		tablet_unset_status(tablet, TABLET_AXES_UPDATED);
1694a46c0ec8Sopenharmony_ci		tablet_unset_status(tablet, TABLET_TOOL_LEAVING_CONTACT);
1695a46c0ec8Sopenharmony_ci		tablet_unset_status(tablet, TABLET_TOOL_IN_CONTACT);
1696a46c0ec8Sopenharmony_ci
1697a46c0ec8Sopenharmony_ci		tablet_reset_changed_axes(tablet);
1698a46c0ec8Sopenharmony_ci		axes->delta.x = 0;
1699a46c0ec8Sopenharmony_ci		axes->delta.y = 0;
1700a46c0ec8Sopenharmony_ci
1701a46c0ec8Sopenharmony_ci		return true;
1702a46c0ec8Sopenharmony_ci	}
1703a46c0ec8Sopenharmony_ci
1704a46c0ec8Sopenharmony_ci	return false;
1705a46c0ec8Sopenharmony_ci}
1706a46c0ec8Sopenharmony_ci
1707a46c0ec8Sopenharmony_cistatic inline void
1708a46c0ec8Sopenharmony_citablet_send_axes(struct tablet_dispatch *tablet,
1709a46c0ec8Sopenharmony_ci		 struct libinput_tablet_tool *tool,
1710a46c0ec8Sopenharmony_ci		 struct evdev_device *device,
1711a46c0ec8Sopenharmony_ci		 struct tablet_axes *axes,
1712a46c0ec8Sopenharmony_ci		 uint64_t time)
1713a46c0ec8Sopenharmony_ci{
1714a46c0ec8Sopenharmony_ci	enum libinput_tablet_tool_tip_state tip_state;
1715a46c0ec8Sopenharmony_ci
1716a46c0ec8Sopenharmony_ci	if (!tablet_has_status(tablet, TABLET_AXES_UPDATED))
1717a46c0ec8Sopenharmony_ci		return;
1718a46c0ec8Sopenharmony_ci
1719a46c0ec8Sopenharmony_ci	if (tablet_has_status(tablet,
1720a46c0ec8Sopenharmony_ci			      TABLET_TOOL_IN_CONTACT))
1721a46c0ec8Sopenharmony_ci		tip_state = LIBINPUT_TABLET_TOOL_TIP_DOWN;
1722a46c0ec8Sopenharmony_ci	else
1723a46c0ec8Sopenharmony_ci		tip_state = LIBINPUT_TABLET_TOOL_TIP_UP;
1724a46c0ec8Sopenharmony_ci
1725a46c0ec8Sopenharmony_ci	tablet_notify_axis(&device->base,
1726a46c0ec8Sopenharmony_ci			   time,
1727a46c0ec8Sopenharmony_ci			   tool,
1728a46c0ec8Sopenharmony_ci			   tip_state,
1729a46c0ec8Sopenharmony_ci			   tablet->changed_axes,
1730a46c0ec8Sopenharmony_ci			   axes);
1731a46c0ec8Sopenharmony_ci	tablet_unset_status(tablet, TABLET_AXES_UPDATED);
1732a46c0ec8Sopenharmony_ci	tablet_reset_changed_axes(tablet);
1733a46c0ec8Sopenharmony_ci	axes->delta.x = 0;
1734a46c0ec8Sopenharmony_ci	axes->delta.y = 0;
1735a46c0ec8Sopenharmony_ci}
1736a46c0ec8Sopenharmony_ci
1737a46c0ec8Sopenharmony_cistatic inline void
1738a46c0ec8Sopenharmony_citablet_send_buttons(struct tablet_dispatch *tablet,
1739a46c0ec8Sopenharmony_ci		    struct libinput_tablet_tool *tool,
1740a46c0ec8Sopenharmony_ci		    struct evdev_device *device,
1741a46c0ec8Sopenharmony_ci		    uint64_t time)
1742a46c0ec8Sopenharmony_ci{
1743a46c0ec8Sopenharmony_ci	if (tablet_has_status(tablet, TABLET_BUTTONS_RELEASED)) {
1744a46c0ec8Sopenharmony_ci		tablet_notify_buttons(tablet,
1745a46c0ec8Sopenharmony_ci				      device,
1746a46c0ec8Sopenharmony_ci				      time,
1747a46c0ec8Sopenharmony_ci				      tool,
1748a46c0ec8Sopenharmony_ci				      LIBINPUT_BUTTON_STATE_RELEASED);
1749a46c0ec8Sopenharmony_ci		tablet_unset_status(tablet, TABLET_BUTTONS_RELEASED);
1750a46c0ec8Sopenharmony_ci	}
1751a46c0ec8Sopenharmony_ci
1752a46c0ec8Sopenharmony_ci	if (tablet_has_status(tablet, TABLET_BUTTONS_PRESSED)) {
1753a46c0ec8Sopenharmony_ci		tablet_notify_buttons(tablet,
1754a46c0ec8Sopenharmony_ci				      device,
1755a46c0ec8Sopenharmony_ci				      time,
1756a46c0ec8Sopenharmony_ci				      tool,
1757a46c0ec8Sopenharmony_ci				      LIBINPUT_BUTTON_STATE_PRESSED);
1758a46c0ec8Sopenharmony_ci		tablet_unset_status(tablet, TABLET_BUTTONS_PRESSED);
1759a46c0ec8Sopenharmony_ci	}
1760a46c0ec8Sopenharmony_ci}
1761a46c0ec8Sopenharmony_ci
1762a46c0ec8Sopenharmony_cistatic void
1763a46c0ec8Sopenharmony_citablet_send_events(struct tablet_dispatch *tablet,
1764a46c0ec8Sopenharmony_ci		   struct libinput_tablet_tool *tool,
1765a46c0ec8Sopenharmony_ci		   struct evdev_device *device,
1766a46c0ec8Sopenharmony_ci		   uint64_t time)
1767a46c0ec8Sopenharmony_ci{
1768a46c0ec8Sopenharmony_ci	struct tablet_axes axes = {0};
1769a46c0ec8Sopenharmony_ci
1770a46c0ec8Sopenharmony_ci	if (tablet_has_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY)) {
1771a46c0ec8Sopenharmony_ci		/* Tool is leaving proximity, we can't rely on the last axis
1772a46c0ec8Sopenharmony_ci		 * information (it'll be mostly 0), so we just get the
1773a46c0ec8Sopenharmony_ci		 * current state and skip over updating the axes.
1774a46c0ec8Sopenharmony_ci		 */
1775a46c0ec8Sopenharmony_ci		axes = tablet->axes;
1776a46c0ec8Sopenharmony_ci
1777a46c0ec8Sopenharmony_ci		/* Don't send an axis event, but we may have a tip event
1778a46c0ec8Sopenharmony_ci		 * update */
1779a46c0ec8Sopenharmony_ci		tablet_unset_status(tablet, TABLET_AXES_UPDATED);
1780a46c0ec8Sopenharmony_ci	} else {
1781a46c0ec8Sopenharmony_ci		if (tablet_check_notify_axes(tablet, device, tool, &axes, time))
1782a46c0ec8Sopenharmony_ci			tablet_update_touch_device_rect(tablet, &axes, time);
1783a46c0ec8Sopenharmony_ci	}
1784a46c0ec8Sopenharmony_ci
1785a46c0ec8Sopenharmony_ci	assert(tablet->axes.delta.x == 0);
1786a46c0ec8Sopenharmony_ci	assert(tablet->axes.delta.y == 0);
1787a46c0ec8Sopenharmony_ci
1788a46c0ec8Sopenharmony_ci	tablet_send_proximity_in(tablet, tool, device, &axes, time);
1789a46c0ec8Sopenharmony_ci	if (!tablet_send_tip(tablet, tool, device, &axes, time))
1790a46c0ec8Sopenharmony_ci		tablet_send_axes(tablet, tool, device, &axes, time);
1791a46c0ec8Sopenharmony_ci
1792a46c0ec8Sopenharmony_ci	tablet_unset_status(tablet, TABLET_TOOL_ENTERING_CONTACT);
1793a46c0ec8Sopenharmony_ci	tablet_reset_changed_axes(tablet);
1794a46c0ec8Sopenharmony_ci
1795a46c0ec8Sopenharmony_ci	tablet_send_buttons(tablet, tool, device, time);
1796a46c0ec8Sopenharmony_ci
1797a46c0ec8Sopenharmony_ci	if (tablet_send_proximity_out(tablet, tool, device, &axes, time)) {
1798a46c0ec8Sopenharmony_ci		tablet_change_to_left_handed(device);
1799a46c0ec8Sopenharmony_ci		tablet_apply_rotation(device);
1800a46c0ec8Sopenharmony_ci		tablet_history_reset(tablet);
1801a46c0ec8Sopenharmony_ci	}
1802a46c0ec8Sopenharmony_ci}
1803a46c0ec8Sopenharmony_ci
1804a46c0ec8Sopenharmony_ci/**
1805a46c0ec8Sopenharmony_ci * Handling for the proximity out workaround. Some tablets only send
1806a46c0ec8Sopenharmony_ci * BTN_TOOL_PEN on the very first event, then leave it set even when the pen
1807a46c0ec8Sopenharmony_ci * leaves the detectable range. To libinput this looks like we always have
1808a46c0ec8Sopenharmony_ci * the pen in proximity.
1809a46c0ec8Sopenharmony_ci *
1810a46c0ec8Sopenharmony_ci * To avoid this, we set a timer on BTN_TOOL_PEN in. We expect the tablet to
1811a46c0ec8Sopenharmony_ci * continuously send events, and while it's doing so we keep updating the
1812a46c0ec8Sopenharmony_ci * timer. Once we go Xms without an event we assume proximity out and inject
1813a46c0ec8Sopenharmony_ci * a BTN_TOOL_PEN event into the sequence through the timer func.
1814a46c0ec8Sopenharmony_ci *
1815a46c0ec8Sopenharmony_ci * We need to remember that we did that, on the first event after the
1816a46c0ec8Sopenharmony_ci * timeout we need to emulate a BTN_TOOL_PEN event again to force proximity
1817a46c0ec8Sopenharmony_ci * in.
1818a46c0ec8Sopenharmony_ci *
1819a46c0ec8Sopenharmony_ci * Other tools never send the BTN_TOOL_PEN event. For those tools, we
1820a46c0ec8Sopenharmony_ci * piggyback along with the proximity out quirks by injecting
1821a46c0ec8Sopenharmony_ci * the event during the first event frame.
1822a46c0ec8Sopenharmony_ci */
1823a46c0ec8Sopenharmony_cistatic inline void
1824a46c0ec8Sopenharmony_citablet_proximity_out_quirk_set_timer(struct tablet_dispatch *tablet,
1825a46c0ec8Sopenharmony_ci				     uint64_t time)
1826a46c0ec8Sopenharmony_ci{
1827a46c0ec8Sopenharmony_ci	if (tablet->quirks.need_to_force_prox_out)
1828a46c0ec8Sopenharmony_ci		libinput_timer_set(&tablet->quirks.prox_out_timer,
1829a46c0ec8Sopenharmony_ci				   time + FORCED_PROXOUT_TIMEOUT);
1830a46c0ec8Sopenharmony_ci}
1831a46c0ec8Sopenharmony_ci
1832a46c0ec8Sopenharmony_cistatic bool
1833a46c0ec8Sopenharmony_citablet_update_tool_state(struct tablet_dispatch *tablet,
1834a46c0ec8Sopenharmony_ci			 struct evdev_device *device,
1835a46c0ec8Sopenharmony_ci			 uint64_t time)
1836a46c0ec8Sopenharmony_ci{
1837a46c0ec8Sopenharmony_ci	enum libinput_tablet_tool_type type;
1838a46c0ec8Sopenharmony_ci	uint32_t changed;
1839a46c0ec8Sopenharmony_ci	int state;
1840a46c0ec8Sopenharmony_ci	uint32_t doubled_up_new_tool_bit = 0;
1841a46c0ec8Sopenharmony_ci
1842a46c0ec8Sopenharmony_ci	/* we were already out of proximity but now got a tool update but
1843a46c0ec8Sopenharmony_ci	 * our tool state is zero - i.e. we got a valid prox out from the
1844a46c0ec8Sopenharmony_ci	 * device.
1845a46c0ec8Sopenharmony_ci	 */
1846a46c0ec8Sopenharmony_ci	if (tablet->quirks.proximity_out_forced &&
1847a46c0ec8Sopenharmony_ci	    tablet_has_status(tablet, TABLET_TOOL_UPDATED) &&
1848a46c0ec8Sopenharmony_ci	    !tablet->tool_state) {
1849a46c0ec8Sopenharmony_ci		tablet->quirks.need_to_force_prox_out = false;
1850a46c0ec8Sopenharmony_ci		tablet->quirks.proximity_out_forced = false;
1851a46c0ec8Sopenharmony_ci	}
1852a46c0ec8Sopenharmony_ci	/* We need to emulate a BTN_TOOL_PEN if we get an axis event (i.e.
1853a46c0ec8Sopenharmony_ci	 * stylus is def. in proximity) and:
1854a46c0ec8Sopenharmony_ci	 * - we forced a proximity out before, or
1855a46c0ec8Sopenharmony_ci	 * - on the very first event after init, because if we didn't get a
1856a46c0ec8Sopenharmony_ci	 *   BTN_TOOL_PEN and the state for the tool was 0, this device will
1857a46c0ec8Sopenharmony_ci	 *   never send the event.
1858a46c0ec8Sopenharmony_ci	 * We don't do this for pure button events because we discard those.
1859a46c0ec8Sopenharmony_ci	 *
1860a46c0ec8Sopenharmony_ci	 * But: on some devices the proximity out is delayed by the kernel,
1861a46c0ec8Sopenharmony_ci	 * so we get it after our forced prox-out has triggered. In that
1862a46c0ec8Sopenharmony_ci	 * case we need to just ignore the change.
1863a46c0ec8Sopenharmony_ci	 */
1864a46c0ec8Sopenharmony_ci	if (tablet_has_status(tablet, TABLET_AXES_UPDATED)) {
1865a46c0ec8Sopenharmony_ci		if (tablet->quirks.proximity_out_forced) {
1866a46c0ec8Sopenharmony_ci			if (!tablet_has_status(tablet, TABLET_TOOL_UPDATED)  &&
1867a46c0ec8Sopenharmony_ci			    !tablet->tool_state)
1868a46c0ec8Sopenharmony_ci				tablet->tool_state = bit(LIBINPUT_TABLET_TOOL_TYPE_PEN);
1869a46c0ec8Sopenharmony_ci			tablet->quirks.proximity_out_forced = false;
1870a46c0ec8Sopenharmony_ci		} else if (tablet->tool_state == 0 &&
1871a46c0ec8Sopenharmony_ci			    tablet->current_tool.type == LIBINPUT_TOOL_NONE) {
1872a46c0ec8Sopenharmony_ci			tablet->tool_state = bit(LIBINPUT_TABLET_TOOL_TYPE_PEN);
1873a46c0ec8Sopenharmony_ci			tablet->quirks.proximity_out_forced = false;
1874a46c0ec8Sopenharmony_ci		}
1875a46c0ec8Sopenharmony_ci	}
1876a46c0ec8Sopenharmony_ci
1877a46c0ec8Sopenharmony_ci	if (tablet->tool_state == tablet->prev_tool_state)
1878a46c0ec8Sopenharmony_ci		return false;
1879a46c0ec8Sopenharmony_ci
1880a46c0ec8Sopenharmony_ci	/* Kernel tools are supposed to be mutually exclusive, but we may have
1881a46c0ec8Sopenharmony_ci	 * two bits set due to firmware/kernel bugs.
1882a46c0ec8Sopenharmony_ci	 * Two cases that have been seen in the wild:
1883a46c0ec8Sopenharmony_ci	 * - BTN_TOOL_PEN on proximity in, followed by
1884a46c0ec8Sopenharmony_ci	 *   BTN_TOOL_RUBBER later, see #259
1885a46c0ec8Sopenharmony_ci	 *   -> We force a prox-out of the pen, trigger prox-in for eraser
1886a46c0ec8Sopenharmony_ci	 * - BTN_TOOL_RUBBER on proximity in, but BTN_TOOL_PEN when
1887a46c0ec8Sopenharmony_ci	 *   the tip is down, see #702.
1888a46c0ec8Sopenharmony_ci	 *   -> We ignore BTN_TOOL_PEN
1889a46c0ec8Sopenharmony_ci	 * In both cases the eraser is what we want, so we bias
1890a46c0ec8Sopenharmony_ci	 * towards that.
1891a46c0ec8Sopenharmony_ci	 */
1892a46c0ec8Sopenharmony_ci	if (tablet->tool_state & (tablet->tool_state - 1)) {
1893a46c0ec8Sopenharmony_ci		doubled_up_new_tool_bit = tablet->tool_state ^ tablet->prev_tool_state;
1894a46c0ec8Sopenharmony_ci
1895a46c0ec8Sopenharmony_ci		/* The new tool is the pen. Ignore it */
1896a46c0ec8Sopenharmony_ci		if (doubled_up_new_tool_bit == bit(LIBINPUT_TABLET_TOOL_TYPE_PEN)) {
1897a46c0ec8Sopenharmony_ci			tablet->tool_state &= ~bit(LIBINPUT_TABLET_TOOL_TYPE_PEN);
1898a46c0ec8Sopenharmony_ci			return false;
1899a46c0ec8Sopenharmony_ci		}
1900a46c0ec8Sopenharmony_ci
1901a46c0ec8Sopenharmony_ci		/* The new tool is some tool other than pen (usually eraser).
1902a46c0ec8Sopenharmony_ci		 * We set the current tool state to zero, thus setting
1903a46c0ec8Sopenharmony_ci		 * everything up for a prox out on the tool. Once that is set
1904a46c0ec8Sopenharmony_ci		 * up, we change the tool state to be the new one we just got.
1905a46c0ec8Sopenharmony_ci		 * When we re-process this function we now get the new tool
1906a46c0ec8Sopenharmony_ci		 * as prox in. Importantly, we basically rely on nothing else
1907a46c0ec8Sopenharmony_ci		 * happening in the meantime.
1908a46c0ec8Sopenharmony_ci		 */
1909a46c0ec8Sopenharmony_ci		tablet->tool_state = 0;
1910a46c0ec8Sopenharmony_ci	}
1911a46c0ec8Sopenharmony_ci
1912a46c0ec8Sopenharmony_ci	changed = tablet->tool_state ^ tablet->prev_tool_state;
1913a46c0ec8Sopenharmony_ci	type = ffs(changed) - 1;
1914a46c0ec8Sopenharmony_ci	state = !!(tablet->tool_state & bit(type));
1915a46c0ec8Sopenharmony_ci
1916a46c0ec8Sopenharmony_ci	tablet_update_tool(tablet, device, type, state);
1917a46c0ec8Sopenharmony_ci
1918a46c0ec8Sopenharmony_ci	/* The proximity timeout is only needed for BTN_TOOL_PEN, devices
1919a46c0ec8Sopenharmony_ci	 * that require it don't do erasers */
1920a46c0ec8Sopenharmony_ci	if (type == LIBINPUT_TABLET_TOOL_TYPE_PEN) {
1921a46c0ec8Sopenharmony_ci		if (state) {
1922a46c0ec8Sopenharmony_ci			tablet_proximity_out_quirk_set_timer(tablet, time);
1923a46c0ec8Sopenharmony_ci		} else {
1924a46c0ec8Sopenharmony_ci			/* If we get a BTN_TOOL_PEN 0 when *not* injecting
1925a46c0ec8Sopenharmony_ci			 * events it means the tablet will give us the right
1926a46c0ec8Sopenharmony_ci			 * events after all and we can disable our
1927a46c0ec8Sopenharmony_ci			 * timer-based proximity out.
1928a46c0ec8Sopenharmony_ci			 */
1929a46c0ec8Sopenharmony_ci			if (!tablet->quirks.proximity_out_in_progress)
1930a46c0ec8Sopenharmony_ci				tablet->quirks.need_to_force_prox_out = false;
1931a46c0ec8Sopenharmony_ci
1932a46c0ec8Sopenharmony_ci			libinput_timer_cancel(&tablet->quirks.prox_out_timer);
1933a46c0ec8Sopenharmony_ci		}
1934a46c0ec8Sopenharmony_ci	}
1935a46c0ec8Sopenharmony_ci
1936a46c0ec8Sopenharmony_ci	tablet->prev_tool_state = tablet->tool_state;
1937a46c0ec8Sopenharmony_ci
1938a46c0ec8Sopenharmony_ci	if (doubled_up_new_tool_bit) {
1939a46c0ec8Sopenharmony_ci		tablet->tool_state = doubled_up_new_tool_bit;
1940a46c0ec8Sopenharmony_ci		return true; /* need to re-process */
1941a46c0ec8Sopenharmony_ci	}
1942a46c0ec8Sopenharmony_ci	return false;
1943a46c0ec8Sopenharmony_ci}
1944a46c0ec8Sopenharmony_ci
1945a46c0ec8Sopenharmony_cistatic struct libinput_tablet_tool *
1946a46c0ec8Sopenharmony_citablet_get_current_tool(struct tablet_dispatch *tablet)
1947a46c0ec8Sopenharmony_ci{
1948a46c0ec8Sopenharmony_ci	if (tablet->current_tool.type == LIBINPUT_TOOL_NONE)
1949a46c0ec8Sopenharmony_ci		return NULL;
1950a46c0ec8Sopenharmony_ci
1951a46c0ec8Sopenharmony_ci	return tablet_get_tool(tablet,
1952a46c0ec8Sopenharmony_ci			       tablet->current_tool.type,
1953a46c0ec8Sopenharmony_ci			       tablet->current_tool.id,
1954a46c0ec8Sopenharmony_ci			       tablet->current_tool.serial);
1955a46c0ec8Sopenharmony_ci}
1956a46c0ec8Sopenharmony_ci
1957a46c0ec8Sopenharmony_cistatic void
1958a46c0ec8Sopenharmony_citablet_flush(struct tablet_dispatch *tablet,
1959a46c0ec8Sopenharmony_ci	     struct evdev_device *device,
1960a46c0ec8Sopenharmony_ci	     uint64_t time)
1961a46c0ec8Sopenharmony_ci{
1962a46c0ec8Sopenharmony_ci	struct libinput_tablet_tool *tool;
1963a46c0ec8Sopenharmony_ci	bool process_tool_twice;
1964a46c0ec8Sopenharmony_ci
1965a46c0ec8Sopenharmony_cireprocess:
1966a46c0ec8Sopenharmony_ci	process_tool_twice = tablet_update_tool_state(tablet, device, time);
1967a46c0ec8Sopenharmony_ci
1968a46c0ec8Sopenharmony_ci	tool = tablet_get_current_tool(tablet);
1969a46c0ec8Sopenharmony_ci	if (!tool)
1970a46c0ec8Sopenharmony_ci		return; /* OOM */
1971a46c0ec8Sopenharmony_ci
1972a46c0ec8Sopenharmony_ci	if (tool->type == LIBINPUT_TABLET_TOOL_TYPE_MOUSE ||
1973a46c0ec8Sopenharmony_ci	    tool->type == LIBINPUT_TABLET_TOOL_TYPE_LENS)
1974a46c0ec8Sopenharmony_ci		tablet_update_proximity_state(tablet, device, tool);
1975a46c0ec8Sopenharmony_ci
1976a46c0ec8Sopenharmony_ci	if (tablet_has_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY) ||
1977a46c0ec8Sopenharmony_ci	    tablet_has_status(tablet, TABLET_TOOL_OUT_OF_RANGE))
1978a46c0ec8Sopenharmony_ci		return;
1979a46c0ec8Sopenharmony_ci
1980a46c0ec8Sopenharmony_ci	if (tablet_has_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY)) {
1981a46c0ec8Sopenharmony_ci		/* Release all stylus buttons */
1982a46c0ec8Sopenharmony_ci		memset(tablet->button_state.bits,
1983a46c0ec8Sopenharmony_ci		       0,
1984a46c0ec8Sopenharmony_ci		       sizeof(tablet->button_state.bits));
1985a46c0ec8Sopenharmony_ci		tablet_set_status(tablet, TABLET_BUTTONS_RELEASED);
1986a46c0ec8Sopenharmony_ci		if (tablet_has_status(tablet, TABLET_TOOL_IN_CONTACT))
1987a46c0ec8Sopenharmony_ci			tablet_set_status(tablet, TABLET_TOOL_LEAVING_CONTACT);
1988a46c0ec8Sopenharmony_ci	} else if (tablet_has_status(tablet, TABLET_TOOL_ENTERING_PROXIMITY)) {
1989a46c0ec8Sopenharmony_ci		tablet_mark_all_axes_changed(tablet, tool);
1990a46c0ec8Sopenharmony_ci		update_pressure_offset(tablet, device, tool);
1991a46c0ec8Sopenharmony_ci		detect_pressure_offset(tablet, device, tool);
1992a46c0ec8Sopenharmony_ci		detect_tool_contact(tablet, device, tool);
1993a46c0ec8Sopenharmony_ci		sanitize_tablet_axes(tablet, tool);
1994a46c0ec8Sopenharmony_ci	} else if (tablet_has_status(tablet, TABLET_AXES_UPDATED)) {
1995a46c0ec8Sopenharmony_ci		update_pressure_offset(tablet, device, tool);
1996a46c0ec8Sopenharmony_ci		detect_tool_contact(tablet, device, tool);
1997a46c0ec8Sopenharmony_ci		sanitize_tablet_axes(tablet, tool);
1998a46c0ec8Sopenharmony_ci	}
1999a46c0ec8Sopenharmony_ci
2000a46c0ec8Sopenharmony_ci	tablet_send_events(tablet, tool, device, time);
2001a46c0ec8Sopenharmony_ci
2002a46c0ec8Sopenharmony_ci	if (process_tool_twice)
2003a46c0ec8Sopenharmony_ci		goto reprocess;
2004a46c0ec8Sopenharmony_ci}
2005a46c0ec8Sopenharmony_ci
2006a46c0ec8Sopenharmony_cistatic inline void
2007a46c0ec8Sopenharmony_citablet_set_touch_device_enabled(struct tablet_dispatch *tablet,
2008a46c0ec8Sopenharmony_ci				enum evdev_arbitration_state which,
2009a46c0ec8Sopenharmony_ci				const struct phys_rect *rect,
2010a46c0ec8Sopenharmony_ci				uint64_t time)
2011a46c0ec8Sopenharmony_ci{
2012a46c0ec8Sopenharmony_ci	struct evdev_device *touch_device = tablet->touch_device;
2013a46c0ec8Sopenharmony_ci	struct evdev_dispatch *dispatch;
2014a46c0ec8Sopenharmony_ci
2015a46c0ec8Sopenharmony_ci	if (touch_device == NULL)
2016a46c0ec8Sopenharmony_ci		return;
2017a46c0ec8Sopenharmony_ci
2018a46c0ec8Sopenharmony_ci	tablet->arbitration = which;
2019a46c0ec8Sopenharmony_ci
2020a46c0ec8Sopenharmony_ci	dispatch = touch_device->dispatch;
2021a46c0ec8Sopenharmony_ci	if (dispatch->interface->touch_arbitration_toggle)
2022a46c0ec8Sopenharmony_ci		dispatch->interface->touch_arbitration_toggle(dispatch,
2023a46c0ec8Sopenharmony_ci							      touch_device,
2024a46c0ec8Sopenharmony_ci							      which,
2025a46c0ec8Sopenharmony_ci							      rect,
2026a46c0ec8Sopenharmony_ci							      time);
2027a46c0ec8Sopenharmony_ci}
2028a46c0ec8Sopenharmony_ci
2029a46c0ec8Sopenharmony_cistatic inline void
2030a46c0ec8Sopenharmony_citablet_toggle_touch_device(struct tablet_dispatch *tablet,
2031a46c0ec8Sopenharmony_ci			   struct evdev_device *tablet_device,
2032a46c0ec8Sopenharmony_ci			   uint64_t time)
2033a46c0ec8Sopenharmony_ci{
2034a46c0ec8Sopenharmony_ci	enum evdev_arbitration_state which;
2035a46c0ec8Sopenharmony_ci	struct phys_rect r = {0};
2036a46c0ec8Sopenharmony_ci	struct phys_rect *rect = NULL;
2037a46c0ec8Sopenharmony_ci
2038a46c0ec8Sopenharmony_ci	if (tablet_has_status(tablet,
2039a46c0ec8Sopenharmony_ci			      TABLET_TOOL_OUT_OF_RANGE) ||
2040a46c0ec8Sopenharmony_ci	    tablet_has_status(tablet, TABLET_NONE) ||
2041a46c0ec8Sopenharmony_ci	    tablet_has_status(tablet,
2042a46c0ec8Sopenharmony_ci			      TABLET_TOOL_LEAVING_PROXIMITY) ||
2043a46c0ec8Sopenharmony_ci	    tablet_has_status(tablet,
2044a46c0ec8Sopenharmony_ci			      TABLET_TOOL_OUT_OF_PROXIMITY)) {
2045a46c0ec8Sopenharmony_ci		which = ARBITRATION_NOT_ACTIVE;
2046a46c0ec8Sopenharmony_ci	} else if (tablet->axes.tilt.x == 0) {
2047a46c0ec8Sopenharmony_ci		which = ARBITRATION_IGNORE_ALL;
2048a46c0ec8Sopenharmony_ci	} else if (tablet->arbitration != ARBITRATION_IGNORE_RECT) {
2049a46c0ec8Sopenharmony_ci		/* This enables rect-based arbitration, updates are sent
2050a46c0ec8Sopenharmony_ci		 * elsewhere */
2051a46c0ec8Sopenharmony_ci		r = tablet_calculate_arbitration_rect(tablet);
2052a46c0ec8Sopenharmony_ci		rect = &r;
2053a46c0ec8Sopenharmony_ci		which = ARBITRATION_IGNORE_RECT;
2054a46c0ec8Sopenharmony_ci	} else {
2055a46c0ec8Sopenharmony_ci		return;
2056a46c0ec8Sopenharmony_ci	}
2057a46c0ec8Sopenharmony_ci
2058a46c0ec8Sopenharmony_ci	tablet_set_touch_device_enabled(tablet,
2059a46c0ec8Sopenharmony_ci					which,
2060a46c0ec8Sopenharmony_ci					rect,
2061a46c0ec8Sopenharmony_ci					time);
2062a46c0ec8Sopenharmony_ci}
2063a46c0ec8Sopenharmony_ci
2064a46c0ec8Sopenharmony_cistatic inline void
2065a46c0ec8Sopenharmony_citablet_reset_state(struct tablet_dispatch *tablet)
2066a46c0ec8Sopenharmony_ci{
2067a46c0ec8Sopenharmony_ci	struct button_state zero = {0};
2068a46c0ec8Sopenharmony_ci
2069a46c0ec8Sopenharmony_ci	/* Update state */
2070a46c0ec8Sopenharmony_ci	memcpy(&tablet->prev_button_state,
2071a46c0ec8Sopenharmony_ci	       &tablet->button_state,
2072a46c0ec8Sopenharmony_ci	       sizeof(tablet->button_state));
2073a46c0ec8Sopenharmony_ci	tablet_unset_status(tablet, TABLET_TOOL_UPDATED);
2074a46c0ec8Sopenharmony_ci
2075a46c0ec8Sopenharmony_ci	if (memcmp(&tablet->button_state, &zero, sizeof(zero)) == 0)
2076a46c0ec8Sopenharmony_ci		tablet_unset_status(tablet, TABLET_BUTTONS_DOWN);
2077a46c0ec8Sopenharmony_ci	else
2078a46c0ec8Sopenharmony_ci		tablet_set_status(tablet, TABLET_BUTTONS_DOWN);
2079a46c0ec8Sopenharmony_ci}
2080a46c0ec8Sopenharmony_ci
2081a46c0ec8Sopenharmony_cistatic void
2082a46c0ec8Sopenharmony_citablet_proximity_out_quirk_timer_func(uint64_t now, void *data)
2083a46c0ec8Sopenharmony_ci{
2084a46c0ec8Sopenharmony_ci	struct tablet_dispatch *tablet = data;
2085a46c0ec8Sopenharmony_ci	struct timeval tv = us2tv(now);
2086a46c0ec8Sopenharmony_ci	struct input_event events[2] = {
2087a46c0ec8Sopenharmony_ci		{ .input_event_sec = tv.tv_sec,
2088a46c0ec8Sopenharmony_ci		  .input_event_usec = tv.tv_usec,
2089a46c0ec8Sopenharmony_ci		  .type = EV_KEY,
2090a46c0ec8Sopenharmony_ci		  .code = BTN_TOOL_PEN,
2091a46c0ec8Sopenharmony_ci		  .value = 0 },
2092a46c0ec8Sopenharmony_ci		{ .input_event_sec = tv.tv_sec,
2093a46c0ec8Sopenharmony_ci		  .input_event_usec = tv.tv_usec,
2094a46c0ec8Sopenharmony_ci		  .type = EV_SYN,
2095a46c0ec8Sopenharmony_ci		  .code = SYN_REPORT,
2096a46c0ec8Sopenharmony_ci		  .value = 0 },
2097a46c0ec8Sopenharmony_ci	};
2098a46c0ec8Sopenharmony_ci
2099a46c0ec8Sopenharmony_ci	if (tablet_has_status(tablet, TABLET_TOOL_IN_CONTACT) ||
2100a46c0ec8Sopenharmony_ci	    tablet_has_status(tablet, TABLET_BUTTONS_DOWN)) {
2101a46c0ec8Sopenharmony_ci		tablet_proximity_out_quirk_set_timer(tablet, now);
2102a46c0ec8Sopenharmony_ci		return;
2103a46c0ec8Sopenharmony_ci	}
2104a46c0ec8Sopenharmony_ci
2105a46c0ec8Sopenharmony_ci	if (tablet->quirks.last_event_time > now - FORCED_PROXOUT_TIMEOUT) {
2106a46c0ec8Sopenharmony_ci		tablet_proximity_out_quirk_set_timer(tablet,
2107a46c0ec8Sopenharmony_ci						     tablet->quirks.last_event_time);
2108a46c0ec8Sopenharmony_ci		return;
2109a46c0ec8Sopenharmony_ci	}
2110a46c0ec8Sopenharmony_ci
2111a46c0ec8Sopenharmony_ci	evdev_log_debug(tablet->device, "tablet: forcing proximity after timeout\n");
2112a46c0ec8Sopenharmony_ci
2113a46c0ec8Sopenharmony_ci	tablet->quirks.proximity_out_in_progress = true;
2114a46c0ec8Sopenharmony_ci	ARRAY_FOR_EACH(events, e) {
2115a46c0ec8Sopenharmony_ci		tablet->base.interface->process(&tablet->base,
2116a46c0ec8Sopenharmony_ci						 tablet->device,
2117a46c0ec8Sopenharmony_ci						 e,
2118a46c0ec8Sopenharmony_ci						 now);
2119a46c0ec8Sopenharmony_ci	}
2120a46c0ec8Sopenharmony_ci	tablet->quirks.proximity_out_in_progress = false;
2121a46c0ec8Sopenharmony_ci
2122a46c0ec8Sopenharmony_ci	tablet->quirks.proximity_out_forced = true;
2123a46c0ec8Sopenharmony_ci}
2124a46c0ec8Sopenharmony_ci
2125a46c0ec8Sopenharmony_cistatic void
2126a46c0ec8Sopenharmony_citablet_process(struct evdev_dispatch *dispatch,
2127a46c0ec8Sopenharmony_ci	       struct evdev_device *device,
2128a46c0ec8Sopenharmony_ci	       struct input_event *e,
2129a46c0ec8Sopenharmony_ci	       uint64_t time)
2130a46c0ec8Sopenharmony_ci{
2131a46c0ec8Sopenharmony_ci	struct tablet_dispatch *tablet = tablet_dispatch(dispatch);
2132a46c0ec8Sopenharmony_ci
2133a46c0ec8Sopenharmony_ci	switch (e->type) {
2134a46c0ec8Sopenharmony_ci	case EV_ABS:
2135a46c0ec8Sopenharmony_ci		tablet_process_absolute(tablet, device, e, time);
2136a46c0ec8Sopenharmony_ci		break;
2137a46c0ec8Sopenharmony_ci	case EV_REL:
2138a46c0ec8Sopenharmony_ci		tablet_process_relative(tablet, device, e, time);
2139a46c0ec8Sopenharmony_ci		break;
2140a46c0ec8Sopenharmony_ci	case EV_KEY:
2141a46c0ec8Sopenharmony_ci		tablet_process_key(tablet, device, e, time);
2142a46c0ec8Sopenharmony_ci		break;
2143a46c0ec8Sopenharmony_ci	case EV_MSC:
2144a46c0ec8Sopenharmony_ci		tablet_process_misc(tablet, device, e, time);
2145a46c0ec8Sopenharmony_ci		break;
2146a46c0ec8Sopenharmony_ci	case EV_SYN:
2147a46c0ec8Sopenharmony_ci		tablet_flush(tablet, device, time);
2148a46c0ec8Sopenharmony_ci		tablet_toggle_touch_device(tablet, device, time);
2149a46c0ec8Sopenharmony_ci		tablet_reset_state(tablet);
2150a46c0ec8Sopenharmony_ci		tablet->quirks.last_event_time = time;
2151a46c0ec8Sopenharmony_ci		break;
2152a46c0ec8Sopenharmony_ci	default:
2153a46c0ec8Sopenharmony_ci		evdev_log_error(device,
2154a46c0ec8Sopenharmony_ci				"Unexpected event type %s (%#x)\n",
2155a46c0ec8Sopenharmony_ci				libevdev_event_type_get_name(e->type),
2156a46c0ec8Sopenharmony_ci				e->type);
2157a46c0ec8Sopenharmony_ci		break;
2158a46c0ec8Sopenharmony_ci	}
2159a46c0ec8Sopenharmony_ci}
2160a46c0ec8Sopenharmony_ci
2161a46c0ec8Sopenharmony_cistatic void
2162a46c0ec8Sopenharmony_citablet_suspend(struct evdev_dispatch *dispatch,
2163a46c0ec8Sopenharmony_ci	       struct evdev_device *device)
2164a46c0ec8Sopenharmony_ci{
2165a46c0ec8Sopenharmony_ci	struct tablet_dispatch *tablet = tablet_dispatch(dispatch);
2166a46c0ec8Sopenharmony_ci	struct libinput *li = tablet_libinput_context(tablet);
2167a46c0ec8Sopenharmony_ci	uint64_t now = libinput_now(li);
2168a46c0ec8Sopenharmony_ci
2169a46c0ec8Sopenharmony_ci	tablet_set_touch_device_enabled(tablet,
2170a46c0ec8Sopenharmony_ci					ARBITRATION_NOT_ACTIVE,
2171a46c0ec8Sopenharmony_ci					NULL,
2172a46c0ec8Sopenharmony_ci					now);
2173a46c0ec8Sopenharmony_ci
2174a46c0ec8Sopenharmony_ci	if (!tablet_has_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY)) {
2175a46c0ec8Sopenharmony_ci		tablet_set_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY);
2176a46c0ec8Sopenharmony_ci		tablet_flush(tablet, device, libinput_now(li));
2177a46c0ec8Sopenharmony_ci	}
2178a46c0ec8Sopenharmony_ci}
2179a46c0ec8Sopenharmony_ci
2180a46c0ec8Sopenharmony_cistatic void
2181a46c0ec8Sopenharmony_citablet_destroy(struct evdev_dispatch *dispatch)
2182a46c0ec8Sopenharmony_ci{
2183a46c0ec8Sopenharmony_ci	struct tablet_dispatch *tablet = tablet_dispatch(dispatch);
2184a46c0ec8Sopenharmony_ci	struct libinput_tablet_tool *tool;
2185a46c0ec8Sopenharmony_ci	struct libinput *li = tablet_libinput_context(tablet);
2186a46c0ec8Sopenharmony_ci
2187a46c0ec8Sopenharmony_ci	libinput_timer_cancel(&tablet->quirks.prox_out_timer);
2188a46c0ec8Sopenharmony_ci	libinput_timer_destroy(&tablet->quirks.prox_out_timer);
2189a46c0ec8Sopenharmony_ci
2190a46c0ec8Sopenharmony_ci	list_for_each_safe(tool, &tablet->tool_list, link) {
2191a46c0ec8Sopenharmony_ci		libinput_tablet_tool_unref(tool);
2192a46c0ec8Sopenharmony_ci	}
2193a46c0ec8Sopenharmony_ci
2194a46c0ec8Sopenharmony_ci	libinput_libwacom_unref(li);
2195a46c0ec8Sopenharmony_ci
2196a46c0ec8Sopenharmony_ci	free(tablet);
2197a46c0ec8Sopenharmony_ci}
2198a46c0ec8Sopenharmony_ci
2199a46c0ec8Sopenharmony_cistatic void
2200a46c0ec8Sopenharmony_citablet_setup_touch_arbitration(struct evdev_device *device,
2201a46c0ec8Sopenharmony_ci			       struct evdev_device *new_device)
2202a46c0ec8Sopenharmony_ci{
2203a46c0ec8Sopenharmony_ci	struct tablet_dispatch *tablet = tablet_dispatch(device->dispatch);
2204a46c0ec8Sopenharmony_ci
2205a46c0ec8Sopenharmony_ci        /* We enable touch arbitration with the first touch screen/external
2206a46c0ec8Sopenharmony_ci         * touchpad we see. This may be wrong in some cases, so we have some
2207a46c0ec8Sopenharmony_ci         * heuristics in case we find a "better" device.
2208a46c0ec8Sopenharmony_ci         */
2209a46c0ec8Sopenharmony_ci        if (tablet->touch_device != NULL) {
2210a46c0ec8Sopenharmony_ci		struct libinput_device_group *group1 = libinput_device_get_device_group(&device->base);
2211a46c0ec8Sopenharmony_ci		struct libinput_device_group *group2 = libinput_device_get_device_group(&new_device->base);
2212a46c0ec8Sopenharmony_ci
2213a46c0ec8Sopenharmony_ci		/* same phsical device? -> better, otherwise keep the one we have */
2214a46c0ec8Sopenharmony_ci		if (group1 != group2)
2215a46c0ec8Sopenharmony_ci			return;
2216a46c0ec8Sopenharmony_ci
2217a46c0ec8Sopenharmony_ci		/* We found a better device, let's swap it out */
2218a46c0ec8Sopenharmony_ci		struct libinput *li = tablet_libinput_context(tablet);
2219a46c0ec8Sopenharmony_ci		tablet_set_touch_device_enabled(tablet,
2220a46c0ec8Sopenharmony_ci						ARBITRATION_NOT_ACTIVE,
2221a46c0ec8Sopenharmony_ci						NULL,
2222a46c0ec8Sopenharmony_ci						libinput_now(li));
2223a46c0ec8Sopenharmony_ci		evdev_log_debug(device,
2224a46c0ec8Sopenharmony_ci				"touch-arbitration: removing pairing for %s<->%s\n",
2225a46c0ec8Sopenharmony_ci				device->devname,
2226a46c0ec8Sopenharmony_ci				tablet->touch_device->devname);
2227a46c0ec8Sopenharmony_ci	}
2228a46c0ec8Sopenharmony_ci
2229a46c0ec8Sopenharmony_ci	evdev_log_debug(device,
2230a46c0ec8Sopenharmony_ci			"touch-arbitration: activated for %s<->%s\n",
2231a46c0ec8Sopenharmony_ci			device->devname,
2232a46c0ec8Sopenharmony_ci			new_device->devname);
2233a46c0ec8Sopenharmony_ci	tablet->touch_device = new_device;
2234a46c0ec8Sopenharmony_ci}
2235a46c0ec8Sopenharmony_ci
2236a46c0ec8Sopenharmony_cistatic void
2237a46c0ec8Sopenharmony_citablet_setup_rotation(struct evdev_device *device,
2238a46c0ec8Sopenharmony_ci		      struct evdev_device *new_device)
2239a46c0ec8Sopenharmony_ci{
2240a46c0ec8Sopenharmony_ci	struct tablet_dispatch *tablet = tablet_dispatch(device->dispatch);
2241a46c0ec8Sopenharmony_ci	struct libinput_device_group *group1 = libinput_device_get_device_group(&device->base);
2242a46c0ec8Sopenharmony_ci	struct libinput_device_group *group2 = libinput_device_get_device_group(&new_device->base);
2243a46c0ec8Sopenharmony_ci
2244a46c0ec8Sopenharmony_ci	if (tablet->rotation.touch_device == NULL && (group1 == group2)) {
2245a46c0ec8Sopenharmony_ci		evdev_log_debug(device,
2246a46c0ec8Sopenharmony_ci				"tablet-rotation: %s will rotate %s\n",
2247a46c0ec8Sopenharmony_ci				device->devname,
2248a46c0ec8Sopenharmony_ci				new_device->devname);
2249a46c0ec8Sopenharmony_ci		tablet->rotation.touch_device = new_device;
2250a46c0ec8Sopenharmony_ci
2251a46c0ec8Sopenharmony_ci		if (libinput_device_config_left_handed_get(&new_device->base)) {
2252a46c0ec8Sopenharmony_ci			tablet->rotation.touch_device_left_handed_state = true;
2253a46c0ec8Sopenharmony_ci			tablet_change_rotation(device, DO_NOTIFY);
2254a46c0ec8Sopenharmony_ci		}
2255a46c0ec8Sopenharmony_ci	}
2256a46c0ec8Sopenharmony_ci}
2257a46c0ec8Sopenharmony_ci
2258a46c0ec8Sopenharmony_cistatic void
2259a46c0ec8Sopenharmony_citablet_device_added(struct evdev_device *device,
2260a46c0ec8Sopenharmony_ci		    struct evdev_device *added_device)
2261a46c0ec8Sopenharmony_ci{
2262a46c0ec8Sopenharmony_ci	bool is_touchscreen, is_ext_touchpad;
2263a46c0ec8Sopenharmony_ci
2264a46c0ec8Sopenharmony_ci	is_touchscreen = evdev_device_has_capability(added_device,
2265a46c0ec8Sopenharmony_ci						     LIBINPUT_DEVICE_CAP_TOUCH);
2266a46c0ec8Sopenharmony_ci	is_ext_touchpad = evdev_device_has_capability(added_device,
2267a46c0ec8Sopenharmony_ci						      LIBINPUT_DEVICE_CAP_POINTER) &&
2268a46c0ec8Sopenharmony_ci			  (added_device->tags & EVDEV_TAG_EXTERNAL_TOUCHPAD);
2269a46c0ec8Sopenharmony_ci
2270a46c0ec8Sopenharmony_ci	if (is_touchscreen || is_ext_touchpad)
2271a46c0ec8Sopenharmony_ci		tablet_setup_touch_arbitration(device, added_device);
2272a46c0ec8Sopenharmony_ci
2273a46c0ec8Sopenharmony_ci	if (is_ext_touchpad)
2274a46c0ec8Sopenharmony_ci		tablet_setup_rotation(device, added_device);
2275a46c0ec8Sopenharmony_ci}
2276a46c0ec8Sopenharmony_ci
2277a46c0ec8Sopenharmony_cistatic void
2278a46c0ec8Sopenharmony_citablet_device_removed(struct evdev_device *device,
2279a46c0ec8Sopenharmony_ci		      struct evdev_device *removed_device)
2280a46c0ec8Sopenharmony_ci{
2281a46c0ec8Sopenharmony_ci	struct tablet_dispatch *tablet = tablet_dispatch(device->dispatch);
2282a46c0ec8Sopenharmony_ci
2283a46c0ec8Sopenharmony_ci	if (tablet->touch_device == removed_device)
2284a46c0ec8Sopenharmony_ci		tablet->touch_device = NULL;
2285a46c0ec8Sopenharmony_ci
2286a46c0ec8Sopenharmony_ci	if (tablet->rotation.touch_device == removed_device) {
2287a46c0ec8Sopenharmony_ci		tablet->rotation.touch_device = NULL;
2288a46c0ec8Sopenharmony_ci		tablet->rotation.touch_device_left_handed_state = false;
2289a46c0ec8Sopenharmony_ci		tablet_change_rotation(device, DO_NOTIFY);
2290a46c0ec8Sopenharmony_ci	}
2291a46c0ec8Sopenharmony_ci}
2292a46c0ec8Sopenharmony_ci
2293a46c0ec8Sopenharmony_cistatic void
2294a46c0ec8Sopenharmony_citablet_check_initial_proximity(struct evdev_device *device,
2295a46c0ec8Sopenharmony_ci			       struct evdev_dispatch *dispatch)
2296a46c0ec8Sopenharmony_ci{
2297a46c0ec8Sopenharmony_ci	struct tablet_dispatch *tablet = tablet_dispatch(dispatch);
2298a46c0ec8Sopenharmony_ci	struct libinput *li = tablet_libinput_context(tablet);
2299a46c0ec8Sopenharmony_ci	int code, state;
2300a46c0ec8Sopenharmony_ci	enum libinput_tablet_tool_type tool;
2301a46c0ec8Sopenharmony_ci
2302a46c0ec8Sopenharmony_ci	for (tool = LIBINPUT_TABLET_TOOL_TYPE_PEN;
2303a46c0ec8Sopenharmony_ci	     tool <= LIBINPUT_TABLET_TOOL_TYPE_MAX;
2304a46c0ec8Sopenharmony_ci	     tool++) {
2305a46c0ec8Sopenharmony_ci		code = tablet_tool_to_evcode(tool);
2306a46c0ec8Sopenharmony_ci
2307a46c0ec8Sopenharmony_ci		/* we only expect one tool to be in proximity at a time */
2308a46c0ec8Sopenharmony_ci		if (libevdev_fetch_event_value(device->evdev,
2309a46c0ec8Sopenharmony_ci						EV_KEY,
2310a46c0ec8Sopenharmony_ci						code,
2311a46c0ec8Sopenharmony_ci						&state) && state) {
2312a46c0ec8Sopenharmony_ci			tablet->tool_state = bit(tool);
2313a46c0ec8Sopenharmony_ci			tablet->prev_tool_state = bit(tool);
2314a46c0ec8Sopenharmony_ci			break;
2315a46c0ec8Sopenharmony_ci		}
2316a46c0ec8Sopenharmony_ci	}
2317a46c0ec8Sopenharmony_ci
2318a46c0ec8Sopenharmony_ci	if (!tablet->tool_state)
2319a46c0ec8Sopenharmony_ci		return;
2320a46c0ec8Sopenharmony_ci
2321a46c0ec8Sopenharmony_ci	tablet_update_tool(tablet, device, tool, state);
2322a46c0ec8Sopenharmony_ci	if (tablet->quirks.need_to_force_prox_out)
2323a46c0ec8Sopenharmony_ci		tablet_proximity_out_quirk_set_timer(tablet, libinput_now(li));
2324a46c0ec8Sopenharmony_ci
2325a46c0ec8Sopenharmony_ci	tablet->current_tool.id =
2326a46c0ec8Sopenharmony_ci		libevdev_get_event_value(device->evdev,
2327a46c0ec8Sopenharmony_ci					 EV_ABS,
2328a46c0ec8Sopenharmony_ci					 ABS_MISC);
2329a46c0ec8Sopenharmony_ci
2330a46c0ec8Sopenharmony_ci	/* we can't fetch MSC_SERIAL from the kernel, so we set the serial
2331a46c0ec8Sopenharmony_ci	 * to 0 for now. On the first real event from the device we get the
2332a46c0ec8Sopenharmony_ci	 * serial (if any) and that event will be converted into a proximity
2333a46c0ec8Sopenharmony_ci	 * event */
2334a46c0ec8Sopenharmony_ci	tablet->current_tool.serial = 0;
2335a46c0ec8Sopenharmony_ci}
2336a46c0ec8Sopenharmony_ci
2337a46c0ec8Sopenharmony_ci/* Called when the touchpad toggles to left-handed */
2338a46c0ec8Sopenharmony_cistatic void
2339a46c0ec8Sopenharmony_citablet_left_handed_toggled(struct evdev_dispatch *dispatch,
2340a46c0ec8Sopenharmony_ci			   struct evdev_device *device,
2341a46c0ec8Sopenharmony_ci			   bool left_handed_enabled)
2342a46c0ec8Sopenharmony_ci{
2343a46c0ec8Sopenharmony_ci	struct tablet_dispatch *tablet = tablet_dispatch(dispatch);
2344a46c0ec8Sopenharmony_ci
2345a46c0ec8Sopenharmony_ci	if (!tablet->rotation.touch_device)
2346a46c0ec8Sopenharmony_ci		return;
2347a46c0ec8Sopenharmony_ci
2348a46c0ec8Sopenharmony_ci	evdev_log_debug(device,
2349a46c0ec8Sopenharmony_ci			"tablet-rotation: touchpad is %s\n",
2350a46c0ec8Sopenharmony_ci			left_handed_enabled ? "left-handed" : "right-handed");
2351a46c0ec8Sopenharmony_ci
2352a46c0ec8Sopenharmony_ci	/* Our left-handed config is independent even though rotation is
2353a46c0ec8Sopenharmony_ci	 * locked. So we rotate when either device is left-handed. But it
2354a46c0ec8Sopenharmony_ci	 * can only be actually changed when the device is in a neutral
2355a46c0ec8Sopenharmony_ci	 * state, hence the want_rotate.
2356a46c0ec8Sopenharmony_ci	 */
2357a46c0ec8Sopenharmony_ci	tablet->rotation.touch_device_left_handed_state = left_handed_enabled;
2358a46c0ec8Sopenharmony_ci	tablet_change_rotation(device, DONT_NOTIFY);
2359a46c0ec8Sopenharmony_ci}
2360a46c0ec8Sopenharmony_ci
2361a46c0ec8Sopenharmony_cistatic struct evdev_dispatch_interface tablet_interface = {
2362a46c0ec8Sopenharmony_ci	.process = tablet_process,
2363a46c0ec8Sopenharmony_ci	.suspend = tablet_suspend,
2364a46c0ec8Sopenharmony_ci	.remove = NULL,
2365a46c0ec8Sopenharmony_ci	.destroy = tablet_destroy,
2366a46c0ec8Sopenharmony_ci	.device_added = tablet_device_added,
2367a46c0ec8Sopenharmony_ci	.device_removed = tablet_device_removed,
2368a46c0ec8Sopenharmony_ci	.device_suspended = NULL,
2369a46c0ec8Sopenharmony_ci	.device_resumed = NULL,
2370a46c0ec8Sopenharmony_ci	.post_added = tablet_check_initial_proximity,
2371a46c0ec8Sopenharmony_ci	.touch_arbitration_toggle = NULL,
2372a46c0ec8Sopenharmony_ci	.touch_arbitration_update_rect = NULL,
2373a46c0ec8Sopenharmony_ci	.get_switch_state = NULL,
2374a46c0ec8Sopenharmony_ci	.left_handed_toggle = tablet_left_handed_toggled,
2375a46c0ec8Sopenharmony_ci};
2376a46c0ec8Sopenharmony_ci
2377a46c0ec8Sopenharmony_cistatic void
2378a46c0ec8Sopenharmony_citablet_init_calibration(struct tablet_dispatch *tablet,
2379a46c0ec8Sopenharmony_ci			struct evdev_device *device)
2380a46c0ec8Sopenharmony_ci{
2381a46c0ec8Sopenharmony_ci	if (libevdev_has_property(device->evdev, INPUT_PROP_DIRECT))
2382a46c0ec8Sopenharmony_ci		evdev_init_calibration(device, &tablet->calibration);
2383a46c0ec8Sopenharmony_ci}
2384a46c0ec8Sopenharmony_ci
2385a46c0ec8Sopenharmony_cistatic void
2386a46c0ec8Sopenharmony_citablet_init_proximity_threshold(struct tablet_dispatch *tablet,
2387a46c0ec8Sopenharmony_ci				struct evdev_device *device)
2388a46c0ec8Sopenharmony_ci{
2389a46c0ec8Sopenharmony_ci	/* This rules out most of the bamboos and other devices, we're
2390a46c0ec8Sopenharmony_ci	 * pretty much down to
2391a46c0ec8Sopenharmony_ci	 */
2392a46c0ec8Sopenharmony_ci	if (!libevdev_has_event_code(device->evdev, EV_KEY, BTN_TOOL_MOUSE) &&
2393a46c0ec8Sopenharmony_ci	    !libevdev_has_event_code(device->evdev, EV_KEY, BTN_TOOL_LENS))
2394a46c0ec8Sopenharmony_ci		return;
2395a46c0ec8Sopenharmony_ci
2396a46c0ec8Sopenharmony_ci	/* 42 is the default proximity threshold the xf86-input-wacom driver
2397a46c0ec8Sopenharmony_ci	 * uses for Intuos/Cintiq models. Graphire models have a threshold
2398a46c0ec8Sopenharmony_ci	 * of 10 but since they haven't been manufactured in ages and the
2399a46c0ec8Sopenharmony_ci	 * intersection of users having a graphire, running libinput and
2400a46c0ec8Sopenharmony_ci	 * wanting to use the mouse/lens cursor tool is small enough to not
2401a46c0ec8Sopenharmony_ci	 * worry about it for now. If we need to, we can introduce a udev
2402a46c0ec8Sopenharmony_ci	 * property later.
2403a46c0ec8Sopenharmony_ci	 *
2404a46c0ec8Sopenharmony_ci	 * Value is in device coordinates.
2405a46c0ec8Sopenharmony_ci	 */
2406a46c0ec8Sopenharmony_ci	tablet->cursor_proximity_threshold = 42;
2407a46c0ec8Sopenharmony_ci}
2408a46c0ec8Sopenharmony_ci
2409a46c0ec8Sopenharmony_cistatic uint32_t
2410a46c0ec8Sopenharmony_citablet_accel_config_get_profiles(struct libinput_device *libinput_device)
2411a46c0ec8Sopenharmony_ci{
2412a46c0ec8Sopenharmony_ci	return LIBINPUT_CONFIG_ACCEL_PROFILE_NONE;
2413a46c0ec8Sopenharmony_ci}
2414a46c0ec8Sopenharmony_ci
2415a46c0ec8Sopenharmony_cistatic enum libinput_config_status
2416a46c0ec8Sopenharmony_citablet_accel_config_set_profile(struct libinput_device *libinput_device,
2417a46c0ec8Sopenharmony_ci			    enum libinput_config_accel_profile profile)
2418a46c0ec8Sopenharmony_ci{
2419a46c0ec8Sopenharmony_ci	return LIBINPUT_CONFIG_STATUS_UNSUPPORTED;
2420a46c0ec8Sopenharmony_ci}
2421a46c0ec8Sopenharmony_ci
2422a46c0ec8Sopenharmony_cistatic enum libinput_config_accel_profile
2423a46c0ec8Sopenharmony_citablet_accel_config_get_profile(struct libinput_device *libinput_device)
2424a46c0ec8Sopenharmony_ci{
2425a46c0ec8Sopenharmony_ci	return LIBINPUT_CONFIG_ACCEL_PROFILE_NONE;
2426a46c0ec8Sopenharmony_ci}
2427a46c0ec8Sopenharmony_ci
2428a46c0ec8Sopenharmony_cistatic enum libinput_config_accel_profile
2429a46c0ec8Sopenharmony_citablet_accel_config_get_default_profile(struct libinput_device *libinput_device)
2430a46c0ec8Sopenharmony_ci{
2431a46c0ec8Sopenharmony_ci	return LIBINPUT_CONFIG_ACCEL_PROFILE_NONE;
2432a46c0ec8Sopenharmony_ci}
2433a46c0ec8Sopenharmony_ci
2434a46c0ec8Sopenharmony_cistatic int
2435a46c0ec8Sopenharmony_citablet_init_accel(struct tablet_dispatch *tablet, struct evdev_device *device)
2436a46c0ec8Sopenharmony_ci{
2437a46c0ec8Sopenharmony_ci	const struct input_absinfo *x, *y;
2438a46c0ec8Sopenharmony_ci	struct motion_filter *filter;
2439a46c0ec8Sopenharmony_ci
2440a46c0ec8Sopenharmony_ci	x = device->abs.absinfo_x;
2441a46c0ec8Sopenharmony_ci	y = device->abs.absinfo_y;
2442a46c0ec8Sopenharmony_ci
2443a46c0ec8Sopenharmony_ci	filter = create_pointer_accelerator_filter_tablet(x->resolution,
2444a46c0ec8Sopenharmony_ci							  y->resolution);
2445a46c0ec8Sopenharmony_ci	if (!filter)
2446a46c0ec8Sopenharmony_ci		return -1;
2447a46c0ec8Sopenharmony_ci
2448a46c0ec8Sopenharmony_ci	evdev_device_init_pointer_acceleration(device, filter);
2449a46c0ec8Sopenharmony_ci
2450a46c0ec8Sopenharmony_ci	/* we override the profile hooks for accel configuration with hooks
2451a46c0ec8Sopenharmony_ci	 * that don't allow selection of profiles */
2452a46c0ec8Sopenharmony_ci	device->pointer.config.get_profiles = tablet_accel_config_get_profiles;
2453a46c0ec8Sopenharmony_ci	device->pointer.config.set_profile = tablet_accel_config_set_profile;
2454a46c0ec8Sopenharmony_ci	device->pointer.config.get_profile = tablet_accel_config_get_profile;
2455a46c0ec8Sopenharmony_ci	device->pointer.config.get_default_profile = tablet_accel_config_get_default_profile;
2456a46c0ec8Sopenharmony_ci
2457a46c0ec8Sopenharmony_ci	return 0;
2458a46c0ec8Sopenharmony_ci}
2459a46c0ec8Sopenharmony_ci
2460a46c0ec8Sopenharmony_cistatic void
2461a46c0ec8Sopenharmony_citablet_init_left_handed(struct evdev_device *device)
2462a46c0ec8Sopenharmony_ci{
2463a46c0ec8Sopenharmony_ci	if (evdev_tablet_has_left_handed(device))
2464a46c0ec8Sopenharmony_ci		evdev_init_left_handed(device,
2465a46c0ec8Sopenharmony_ci				       tablet_change_to_left_handed);
2466a46c0ec8Sopenharmony_ci}
2467a46c0ec8Sopenharmony_ci
2468a46c0ec8Sopenharmony_cistatic bool
2469a46c0ec8Sopenharmony_citablet_is_aes(struct evdev_device *device,
2470a46c0ec8Sopenharmony_ci	      struct tablet_dispatch *tablet)
2471a46c0ec8Sopenharmony_ci{
2472a46c0ec8Sopenharmony_ci	bool is_aes = false;
2473a46c0ec8Sopenharmony_ci#if HAVE_LIBWACOM
2474a46c0ec8Sopenharmony_ci	const char *devnode;
2475a46c0ec8Sopenharmony_ci	WacomDeviceDatabase *db;
2476a46c0ec8Sopenharmony_ci	WacomDevice *libwacom_device = NULL;
2477a46c0ec8Sopenharmony_ci	const int *stylus_ids;
2478a46c0ec8Sopenharmony_ci	int nstyli;
2479a46c0ec8Sopenharmony_ci	int vid = evdev_device_get_id_vendor(device);
2480a46c0ec8Sopenharmony_ci
2481a46c0ec8Sopenharmony_ci	/* Wacom-specific check for whether smoothing is required:
2482a46c0ec8Sopenharmony_ci	 * libwacom keeps all the AES pens in a single group, so any device
2483a46c0ec8Sopenharmony_ci	 * that supports AES pens will list all AES pens. 0x11 is one of the
2484a46c0ec8Sopenharmony_ci	 * lenovo pens so we use that as the flag of whether the tablet
2485a46c0ec8Sopenharmony_ci	 * is an AES tablet
2486a46c0ec8Sopenharmony_ci	 */
2487a46c0ec8Sopenharmony_ci	if (vid != VENDOR_ID_WACOM)
2488a46c0ec8Sopenharmony_ci		goto out;
2489a46c0ec8Sopenharmony_ci
2490a46c0ec8Sopenharmony_ci	db = tablet_libinput_context(tablet)->libwacom.db;
2491a46c0ec8Sopenharmony_ci	if (!db)
2492a46c0ec8Sopenharmony_ci		goto out;
2493a46c0ec8Sopenharmony_ci
2494a46c0ec8Sopenharmony_ci	devnode = udev_device_get_devnode(device->udev_device);
2495a46c0ec8Sopenharmony_ci	libwacom_device = libwacom_new_from_path(db, devnode, WFALLBACK_NONE, NULL);
2496a46c0ec8Sopenharmony_ci	if (!libwacom_device)
2497a46c0ec8Sopenharmony_ci		goto out;
2498a46c0ec8Sopenharmony_ci
2499a46c0ec8Sopenharmony_ci	stylus_ids = libwacom_get_supported_styli(libwacom_device, &nstyli);
2500a46c0ec8Sopenharmony_ci	for (int i = 0; i < nstyli; i++) {
2501a46c0ec8Sopenharmony_ci		if (stylus_ids[i] == 0x11) {
2502a46c0ec8Sopenharmony_ci			is_aes = true;
2503a46c0ec8Sopenharmony_ci			break;
2504a46c0ec8Sopenharmony_ci		}
2505a46c0ec8Sopenharmony_ci	}
2506a46c0ec8Sopenharmony_ci
2507a46c0ec8Sopenharmony_ci	libwacom_destroy(libwacom_device);
2508a46c0ec8Sopenharmony_ci
2509a46c0ec8Sopenharmony_ciout:
2510a46c0ec8Sopenharmony_ci#endif
2511a46c0ec8Sopenharmony_ci	return is_aes;
2512a46c0ec8Sopenharmony_ci}
2513a46c0ec8Sopenharmony_ci
2514a46c0ec8Sopenharmony_cistatic void
2515a46c0ec8Sopenharmony_citablet_init_smoothing(struct evdev_device *device,
2516a46c0ec8Sopenharmony_ci		      struct tablet_dispatch *tablet)
2517a46c0ec8Sopenharmony_ci{
2518a46c0ec8Sopenharmony_ci	size_t history_size = ARRAY_LENGTH(tablet->history.samples);
2519a46c0ec8Sopenharmony_ci	struct quirks_context *quirks = NULL;
2520a46c0ec8Sopenharmony_ci	struct quirks *q = NULL;
2521a46c0ec8Sopenharmony_ci	bool use_smoothing = true;
2522a46c0ec8Sopenharmony_ci
2523a46c0ec8Sopenharmony_ci	quirks = evdev_libinput_context(device)->quirks;
2524a46c0ec8Sopenharmony_ci	q = quirks_fetch_for_device(quirks, device->udev_device);
2525a46c0ec8Sopenharmony_ci
2526a46c0ec8Sopenharmony_ci	/* By default, always enable smoothing except on AES devices.
2527a46c0ec8Sopenharmony_ci	 * AttrTabletSmoothing can override this, if necessary.
2528a46c0ec8Sopenharmony_ci	 */
2529a46c0ec8Sopenharmony_ci	if (!q || !quirks_get_bool(q, QUIRK_ATTR_TABLET_SMOOTHING, &use_smoothing))
2530a46c0ec8Sopenharmony_ci		use_smoothing = !tablet_is_aes(device, tablet);
2531a46c0ec8Sopenharmony_ci
2532a46c0ec8Sopenharmony_ci	/* Setting the history size to 1 means we never do any actual smoothing. */
2533a46c0ec8Sopenharmony_ci	if (!use_smoothing)
2534a46c0ec8Sopenharmony_ci		history_size = 1;
2535a46c0ec8Sopenharmony_ci
2536a46c0ec8Sopenharmony_ci	quirks_unref(q);
2537a46c0ec8Sopenharmony_ci	tablet->history.size = history_size;
2538a46c0ec8Sopenharmony_ci}
2539a46c0ec8Sopenharmony_ci
2540a46c0ec8Sopenharmony_cistatic bool
2541a46c0ec8Sopenharmony_citablet_reject_device(struct evdev_device *device)
2542a46c0ec8Sopenharmony_ci{
2543a46c0ec8Sopenharmony_ci	struct libevdev *evdev = device->evdev;
2544a46c0ec8Sopenharmony_ci	double w, h;
2545a46c0ec8Sopenharmony_ci	bool has_xy, has_pen, has_btn_stylus, has_size;
2546a46c0ec8Sopenharmony_ci
2547a46c0ec8Sopenharmony_ci	has_xy = libevdev_has_event_code(evdev, EV_ABS, ABS_X) &&
2548a46c0ec8Sopenharmony_ci	         libevdev_has_event_code(evdev, EV_ABS, ABS_Y);
2549a46c0ec8Sopenharmony_ci	has_pen = libevdev_has_event_code(evdev, EV_KEY, BTN_TOOL_PEN);
2550a46c0ec8Sopenharmony_ci	has_btn_stylus = libevdev_has_event_code(evdev, EV_KEY, BTN_STYLUS);
2551a46c0ec8Sopenharmony_ci	has_size = evdev_device_get_size(device, &w, &h) == 0;
2552a46c0ec8Sopenharmony_ci
2553a46c0ec8Sopenharmony_ci	if (has_xy && (has_pen || has_btn_stylus) && has_size)
2554a46c0ec8Sopenharmony_ci		return false;
2555a46c0ec8Sopenharmony_ci
2556a46c0ec8Sopenharmony_ci	evdev_log_bug_libinput(device,
2557a46c0ec8Sopenharmony_ci			       "missing tablet capabilities:%s%s%s%s. "
2558a46c0ec8Sopenharmony_ci			       "Ignoring this device.\n",
2559a46c0ec8Sopenharmony_ci			       has_xy ? "" : " xy",
2560a46c0ec8Sopenharmony_ci			       has_pen ? "" : " pen",
2561a46c0ec8Sopenharmony_ci			       has_btn_stylus ? "" : " btn-stylus",
2562a46c0ec8Sopenharmony_ci			       has_size ? "" : " resolution");
2563a46c0ec8Sopenharmony_ci	return true;
2564a46c0ec8Sopenharmony_ci}
2565a46c0ec8Sopenharmony_ci
2566a46c0ec8Sopenharmony_cistatic int
2567a46c0ec8Sopenharmony_citablet_init(struct tablet_dispatch *tablet,
2568a46c0ec8Sopenharmony_ci	    struct evdev_device *device)
2569a46c0ec8Sopenharmony_ci{
2570a46c0ec8Sopenharmony_ci	struct libevdev *evdev = device->evdev;
2571a46c0ec8Sopenharmony_ci	enum libinput_tablet_tool_axis axis;
2572a46c0ec8Sopenharmony_ci	int rc;
2573a46c0ec8Sopenharmony_ci
2574a46c0ec8Sopenharmony_ci	tablet->base.dispatch_type = DISPATCH_TABLET;
2575a46c0ec8Sopenharmony_ci	tablet->base.interface = &tablet_interface;
2576a46c0ec8Sopenharmony_ci	tablet->device = device;
2577a46c0ec8Sopenharmony_ci	tablet->status = TABLET_NONE;
2578a46c0ec8Sopenharmony_ci	tablet->current_tool.type = LIBINPUT_TOOL_NONE;
2579a46c0ec8Sopenharmony_ci	list_init(&tablet->tool_list);
2580a46c0ec8Sopenharmony_ci
2581a46c0ec8Sopenharmony_ci	if (tablet_reject_device(device))
2582a46c0ec8Sopenharmony_ci		return -1;
2583a46c0ec8Sopenharmony_ci
2584a46c0ec8Sopenharmony_ci	if (!libevdev_has_event_code(evdev, EV_KEY, BTN_TOOL_PEN)) {
2585a46c0ec8Sopenharmony_ci		libevdev_enable_event_code(evdev, EV_KEY, BTN_TOOL_PEN, NULL);
2586a46c0ec8Sopenharmony_ci		tablet->quirks.proximity_out_forced = true;
2587a46c0ec8Sopenharmony_ci	}
2588a46c0ec8Sopenharmony_ci
2589a46c0ec8Sopenharmony_ci	/* Our rotation code only works with Wacoms, let's wait until
2590a46c0ec8Sopenharmony_ci	 * someone shouts */
2591a46c0ec8Sopenharmony_ci	if (evdev_device_get_id_vendor(device) != VENDOR_ID_WACOM) {
2592a46c0ec8Sopenharmony_ci		libevdev_disable_event_code(evdev, EV_KEY, BTN_TOOL_MOUSE);
2593a46c0ec8Sopenharmony_ci		libevdev_disable_event_code(evdev, EV_KEY, BTN_TOOL_LENS);
2594a46c0ec8Sopenharmony_ci	}
2595a46c0ec8Sopenharmony_ci
2596a46c0ec8Sopenharmony_ci	tablet_init_calibration(tablet, device);
2597a46c0ec8Sopenharmony_ci	tablet_init_proximity_threshold(tablet, device);
2598a46c0ec8Sopenharmony_ci	rc = tablet_init_accel(tablet, device);
2599a46c0ec8Sopenharmony_ci	if (rc != 0)
2600a46c0ec8Sopenharmony_ci		return rc;
2601a46c0ec8Sopenharmony_ci
2602a46c0ec8Sopenharmony_ci	evdev_init_sendevents(device, &tablet->base);
2603a46c0ec8Sopenharmony_ci	tablet_init_left_handed(device);
2604a46c0ec8Sopenharmony_ci	tablet_init_smoothing(device, tablet);
2605a46c0ec8Sopenharmony_ci
2606a46c0ec8Sopenharmony_ci	for (axis = LIBINPUT_TABLET_TOOL_AXIS_X;
2607a46c0ec8Sopenharmony_ci	     axis <= LIBINPUT_TABLET_TOOL_AXIS_MAX;
2608a46c0ec8Sopenharmony_ci	     axis++) {
2609a46c0ec8Sopenharmony_ci		if (tablet_device_has_axis(tablet, axis))
2610a46c0ec8Sopenharmony_ci			set_bit(tablet->axis_caps, axis);
2611a46c0ec8Sopenharmony_ci	}
2612a46c0ec8Sopenharmony_ci
2613a46c0ec8Sopenharmony_ci	tablet_set_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY);
2614a46c0ec8Sopenharmony_ci
2615a46c0ec8Sopenharmony_ci	/* We always enable the proximity out quirk, but disable it once a
2616a46c0ec8Sopenharmony_ci	   device gives us the right event sequence */
2617a46c0ec8Sopenharmony_ci	tablet->quirks.need_to_force_prox_out = true;
2618a46c0ec8Sopenharmony_ci
2619a46c0ec8Sopenharmony_ci	libinput_timer_init(&tablet->quirks.prox_out_timer,
2620a46c0ec8Sopenharmony_ci			    tablet_libinput_context(tablet),
2621a46c0ec8Sopenharmony_ci			    "proxout",
2622a46c0ec8Sopenharmony_ci			    tablet_proximity_out_quirk_timer_func,
2623a46c0ec8Sopenharmony_ci			    tablet);
2624a46c0ec8Sopenharmony_ci
2625a46c0ec8Sopenharmony_ci	return 0;
2626a46c0ec8Sopenharmony_ci}
2627a46c0ec8Sopenharmony_ci
2628a46c0ec8Sopenharmony_cistruct evdev_dispatch *
2629a46c0ec8Sopenharmony_cievdev_tablet_create(struct evdev_device *device)
2630a46c0ec8Sopenharmony_ci{
2631a46c0ec8Sopenharmony_ci	struct tablet_dispatch *tablet;
2632a46c0ec8Sopenharmony_ci	struct libinput *li = evdev_libinput_context(device);
2633a46c0ec8Sopenharmony_ci
2634a46c0ec8Sopenharmony_ci	libinput_libwacom_ref(li);
2635a46c0ec8Sopenharmony_ci
2636a46c0ec8Sopenharmony_ci	/* Stop false positives caused by the forced proximity code */
2637a46c0ec8Sopenharmony_ci	if (getenv("LIBINPUT_RUNNING_TEST_SUITE"))
2638a46c0ec8Sopenharmony_ci		FORCED_PROXOUT_TIMEOUT = 150 * 1000; /* µs */
2639a46c0ec8Sopenharmony_ci
2640a46c0ec8Sopenharmony_ci	tablet = zalloc(sizeof *tablet);
2641a46c0ec8Sopenharmony_ci
2642a46c0ec8Sopenharmony_ci	if (tablet_init(tablet, device) != 0) {
2643a46c0ec8Sopenharmony_ci		tablet_destroy(&tablet->base);
2644a46c0ec8Sopenharmony_ci		return NULL;
2645a46c0ec8Sopenharmony_ci	}
2646a46c0ec8Sopenharmony_ci
2647a46c0ec8Sopenharmony_ci	return &tablet->base;
2648a46c0ec8Sopenharmony_ci}
2649