1a46c0ec8Sopenharmony_ci/*
2a46c0ec8Sopenharmony_ci * Copyright © 2018 Red Hat, Inc.
3a46c0ec8Sopenharmony_ci *
4a46c0ec8Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
5a46c0ec8Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
6a46c0ec8Sopenharmony_ci * to deal in the Software without restriction, including without limitation
7a46c0ec8Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8a46c0ec8Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
9a46c0ec8Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
10a46c0ec8Sopenharmony_ci *
11a46c0ec8Sopenharmony_ci * The above copyright notice and this permission notice (including the next
12a46c0ec8Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
13a46c0ec8Sopenharmony_ci * Software.
14a46c0ec8Sopenharmony_ci *
15a46c0ec8Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16a46c0ec8Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17a46c0ec8Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18a46c0ec8Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19a46c0ec8Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20a46c0ec8Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21a46c0ec8Sopenharmony_ci * DEALINGS IN THE SOFTWARE.
22a46c0ec8Sopenharmony_ci */
23a46c0ec8Sopenharmony_ci
24a46c0ec8Sopenharmony_ci#include "config.h"
25a46c0ec8Sopenharmony_ci#include "evdev.h"
26a46c0ec8Sopenharmony_ci
27a46c0ec8Sopenharmony_cienum totem_slot_state {
28a46c0ec8Sopenharmony_ci	SLOT_STATE_NONE,
29a46c0ec8Sopenharmony_ci	SLOT_STATE_BEGIN,
30a46c0ec8Sopenharmony_ci	SLOT_STATE_UPDATE,
31a46c0ec8Sopenharmony_ci	SLOT_STATE_END,
32a46c0ec8Sopenharmony_ci};
33a46c0ec8Sopenharmony_ci
34a46c0ec8Sopenharmony_cistruct totem_slot {
35a46c0ec8Sopenharmony_ci	bool dirty;
36a46c0ec8Sopenharmony_ci	unsigned int index;
37a46c0ec8Sopenharmony_ci	enum totem_slot_state state;
38a46c0ec8Sopenharmony_ci	struct libinput_tablet_tool *tool;
39a46c0ec8Sopenharmony_ci	struct tablet_axes axes;
40a46c0ec8Sopenharmony_ci	unsigned char changed_axes[NCHARS(LIBINPUT_TABLET_TOOL_AXIS_MAX + 1)];
41a46c0ec8Sopenharmony_ci
42a46c0ec8Sopenharmony_ci	struct device_coords last_point;
43a46c0ec8Sopenharmony_ci};
44a46c0ec8Sopenharmony_ci
45a46c0ec8Sopenharmony_cistruct totem_dispatch {
46a46c0ec8Sopenharmony_ci	struct evdev_dispatch base;
47a46c0ec8Sopenharmony_ci	struct evdev_device *device;
48a46c0ec8Sopenharmony_ci
49a46c0ec8Sopenharmony_ci	int slot; /* current slot */
50a46c0ec8Sopenharmony_ci	struct totem_slot *slots;
51a46c0ec8Sopenharmony_ci	size_t nslots;
52a46c0ec8Sopenharmony_ci
53a46c0ec8Sopenharmony_ci	struct evdev_device *touch_device;
54a46c0ec8Sopenharmony_ci
55a46c0ec8Sopenharmony_ci	/* We only have one button */
56a46c0ec8Sopenharmony_ci	bool button_state_now;
57a46c0ec8Sopenharmony_ci	bool button_state_previous;
58a46c0ec8Sopenharmony_ci
59a46c0ec8Sopenharmony_ci	enum evdev_arbitration_state arbitration_state;
60a46c0ec8Sopenharmony_ci};
61a46c0ec8Sopenharmony_ci
62a46c0ec8Sopenharmony_cistatic inline struct totem_dispatch*
63a46c0ec8Sopenharmony_citotem_dispatch(struct evdev_dispatch *totem)
64a46c0ec8Sopenharmony_ci{
65a46c0ec8Sopenharmony_ci	evdev_verify_dispatch_type(totem, DISPATCH_TOTEM);
66a46c0ec8Sopenharmony_ci
67a46c0ec8Sopenharmony_ci	return container_of(totem, struct totem_dispatch, base);
68a46c0ec8Sopenharmony_ci}
69a46c0ec8Sopenharmony_ci
70a46c0ec8Sopenharmony_cistatic inline struct libinput *
71a46c0ec8Sopenharmony_citotem_libinput_context(const struct totem_dispatch *totem)
72a46c0ec8Sopenharmony_ci{
73a46c0ec8Sopenharmony_ci	return evdev_libinput_context(totem->device);
74a46c0ec8Sopenharmony_ci}
75a46c0ec8Sopenharmony_ci
76a46c0ec8Sopenharmony_cistatic struct libinput_tablet_tool *
77a46c0ec8Sopenharmony_citotem_new_tool(struct totem_dispatch *totem)
78a46c0ec8Sopenharmony_ci{
79a46c0ec8Sopenharmony_ci	struct libinput *libinput = totem_libinput_context(totem);
80a46c0ec8Sopenharmony_ci	struct libinput_tablet_tool *tool;
81a46c0ec8Sopenharmony_ci
82a46c0ec8Sopenharmony_ci	tool = zalloc(sizeof *tool);
83a46c0ec8Sopenharmony_ci
84a46c0ec8Sopenharmony_ci	*tool = (struct libinput_tablet_tool) {
85a46c0ec8Sopenharmony_ci		.type = LIBINPUT_TABLET_TOOL_TYPE_TOTEM,
86a46c0ec8Sopenharmony_ci		.serial = 0,
87a46c0ec8Sopenharmony_ci		.tool_id = 0,
88a46c0ec8Sopenharmony_ci		.refcount = 1,
89a46c0ec8Sopenharmony_ci	};
90a46c0ec8Sopenharmony_ci
91a46c0ec8Sopenharmony_ci	tool->pressure.offset = 0;
92a46c0ec8Sopenharmony_ci	tool->pressure.has_offset = false;
93a46c0ec8Sopenharmony_ci	tool->pressure.threshold.lower = 0;
94a46c0ec8Sopenharmony_ci	tool->pressure.threshold.upper = 1;
95a46c0ec8Sopenharmony_ci
96a46c0ec8Sopenharmony_ci	set_bit(tool->axis_caps, LIBINPUT_TABLET_TOOL_AXIS_X);
97a46c0ec8Sopenharmony_ci	set_bit(tool->axis_caps, LIBINPUT_TABLET_TOOL_AXIS_Y);
98a46c0ec8Sopenharmony_ci	set_bit(tool->axis_caps, LIBINPUT_TABLET_TOOL_AXIS_ROTATION_Z);
99a46c0ec8Sopenharmony_ci	set_bit(tool->axis_caps, LIBINPUT_TABLET_TOOL_AXIS_SIZE_MAJOR);
100a46c0ec8Sopenharmony_ci	set_bit(tool->axis_caps, LIBINPUT_TABLET_TOOL_AXIS_SIZE_MINOR);
101a46c0ec8Sopenharmony_ci	set_bit(tool->buttons, BTN_0);
102a46c0ec8Sopenharmony_ci
103a46c0ec8Sopenharmony_ci	list_insert(&libinput->tool_list, &tool->link);
104a46c0ec8Sopenharmony_ci
105a46c0ec8Sopenharmony_ci	return tool;
106a46c0ec8Sopenharmony_ci}
107a46c0ec8Sopenharmony_ci
108a46c0ec8Sopenharmony_cistatic inline void
109a46c0ec8Sopenharmony_citotem_set_touch_device_enabled(struct totem_dispatch *totem,
110a46c0ec8Sopenharmony_ci			       bool enable_touch_device,
111a46c0ec8Sopenharmony_ci			       uint64_t time)
112a46c0ec8Sopenharmony_ci{
113a46c0ec8Sopenharmony_ci	struct evdev_device *touch_device = totem->touch_device;
114a46c0ec8Sopenharmony_ci	struct evdev_dispatch *dispatch;
115a46c0ec8Sopenharmony_ci	struct phys_rect r, *rect = NULL;
116a46c0ec8Sopenharmony_ci	enum evdev_arbitration_state state = ARBITRATION_NOT_ACTIVE;
117a46c0ec8Sopenharmony_ci
118a46c0ec8Sopenharmony_ci	if (touch_device == NULL)
119a46c0ec8Sopenharmony_ci		return;
120a46c0ec8Sopenharmony_ci
121a46c0ec8Sopenharmony_ci	/* We just pick the coordinates of the first touch we find. The
122a46c0ec8Sopenharmony_ci	 * totem only does one tool right now despite being nominally an MT
123a46c0ec8Sopenharmony_ci	 * device, so let's not go too hard on ourselves*/
124a46c0ec8Sopenharmony_ci	for (size_t i = 0; !enable_touch_device && i < totem->nslots; i++) {
125a46c0ec8Sopenharmony_ci		struct totem_slot *slot = &totem->slots[i];
126a46c0ec8Sopenharmony_ci		struct phys_coords mm;
127a46c0ec8Sopenharmony_ci
128a46c0ec8Sopenharmony_ci		if (slot->state == SLOT_STATE_NONE)
129a46c0ec8Sopenharmony_ci			continue;
130a46c0ec8Sopenharmony_ci
131a46c0ec8Sopenharmony_ci		/* Totem size is ~70mm. We could calculate the real size but
132a46c0ec8Sopenharmony_ci		   until we need that, hardcoding it is enough */
133a46c0ec8Sopenharmony_ci		mm = evdev_device_units_to_mm(totem->device, &slot->axes.point);
134a46c0ec8Sopenharmony_ci		r.x = mm.x - 30;
135a46c0ec8Sopenharmony_ci		r.y = mm.y - 30;
136a46c0ec8Sopenharmony_ci		r.w = 100;
137a46c0ec8Sopenharmony_ci		r.h = 100;
138a46c0ec8Sopenharmony_ci
139a46c0ec8Sopenharmony_ci		rect = &r;
140a46c0ec8Sopenharmony_ci
141a46c0ec8Sopenharmony_ci		state = ARBITRATION_IGNORE_RECT;
142a46c0ec8Sopenharmony_ci		break;
143a46c0ec8Sopenharmony_ci	}
144a46c0ec8Sopenharmony_ci
145a46c0ec8Sopenharmony_ci	dispatch = touch_device->dispatch;
146a46c0ec8Sopenharmony_ci
147a46c0ec8Sopenharmony_ci	if (enable_touch_device) {
148a46c0ec8Sopenharmony_ci	    if (dispatch->interface->touch_arbitration_toggle)
149a46c0ec8Sopenharmony_ci		dispatch->interface->touch_arbitration_toggle(dispatch,
150a46c0ec8Sopenharmony_ci							      touch_device,
151a46c0ec8Sopenharmony_ci							      state,
152a46c0ec8Sopenharmony_ci							      rect,
153a46c0ec8Sopenharmony_ci							      time);
154a46c0ec8Sopenharmony_ci	} else {
155a46c0ec8Sopenharmony_ci		switch (totem->arbitration_state) {
156a46c0ec8Sopenharmony_ci		case ARBITRATION_IGNORE_ALL:
157a46c0ec8Sopenharmony_ci			abort();
158a46c0ec8Sopenharmony_ci		case ARBITRATION_NOT_ACTIVE:
159a46c0ec8Sopenharmony_ci			if (dispatch->interface->touch_arbitration_toggle)
160a46c0ec8Sopenharmony_ci				dispatch->interface->touch_arbitration_toggle(dispatch,
161a46c0ec8Sopenharmony_ci									      touch_device,
162a46c0ec8Sopenharmony_ci									      state,
163a46c0ec8Sopenharmony_ci									      rect,
164a46c0ec8Sopenharmony_ci									      time);
165a46c0ec8Sopenharmony_ci			break;
166a46c0ec8Sopenharmony_ci		case ARBITRATION_IGNORE_RECT:
167a46c0ec8Sopenharmony_ci			if (dispatch->interface->touch_arbitration_update_rect)
168a46c0ec8Sopenharmony_ci				dispatch->interface->touch_arbitration_update_rect(dispatch,
169a46c0ec8Sopenharmony_ci										   touch_device,
170a46c0ec8Sopenharmony_ci										   rect,
171a46c0ec8Sopenharmony_ci										   time);
172a46c0ec8Sopenharmony_ci			break;
173a46c0ec8Sopenharmony_ci		}
174a46c0ec8Sopenharmony_ci	}
175a46c0ec8Sopenharmony_ci	totem->arbitration_state = state;
176a46c0ec8Sopenharmony_ci}
177a46c0ec8Sopenharmony_ci
178a46c0ec8Sopenharmony_cistatic void
179a46c0ec8Sopenharmony_citotem_process_key(struct totem_dispatch *totem,
180a46c0ec8Sopenharmony_ci		  struct evdev_device *device,
181a46c0ec8Sopenharmony_ci		  struct input_event *e,
182a46c0ec8Sopenharmony_ci		  uint64_t time)
183a46c0ec8Sopenharmony_ci{
184a46c0ec8Sopenharmony_ci	/* ignore kernel key repeat */
185a46c0ec8Sopenharmony_ci	if (e->value == 2)
186a46c0ec8Sopenharmony_ci		return;
187a46c0ec8Sopenharmony_ci
188a46c0ec8Sopenharmony_ci	switch(e->code) {
189a46c0ec8Sopenharmony_ci	case BTN_0:
190a46c0ec8Sopenharmony_ci		totem->button_state_now = !!e->value;
191a46c0ec8Sopenharmony_ci		break;
192a46c0ec8Sopenharmony_ci	default:
193a46c0ec8Sopenharmony_ci		evdev_log_info(device,
194a46c0ec8Sopenharmony_ci			       "Unhandled KEY event code %#x\n",
195a46c0ec8Sopenharmony_ci			       e->code);
196a46c0ec8Sopenharmony_ci		break;
197a46c0ec8Sopenharmony_ci	}
198a46c0ec8Sopenharmony_ci}
199a46c0ec8Sopenharmony_ci
200a46c0ec8Sopenharmony_cistatic void
201a46c0ec8Sopenharmony_citotem_process_abs(struct totem_dispatch *totem,
202a46c0ec8Sopenharmony_ci		  struct evdev_device *device,
203a46c0ec8Sopenharmony_ci		  struct input_event *e,
204a46c0ec8Sopenharmony_ci		  uint64_t time)
205a46c0ec8Sopenharmony_ci{
206a46c0ec8Sopenharmony_ci	struct totem_slot *slot = &totem->slots[totem->slot];
207a46c0ec8Sopenharmony_ci
208a46c0ec8Sopenharmony_ci	switch(e->code) {
209a46c0ec8Sopenharmony_ci	case ABS_MT_SLOT:
210a46c0ec8Sopenharmony_ci		if ((size_t)e->value >= totem->nslots) {
211a46c0ec8Sopenharmony_ci			evdev_log_bug_libinput(device,
212a46c0ec8Sopenharmony_ci					       "exceeded slot count (%d of max %zd)\n",
213a46c0ec8Sopenharmony_ci					       e->value,
214a46c0ec8Sopenharmony_ci					       totem->nslots);
215a46c0ec8Sopenharmony_ci			e->value = totem->nslots - 1;
216a46c0ec8Sopenharmony_ci		}
217a46c0ec8Sopenharmony_ci		totem->slot = e->value;
218a46c0ec8Sopenharmony_ci		return;
219a46c0ec8Sopenharmony_ci	case ABS_MT_TRACKING_ID:
220a46c0ec8Sopenharmony_ci		/* If the totem is already down on init, we currently
221a46c0ec8Sopenharmony_ci		   ignore it */
222a46c0ec8Sopenharmony_ci		if (e->value >= 0)
223a46c0ec8Sopenharmony_ci			slot->state = SLOT_STATE_BEGIN;
224a46c0ec8Sopenharmony_ci		else if (slot->state != SLOT_STATE_NONE)
225a46c0ec8Sopenharmony_ci			slot->state = SLOT_STATE_END;
226a46c0ec8Sopenharmony_ci		break;
227a46c0ec8Sopenharmony_ci	case ABS_MT_POSITION_X:
228a46c0ec8Sopenharmony_ci		set_bit(slot->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_X);
229a46c0ec8Sopenharmony_ci		break;
230a46c0ec8Sopenharmony_ci	case ABS_MT_POSITION_Y:
231a46c0ec8Sopenharmony_ci		set_bit(slot->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_Y);
232a46c0ec8Sopenharmony_ci		break;
233a46c0ec8Sopenharmony_ci	case ABS_MT_TOUCH_MAJOR:
234a46c0ec8Sopenharmony_ci		set_bit(slot->changed_axes,
235a46c0ec8Sopenharmony_ci			LIBINPUT_TABLET_TOOL_AXIS_SIZE_MAJOR);
236a46c0ec8Sopenharmony_ci		break;
237a46c0ec8Sopenharmony_ci	case ABS_MT_TOUCH_MINOR:
238a46c0ec8Sopenharmony_ci		set_bit(slot->changed_axes,
239a46c0ec8Sopenharmony_ci			LIBINPUT_TABLET_TOOL_AXIS_SIZE_MINOR);
240a46c0ec8Sopenharmony_ci		break;
241a46c0ec8Sopenharmony_ci	case ABS_MT_ORIENTATION:
242a46c0ec8Sopenharmony_ci		set_bit(slot->changed_axes,
243a46c0ec8Sopenharmony_ci			LIBINPUT_TABLET_TOOL_AXIS_ROTATION_Z);
244a46c0ec8Sopenharmony_ci		break;
245a46c0ec8Sopenharmony_ci	case ABS_MT_TOOL_TYPE:
246a46c0ec8Sopenharmony_ci		if (e->value != MT_TOOL_DIAL) {
247a46c0ec8Sopenharmony_ci			evdev_log_info(device,
248a46c0ec8Sopenharmony_ci				       "Unexpected tool type %#x, changing to dial\n",
249a46c0ec8Sopenharmony_ci				       e->code);
250a46c0ec8Sopenharmony_ci		}
251a46c0ec8Sopenharmony_ci		break;
252a46c0ec8Sopenharmony_ci	default:
253a46c0ec8Sopenharmony_ci		evdev_log_info(device,
254a46c0ec8Sopenharmony_ci			       "Unhandled ABS event code %#x\n",
255a46c0ec8Sopenharmony_ci			       e->code);
256a46c0ec8Sopenharmony_ci		break;
257a46c0ec8Sopenharmony_ci	}
258a46c0ec8Sopenharmony_ci}
259a46c0ec8Sopenharmony_ci
260a46c0ec8Sopenharmony_cistatic bool
261a46c0ec8Sopenharmony_citotem_slot_fetch_axes(struct totem_dispatch *totem,
262a46c0ec8Sopenharmony_ci		      struct totem_slot *slot,
263a46c0ec8Sopenharmony_ci		      struct libinput_tablet_tool *tool,
264a46c0ec8Sopenharmony_ci		      struct tablet_axes *axes_out,
265a46c0ec8Sopenharmony_ci		      uint64_t time)
266a46c0ec8Sopenharmony_ci{
267a46c0ec8Sopenharmony_ci	struct evdev_device *device = totem->device;
268a46c0ec8Sopenharmony_ci	const char tmp[sizeof(slot->changed_axes)] = {0};
269a46c0ec8Sopenharmony_ci	struct tablet_axes axes = {0};
270a46c0ec8Sopenharmony_ci	struct device_float_coords delta;
271a46c0ec8Sopenharmony_ci	bool rc = false;
272a46c0ec8Sopenharmony_ci
273a46c0ec8Sopenharmony_ci	if (memcmp(tmp, slot->changed_axes, sizeof(tmp)) == 0) {
274a46c0ec8Sopenharmony_ci		axes = slot->axes;
275a46c0ec8Sopenharmony_ci		goto out;
276a46c0ec8Sopenharmony_ci	}
277a46c0ec8Sopenharmony_ci
278a46c0ec8Sopenharmony_ci	if (bit_is_set(slot->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_X) ||
279a46c0ec8Sopenharmony_ci	    bit_is_set(slot->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_Y)) {
280a46c0ec8Sopenharmony_ci		slot->axes.point.x = libevdev_get_slot_value(device->evdev,
281a46c0ec8Sopenharmony_ci							     slot->index,
282a46c0ec8Sopenharmony_ci							     ABS_MT_POSITION_X);
283a46c0ec8Sopenharmony_ci		slot->axes.point.y = libevdev_get_slot_value(device->evdev,
284a46c0ec8Sopenharmony_ci							     slot->index,
285a46c0ec8Sopenharmony_ci							     ABS_MT_POSITION_Y);
286a46c0ec8Sopenharmony_ci	}
287a46c0ec8Sopenharmony_ci
288a46c0ec8Sopenharmony_ci	if (bit_is_set(slot->changed_axes,
289a46c0ec8Sopenharmony_ci		       LIBINPUT_TABLET_TOOL_AXIS_ROTATION_Z)) {
290a46c0ec8Sopenharmony_ci		int angle = libevdev_get_slot_value(device->evdev,
291a46c0ec8Sopenharmony_ci						    slot->index,
292a46c0ec8Sopenharmony_ci						    ABS_MT_ORIENTATION);
293a46c0ec8Sopenharmony_ci		/* The kernel gives us ±90 degrees off neutral */
294a46c0ec8Sopenharmony_ci		slot->axes.rotation = (360 - angle) % 360;
295a46c0ec8Sopenharmony_ci	}
296a46c0ec8Sopenharmony_ci
297a46c0ec8Sopenharmony_ci	if (bit_is_set(slot->changed_axes,
298a46c0ec8Sopenharmony_ci		       LIBINPUT_TABLET_TOOL_AXIS_SIZE_MAJOR) ||
299a46c0ec8Sopenharmony_ci	    bit_is_set(slot->changed_axes,
300a46c0ec8Sopenharmony_ci		       LIBINPUT_TABLET_TOOL_AXIS_SIZE_MINOR)) {
301a46c0ec8Sopenharmony_ci		int major, minor;
302a46c0ec8Sopenharmony_ci		unsigned int rmajor, rminor;
303a46c0ec8Sopenharmony_ci
304a46c0ec8Sopenharmony_ci		major = libevdev_get_slot_value(device->evdev,
305a46c0ec8Sopenharmony_ci						slot->index,
306a46c0ec8Sopenharmony_ci						ABS_MT_TOUCH_MAJOR);
307a46c0ec8Sopenharmony_ci		minor = libevdev_get_slot_value(device->evdev,
308a46c0ec8Sopenharmony_ci						slot->index,
309a46c0ec8Sopenharmony_ci						ABS_MT_TOUCH_MINOR);
310a46c0ec8Sopenharmony_ci		rmajor = libevdev_get_abs_resolution(device->evdev, ABS_MT_TOUCH_MAJOR);
311a46c0ec8Sopenharmony_ci		rminor = libevdev_get_abs_resolution(device->evdev, ABS_MT_TOUCH_MINOR);
312a46c0ec8Sopenharmony_ci		slot->axes.size.major = (double)major/rmajor;
313a46c0ec8Sopenharmony_ci		slot->axes.size.minor = (double)minor/rminor;
314a46c0ec8Sopenharmony_ci	}
315a46c0ec8Sopenharmony_ci
316a46c0ec8Sopenharmony_ci	axes.point = slot->axes.point;
317a46c0ec8Sopenharmony_ci	axes.rotation = slot->axes.rotation;
318a46c0ec8Sopenharmony_ci	axes.size = slot->axes.size;
319a46c0ec8Sopenharmony_ci
320a46c0ec8Sopenharmony_ci	delta.x = slot->axes.point.x - slot->last_point.x;
321a46c0ec8Sopenharmony_ci	delta.y = slot->axes.point.y - slot->last_point.y;
322a46c0ec8Sopenharmony_ci	axes.delta = filter_dispatch(device->pointer.filter, &delta, tool, time);
323a46c0ec8Sopenharmony_ci
324a46c0ec8Sopenharmony_ci	rc = true;
325a46c0ec8Sopenharmony_ciout:
326a46c0ec8Sopenharmony_ci	*axes_out = axes;
327a46c0ec8Sopenharmony_ci	return rc;
328a46c0ec8Sopenharmony_ci
329a46c0ec8Sopenharmony_ci}
330a46c0ec8Sopenharmony_ci
331a46c0ec8Sopenharmony_cistatic void
332a46c0ec8Sopenharmony_citotem_slot_mark_all_axes_changed(struct totem_dispatch *totem,
333a46c0ec8Sopenharmony_ci			    struct totem_slot *slot,
334a46c0ec8Sopenharmony_ci			    struct libinput_tablet_tool *tool)
335a46c0ec8Sopenharmony_ci{
336a46c0ec8Sopenharmony_ci	static_assert(sizeof(slot->changed_axes) ==
337a46c0ec8Sopenharmony_ci			      sizeof(tool->axis_caps),
338a46c0ec8Sopenharmony_ci		      "Mismatching array sizes");
339a46c0ec8Sopenharmony_ci
340a46c0ec8Sopenharmony_ci	memcpy(slot->changed_axes,
341a46c0ec8Sopenharmony_ci	       tool->axis_caps,
342a46c0ec8Sopenharmony_ci	       sizeof(slot->changed_axes));
343a46c0ec8Sopenharmony_ci}
344a46c0ec8Sopenharmony_ci
345a46c0ec8Sopenharmony_cistatic inline void
346a46c0ec8Sopenharmony_citotem_slot_reset_changed_axes(struct totem_dispatch *totem,
347a46c0ec8Sopenharmony_ci			      struct totem_slot *slot)
348a46c0ec8Sopenharmony_ci{
349a46c0ec8Sopenharmony_ci	memset(slot->changed_axes, 0, sizeof(slot->changed_axes));
350a46c0ec8Sopenharmony_ci}
351a46c0ec8Sopenharmony_ci
352a46c0ec8Sopenharmony_cistatic inline void
353a46c0ec8Sopenharmony_cislot_axes_initialize(struct totem_dispatch *totem,
354a46c0ec8Sopenharmony_ci		     struct totem_slot *slot)
355a46c0ec8Sopenharmony_ci{
356a46c0ec8Sopenharmony_ci	struct evdev_device *device = totem->device;
357a46c0ec8Sopenharmony_ci
358a46c0ec8Sopenharmony_ci	slot->axes.point.x = libevdev_get_slot_value(device->evdev,
359a46c0ec8Sopenharmony_ci						     slot->index,
360a46c0ec8Sopenharmony_ci						     ABS_MT_POSITION_X);
361a46c0ec8Sopenharmony_ci	slot->axes.point.y = libevdev_get_slot_value(device->evdev,
362a46c0ec8Sopenharmony_ci						     slot->index,
363a46c0ec8Sopenharmony_ci						     ABS_MT_POSITION_Y);
364a46c0ec8Sopenharmony_ci	slot->last_point.x = slot->axes.point.x;
365a46c0ec8Sopenharmony_ci	slot->last_point.y = slot->axes.point.y;
366a46c0ec8Sopenharmony_ci}
367a46c0ec8Sopenharmony_ci
368a46c0ec8Sopenharmony_cistatic enum totem_slot_state
369a46c0ec8Sopenharmony_citotem_handle_slot_state(struct totem_dispatch *totem,
370a46c0ec8Sopenharmony_ci			struct totem_slot *slot,
371a46c0ec8Sopenharmony_ci			uint64_t time)
372a46c0ec8Sopenharmony_ci{
373a46c0ec8Sopenharmony_ci	struct evdev_device *device = totem->device;
374a46c0ec8Sopenharmony_ci	struct tablet_axes axes;
375a46c0ec8Sopenharmony_ci	enum libinput_tablet_tool_tip_state tip_state;
376a46c0ec8Sopenharmony_ci	bool updated;
377a46c0ec8Sopenharmony_ci
378a46c0ec8Sopenharmony_ci	switch (slot->state) {
379a46c0ec8Sopenharmony_ci	case SLOT_STATE_BEGIN:
380a46c0ec8Sopenharmony_ci		if (!slot->tool)
381a46c0ec8Sopenharmony_ci			slot->tool = totem_new_tool(totem);
382a46c0ec8Sopenharmony_ci		slot_axes_initialize(totem, slot);
383a46c0ec8Sopenharmony_ci		totem_slot_mark_all_axes_changed(totem, slot, slot->tool);
384a46c0ec8Sopenharmony_ci		break;
385a46c0ec8Sopenharmony_ci	case SLOT_STATE_UPDATE:
386a46c0ec8Sopenharmony_ci	case SLOT_STATE_END:
387a46c0ec8Sopenharmony_ci		assert(slot->tool);
388a46c0ec8Sopenharmony_ci		break;
389a46c0ec8Sopenharmony_ci	case SLOT_STATE_NONE:
390a46c0ec8Sopenharmony_ci		return SLOT_STATE_NONE;
391a46c0ec8Sopenharmony_ci	}
392a46c0ec8Sopenharmony_ci
393a46c0ec8Sopenharmony_ci	tip_state = LIBINPUT_TABLET_TOOL_TIP_UP;
394a46c0ec8Sopenharmony_ci	updated = totem_slot_fetch_axes(totem, slot, slot->tool, &axes, time);
395a46c0ec8Sopenharmony_ci
396a46c0ec8Sopenharmony_ci	switch (slot->state) {
397a46c0ec8Sopenharmony_ci	case SLOT_STATE_BEGIN:
398a46c0ec8Sopenharmony_ci		tip_state = LIBINPUT_TABLET_TOOL_TIP_DOWN;
399a46c0ec8Sopenharmony_ci		tablet_notify_proximity(&device->base,
400a46c0ec8Sopenharmony_ci					time,
401a46c0ec8Sopenharmony_ci					slot->tool,
402a46c0ec8Sopenharmony_ci					LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN,
403a46c0ec8Sopenharmony_ci					slot->changed_axes,
404a46c0ec8Sopenharmony_ci					&axes);
405a46c0ec8Sopenharmony_ci		totem_slot_reset_changed_axes(totem, slot);
406a46c0ec8Sopenharmony_ci		tablet_notify_tip(&device->base,
407a46c0ec8Sopenharmony_ci				  time,
408a46c0ec8Sopenharmony_ci				  slot->tool,
409a46c0ec8Sopenharmony_ci				  tip_state,
410a46c0ec8Sopenharmony_ci				  slot->changed_axes,
411a46c0ec8Sopenharmony_ci				  &axes);
412a46c0ec8Sopenharmony_ci		slot->state = SLOT_STATE_UPDATE;
413a46c0ec8Sopenharmony_ci		break;
414a46c0ec8Sopenharmony_ci	case SLOT_STATE_UPDATE:
415a46c0ec8Sopenharmony_ci		tip_state = LIBINPUT_TABLET_TOOL_TIP_DOWN;
416a46c0ec8Sopenharmony_ci		if (updated) {
417a46c0ec8Sopenharmony_ci			tablet_notify_axis(&device->base,
418a46c0ec8Sopenharmony_ci					   time,
419a46c0ec8Sopenharmony_ci					   slot->tool,
420a46c0ec8Sopenharmony_ci					   tip_state,
421a46c0ec8Sopenharmony_ci					   slot->changed_axes,
422a46c0ec8Sopenharmony_ci					   &axes);
423a46c0ec8Sopenharmony_ci		}
424a46c0ec8Sopenharmony_ci		break;
425a46c0ec8Sopenharmony_ci	case SLOT_STATE_END:
426a46c0ec8Sopenharmony_ci		/* prox out is handled after button events */
427a46c0ec8Sopenharmony_ci		break;
428a46c0ec8Sopenharmony_ci	case SLOT_STATE_NONE:
429a46c0ec8Sopenharmony_ci		abort();
430a46c0ec8Sopenharmony_ci		break;
431a46c0ec8Sopenharmony_ci	}
432a46c0ec8Sopenharmony_ci
433a46c0ec8Sopenharmony_ci	/* We only have one button but possibly multiple totems. It's not
434a46c0ec8Sopenharmony_ci	 * clear how the firmware will work, so for now we just handle the
435a46c0ec8Sopenharmony_ci	 * button state in the first slot.
436a46c0ec8Sopenharmony_ci	 *
437a46c0ec8Sopenharmony_ci	 * Due to the design of the totem we're also less fancy about
438a46c0ec8Sopenharmony_ci	 * button handling than the tablet code. Worst case, you might get
439a46c0ec8Sopenharmony_ci	 * tip up before button up but meh.
440a46c0ec8Sopenharmony_ci	 */
441a46c0ec8Sopenharmony_ci	if (totem->button_state_now != totem->button_state_previous) {
442a46c0ec8Sopenharmony_ci		enum libinput_button_state btn_state;
443a46c0ec8Sopenharmony_ci
444a46c0ec8Sopenharmony_ci		if (totem->button_state_now)
445a46c0ec8Sopenharmony_ci			btn_state = LIBINPUT_BUTTON_STATE_PRESSED;
446a46c0ec8Sopenharmony_ci		else
447a46c0ec8Sopenharmony_ci			btn_state = LIBINPUT_BUTTON_STATE_RELEASED;
448a46c0ec8Sopenharmony_ci
449a46c0ec8Sopenharmony_ci		tablet_notify_button(&device->base,
450a46c0ec8Sopenharmony_ci				     time,
451a46c0ec8Sopenharmony_ci				     slot->tool,
452a46c0ec8Sopenharmony_ci				     tip_state,
453a46c0ec8Sopenharmony_ci				     &axes,
454a46c0ec8Sopenharmony_ci				     BTN_0,
455a46c0ec8Sopenharmony_ci				     btn_state);
456a46c0ec8Sopenharmony_ci
457a46c0ec8Sopenharmony_ci		totem->button_state_previous = totem->button_state_now;
458a46c0ec8Sopenharmony_ci	}
459a46c0ec8Sopenharmony_ci
460a46c0ec8Sopenharmony_ci	switch(slot->state) {
461a46c0ec8Sopenharmony_ci	case SLOT_STATE_BEGIN:
462a46c0ec8Sopenharmony_ci	case SLOT_STATE_UPDATE:
463a46c0ec8Sopenharmony_ci		break;
464a46c0ec8Sopenharmony_ci	case SLOT_STATE_END:
465a46c0ec8Sopenharmony_ci		tip_state = LIBINPUT_TABLET_TOOL_TIP_UP;
466a46c0ec8Sopenharmony_ci		tablet_notify_tip(&device->base,
467a46c0ec8Sopenharmony_ci				  time,
468a46c0ec8Sopenharmony_ci				  slot->tool,
469a46c0ec8Sopenharmony_ci				  tip_state,
470a46c0ec8Sopenharmony_ci				  slot->changed_axes,
471a46c0ec8Sopenharmony_ci				  &axes);
472a46c0ec8Sopenharmony_ci		totem_slot_reset_changed_axes(totem, slot);
473a46c0ec8Sopenharmony_ci		tablet_notify_proximity(&device->base,
474a46c0ec8Sopenharmony_ci					time,
475a46c0ec8Sopenharmony_ci					slot->tool,
476a46c0ec8Sopenharmony_ci					LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT,
477a46c0ec8Sopenharmony_ci					slot->changed_axes,
478a46c0ec8Sopenharmony_ci					&axes);
479a46c0ec8Sopenharmony_ci		slot->state = SLOT_STATE_NONE;
480a46c0ec8Sopenharmony_ci		break;
481a46c0ec8Sopenharmony_ci	case SLOT_STATE_NONE:
482a46c0ec8Sopenharmony_ci		abort();
483a46c0ec8Sopenharmony_ci		break;
484a46c0ec8Sopenharmony_ci	}
485a46c0ec8Sopenharmony_ci
486a46c0ec8Sopenharmony_ci	slot->last_point = slot->axes.point;
487a46c0ec8Sopenharmony_ci	totem_slot_reset_changed_axes(totem, slot);
488a46c0ec8Sopenharmony_ci
489a46c0ec8Sopenharmony_ci	return slot->state;
490a46c0ec8Sopenharmony_ci}
491a46c0ec8Sopenharmony_ci
492a46c0ec8Sopenharmony_cistatic enum totem_slot_state
493a46c0ec8Sopenharmony_citotem_handle_state(struct totem_dispatch *totem,
494a46c0ec8Sopenharmony_ci		   uint64_t time)
495a46c0ec8Sopenharmony_ci{
496a46c0ec8Sopenharmony_ci	enum totem_slot_state global_state = SLOT_STATE_NONE;
497a46c0ec8Sopenharmony_ci
498a46c0ec8Sopenharmony_ci	for (size_t i = 0; i < totem->nslots; i++) {
499a46c0ec8Sopenharmony_ci		enum totem_slot_state s;
500a46c0ec8Sopenharmony_ci
501a46c0ec8Sopenharmony_ci		s = totem_handle_slot_state(totem,
502a46c0ec8Sopenharmony_ci					    &totem->slots[i],
503a46c0ec8Sopenharmony_ci					    time);
504a46c0ec8Sopenharmony_ci
505a46c0ec8Sopenharmony_ci		/* If one slot is active, the totem is active */
506a46c0ec8Sopenharmony_ci		if (s != SLOT_STATE_NONE)
507a46c0ec8Sopenharmony_ci			global_state = SLOT_STATE_UPDATE;
508a46c0ec8Sopenharmony_ci	}
509a46c0ec8Sopenharmony_ci
510a46c0ec8Sopenharmony_ci	return global_state;
511a46c0ec8Sopenharmony_ci}
512a46c0ec8Sopenharmony_ci
513a46c0ec8Sopenharmony_cistatic void
514a46c0ec8Sopenharmony_citotem_interface_process(struct evdev_dispatch *dispatch,
515a46c0ec8Sopenharmony_ci			struct evdev_device *device,
516a46c0ec8Sopenharmony_ci			struct input_event *e,
517a46c0ec8Sopenharmony_ci			uint64_t time)
518a46c0ec8Sopenharmony_ci{
519a46c0ec8Sopenharmony_ci	struct totem_dispatch *totem = totem_dispatch(dispatch);
520a46c0ec8Sopenharmony_ci	enum totem_slot_state global_state;
521a46c0ec8Sopenharmony_ci	bool enable_touch;
522a46c0ec8Sopenharmony_ci
523a46c0ec8Sopenharmony_ci	switch(e->type) {
524a46c0ec8Sopenharmony_ci	case EV_ABS:
525a46c0ec8Sopenharmony_ci		totem_process_abs(totem, device, e, time);
526a46c0ec8Sopenharmony_ci		break;
527a46c0ec8Sopenharmony_ci	case EV_KEY:
528a46c0ec8Sopenharmony_ci		totem_process_key(totem, device, e, time);
529a46c0ec8Sopenharmony_ci		break;
530a46c0ec8Sopenharmony_ci	case EV_MSC:
531a46c0ec8Sopenharmony_ci		/* timestamp, ignore */
532a46c0ec8Sopenharmony_ci		break;
533a46c0ec8Sopenharmony_ci	case EV_SYN:
534a46c0ec8Sopenharmony_ci		global_state = totem_handle_state(totem, time);
535a46c0ec8Sopenharmony_ci		enable_touch = (global_state == SLOT_STATE_NONE);
536a46c0ec8Sopenharmony_ci		totem_set_touch_device_enabled(totem,
537a46c0ec8Sopenharmony_ci					       enable_touch,
538a46c0ec8Sopenharmony_ci					       time);
539a46c0ec8Sopenharmony_ci		break;
540a46c0ec8Sopenharmony_ci	default:
541a46c0ec8Sopenharmony_ci		evdev_log_error(device,
542a46c0ec8Sopenharmony_ci				"Unexpected event type %s (%#x)\n",
543a46c0ec8Sopenharmony_ci				libevdev_event_type_get_name(e->type),
544a46c0ec8Sopenharmony_ci				e->type);
545a46c0ec8Sopenharmony_ci		break;
546a46c0ec8Sopenharmony_ci	}
547a46c0ec8Sopenharmony_ci}
548a46c0ec8Sopenharmony_ci
549a46c0ec8Sopenharmony_cistatic void
550a46c0ec8Sopenharmony_citotem_interface_suspend(struct evdev_dispatch *dispatch,
551a46c0ec8Sopenharmony_ci			struct evdev_device *device)
552a46c0ec8Sopenharmony_ci{
553a46c0ec8Sopenharmony_ci	struct totem_dispatch *totem = totem_dispatch(dispatch);
554a46c0ec8Sopenharmony_ci	uint64_t now = libinput_now(evdev_libinput_context(device));
555a46c0ec8Sopenharmony_ci
556a46c0ec8Sopenharmony_ci	for (size_t i = 0; i < totem->nslots; i++) {
557a46c0ec8Sopenharmony_ci		struct totem_slot *slot = &totem->slots[i];
558a46c0ec8Sopenharmony_ci		struct tablet_axes axes;
559a46c0ec8Sopenharmony_ci		enum libinput_tablet_tool_tip_state tip_state;
560a46c0ec8Sopenharmony_ci
561a46c0ec8Sopenharmony_ci		/* If we never initialized a tool, we can skip everything */
562a46c0ec8Sopenharmony_ci		if (!slot->tool)
563a46c0ec8Sopenharmony_ci			continue;
564a46c0ec8Sopenharmony_ci
565a46c0ec8Sopenharmony_ci		totem_slot_fetch_axes(totem, slot, slot->tool, &axes, now);
566a46c0ec8Sopenharmony_ci		totem_slot_reset_changed_axes(totem, slot);
567a46c0ec8Sopenharmony_ci
568a46c0ec8Sopenharmony_ci		if (slot->state == SLOT_STATE_NONE)
569a46c0ec8Sopenharmony_ci			tip_state = LIBINPUT_TABLET_TOOL_TIP_UP;
570a46c0ec8Sopenharmony_ci		else
571a46c0ec8Sopenharmony_ci			tip_state = LIBINPUT_TABLET_TOOL_TIP_DOWN;
572a46c0ec8Sopenharmony_ci
573a46c0ec8Sopenharmony_ci		if (totem->button_state_now) {
574a46c0ec8Sopenharmony_ci			tablet_notify_button(&device->base,
575a46c0ec8Sopenharmony_ci					     now,
576a46c0ec8Sopenharmony_ci					     slot->tool,
577a46c0ec8Sopenharmony_ci					     tip_state,
578a46c0ec8Sopenharmony_ci					     &axes,
579a46c0ec8Sopenharmony_ci					     BTN_0,
580a46c0ec8Sopenharmony_ci					     LIBINPUT_BUTTON_STATE_RELEASED);
581a46c0ec8Sopenharmony_ci
582a46c0ec8Sopenharmony_ci			totem->button_state_now = false;
583a46c0ec8Sopenharmony_ci			totem->button_state_previous = false;
584a46c0ec8Sopenharmony_ci		}
585a46c0ec8Sopenharmony_ci
586a46c0ec8Sopenharmony_ci		if (slot->state != SLOT_STATE_NONE) {
587a46c0ec8Sopenharmony_ci			tablet_notify_tip(&device->base,
588a46c0ec8Sopenharmony_ci					  now,
589a46c0ec8Sopenharmony_ci					  slot->tool,
590a46c0ec8Sopenharmony_ci					  LIBINPUT_TABLET_TOOL_TIP_UP,
591a46c0ec8Sopenharmony_ci					  slot->changed_axes,
592a46c0ec8Sopenharmony_ci					  &axes);
593a46c0ec8Sopenharmony_ci		}
594a46c0ec8Sopenharmony_ci		tablet_notify_proximity(&device->base,
595a46c0ec8Sopenharmony_ci					now,
596a46c0ec8Sopenharmony_ci					slot->tool,
597a46c0ec8Sopenharmony_ci					LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT,
598a46c0ec8Sopenharmony_ci					slot->changed_axes,
599a46c0ec8Sopenharmony_ci					&axes);
600a46c0ec8Sopenharmony_ci	}
601a46c0ec8Sopenharmony_ci	totem_set_touch_device_enabled(totem, true, now);
602a46c0ec8Sopenharmony_ci}
603a46c0ec8Sopenharmony_ci
604a46c0ec8Sopenharmony_cistatic void
605a46c0ec8Sopenharmony_citotem_interface_destroy(struct evdev_dispatch *dispatch)
606a46c0ec8Sopenharmony_ci{
607a46c0ec8Sopenharmony_ci	struct totem_dispatch *totem = totem_dispatch(dispatch);
608a46c0ec8Sopenharmony_ci
609a46c0ec8Sopenharmony_ci	free(totem->slots);
610a46c0ec8Sopenharmony_ci	free(totem);
611a46c0ec8Sopenharmony_ci}
612a46c0ec8Sopenharmony_ci
613a46c0ec8Sopenharmony_cistatic void
614a46c0ec8Sopenharmony_citotem_interface_device_added(struct evdev_device *device,
615a46c0ec8Sopenharmony_ci			     struct evdev_device *added_device)
616a46c0ec8Sopenharmony_ci{
617a46c0ec8Sopenharmony_ci	struct totem_dispatch *totem = totem_dispatch(device->dispatch);
618a46c0ec8Sopenharmony_ci	struct libinput_device_group *g1, *g2;
619a46c0ec8Sopenharmony_ci
620a46c0ec8Sopenharmony_ci	if ((evdev_device_get_id_vendor(added_device) !=
621a46c0ec8Sopenharmony_ci	    evdev_device_get_id_vendor(device)) ||
622a46c0ec8Sopenharmony_ci	    (evdev_device_get_id_product(added_device) !=
623a46c0ec8Sopenharmony_ci	     evdev_device_get_id_product(device)))
624a46c0ec8Sopenharmony_ci	    return;
625a46c0ec8Sopenharmony_ci
626a46c0ec8Sopenharmony_ci	/* virtual devices don't have device groups, so check for that
627a46c0ec8Sopenharmony_ci	   libinput replay */
628a46c0ec8Sopenharmony_ci	g1 = libinput_device_get_device_group(&device->base);
629a46c0ec8Sopenharmony_ci	g2 = libinput_device_get_device_group(&added_device->base);
630a46c0ec8Sopenharmony_ci	if (g1 && g2 && g1->identifier != g2->identifier)
631a46c0ec8Sopenharmony_ci		return;
632a46c0ec8Sopenharmony_ci
633a46c0ec8Sopenharmony_ci	if (totem->touch_device != NULL) {
634a46c0ec8Sopenharmony_ci		evdev_log_bug_libinput(device,
635a46c0ec8Sopenharmony_ci				       "already has a paired touch device, ignoring (%s)\n",
636a46c0ec8Sopenharmony_ci				       added_device->devname);
637a46c0ec8Sopenharmony_ci		return;
638a46c0ec8Sopenharmony_ci	}
639a46c0ec8Sopenharmony_ci
640a46c0ec8Sopenharmony_ci	totem->touch_device = added_device;
641a46c0ec8Sopenharmony_ci	evdev_log_info(device, "%s: is the totem touch device\n", added_device->devname);
642a46c0ec8Sopenharmony_ci}
643a46c0ec8Sopenharmony_ci
644a46c0ec8Sopenharmony_cistatic void
645a46c0ec8Sopenharmony_citotem_interface_device_removed(struct evdev_device *device,
646a46c0ec8Sopenharmony_ci			       struct evdev_device *removed_device)
647a46c0ec8Sopenharmony_ci{
648a46c0ec8Sopenharmony_ci	struct totem_dispatch *totem = totem_dispatch(device->dispatch);
649a46c0ec8Sopenharmony_ci
650a46c0ec8Sopenharmony_ci	if (totem->touch_device != removed_device)
651a46c0ec8Sopenharmony_ci		return;
652a46c0ec8Sopenharmony_ci
653a46c0ec8Sopenharmony_ci	totem_set_touch_device_enabled(totem, true,
654a46c0ec8Sopenharmony_ci				       libinput_now(evdev_libinput_context(device)));
655a46c0ec8Sopenharmony_ci	totem->touch_device = NULL;
656a46c0ec8Sopenharmony_ci}
657a46c0ec8Sopenharmony_ci
658a46c0ec8Sopenharmony_cistatic void
659a46c0ec8Sopenharmony_citotem_interface_initial_proximity(struct evdev_device *device,
660a46c0ec8Sopenharmony_ci				  struct evdev_dispatch *dispatch)
661a46c0ec8Sopenharmony_ci{
662a46c0ec8Sopenharmony_ci	struct totem_dispatch *totem = totem_dispatch(dispatch);
663a46c0ec8Sopenharmony_ci	uint64_t now = libinput_now(evdev_libinput_context(device));
664a46c0ec8Sopenharmony_ci	bool enable_touch = true;
665a46c0ec8Sopenharmony_ci
666a46c0ec8Sopenharmony_ci	for (size_t i = 0; i < totem->nslots; i++) {
667a46c0ec8Sopenharmony_ci		struct totem_slot *slot = &totem->slots[i];
668a46c0ec8Sopenharmony_ci		struct tablet_axes axes;
669a46c0ec8Sopenharmony_ci		int tracking_id;
670a46c0ec8Sopenharmony_ci
671a46c0ec8Sopenharmony_ci		tracking_id = libevdev_get_slot_value(device->evdev,
672a46c0ec8Sopenharmony_ci						      i,
673a46c0ec8Sopenharmony_ci						      ABS_MT_TRACKING_ID);
674a46c0ec8Sopenharmony_ci		if (tracking_id == -1)
675a46c0ec8Sopenharmony_ci			continue;
676a46c0ec8Sopenharmony_ci
677a46c0ec8Sopenharmony_ci		slot->tool = totem_new_tool(totem);
678a46c0ec8Sopenharmony_ci		slot_axes_initialize(totem, slot);
679a46c0ec8Sopenharmony_ci		totem_slot_mark_all_axes_changed(totem, slot, slot->tool);
680a46c0ec8Sopenharmony_ci		totem_slot_fetch_axes(totem, slot, slot->tool, &axes, now);
681a46c0ec8Sopenharmony_ci		tablet_notify_proximity(&device->base,
682a46c0ec8Sopenharmony_ci					now,
683a46c0ec8Sopenharmony_ci					slot->tool,
684a46c0ec8Sopenharmony_ci					LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN,
685a46c0ec8Sopenharmony_ci					slot->changed_axes,
686a46c0ec8Sopenharmony_ci					&axes);
687a46c0ec8Sopenharmony_ci		totem_slot_reset_changed_axes(totem, slot);
688a46c0ec8Sopenharmony_ci		tablet_notify_tip(&device->base,
689a46c0ec8Sopenharmony_ci				  now,
690a46c0ec8Sopenharmony_ci				  slot->tool,
691a46c0ec8Sopenharmony_ci				  LIBINPUT_TABLET_TOOL_TIP_DOWN,
692a46c0ec8Sopenharmony_ci				  slot->changed_axes,
693a46c0ec8Sopenharmony_ci				  &axes);
694a46c0ec8Sopenharmony_ci		slot->state = SLOT_STATE_UPDATE;
695a46c0ec8Sopenharmony_ci		enable_touch = false;
696a46c0ec8Sopenharmony_ci	}
697a46c0ec8Sopenharmony_ci
698a46c0ec8Sopenharmony_ci	totem_set_touch_device_enabled(totem, enable_touch, now);
699a46c0ec8Sopenharmony_ci}
700a46c0ec8Sopenharmony_ci
701a46c0ec8Sopenharmony_cistruct evdev_dispatch_interface totem_interface = {
702a46c0ec8Sopenharmony_ci	.process = totem_interface_process,
703a46c0ec8Sopenharmony_ci	.suspend = totem_interface_suspend,
704a46c0ec8Sopenharmony_ci	.remove = NULL,
705a46c0ec8Sopenharmony_ci	.destroy = totem_interface_destroy,
706a46c0ec8Sopenharmony_ci	.device_added = totem_interface_device_added,
707a46c0ec8Sopenharmony_ci	.device_removed = totem_interface_device_removed,
708a46c0ec8Sopenharmony_ci	.device_suspended = totem_interface_device_removed, /* treat as remove */
709a46c0ec8Sopenharmony_ci	.device_resumed = totem_interface_device_added, /* treat as add */
710a46c0ec8Sopenharmony_ci	.post_added = totem_interface_initial_proximity,
711a46c0ec8Sopenharmony_ci	.touch_arbitration_toggle = NULL,
712a46c0ec8Sopenharmony_ci	.touch_arbitration_update_rect = NULL,
713a46c0ec8Sopenharmony_ci	.get_switch_state = NULL,
714a46c0ec8Sopenharmony_ci};
715a46c0ec8Sopenharmony_ci
716a46c0ec8Sopenharmony_cistatic bool
717a46c0ec8Sopenharmony_citotem_reject_device(struct evdev_device *device)
718a46c0ec8Sopenharmony_ci{
719a46c0ec8Sopenharmony_ci	struct libevdev *evdev = device->evdev;
720a46c0ec8Sopenharmony_ci	bool has_xy, has_slot, has_tool_dial, has_size, has_touch_size;
721a46c0ec8Sopenharmony_ci	double w, h;
722a46c0ec8Sopenharmony_ci
723a46c0ec8Sopenharmony_ci	has_xy = libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_X) &&
724a46c0ec8Sopenharmony_ci	         libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_Y);
725a46c0ec8Sopenharmony_ci	has_slot = libevdev_has_event_code(evdev, EV_ABS, ABS_MT_SLOT);
726a46c0ec8Sopenharmony_ci	has_tool_dial = libevdev_has_event_code(evdev, EV_ABS, ABS_MT_TOOL_TYPE) &&
727a46c0ec8Sopenharmony_ci			libevdev_get_abs_maximum(evdev, ABS_MT_TOOL_TYPE) >= MT_TOOL_DIAL;
728a46c0ec8Sopenharmony_ci	has_size = evdev_device_get_size(device, &w, &h) == 0;
729a46c0ec8Sopenharmony_ci	has_touch_size =
730a46c0ec8Sopenharmony_ci		libevdev_get_abs_resolution(device->evdev, ABS_MT_TOUCH_MAJOR) > 0 ||
731a46c0ec8Sopenharmony_ci		libevdev_get_abs_resolution(device->evdev, ABS_MT_TOUCH_MINOR) > 0;
732a46c0ec8Sopenharmony_ci
733a46c0ec8Sopenharmony_ci	if (has_xy && has_slot && has_tool_dial && has_size && has_touch_size)
734a46c0ec8Sopenharmony_ci		return false;
735a46c0ec8Sopenharmony_ci
736a46c0ec8Sopenharmony_ci	evdev_log_bug_libinput(device,
737a46c0ec8Sopenharmony_ci			       "missing totem capabilities:%s%s%s%s%s. "
738a46c0ec8Sopenharmony_ci			       "Ignoring this device.\n",
739a46c0ec8Sopenharmony_ci			       has_xy ? "" : " xy",
740a46c0ec8Sopenharmony_ci			       has_slot ? "" : " slot",
741a46c0ec8Sopenharmony_ci			       has_tool_dial ? "" : " dial",
742a46c0ec8Sopenharmony_ci			       has_size ? "" : " resolutions",
743a46c0ec8Sopenharmony_ci			       has_touch_size ? "" : " touch-size");
744a46c0ec8Sopenharmony_ci	return true;
745a46c0ec8Sopenharmony_ci}
746a46c0ec8Sopenharmony_ci
747a46c0ec8Sopenharmony_cistatic uint32_t
748a46c0ec8Sopenharmony_citotem_accel_config_get_profiles(struct libinput_device *libinput_device)
749a46c0ec8Sopenharmony_ci{
750a46c0ec8Sopenharmony_ci	return LIBINPUT_CONFIG_ACCEL_PROFILE_NONE;
751a46c0ec8Sopenharmony_ci}
752a46c0ec8Sopenharmony_ci
753a46c0ec8Sopenharmony_cistatic enum libinput_config_status
754a46c0ec8Sopenharmony_citotem_accel_config_set_profile(struct libinput_device *libinput_device,
755a46c0ec8Sopenharmony_ci			    enum libinput_config_accel_profile profile)
756a46c0ec8Sopenharmony_ci{
757a46c0ec8Sopenharmony_ci	return LIBINPUT_CONFIG_STATUS_UNSUPPORTED;
758a46c0ec8Sopenharmony_ci}
759a46c0ec8Sopenharmony_ci
760a46c0ec8Sopenharmony_cistatic enum libinput_config_accel_profile
761a46c0ec8Sopenharmony_citotem_accel_config_get_profile(struct libinput_device *libinput_device)
762a46c0ec8Sopenharmony_ci{
763a46c0ec8Sopenharmony_ci	return LIBINPUT_CONFIG_ACCEL_PROFILE_NONE;
764a46c0ec8Sopenharmony_ci}
765a46c0ec8Sopenharmony_ci
766a46c0ec8Sopenharmony_cistatic enum libinput_config_accel_profile
767a46c0ec8Sopenharmony_citotem_accel_config_get_default_profile(struct libinput_device *libinput_device)
768a46c0ec8Sopenharmony_ci{
769a46c0ec8Sopenharmony_ci	return LIBINPUT_CONFIG_ACCEL_PROFILE_NONE;
770a46c0ec8Sopenharmony_ci}
771a46c0ec8Sopenharmony_ci
772a46c0ec8Sopenharmony_cistatic int
773a46c0ec8Sopenharmony_citotem_init_accel(struct totem_dispatch *totem, struct evdev_device *device)
774a46c0ec8Sopenharmony_ci{
775a46c0ec8Sopenharmony_ci	const struct input_absinfo *x, *y;
776a46c0ec8Sopenharmony_ci	struct motion_filter *filter;
777a46c0ec8Sopenharmony_ci
778a46c0ec8Sopenharmony_ci	x = device->abs.absinfo_x;
779a46c0ec8Sopenharmony_ci	y = device->abs.absinfo_y;
780a46c0ec8Sopenharmony_ci
781a46c0ec8Sopenharmony_ci	/* same filter as the tablet */
782a46c0ec8Sopenharmony_ci	filter = create_pointer_accelerator_filter_tablet(x->resolution,
783a46c0ec8Sopenharmony_ci							  y->resolution);
784a46c0ec8Sopenharmony_ci	if (!filter)
785a46c0ec8Sopenharmony_ci		return -1;
786a46c0ec8Sopenharmony_ci
787a46c0ec8Sopenharmony_ci	evdev_device_init_pointer_acceleration(device, filter);
788a46c0ec8Sopenharmony_ci
789a46c0ec8Sopenharmony_ci	/* we override the profile hooks for accel configuration with hooks
790a46c0ec8Sopenharmony_ci	 * that don't allow selection of profiles */
791a46c0ec8Sopenharmony_ci	device->pointer.config.get_profiles = totem_accel_config_get_profiles;
792a46c0ec8Sopenharmony_ci	device->pointer.config.set_profile = totem_accel_config_set_profile;
793a46c0ec8Sopenharmony_ci	device->pointer.config.get_profile = totem_accel_config_get_profile;
794a46c0ec8Sopenharmony_ci	device->pointer.config.get_default_profile = totem_accel_config_get_default_profile;
795a46c0ec8Sopenharmony_ci
796a46c0ec8Sopenharmony_ci	return 0;
797a46c0ec8Sopenharmony_ci}
798a46c0ec8Sopenharmony_ci
799a46c0ec8Sopenharmony_cistruct evdev_dispatch *
800a46c0ec8Sopenharmony_cievdev_totem_create(struct evdev_device *device)
801a46c0ec8Sopenharmony_ci{
802a46c0ec8Sopenharmony_ci	struct totem_dispatch *totem;
803a46c0ec8Sopenharmony_ci	struct totem_slot *slots;
804a46c0ec8Sopenharmony_ci	int num_slots;
805a46c0ec8Sopenharmony_ci
806a46c0ec8Sopenharmony_ci	if (totem_reject_device(device))
807a46c0ec8Sopenharmony_ci		return NULL;
808a46c0ec8Sopenharmony_ci
809a46c0ec8Sopenharmony_ci	totem = zalloc(sizeof *totem);
810a46c0ec8Sopenharmony_ci	totem->device = device;
811a46c0ec8Sopenharmony_ci	totem->base.dispatch_type = DISPATCH_TOTEM;
812a46c0ec8Sopenharmony_ci	totem->base.interface = &totem_interface;
813a46c0ec8Sopenharmony_ci
814a46c0ec8Sopenharmony_ci	num_slots = libevdev_get_num_slots(device->evdev);
815a46c0ec8Sopenharmony_ci	if (num_slots <= 0)
816a46c0ec8Sopenharmony_ci		goto error;
817a46c0ec8Sopenharmony_ci
818a46c0ec8Sopenharmony_ci	totem->slot = libevdev_get_current_slot(device->evdev);
819a46c0ec8Sopenharmony_ci	slots = zalloc(num_slots * sizeof(*totem->slots));
820a46c0ec8Sopenharmony_ci
821a46c0ec8Sopenharmony_ci	for (int slot = 0; slot < num_slots; ++slot) {
822a46c0ec8Sopenharmony_ci		slots[slot].index = slot;
823a46c0ec8Sopenharmony_ci	}
824a46c0ec8Sopenharmony_ci
825a46c0ec8Sopenharmony_ci	totem->slots = slots;
826a46c0ec8Sopenharmony_ci	totem->nslots = num_slots;
827a46c0ec8Sopenharmony_ci
828a46c0ec8Sopenharmony_ci	evdev_init_sendevents(device, &totem->base);
829a46c0ec8Sopenharmony_ci	totem_init_accel(totem, device);
830a46c0ec8Sopenharmony_ci
831a46c0ec8Sopenharmony_ci	return &totem->base;
832a46c0ec8Sopenharmony_cierror:
833a46c0ec8Sopenharmony_ci	totem_interface_destroy(&totem->base);
834a46c0ec8Sopenharmony_ci	return NULL;
835a46c0ec8Sopenharmony_ci}
836