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