• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
27 enum totem_slot_state {
28 	SLOT_STATE_NONE,
29 	SLOT_STATE_BEGIN,
30 	SLOT_STATE_UPDATE,
31 	SLOT_STATE_END,
32 };
33 
34 struct 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 
45 struct 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 
62 static inline struct totem_dispatch*
totem_dispatch(struct evdev_dispatch * totem)63 totem_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 
70 static inline struct libinput *
totem_libinput_context(const struct totem_dispatch * totem)71 totem_libinput_context(const struct totem_dispatch *totem)
72 {
73 	return evdev_libinput_context(totem->device);
74 }
75 
76 static struct libinput_tablet_tool *
totem_new_tool(struct totem_dispatch * totem)77 totem_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->has_pressure_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 
108 static inline void
totem_set_touch_device_enabled(struct totem_dispatch * totem,bool enable_touch_device,uint64_t time)109 totem_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 
178 static void
totem_process_key(struct totem_dispatch * totem,struct evdev_device * device,struct input_event * e,uint64_t time)179 totem_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 
200 static void
totem_process_abs(struct totem_dispatch * totem,struct evdev_device * device,struct input_event * e,uint64_t time)201 totem_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 
260 static bool
totem_slot_fetch_axes(struct totem_dispatch * totem,struct totem_slot * slot,struct libinput_tablet_tool * tool,struct tablet_axes * axes_out,uint64_t time)261 totem_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;
325 out:
326 	*axes_out = axes;
327 	return rc;
328 
329 }
330 
331 static void
totem_slot_mark_all_axes_changed(struct totem_dispatch * totem,struct totem_slot * slot,struct libinput_tablet_tool * tool)332 totem_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 
345 static inline void
totem_slot_reset_changed_axes(struct totem_dispatch * totem,struct totem_slot * slot)346 totem_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 
352 static inline void
slot_axes_initialize(struct totem_dispatch * totem,struct totem_slot * slot)353 slot_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 
368 static enum totem_slot_state
totem_handle_slot_state(struct totem_dispatch * totem,struct totem_slot * slot,uint64_t time)369 totem_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 
492 static enum totem_slot_state
totem_handle_state(struct totem_dispatch * totem,uint64_t time)493 totem_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 
513 static void
totem_interface_process(struct evdev_dispatch * dispatch,struct evdev_device * device,struct input_event * e,uint64_t time)514 totem_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 
549 static void
totem_interface_suspend(struct evdev_dispatch * dispatch,struct evdev_device * device)550 totem_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 
604 static void
totem_interface_destroy(struct evdev_dispatch * dispatch)605 totem_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 
613 static void
totem_interface_device_added(struct evdev_device * device,struct evdev_device * added_device)614 totem_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 
644 static void
totem_interface_device_removed(struct evdev_device * device,struct evdev_device * removed_device)645 totem_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 
658 static void
totem_interface_initial_proximity(struct evdev_device * device,struct evdev_dispatch * dispatch)659 totem_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 
701 struct 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_added, /* treat as remove */
709 	.device_resumed = totem_interface_device_removed, /* 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 
716 static bool
totem_reject_device(struct evdev_device * device)717 totem_reject_device(struct evdev_device *device)
718 {
719 	struct libevdev *evdev = device->evdev;
720 	bool has_xy, has_slot, has_tool_dial, has_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_size |= libevdev_get_abs_resolution(device->evdev, ABS_MT_TOUCH_MAJOR) > 0;
730 	has_size |= libevdev_get_abs_resolution(device->evdev, ABS_MT_TOUCH_MINOR) > 0;
731 
732 	if (has_xy && has_slot && has_tool_dial && has_size)
733 		return false;
734 
735 	evdev_log_bug_libinput(device,
736 			       "missing totem capabilities:%s%s%s%s. "
737 			       "Ignoring this device.\n",
738 			       has_xy ? "" : " xy",
739 			       has_slot ? "" : " slot",
740 			       has_tool_dial ? "" : " dial",
741 			       has_size ? "" : " resolutions");
742 	return true;
743 }
744 
745 static uint32_t
totem_accel_config_get_profiles(struct libinput_device * libinput_device)746 totem_accel_config_get_profiles(struct libinput_device *libinput_device)
747 {
748 	return LIBINPUT_CONFIG_ACCEL_PROFILE_NONE;
749 }
750 
751 static enum libinput_config_status
totem_accel_config_set_profile(struct libinput_device * libinput_device,enum libinput_config_accel_profile profile)752 totem_accel_config_set_profile(struct libinput_device *libinput_device,
753 			    enum libinput_config_accel_profile profile)
754 {
755 	return LIBINPUT_CONFIG_STATUS_UNSUPPORTED;
756 }
757 
758 static enum libinput_config_accel_profile
totem_accel_config_get_profile(struct libinput_device * libinput_device)759 totem_accel_config_get_profile(struct libinput_device *libinput_device)
760 {
761 	return LIBINPUT_CONFIG_ACCEL_PROFILE_NONE;
762 }
763 
764 static enum libinput_config_accel_profile
totem_accel_config_get_default_profile(struct libinput_device * libinput_device)765 totem_accel_config_get_default_profile(struct libinput_device *libinput_device)
766 {
767 	return LIBINPUT_CONFIG_ACCEL_PROFILE_NONE;
768 }
769 
770 static int
totem_init_accel(struct totem_dispatch * totem,struct evdev_device * device)771 totem_init_accel(struct totem_dispatch *totem, struct evdev_device *device)
772 {
773 	const struct input_absinfo *x, *y;
774 	struct motion_filter *filter;
775 
776 	x = device->abs.absinfo_x;
777 	y = device->abs.absinfo_y;
778 
779 	/* same filter as the tablet */
780 	filter = create_pointer_accelerator_filter_tablet(x->resolution,
781 							  y->resolution);
782 	if (!filter)
783 		return -1;
784 
785 	evdev_device_init_pointer_acceleration(device, filter);
786 
787 	/* we override the profile hooks for accel configuration with hooks
788 	 * that don't allow selection of profiles */
789 	device->pointer.config.get_profiles = totem_accel_config_get_profiles;
790 	device->pointer.config.set_profile = totem_accel_config_set_profile;
791 	device->pointer.config.get_profile = totem_accel_config_get_profile;
792 	device->pointer.config.get_default_profile = totem_accel_config_get_default_profile;
793 
794 	return 0;
795 }
796 
797 struct evdev_dispatch *
evdev_totem_create(struct evdev_device * device)798 evdev_totem_create(struct evdev_device *device)
799 {
800 	struct totem_dispatch *totem;
801 	struct totem_slot *slots;
802 	int num_slots;
803 
804 	if (totem_reject_device(device))
805 		return NULL;
806 
807 	totem = zalloc(sizeof *totem);
808 	totem->device = device;
809 	totem->base.dispatch_type = DISPATCH_TOTEM;
810 	totem->base.interface = &totem_interface;
811 
812 	num_slots = libevdev_get_num_slots(device->evdev);
813 	if (num_slots <= 0)
814 		goto error;
815 
816 	totem->slot = libevdev_get_current_slot(device->evdev);
817 	slots = zalloc(num_slots * sizeof(*totem->slots));
818 
819 	for (int slot = 0; slot < num_slots; ++slot) {
820 		slots[slot].index = slot;
821 	}
822 
823 	totem->slots = slots;
824 	totem->nslots = num_slots;
825 
826 	evdev_init_sendevents(device, &totem->base);
827 	totem_init_accel(totem, device);
828 
829 	return &totem->base;
830 error:
831 	totem_interface_destroy(&totem->base);
832 	return NULL;
833 }
834