aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar John Chadwick <johnwchadwick@gmail.com>2019-09-17 21:46:29 -0700
committerLibravatar Drew DeVault <sir@cmpwn.com>2019-09-25 23:10:33 -0400
commit7e420cb6e4a334dea7296060820de12a768b76da (patch)
tree143678e6ff0a4b4223e2dfe30086eb5e2d2ab174
parentAdd support for fullscreen view direct scan-out (diff)
downloadsway-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.
-rw-r--r--include/sway/input/cursor.h3
-rw-r--r--include/sway/input/seat.h2
-rw-r--r--include/sway/input/tablet.h62
-rw-r--r--include/sway/server.h2
-rw-r--r--protocols/meson.build1
-rw-r--r--sway/input/cursor.c242
-rw-r--r--sway/input/seat.c29
-rw-r--r--sway/input/tablet.c345
-rw-r--r--sway/meson.build1
-rw-r--r--sway/server.c3
10 files changed, 644 insertions, 46 deletions
diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h
index 516718c9..e46c9b18 100644
--- a/include/sway/input/cursor.h
+++ b/include/sway/input/cursor.h
@@ -21,6 +21,8 @@ struct sway_cursor {
21 struct sway_node *node; 21 struct sway_node *node;
22 } previous; 22 } previous;
23 struct wlr_xcursor_manager *xcursor_manager; 23 struct wlr_xcursor_manager *xcursor_manager;
24 struct wl_list tablets;
25 struct wl_list tablet_pads;
24 26
25 const char *image; 27 const char *image;
26 struct wl_client *image_client; 28 struct wl_client *image_client;
@@ -42,6 +44,7 @@ struct sway_cursor {
42 44
43 struct wl_listener tool_axis; 45 struct wl_listener tool_axis;
44 struct wl_listener tool_tip; 46 struct wl_listener tool_tip;
47 struct wl_listener tool_proximity;
45 struct wl_listener tool_button; 48 struct wl_listener tool_button;
46 uint32_t tool_buttons; 49 uint32_t tool_buttons;
47 50
diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h
index c963de9b..24a6fed4 100644
--- a/include/sway/input/seat.h
+++ b/include/sway/input/seat.h
@@ -28,6 +28,8 @@ struct sway_seat_device {
28 struct sway_input_device *input_device; 28 struct sway_input_device *input_device;
29 struct sway_keyboard *keyboard; 29 struct sway_keyboard *keyboard;
30 struct sway_switch *switch_device; 30 struct sway_switch *switch_device;
31 struct sway_tablet *tablet;
32 struct sway_tablet_pad *tablet_pad;
31 struct wl_list link; // sway_seat::devices 33 struct wl_list link; // sway_seat::devices
32}; 34};
33 35
diff --git a/include/sway/input/tablet.h b/include/sway/input/tablet.h
new file mode 100644
index 00000000..f30e232a
--- /dev/null
+++ b/include/sway/input/tablet.h
@@ -0,0 +1,62 @@
1#ifndef _SWAY_INPUT_TABLET_H
2#define _SWAY_INPUT_TABLET_H
3#include <wlr/types/wlr_layer_shell_v1.h>
4
5struct sway_seat;
6struct wlr_tablet_tool;
7
8struct sway_tablet {
9 struct wl_list link;
10 struct sway_seat_device *seat_device;
11 struct wlr_tablet_v2_tablet *tablet_v2;
12};
13
14struct sway_tablet_tool {
15 struct sway_seat *seat;
16 struct sway_tablet *tablet;
17 struct wlr_tablet_v2_tablet_tool *tablet_v2_tool;
18
19 double tilt_x, tilt_y;
20
21 struct wl_listener set_cursor;
22 struct wl_listener tool_destroy;
23};
24
25struct sway_tablet_pad {
26 struct wl_list link;
27 struct sway_seat_device *seat_device;
28 struct sway_tablet *tablet;
29 struct wlr_tablet_v2_tablet_pad *tablet_v2_pad;
30
31 struct wl_listener attach;
32 struct wl_listener button;
33 struct wl_listener ring;
34 struct wl_listener strip;
35
36 struct wlr_surface *current_surface;
37 struct wl_listener surface_destroy;
38
39 struct wl_listener tablet_destroy;
40};
41
42struct sway_tablet *sway_tablet_create(struct sway_seat *seat,
43 struct sway_seat_device *device);
44
45void sway_configure_tablet(struct sway_tablet *tablet);
46
47void sway_tablet_destroy(struct sway_tablet *tablet);
48
49void sway_tablet_tool_configure(struct sway_tablet *tablet,
50 struct wlr_tablet_tool *wlr_tool);
51
52struct sway_tablet_pad *sway_tablet_pad_create(struct sway_seat *seat,
53 struct sway_seat_device *device);
54
55void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad);
56
57void sway_tablet_pad_destroy(struct sway_tablet_pad *tablet_pad);
58
59void sway_tablet_pad_notify_enter(struct sway_tablet_pad *tablet_pad,
60 struct wlr_surface *surface);
61
62#endif
diff --git a/include/sway/server.h b/include/sway/server.h
index a43cbf72..cd411d3b 100644
--- a/include/sway/server.h
+++ b/include/sway/server.h
@@ -44,6 +44,8 @@ struct sway_server {
44 struct wlr_xdg_shell *xdg_shell; 44 struct wlr_xdg_shell *xdg_shell;
45 struct wl_listener xdg_shell_surface; 45 struct wl_listener xdg_shell_surface;
46 46
47 struct wlr_tablet_manager_v2 *tablet_v2;
48
47#if HAVE_XWAYLAND 49#if HAVE_XWAYLAND
48 struct sway_xwayland xwayland; 50 struct sway_xwayland xwayland;
49 struct wl_listener xwayland_surface; 51 struct wl_listener xwayland_surface;
diff --git a/protocols/meson.build b/protocols/meson.build
index 4ba6f723..f2f90dad 100644
--- a/protocols/meson.build
+++ b/protocols/meson.build
@@ -14,6 +14,7 @@ protocols = [
14 [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], 14 [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
15 [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'], 15 [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
16 [wl_protocol_dir, 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml'], 16 [wl_protocol_dir, 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml'],
17 [wl_protocol_dir, 'unstable/tablet/tablet-unstable-v2.xml'],
17 ['wlr-layer-shell-unstable-v1.xml'], 18 ['wlr-layer-shell-unstable-v1.xml'],
18 ['idle.xml'], 19 ['idle.xml'],
19 ['wlr-input-inhibitor-unstable-v1.xml'], 20 ['wlr-input-inhibitor-unstable-v1.xml'],
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
448static 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
446static void handle_tool_axis(struct wl_listener *listener, void *data) { 493static 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
477static void handle_tool_tip(struct wl_listener *listener, void *data) { 549static 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
579static 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
590static 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
488static void handle_tool_button(struct wl_listener *listener, void *data) { 620static 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
514static void check_constraint_region(struct sway_cursor *cursor) { 668static 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
diff --git a/sway/input/seat.c b/sway/input/seat.c
index b2243fe3..ebd40343 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -8,6 +8,7 @@
8#include <wlr/types/wlr_data_device.h> 8#include <wlr/types/wlr_data_device.h>
9#include <wlr/types/wlr_output_layout.h> 9#include <wlr/types/wlr_output_layout.h>
10#include <wlr/types/wlr_primary_selection.h> 10#include <wlr/types/wlr_primary_selection.h>
11#include <wlr/types/wlr_tablet_v2.h>
11#include <wlr/types/wlr_xcursor_manager.h> 12#include <wlr/types/wlr_xcursor_manager.h>
12#include "config.h" 13#include "config.h"
13#include "list.h" 14#include "list.h"
@@ -18,6 +19,7 @@
18#include "sway/input/keyboard.h" 19#include "sway/input/keyboard.h"
19#include "sway/input/seat.h" 20#include "sway/input/seat.h"
20#include "sway/input/switch.h" 21#include "sway/input/switch.h"
22#include "sway/input/tablet.h"
21#include "sway/ipc-server.h" 23#include "sway/ipc-server.h"
22#include "sway/layers.h" 24#include "sway/layers.h"
23#include "sway/output.h" 25#include "sway/output.h"
@@ -34,6 +36,8 @@ static void seat_device_destroy(struct sway_seat_device *seat_device) {
34 } 36 }
35 37
36 sway_keyboard_destroy(seat_device->keyboard); 38 sway_keyboard_destroy(seat_device->keyboard);
39 sway_tablet_destroy(seat_device->tablet);
40 sway_tablet_pad_destroy(seat_device->tablet_pad);
37 wlr_cursor_detach_input_device(seat_device->sway_seat->cursor->cursor, 41 wlr_cursor_detach_input_device(seat_device->sway_seat->cursor->cursor,
38 seat_device->input_device->wlr_device); 42 seat_device->input_device->wlr_device);
39 wl_list_remove(&seat_device->link); 43 wl_list_remove(&seat_device->link);
@@ -118,6 +122,14 @@ static void seat_keyboard_notify_enter(struct sway_seat *seat,
118 state->pressed_keycodes, state->npressed, &keyboard->modifiers); 122 state->pressed_keycodes, state->npressed, &keyboard->modifiers);
119} 123}
120 124
125static void seat_tablet_pads_notify_enter(struct sway_seat *seat,
126 struct wlr_surface *surface) {
127 struct sway_seat_device *seat_device;
128 wl_list_for_each(seat_device, &seat->devices, link) {
129 sway_tablet_pad_notify_enter(seat_device->tablet_pad, surface);
130 }
131}
132
121/** 133/**
122 * If con is a view, set it as active and enable keyboard input. 134 * If con is a view, set it as active and enable keyboard input.
123 * If con is a container, set all child views as active and don't enable 135 * If con is a container, set all child views as active and don't enable
@@ -138,6 +150,7 @@ static void seat_send_focus(struct sway_node *node, struct sway_seat *seat) {
138#endif 150#endif
139 151
140 seat_keyboard_notify_enter(seat, view->surface); 152 seat_keyboard_notify_enter(seat, view->surface);
153 seat_tablet_pads_notify_enter(seat, view->surface);
141 154
142 struct wlr_pointer_constraint_v1 *constraint = 155 struct wlr_pointer_constraint_v1 *constraint =
143 wlr_pointer_constraints_v1_constraint_for_surface( 156 wlr_pointer_constraints_v1_constraint_for_surface(
@@ -638,14 +651,23 @@ static void seat_configure_touch(struct sway_seat *seat,
638 651
639static void seat_configure_tablet_tool(struct sway_seat *seat, 652static void seat_configure_tablet_tool(struct sway_seat *seat,
640 struct sway_seat_device *sway_device) { 653 struct sway_seat_device *sway_device) {
641 if ((seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER) == 0) { 654 if (!sway_device->tablet) {
642 seat_configure_xcursor(seat); 655 sway_device->tablet = sway_tablet_create(seat, sway_device);
643 } 656 }
657 sway_configure_tablet(sway_device->tablet);
644 wlr_cursor_attach_input_device(seat->cursor->cursor, 658 wlr_cursor_attach_input_device(seat->cursor->cursor,
645 sway_device->input_device->wlr_device); 659 sway_device->input_device->wlr_device);
646 seat_apply_input_config(seat, sway_device); 660 seat_apply_input_config(seat, sway_device);
647} 661}
648 662
663static void seat_configure_tablet_pad(struct sway_seat *seat,
664 struct sway_seat_device *sway_device) {
665 if (!sway_device->tablet) {
666 sway_device->tablet_pad = sway_tablet_pad_create(seat, sway_device);
667 }
668 sway_configure_tablet_pad(sway_device->tablet_pad);
669}
670
649static struct sway_seat_device *seat_get_device(struct sway_seat *seat, 671static struct sway_seat_device *seat_get_device(struct sway_seat *seat,
650 struct sway_input_device *input_device) { 672 struct sway_input_device *input_device) {
651 struct sway_seat_device *seat_device = NULL; 673 struct sway_seat_device *seat_device = NULL;
@@ -682,7 +704,7 @@ void seat_configure_device(struct sway_seat *seat,
682 seat_configure_tablet_tool(seat, seat_device); 704 seat_configure_tablet_tool(seat, seat_device);
683 break; 705 break;
684 case WLR_INPUT_DEVICE_TABLET_PAD: 706 case WLR_INPUT_DEVICE_TABLET_PAD:
685 sway_log(SWAY_DEBUG, "TODO: configure tablet pad"); 707 seat_configure_tablet_pad(seat, seat_device);
686 break; 708 break;
687 } 709 }
688} 710}
@@ -1079,6 +1101,7 @@ void seat_set_focus_surface(struct sway_seat *seat,
1079 seat->has_focus = false; 1101 seat->has_focus = false;
1080 } 1102 }
1081 seat_keyboard_notify_enter(seat, surface); 1103 seat_keyboard_notify_enter(seat, surface);
1104 seat_tablet_pads_notify_enter(seat, surface);
1082} 1105}
1083 1106
1084void seat_set_focus_layer(struct sway_seat *seat, 1107void seat_set_focus_layer(struct sway_seat *seat,
diff --git a/sway/input/tablet.c b/sway/input/tablet.c
new file mode 100644
index 00000000..b0d4d0c6
--- /dev/null
+++ b/sway/input/tablet.c
@@ -0,0 +1,345 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h>
3#include <wlr/backend/libinput.h>
4#include <wlr/types/wlr_tablet_v2.h>
5#include "log.h"
6#include "sway/input/cursor.h"
7#include "sway/input/seat.h"
8#include "sway/input/tablet.h"
9
10static void handle_pad_tablet_destroy(struct wl_listener *listener, void *data) {
11 struct sway_tablet_pad *pad =
12 wl_container_of(listener, pad, tablet_destroy);
13
14 pad->tablet = NULL;
15
16 wl_list_remove(&pad->tablet_destroy.link);
17 wl_list_init(&pad->tablet_destroy.link);
18}
19
20static void attach_tablet_pad(struct sway_tablet_pad *tablet_pad,
21 struct sway_tablet *tablet) {
22 sway_log(SWAY_DEBUG, "Attaching tablet pad \"%s\" to tablet tool \"%s\"",
23 tablet_pad->seat_device->input_device->wlr_device->name,
24 tablet->seat_device->input_device->wlr_device->name);
25
26 tablet_pad->tablet = tablet;
27
28 wl_list_remove(&tablet_pad->tablet_destroy.link);
29 tablet_pad->tablet_destroy.notify = handle_pad_tablet_destroy;
30 wl_signal_add(&tablet->seat_device->input_device->wlr_device->events.destroy,
31 &tablet_pad->tablet_destroy);
32}
33
34struct sway_tablet *sway_tablet_create(struct sway_seat *seat,
35 struct sway_seat_device *device) {
36 struct sway_tablet *tablet =
37 calloc(1, sizeof(struct sway_tablet));
38 if (!sway_assert(tablet, "could not allocate sway tablet for seat")) {
39 return NULL;
40 }
41
42 wl_list_insert(&seat->cursor->tablets, &tablet->link);
43
44 device->tablet = tablet;
45 tablet->seat_device = device;
46
47 return tablet;
48}
49
50void sway_configure_tablet(struct sway_tablet *tablet) {
51 struct wlr_input_device *device =
52 tablet->seat_device->input_device->wlr_device;
53 struct sway_seat *seat = tablet->seat_device->sway_seat;
54
55 if ((seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER) == 0) {
56 seat_configure_xcursor(seat);
57 }
58
59 tablet->tablet_v2 =
60 wlr_tablet_create(server.tablet_v2, seat->wlr_seat, device);
61
62 /* Search for a sibling tablet pad */
63 if (!wlr_input_device_is_libinput(device)) {
64 /* We can only do this on libinput devices */
65 return;
66 }
67
68 struct libinput_device_group *group =
69 libinput_device_get_device_group(wlr_libinput_get_device_handle(device));
70 struct sway_tablet_pad *tablet_pad;
71 wl_list_for_each(tablet_pad, &seat->cursor->tablet_pads, link) {
72 struct wlr_input_device *pad_device =
73 tablet_pad->seat_device->input_device->wlr_device;
74 if (!wlr_input_device_is_libinput(pad_device)) {
75 continue;
76 }
77
78 struct libinput_device_group *pad_group =
79 libinput_device_get_device_group(wlr_libinput_get_device_handle(pad_device));
80
81 if (pad_group == group) {
82 attach_tablet_pad(tablet_pad, tablet);
83 break;
84 }
85 }
86}
87
88void sway_tablet_destroy(struct sway_tablet *tablet) {
89 if (!tablet) {
90 return;
91 }
92 wl_list_remove(&tablet->link);
93 free(tablet);
94}
95
96static void handle_tablet_tool_set_cursor(struct wl_listener *listener, void *data) {
97 struct sway_tablet_tool *tool =
98 wl_container_of(listener, tool, set_cursor);
99 struct wlr_tablet_v2_event_cursor *event = data;
100
101 struct sway_cursor *cursor = tool->seat->cursor;
102 if (!seatop_allows_set_cursor(cursor->seat)) {
103 return;
104 }
105
106 struct wl_client *focused_client = NULL;
107 struct wlr_surface *focused_surface =
108 cursor->seat->wlr_seat->pointer_state.focused_surface;
109 if (focused_surface != NULL) {
110 focused_client = wl_resource_get_client(focused_surface->resource);
111 }
112
113 // TODO: check cursor mode
114 if (focused_client == NULL ||
115 event->seat_client->client != focused_client) {
116 sway_log(SWAY_DEBUG, "denying request to set cursor from unfocused client");
117 return;
118 }
119
120 cursor_set_image_surface(cursor, event->surface, event->hotspot_x,
121 event->hotspot_y, focused_client);
122}
123
124static void handle_tablet_tool_destroy(struct wl_listener *listener, void *data) {
125 struct sway_tablet_tool *tool =
126 wl_container_of(listener, tool, tool_destroy);
127
128 wl_list_remove(&tool->tool_destroy.link);
129 wl_list_remove(&tool->set_cursor.link);
130
131 free(tool);
132}
133
134void sway_tablet_tool_configure(struct sway_tablet *tablet,
135 struct wlr_tablet_tool *wlr_tool) {
136 struct sway_tablet_tool *tool =
137 calloc(1, sizeof(struct sway_tablet_tool));
138 if (!sway_assert(tool, "could not allocate sway tablet tool for tablet")) {
139 return;
140 }
141
142 tool->seat = tablet->seat_device->sway_seat;
143 tool->tablet = tablet;
144 tool->tablet_v2_tool =
145 wlr_tablet_tool_create(server.tablet_v2,
146 tablet->seat_device->sway_seat->wlr_seat, wlr_tool);
147
148 tool->tool_destroy.notify = handle_tablet_tool_destroy;
149 wl_signal_add(&wlr_tool->events.destroy, &tool->tool_destroy);
150
151 tool->set_cursor.notify = handle_tablet_tool_set_cursor;
152 wl_signal_add(&tool->tablet_v2_tool->events.set_cursor,
153 &tool->set_cursor);
154
155 wlr_tool->data = tool;
156}
157
158static void handle_tablet_pad_attach(struct wl_listener *listener,
159 void *data) {
160 struct sway_tablet_pad *pad = wl_container_of(listener, pad, attach);
161 struct wlr_tablet_tool *wlr_tool = data;
162 struct sway_tablet_tool *tool = wlr_tool->data;
163
164 if (!tool) {
165 return;
166 }
167
168 attach_tablet_pad(pad, tool->tablet);
169}
170
171static void handle_tablet_pad_ring(struct wl_listener *listener, void *data) {
172 struct sway_tablet_pad *pad = wl_container_of(listener, pad, ring);
173 struct wlr_event_tablet_pad_ring *event = data;
174
175 if (!pad->current_surface) {
176 return;
177 }
178
179 wlr_tablet_v2_tablet_pad_notify_ring(pad->tablet_v2_pad,
180 event->ring, event->position,
181 event->source == WLR_TABLET_PAD_RING_SOURCE_FINGER,
182 event->time_msec);
183}
184
185static void handle_tablet_pad_strip(struct wl_listener *listener, void *data) {
186 struct sway_tablet_pad *pad = wl_container_of(listener, pad, strip);
187 struct wlr_event_tablet_pad_strip *event = data;
188
189 if (!pad->current_surface) {
190 return;
191 }
192
193 wlr_tablet_v2_tablet_pad_notify_strip(pad->tablet_v2_pad,
194 event->strip, event->position,
195 event->source == WLR_TABLET_PAD_STRIP_SOURCE_FINGER,
196 event->time_msec);
197}
198
199static void handle_tablet_pad_button(struct wl_listener *listener, void *data) {
200 struct sway_tablet_pad *pad = wl_container_of(listener, pad, button);
201 struct wlr_event_tablet_pad_button *event = data;
202
203 if (!pad->current_surface) {
204 return;
205 }
206
207 wlr_tablet_v2_tablet_pad_notify_mode(pad->tablet_v2_pad,
208 event->group, event->mode, event->time_msec);
209
210 wlr_tablet_v2_tablet_pad_notify_button(pad->tablet_v2_pad,
211 event->button, event->time_msec,
212 (enum zwp_tablet_pad_v2_button_state)event->state);
213}
214
215struct sway_tablet_pad *sway_tablet_pad_create(struct sway_seat *seat,
216 struct sway_seat_device *device) {
217 struct sway_tablet_pad *tablet_pad =
218 calloc(1, sizeof(struct sway_tablet_pad));
219 if (!sway_assert(tablet_pad, "could not allocate sway tablet")) {
220 return NULL;
221 }
222
223 tablet_pad->seat_device = device;
224 wl_list_init(&tablet_pad->attach.link);
225 wl_list_init(&tablet_pad->button.link);
226 wl_list_init(&tablet_pad->strip.link);
227 wl_list_init(&tablet_pad->ring.link);
228 wl_list_init(&tablet_pad->surface_destroy.link);
229 wl_list_init(&tablet_pad->tablet_destroy.link);
230
231 wl_list_insert(&seat->cursor->tablet_pads, &tablet_pad->link);
232
233 return tablet_pad;
234}
235
236void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) {
237 struct wlr_input_device *device =
238 tablet_pad->seat_device->input_device->wlr_device;
239 struct sway_seat *seat = tablet_pad->seat_device->sway_seat;
240
241 tablet_pad->tablet_v2_pad =
242 wlr_tablet_pad_create(server.tablet_v2, seat->wlr_seat, device);
243
244 wl_list_remove(&tablet_pad->attach.link);
245 tablet_pad->attach.notify = handle_tablet_pad_attach;
246 wl_signal_add(&device->tablet_pad->events.attach_tablet,
247 &tablet_pad->attach);
248
249 wl_list_remove(&tablet_pad->button.link);
250 tablet_pad->button.notify = handle_tablet_pad_button;
251 wl_signal_add(&device->tablet_pad->events.button, &tablet_pad->button);
252
253 wl_list_remove(&tablet_pad->strip.link);
254 tablet_pad->strip.notify = handle_tablet_pad_strip;
255 wl_signal_add(&device->tablet_pad->events.strip, &tablet_pad->strip);
256
257 wl_list_remove(&tablet_pad->ring.link);
258 tablet_pad->ring.notify = handle_tablet_pad_ring;
259 wl_signal_add(&device->tablet_pad->events.ring, &tablet_pad->ring);
260
261 /* Search for a sibling tablet */
262 if (!wlr_input_device_is_libinput(device)) {
263 /* We can only do this on libinput devices */
264 return;
265 }
266
267 struct libinput_device_group *group =
268 libinput_device_get_device_group(wlr_libinput_get_device_handle(device));
269 struct sway_tablet *tool;
270 wl_list_for_each(tool, &seat->cursor->tablets, link) {
271 struct wlr_input_device *tablet =
272 tool->seat_device->input_device->wlr_device;
273 if (!wlr_input_device_is_libinput(tablet)) {
274 continue;
275 }
276
277 struct libinput_device_group *tablet_group =
278 libinput_device_get_device_group(wlr_libinput_get_device_handle(tablet));
279
280 if (tablet_group == group) {
281 attach_tablet_pad(tablet_pad, tool);
282 break;
283 }
284 }
285}
286
287void sway_tablet_pad_destroy(struct sway_tablet_pad *tablet_pad) {
288 if (!tablet_pad) {
289 return;
290 }
291
292 wl_list_remove(&tablet_pad->link);
293 wl_list_remove(&tablet_pad->attach.link);
294 wl_list_remove(&tablet_pad->button.link);
295 wl_list_remove(&tablet_pad->strip.link);
296 wl_list_remove(&tablet_pad->ring.link);
297 wl_list_remove(&tablet_pad->surface_destroy.link);
298 wl_list_remove(&tablet_pad->tablet_destroy.link);
299
300 free(tablet_pad);
301}
302
303static void handle_pad_tablet_surface_destroy(struct wl_listener *listener,
304 void *data) {
305 struct sway_tablet_pad *tablet_pad =
306 wl_container_of(listener, tablet_pad, surface_destroy);
307
308 wlr_tablet_v2_tablet_pad_notify_leave(tablet_pad->tablet_v2_pad,
309 tablet_pad->current_surface);
310 wl_list_remove(&tablet_pad->surface_destroy.link);
311 wl_list_init(&tablet_pad->surface_destroy.link);
312 tablet_pad->current_surface = NULL;
313}
314
315void sway_tablet_pad_notify_enter(struct sway_tablet_pad *tablet_pad,
316 struct wlr_surface *surface) {
317 if (!tablet_pad || !tablet_pad->tablet) {
318 return;
319 }
320
321 if (surface == tablet_pad->current_surface) {
322 return;
323 }
324
325 /* Leave current surface */
326 if (tablet_pad->current_surface) {
327 wlr_tablet_v2_tablet_pad_notify_leave(tablet_pad->tablet_v2_pad,
328 tablet_pad->current_surface);
329 wl_list_remove(&tablet_pad->surface_destroy.link);
330 wl_list_init(&tablet_pad->surface_destroy.link);
331 tablet_pad->current_surface = NULL;
332 }
333
334 if (!wlr_surface_accepts_tablet_v2(tablet_pad->tablet->tablet_v2, surface)) {
335 return;
336 }
337
338 wlr_tablet_v2_tablet_pad_notify_enter(tablet_pad->tablet_v2_pad,
339 tablet_pad->tablet->tablet_v2, surface);
340
341 tablet_pad->current_surface = surface;
342 wl_list_remove(&tablet_pad->surface_destroy.link);
343 tablet_pad->surface_destroy.notify = handle_pad_tablet_surface_destroy;
344 wl_signal_add(&surface->events.destroy, &tablet_pad->surface_destroy);
345}
diff --git a/sway/meson.build b/sway/meson.build
index 4783c58f..24628100 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -31,6 +31,7 @@ sway_sources = files(
31 'input/seatop_resize_floating.c', 31 'input/seatop_resize_floating.c',
32 'input/seatop_resize_tiling.c', 32 'input/seatop_resize_tiling.c',
33 'input/switch.c', 33 'input/switch.c',
34 'input/tablet.c',
34 35
35 'config/bar.c', 36 'config/bar.c',
36 'config/output.c', 37 'config/output.c',
diff --git a/sway/server.c b/sway/server.c
index aee2cc87..e2d54947 100644
--- a/sway/server.c
+++ b/sway/server.c
@@ -19,6 +19,7 @@
19#include <wlr/types/wlr_relative_pointer_v1.h> 19#include <wlr/types/wlr_relative_pointer_v1.h>
20#include <wlr/types/wlr_screencopy_v1.h> 20#include <wlr/types/wlr_screencopy_v1.h>
21#include <wlr/types/wlr_server_decoration.h> 21#include <wlr/types/wlr_server_decoration.h>
22#include <wlr/types/wlr_tablet_v2.h>
22#include <wlr/types/wlr_xcursor_manager.h> 23#include <wlr/types/wlr_xcursor_manager.h>
23#include <wlr/types/wlr_xdg_decoration_v1.h> 24#include <wlr/types/wlr_xdg_decoration_v1.h>
24#include <wlr/types/wlr_xdg_output_v1.h> 25#include <wlr/types/wlr_xdg_output_v1.h>
@@ -86,6 +87,8 @@ bool server_init(struct sway_server *server) {
86 &server->xdg_shell_surface); 87 &server->xdg_shell_surface);
87 server->xdg_shell_surface.notify = handle_xdg_shell_surface; 88 server->xdg_shell_surface.notify = handle_xdg_shell_surface;
88 89
90 server->tablet_v2 = wlr_tablet_v2_create(server->wl_display);
91
89 server->server_decoration_manager = 92 server->server_decoration_manager =
90 wlr_server_decoration_manager_create(server->wl_display); 93 wlr_server_decoration_manager_create(server->wl_display);
91 wlr_server_decoration_manager_set_default_mode( 94 wlr_server_decoration_manager_set_default_mode(