diff options
author | John Chadwick <johnwchadwick@gmail.com> | 2019-09-17 21:46:29 -0700 |
---|---|---|
committer | Drew DeVault <sir@cmpwn.com> | 2019-09-25 23:10:33 -0400 |
commit | 7e420cb6e4a334dea7296060820de12a768b76da (patch) | |
tree | 143678e6ff0a4b4223e2dfe30086eb5e2d2ab174 /sway/input/cursor.c | |
parent | Add support for fullscreen view direct scan-out (diff) | |
download | sway-7e420cb6e4a334dea7296060820de12a768b76da.tar.gz sway-7e420cb6e4a334dea7296060820de12a768b76da.tar.zst sway-7e420cb6e4a334dea7296060820de12a768b76da.zip |
input: Add support for tablet protocol.
Sway has basic support for drawing tablets, but does not expose
properties such as pressure sensitivity. This implements the wlr tablet
v2 protocol, providing tablet events to Wayland clients.
Diffstat (limited to 'sway/input/cursor.c')
-rw-r--r-- | sway/input/cursor.c | 242 |
1 files changed, 199 insertions, 43 deletions
diff --git a/sway/input/cursor.c b/sway/input/cursor.c index c6a332b8..574186d7 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c | |||
@@ -10,6 +10,7 @@ | |||
10 | #include <wlr/types/wlr_box.h> | 10 | #include <wlr/types/wlr_box.h> |
11 | #include <wlr/types/wlr_cursor.h> | 11 | #include <wlr/types/wlr_cursor.h> |
12 | #include <wlr/types/wlr_idle.h> | 12 | #include <wlr/types/wlr_idle.h> |
13 | #include <wlr/types/wlr_tablet_v2.h> | ||
13 | #include <wlr/types/wlr_xcursor_manager.h> | 14 | #include <wlr/types/wlr_xcursor_manager.h> |
14 | #include <wlr/util/region.h> | 15 | #include <wlr/util/region.h> |
15 | #include "list.h" | 16 | #include "list.h" |
@@ -20,6 +21,7 @@ | |||
20 | #include "sway/desktop/transaction.h" | 21 | #include "sway/desktop/transaction.h" |
21 | #include "sway/input/cursor.h" | 22 | #include "sway/input/cursor.h" |
22 | #include "sway/input/keyboard.h" | 23 | #include "sway/input/keyboard.h" |
24 | #include "sway/input/tablet.h" | ||
23 | #include "sway/layers.h" | 25 | #include "sway/layers.h" |
24 | #include "sway/output.h" | 26 | #include "sway/output.h" |
25 | #include "sway/tree/arrange.h" | 27 | #include "sway/tree/arrange.h" |
@@ -443,72 +445,224 @@ static void apply_mapping_from_region(struct wlr_input_device *device, | |||
443 | *y = apply_mapping_from_coord(y1, y2, *y); | 445 | *y = apply_mapping_from_coord(y1, y2, *y); |
444 | } | 446 | } |
445 | 447 | ||
448 | static void handle_tablet_tool_position(struct sway_cursor *cursor, | ||
449 | struct sway_tablet *tablet, | ||
450 | struct wlr_tablet_tool *tool, | ||
451 | bool change_x, bool change_y, | ||
452 | double x, double y, double dx, double dy, | ||
453 | int32_t time_msec) { | ||
454 | if (!change_x && !change_y) { | ||
455 | return; | ||
456 | } | ||
457 | |||
458 | struct sway_input_device *input_device = tablet->seat_device->input_device; | ||
459 | struct input_config *ic = input_device_get_config(input_device); | ||
460 | if (ic != NULL && ic->mapped_from_region != NULL) { | ||
461 | apply_mapping_from_region(input_device->wlr_device, | ||
462 | ic->mapped_from_region, &x, &y); | ||
463 | } | ||
464 | |||
465 | switch (tool->type) { | ||
466 | case WLR_TABLET_TOOL_TYPE_MOUSE: | ||
467 | wlr_cursor_move(cursor->cursor, input_device->wlr_device, dx, dy); | ||
468 | break; | ||
469 | default: | ||
470 | wlr_cursor_warp_absolute(cursor->cursor, input_device->wlr_device, | ||
471 | change_x ? x : NAN, change_y ? y : NAN); | ||
472 | } | ||
473 | |||
474 | double sx, sy; | ||
475 | struct wlr_surface *surface = NULL; | ||
476 | struct sway_seat *seat = cursor->seat; | ||
477 | node_at_coords(seat, cursor->cursor->x, cursor->cursor->y, | ||
478 | &surface, &sx, &sy); | ||
479 | struct sway_tablet_tool *sway_tool = tool->data; | ||
480 | |||
481 | if (!surface || !wlr_surface_accepts_tablet_v2(tablet->tablet_v2, surface)) { | ||
482 | wlr_tablet_v2_tablet_tool_notify_proximity_out(sway_tool->tablet_v2_tool); | ||
483 | cursor_motion(cursor, time_msec, input_device->wlr_device, dx, dy, dx, dy); | ||
484 | return; | ||
485 | } | ||
486 | |||
487 | wlr_tablet_v2_tablet_tool_notify_proximity_in(sway_tool->tablet_v2_tool, | ||
488 | tablet->tablet_v2, surface); | ||
489 | |||
490 | wlr_tablet_v2_tablet_tool_notify_motion(sway_tool->tablet_v2_tool, sx, sy); | ||
491 | } | ||
492 | |||
446 | static void handle_tool_axis(struct wl_listener *listener, void *data) { | 493 | static void handle_tool_axis(struct wl_listener *listener, void *data) { |
447 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis); | 494 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis); |
448 | wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat); | 495 | wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat); |
449 | struct wlr_event_tablet_tool_axis *event = data; | 496 | struct wlr_event_tablet_tool_axis *event = data; |
450 | struct sway_input_device *input_device = event->device->data; | 497 | struct sway_tablet_tool *sway_tool = event->tool->data; |
451 | 498 | ||
452 | double x = NAN, y = NAN; | 499 | if (!sway_tool) { |
453 | if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X)) { | 500 | sway_log(SWAY_DEBUG, "tool axis before proximity"); |
454 | x = event->x; | 501 | return; |
455 | } | 502 | } |
456 | if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) { | 503 | |
457 | y = event->y; | 504 | handle_tablet_tool_position(cursor, sway_tool->tablet, event->tool, |
505 | event->updated_axes & WLR_TABLET_TOOL_AXIS_X, | ||
506 | event->updated_axes & WLR_TABLET_TOOL_AXIS_Y, | ||
507 | event->x, event->y, event->dx, event->dy, event->time_msec); | ||
508 | |||
509 | if (event->updated_axes & WLR_TABLET_TOOL_AXIS_PRESSURE) { | ||
510 | wlr_tablet_v2_tablet_tool_notify_pressure( | ||
511 | sway_tool->tablet_v2_tool, event->pressure); | ||
458 | } | 512 | } |
459 | 513 | ||
460 | struct input_config *ic = input_device_get_config(input_device); | 514 | if (event->updated_axes & WLR_TABLET_TOOL_AXIS_DISTANCE) { |
461 | if (ic != NULL && ic->mapped_from_region != NULL) { | 515 | wlr_tablet_v2_tablet_tool_notify_distance( |
462 | apply_mapping_from_region(event->device, ic->mapped_from_region, &x, &y); | 516 | sway_tool->tablet_v2_tool, event->distance); |
463 | } | 517 | } |
464 | 518 | ||
465 | double lx, ly; | 519 | if (event->updated_axes & WLR_TABLET_TOOL_AXIS_TILT_X) { |
466 | wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, | 520 | sway_tool->tilt_x = event->tilt_x; |
467 | x, y, &lx, &ly); | 521 | } |
468 | 522 | ||
469 | double dx = lx - cursor->cursor->x; | 523 | if (event->updated_axes & WLR_TABLET_TOOL_AXIS_TILT_Y) { |
470 | double dy = ly - cursor->cursor->y; | 524 | sway_tool->tilt_y = event->tilt_y; |
525 | } | ||
471 | 526 | ||
472 | cursor_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); | 527 | if (event->updated_axes & (WLR_TABLET_TOOL_AXIS_TILT_X | WLR_TABLET_TOOL_AXIS_TILT_Y)) { |
473 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); | 528 | wlr_tablet_v2_tablet_tool_notify_tilt( |
474 | transaction_commit_dirty(); | 529 | sway_tool->tablet_v2_tool, |
530 | sway_tool->tilt_x, sway_tool->tilt_y); | ||
531 | } | ||
532 | |||
533 | if (event->updated_axes & WLR_TABLET_TOOL_AXIS_ROTATION) { | ||
534 | wlr_tablet_v2_tablet_tool_notify_rotation( | ||
535 | sway_tool->tablet_v2_tool, event->rotation); | ||
536 | } | ||
537 | |||
538 | if (event->updated_axes & WLR_TABLET_TOOL_AXIS_SLIDER) { | ||
539 | wlr_tablet_v2_tablet_tool_notify_slider( | ||
540 | sway_tool->tablet_v2_tool, event->slider); | ||
541 | } | ||
542 | |||
543 | if (event->updated_axes & WLR_TABLET_TOOL_AXIS_WHEEL) { | ||
544 | wlr_tablet_v2_tablet_tool_notify_wheel( | ||
545 | sway_tool->tablet_v2_tool, event->wheel_delta, 0); | ||
546 | } | ||
475 | } | 547 | } |
476 | 548 | ||
477 | static void handle_tool_tip(struct wl_listener *listener, void *data) { | 549 | static void handle_tool_tip(struct wl_listener *listener, void *data) { |
478 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip); | 550 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip); |
479 | wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat); | 551 | wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat); |
480 | struct wlr_event_tablet_tool_tip *event = data; | 552 | struct wlr_event_tablet_tool_tip *event = data; |
481 | dispatch_cursor_button(cursor, event->device, event->time_msec, | 553 | struct sway_tablet_tool *sway_tool = event->tool->data; |
482 | BTN_LEFT, event->state == WLR_TABLET_TOOL_TIP_DOWN ? | 554 | struct wlr_tablet_v2_tablet *tablet_v2 = sway_tool->tablet->tablet_v2; |
483 | WLR_BUTTON_PRESSED : WLR_BUTTON_RELEASED); | 555 | struct sway_seat *seat = cursor->seat; |
484 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); | 556 | |
485 | transaction_commit_dirty(); | 557 | double sx, sy; |
558 | struct wlr_surface *surface = NULL; | ||
559 | node_at_coords(seat, cursor->cursor->x, cursor->cursor->y, | ||
560 | &surface, &sx, &sy); | ||
561 | |||
562 | if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) { | ||
563 | dispatch_cursor_button(cursor, event->device, event->time_msec, | ||
564 | BTN_LEFT, event->state == WLR_TABLET_TOOL_TIP_DOWN ? | ||
565 | WLR_BUTTON_PRESSED : WLR_BUTTON_RELEASED); | ||
566 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); | ||
567 | transaction_commit_dirty(); | ||
568 | return; | ||
569 | } | ||
570 | |||
571 | if (event->state == WLR_TABLET_TOOL_TIP_DOWN) { | ||
572 | wlr_tablet_v2_tablet_tool_notify_down(sway_tool->tablet_v2_tool); | ||
573 | wlr_tablet_tool_v2_start_implicit_grab(sway_tool->tablet_v2_tool); | ||
574 | } else { | ||
575 | wlr_tablet_v2_tablet_tool_notify_up(sway_tool->tablet_v2_tool); | ||
576 | } | ||
577 | } | ||
578 | |||
579 | static struct sway_tablet *get_tablet_for_device(struct sway_cursor *cursor, | ||
580 | struct wlr_input_device *device) { | ||
581 | struct sway_tablet *tablet; | ||
582 | wl_list_for_each(tablet, &cursor->tablets, link) { | ||
583 | if (tablet->seat_device->input_device->wlr_device == device) { | ||
584 | return tablet; | ||
585 | } | ||
586 | } | ||
587 | return NULL; | ||
588 | } | ||
589 | |||
590 | static void handle_tool_proximity(struct wl_listener *listener, void *data) { | ||
591 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_proximity); | ||
592 | wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat); | ||
593 | struct wlr_event_tablet_tool_proximity *event = data; | ||
594 | |||
595 | struct wlr_tablet_tool *tool = event->tool; | ||
596 | if (!tool->data) { | ||
597 | struct sway_tablet *tablet = get_tablet_for_device(cursor, event->device); | ||
598 | if (!tablet) { | ||
599 | sway_log(SWAY_ERROR, "no tablet for tablet tool"); | ||
600 | return; | ||
601 | } | ||
602 | sway_tablet_tool_configure(tablet, tool); | ||
603 | } | ||
604 | |||
605 | struct sway_tablet_tool *sway_tool = tool->data; | ||
606 | if (!sway_tool) { | ||
607 | sway_log(SWAY_ERROR, "tablet tool not initialized"); | ||
608 | return; | ||
609 | } | ||
610 | |||
611 | if (event->state == WLR_TABLET_TOOL_PROXIMITY_OUT) { | ||
612 | wlr_tablet_v2_tablet_tool_notify_proximity_out(sway_tool->tablet_v2_tool); | ||
613 | return; | ||
614 | } | ||
615 | |||
616 | handle_tablet_tool_position(cursor, sway_tool->tablet, event->tool, | ||
617 | true, true, event->x, event->y, 0, 0, event->time_msec); | ||
486 | } | 618 | } |
487 | 619 | ||
488 | static void handle_tool_button(struct wl_listener *listener, void *data) { | 620 | static void handle_tool_button(struct wl_listener *listener, void *data) { |
489 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button); | 621 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button); |
490 | wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat); | 622 | wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat); |
491 | struct wlr_event_tablet_tool_button *event = data; | 623 | struct wlr_event_tablet_tool_button *event = data; |
492 | // TODO: the user may want to configure which tool buttons are mapped to | 624 | struct sway_tablet_tool *sway_tool = event->tool->data; |
493 | // which simulated pointer buttons | 625 | struct wlr_tablet_v2_tablet *tablet_v2 = sway_tool->tablet->tablet_v2; |
494 | switch (event->state) { | 626 | struct sway_seat *seat = cursor->seat; |
495 | case WLR_BUTTON_PRESSED: | 627 | |
496 | if (cursor->tool_buttons == 0) { | 628 | if (!sway_tool) { |
497 | dispatch_cursor_button(cursor, event->device, | 629 | sway_log(SWAY_DEBUG, "tool button before proximity"); |
498 | event->time_msec, BTN_RIGHT, event->state); | 630 | return; |
499 | } | 631 | } |
500 | cursor->tool_buttons++; | 632 | |
501 | break; | 633 | double sx, sy; |
502 | case WLR_BUTTON_RELEASED: | 634 | struct wlr_surface *surface = NULL; |
503 | if (cursor->tool_buttons == 1) { | 635 | |
504 | dispatch_cursor_button(cursor, event->device, | 636 | node_at_coords(seat, cursor->cursor->x, cursor->cursor->y, |
505 | event->time_msec, BTN_RIGHT, event->state); | 637 | &surface, &sx, &sy); |
638 | |||
639 | if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) { | ||
640 | // TODO: the user may want to configure which tool buttons are mapped to | ||
641 | // which simulated pointer buttons | ||
642 | switch (event->state) { | ||
643 | case WLR_BUTTON_PRESSED: | ||
644 | if (cursor->tool_buttons == 0) { | ||
645 | dispatch_cursor_button(cursor, event->device, | ||
646 | event->time_msec, BTN_RIGHT, event->state); | ||
647 | } | ||
648 | cursor->tool_buttons++; | ||
649 | break; | ||
650 | case WLR_BUTTON_RELEASED: | ||
651 | if (cursor->tool_buttons == 1) { | ||
652 | dispatch_cursor_button(cursor, event->device, | ||
653 | event->time_msec, BTN_RIGHT, event->state); | ||
654 | } | ||
655 | cursor->tool_buttons--; | ||
656 | break; | ||
506 | } | 657 | } |
507 | cursor->tool_buttons--; | 658 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); |
508 | break; | 659 | transaction_commit_dirty(); |
660 | return; | ||
509 | } | 661 | } |
510 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); | 662 | |
511 | transaction_commit_dirty(); | 663 | wlr_tablet_v2_tablet_tool_notify_button(sway_tool->tablet_v2_tool, |
664 | (enum zwp_tablet_pad_v2_button_state)event->button, | ||
665 | (enum zwp_tablet_pad_v2_button_state)event->state); | ||
512 | } | 666 | } |
513 | 667 | ||
514 | static void check_constraint_region(struct sway_cursor *cursor) { | 668 | static void check_constraint_region(struct sway_cursor *cursor) { |
@@ -698,9 +852,6 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) { | |||
698 | &cursor->touch_motion); | 852 | &cursor->touch_motion); |
699 | cursor->touch_motion.notify = handle_touch_motion; | 853 | cursor->touch_motion.notify = handle_touch_motion; |
700 | 854 | ||
701 | // TODO: tablet protocol support | ||
702 | // Note: We should emulate pointer events for clients that don't support the | ||
703 | // tablet protocol when the time comes | ||
704 | wl_signal_add(&wlr_cursor->events.tablet_tool_axis, | 855 | wl_signal_add(&wlr_cursor->events.tablet_tool_axis, |
705 | &cursor->tool_axis); | 856 | &cursor->tool_axis); |
706 | cursor->tool_axis.notify = handle_tool_axis; | 857 | cursor->tool_axis.notify = handle_tool_axis; |
@@ -708,6 +859,9 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) { | |||
708 | wl_signal_add(&wlr_cursor->events.tablet_tool_tip, &cursor->tool_tip); | 859 | wl_signal_add(&wlr_cursor->events.tablet_tool_tip, &cursor->tool_tip); |
709 | cursor->tool_tip.notify = handle_tool_tip; | 860 | cursor->tool_tip.notify = handle_tool_tip; |
710 | 861 | ||
862 | wl_signal_add(&wlr_cursor->events.tablet_tool_proximity, &cursor->tool_proximity); | ||
863 | cursor->tool_proximity.notify = handle_tool_proximity; | ||
864 | |||
711 | wl_signal_add(&wlr_cursor->events.tablet_tool_button, &cursor->tool_button); | 865 | wl_signal_add(&wlr_cursor->events.tablet_tool_button, &cursor->tool_button); |
712 | cursor->tool_button.notify = handle_tool_button; | 866 | cursor->tool_button.notify = handle_tool_button; |
713 | 867 | ||
@@ -716,6 +870,8 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) { | |||
716 | cursor->request_set_cursor.notify = handle_request_set_cursor; | 870 | cursor->request_set_cursor.notify = handle_request_set_cursor; |
717 | 871 | ||
718 | wl_list_init(&cursor->constraint_commit.link); | 872 | wl_list_init(&cursor->constraint_commit.link); |
873 | wl_list_init(&cursor->tablets); | ||
874 | wl_list_init(&cursor->tablet_pads); | ||
719 | 875 | ||
720 | cursor->cursor = wlr_cursor; | 876 | cursor->cursor = wlr_cursor; |
721 | 877 | ||