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