diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h index 516718c97..e46c9b18a 100644 --- a/include/sway/input/cursor.h +++ b/include/sway/input/cursor.h @@ -21,6 +21,8 @@ struct sway_cursor { struct sway_node *node; } previous; struct wlr_xcursor_manager *xcursor_manager; + struct wl_list tablets; + struct wl_list tablet_pads; const char *image; struct wl_client *image_client; @@ -42,6 +44,7 @@ struct sway_cursor { struct wl_listener tool_axis; struct wl_listener tool_tip; + struct wl_listener tool_proximity; struct wl_listener tool_button; uint32_t tool_buttons; diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index c963de9b4..24a6fed43 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -28,6 +28,8 @@ struct sway_seat_device { struct sway_input_device *input_device; struct sway_keyboard *keyboard; struct sway_switch *switch_device; + struct sway_tablet *tablet; + struct sway_tablet_pad *tablet_pad; struct wl_list link; // sway_seat::devices }; diff --git a/include/sway/input/tablet.h b/include/sway/input/tablet.h new file mode 100644 index 000000000..f30e232a8 --- /dev/null +++ b/include/sway/input/tablet.h @@ -0,0 +1,62 @@ +#ifndef _SWAY_INPUT_TABLET_H +#define _SWAY_INPUT_TABLET_H +#include + +struct sway_seat; +struct wlr_tablet_tool; + +struct sway_tablet { + struct wl_list link; + struct sway_seat_device *seat_device; + struct wlr_tablet_v2_tablet *tablet_v2; +}; + +struct sway_tablet_tool { + struct sway_seat *seat; + struct sway_tablet *tablet; + struct wlr_tablet_v2_tablet_tool *tablet_v2_tool; + + double tilt_x, tilt_y; + + struct wl_listener set_cursor; + struct wl_listener tool_destroy; +}; + +struct sway_tablet_pad { + struct wl_list link; + struct sway_seat_device *seat_device; + struct sway_tablet *tablet; + struct wlr_tablet_v2_tablet_pad *tablet_v2_pad; + + struct wl_listener attach; + struct wl_listener button; + struct wl_listener ring; + struct wl_listener strip; + + struct wlr_surface *current_surface; + struct wl_listener surface_destroy; + + struct wl_listener tablet_destroy; +}; + +struct sway_tablet *sway_tablet_create(struct sway_seat *seat, + struct sway_seat_device *device); + +void sway_configure_tablet(struct sway_tablet *tablet); + +void sway_tablet_destroy(struct sway_tablet *tablet); + +void sway_tablet_tool_configure(struct sway_tablet *tablet, + struct wlr_tablet_tool *wlr_tool); + +struct sway_tablet_pad *sway_tablet_pad_create(struct sway_seat *seat, + struct sway_seat_device *device); + +void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad); + +void sway_tablet_pad_destroy(struct sway_tablet_pad *tablet_pad); + +void sway_tablet_pad_notify_enter(struct sway_tablet_pad *tablet_pad, + struct wlr_surface *surface); + +#endif diff --git a/include/sway/server.h b/include/sway/server.h index a43cbf728..cd411d3b6 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -44,6 +44,8 @@ struct sway_server { struct wlr_xdg_shell *xdg_shell; struct wl_listener xdg_shell_surface; + struct wlr_tablet_manager_v2 *tablet_v2; + #if HAVE_XWAYLAND struct sway_xwayland xwayland; struct wl_listener xwayland_surface; diff --git a/protocols/meson.build b/protocols/meson.build index 4ba6f723e..f2f90dadc 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -14,6 +14,7 @@ protocols = [ [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'], [wl_protocol_dir, 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml'], + [wl_protocol_dir, 'unstable/tablet/tablet-unstable-v2.xml'], ['wlr-layer-shell-unstable-v1.xml'], ['idle.xml'], ['wlr-input-inhibitor-unstable-v1.xml'], diff --git a/sway/input/cursor.c b/sway/input/cursor.c index c6a332b88..574186d75 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include "list.h" @@ -20,6 +21,7 @@ #include "sway/desktop/transaction.h" #include "sway/input/cursor.h" #include "sway/input/keyboard.h" +#include "sway/input/tablet.h" #include "sway/layers.h" #include "sway/output.h" #include "sway/tree/arrange.h" @@ -443,72 +445,224 @@ static void apply_mapping_from_region(struct wlr_input_device *device, *y = apply_mapping_from_coord(y1, y2, *y); } +static void handle_tablet_tool_position(struct sway_cursor *cursor, + struct sway_tablet *tablet, + struct wlr_tablet_tool *tool, + bool change_x, bool change_y, + double x, double y, double dx, double dy, + int32_t time_msec) { + if (!change_x && !change_y) { + return; + } + + struct sway_input_device *input_device = tablet->seat_device->input_device; + struct input_config *ic = input_device_get_config(input_device); + if (ic != NULL && ic->mapped_from_region != NULL) { + apply_mapping_from_region(input_device->wlr_device, + ic->mapped_from_region, &x, &y); + } + + switch (tool->type) { + case WLR_TABLET_TOOL_TYPE_MOUSE: + wlr_cursor_move(cursor->cursor, input_device->wlr_device, dx, dy); + break; + default: + wlr_cursor_warp_absolute(cursor->cursor, input_device->wlr_device, + change_x ? x : NAN, change_y ? y : NAN); + } + + double sx, sy; + struct wlr_surface *surface = NULL; + struct sway_seat *seat = cursor->seat; + node_at_coords(seat, cursor->cursor->x, cursor->cursor->y, + &surface, &sx, &sy); + struct sway_tablet_tool *sway_tool = tool->data; + + if (!surface || !wlr_surface_accepts_tablet_v2(tablet->tablet_v2, surface)) { + wlr_tablet_v2_tablet_tool_notify_proximity_out(sway_tool->tablet_v2_tool); + cursor_motion(cursor, time_msec, input_device->wlr_device, dx, dy, dx, dy); + return; + } + + wlr_tablet_v2_tablet_tool_notify_proximity_in(sway_tool->tablet_v2_tool, + tablet->tablet_v2, surface); + + wlr_tablet_v2_tablet_tool_notify_motion(sway_tool->tablet_v2_tool, sx, sy); +} + static void handle_tool_axis(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis); wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat); struct wlr_event_tablet_tool_axis *event = data; - struct sway_input_device *input_device = event->device->data; + struct sway_tablet_tool *sway_tool = event->tool->data; - double x = NAN, y = NAN; - if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X)) { - x = event->x; - } - if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) { - y = event->y; + if (!sway_tool) { + sway_log(SWAY_DEBUG, "tool axis before proximity"); + return; } - struct input_config *ic = input_device_get_config(input_device); - if (ic != NULL && ic->mapped_from_region != NULL) { - apply_mapping_from_region(event->device, ic->mapped_from_region, &x, &y); + handle_tablet_tool_position(cursor, sway_tool->tablet, event->tool, + event->updated_axes & WLR_TABLET_TOOL_AXIS_X, + event->updated_axes & WLR_TABLET_TOOL_AXIS_Y, + event->x, event->y, event->dx, event->dy, event->time_msec); + + if (event->updated_axes & WLR_TABLET_TOOL_AXIS_PRESSURE) { + wlr_tablet_v2_tablet_tool_notify_pressure( + sway_tool->tablet_v2_tool, event->pressure); } - double lx, ly; - wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, - x, y, &lx, &ly); + if (event->updated_axes & WLR_TABLET_TOOL_AXIS_DISTANCE) { + wlr_tablet_v2_tablet_tool_notify_distance( + sway_tool->tablet_v2_tool, event->distance); + } - double dx = lx - cursor->cursor->x; - double dy = ly - cursor->cursor->y; + if (event->updated_axes & WLR_TABLET_TOOL_AXIS_TILT_X) { + sway_tool->tilt_x = event->tilt_x; + } - cursor_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); - wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); - transaction_commit_dirty(); + if (event->updated_axes & WLR_TABLET_TOOL_AXIS_TILT_Y) { + sway_tool->tilt_y = event->tilt_y; + } + + if (event->updated_axes & (WLR_TABLET_TOOL_AXIS_TILT_X | WLR_TABLET_TOOL_AXIS_TILT_Y)) { + wlr_tablet_v2_tablet_tool_notify_tilt( + sway_tool->tablet_v2_tool, + sway_tool->tilt_x, sway_tool->tilt_y); + } + + if (event->updated_axes & WLR_TABLET_TOOL_AXIS_ROTATION) { + wlr_tablet_v2_tablet_tool_notify_rotation( + sway_tool->tablet_v2_tool, event->rotation); + } + + if (event->updated_axes & WLR_TABLET_TOOL_AXIS_SLIDER) { + wlr_tablet_v2_tablet_tool_notify_slider( + sway_tool->tablet_v2_tool, event->slider); + } + + if (event->updated_axes & WLR_TABLET_TOOL_AXIS_WHEEL) { + wlr_tablet_v2_tablet_tool_notify_wheel( + sway_tool->tablet_v2_tool, event->wheel_delta, 0); + } } static void handle_tool_tip(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip); wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat); struct wlr_event_tablet_tool_tip *event = data; - dispatch_cursor_button(cursor, event->device, event->time_msec, - BTN_LEFT, event->state == WLR_TABLET_TOOL_TIP_DOWN ? - WLR_BUTTON_PRESSED : WLR_BUTTON_RELEASED); - wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); - transaction_commit_dirty(); + struct sway_tablet_tool *sway_tool = event->tool->data; + struct wlr_tablet_v2_tablet *tablet_v2 = sway_tool->tablet->tablet_v2; + struct sway_seat *seat = cursor->seat; + + double sx, sy; + struct wlr_surface *surface = NULL; + node_at_coords(seat, cursor->cursor->x, cursor->cursor->y, + &surface, &sx, &sy); + + if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) { + dispatch_cursor_button(cursor, event->device, event->time_msec, + BTN_LEFT, event->state == WLR_TABLET_TOOL_TIP_DOWN ? + WLR_BUTTON_PRESSED : WLR_BUTTON_RELEASED); + wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); + transaction_commit_dirty(); + return; + } + + if (event->state == WLR_TABLET_TOOL_TIP_DOWN) { + wlr_tablet_v2_tablet_tool_notify_down(sway_tool->tablet_v2_tool); + wlr_tablet_tool_v2_start_implicit_grab(sway_tool->tablet_v2_tool); + } else { + wlr_tablet_v2_tablet_tool_notify_up(sway_tool->tablet_v2_tool); + } +} + +static struct sway_tablet *get_tablet_for_device(struct sway_cursor *cursor, + struct wlr_input_device *device) { + struct sway_tablet *tablet; + wl_list_for_each(tablet, &cursor->tablets, link) { + if (tablet->seat_device->input_device->wlr_device == device) { + return tablet; + } + } + return NULL; +} + +static void handle_tool_proximity(struct wl_listener *listener, void *data) { + struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_proximity); + wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat); + struct wlr_event_tablet_tool_proximity *event = data; + + struct wlr_tablet_tool *tool = event->tool; + if (!tool->data) { + struct sway_tablet *tablet = get_tablet_for_device(cursor, event->device); + if (!tablet) { + sway_log(SWAY_ERROR, "no tablet for tablet tool"); + return; + } + sway_tablet_tool_configure(tablet, tool); + } + + struct sway_tablet_tool *sway_tool = tool->data; + if (!sway_tool) { + sway_log(SWAY_ERROR, "tablet tool not initialized"); + return; + } + + if (event->state == WLR_TABLET_TOOL_PROXIMITY_OUT) { + wlr_tablet_v2_tablet_tool_notify_proximity_out(sway_tool->tablet_v2_tool); + return; + } + + handle_tablet_tool_position(cursor, sway_tool->tablet, event->tool, + true, true, event->x, event->y, 0, 0, event->time_msec); } static void handle_tool_button(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button); wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat); struct wlr_event_tablet_tool_button *event = data; - // TODO: the user may want to configure which tool buttons are mapped to - // which simulated pointer buttons - switch (event->state) { - case WLR_BUTTON_PRESSED: - if (cursor->tool_buttons == 0) { - dispatch_cursor_button(cursor, event->device, - event->time_msec, BTN_RIGHT, event->state); - } - cursor->tool_buttons++; - break; - case WLR_BUTTON_RELEASED: - if (cursor->tool_buttons == 1) { - dispatch_cursor_button(cursor, event->device, - event->time_msec, BTN_RIGHT, event->state); - } - cursor->tool_buttons--; - break; + struct sway_tablet_tool *sway_tool = event->tool->data; + struct wlr_tablet_v2_tablet *tablet_v2 = sway_tool->tablet->tablet_v2; + struct sway_seat *seat = cursor->seat; + + if (!sway_tool) { + sway_log(SWAY_DEBUG, "tool button before proximity"); + return; } - wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); - transaction_commit_dirty(); + + double sx, sy; + struct wlr_surface *surface = NULL; + + node_at_coords(seat, cursor->cursor->x, cursor->cursor->y, + &surface, &sx, &sy); + + if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) { + // TODO: the user may want to configure which tool buttons are mapped to + // which simulated pointer buttons + switch (event->state) { + case WLR_BUTTON_PRESSED: + if (cursor->tool_buttons == 0) { + dispatch_cursor_button(cursor, event->device, + event->time_msec, BTN_RIGHT, event->state); + } + cursor->tool_buttons++; + break; + case WLR_BUTTON_RELEASED: + if (cursor->tool_buttons == 1) { + dispatch_cursor_button(cursor, event->device, + event->time_msec, BTN_RIGHT, event->state); + } + cursor->tool_buttons--; + break; + } + wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); + transaction_commit_dirty(); + return; + } + + wlr_tablet_v2_tablet_tool_notify_button(sway_tool->tablet_v2_tool, + (enum zwp_tablet_pad_v2_button_state)event->button, + (enum zwp_tablet_pad_v2_button_state)event->state); } static void check_constraint_region(struct sway_cursor *cursor) { @@ -698,9 +852,6 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) { &cursor->touch_motion); cursor->touch_motion.notify = handle_touch_motion; - // TODO: tablet protocol support - // Note: We should emulate pointer events for clients that don't support the - // tablet protocol when the time comes wl_signal_add(&wlr_cursor->events.tablet_tool_axis, &cursor->tool_axis); cursor->tool_axis.notify = handle_tool_axis; @@ -708,6 +859,9 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) { wl_signal_add(&wlr_cursor->events.tablet_tool_tip, &cursor->tool_tip); cursor->tool_tip.notify = handle_tool_tip; + wl_signal_add(&wlr_cursor->events.tablet_tool_proximity, &cursor->tool_proximity); + cursor->tool_proximity.notify = handle_tool_proximity; + wl_signal_add(&wlr_cursor->events.tablet_tool_button, &cursor->tool_button); cursor->tool_button.notify = handle_tool_button; @@ -716,6 +870,8 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) { cursor->request_set_cursor.notify = handle_request_set_cursor; wl_list_init(&cursor->constraint_commit.link); + wl_list_init(&cursor->tablets); + wl_list_init(&cursor->tablet_pads); cursor->cursor = wlr_cursor; diff --git a/sway/input/seat.c b/sway/input/seat.c index b2243fe33..ebd403437 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include "config.h" #include "list.h" @@ -18,6 +19,7 @@ #include "sway/input/keyboard.h" #include "sway/input/seat.h" #include "sway/input/switch.h" +#include "sway/input/tablet.h" #include "sway/ipc-server.h" #include "sway/layers.h" #include "sway/output.h" @@ -34,6 +36,8 @@ static void seat_device_destroy(struct sway_seat_device *seat_device) { } sway_keyboard_destroy(seat_device->keyboard); + sway_tablet_destroy(seat_device->tablet); + sway_tablet_pad_destroy(seat_device->tablet_pad); wlr_cursor_detach_input_device(seat_device->sway_seat->cursor->cursor, seat_device->input_device->wlr_device); wl_list_remove(&seat_device->link); @@ -118,6 +122,14 @@ static void seat_keyboard_notify_enter(struct sway_seat *seat, state->pressed_keycodes, state->npressed, &keyboard->modifiers); } +static void seat_tablet_pads_notify_enter(struct sway_seat *seat, + struct wlr_surface *surface) { + struct sway_seat_device *seat_device; + wl_list_for_each(seat_device, &seat->devices, link) { + sway_tablet_pad_notify_enter(seat_device->tablet_pad, surface); + } +} + /** * If con is a view, set it as active and enable keyboard input. * 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) { #endif seat_keyboard_notify_enter(seat, view->surface); + seat_tablet_pads_notify_enter(seat, view->surface); struct wlr_pointer_constraint_v1 *constraint = wlr_pointer_constraints_v1_constraint_for_surface( @@ -638,14 +651,23 @@ static void seat_configure_touch(struct sway_seat *seat, static void seat_configure_tablet_tool(struct sway_seat *seat, struct sway_seat_device *sway_device) { - if ((seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER) == 0) { - seat_configure_xcursor(seat); + if (!sway_device->tablet) { + sway_device->tablet = sway_tablet_create(seat, sway_device); } + sway_configure_tablet(sway_device->tablet); wlr_cursor_attach_input_device(seat->cursor->cursor, sway_device->input_device->wlr_device); seat_apply_input_config(seat, sway_device); } +static void seat_configure_tablet_pad(struct sway_seat *seat, + struct sway_seat_device *sway_device) { + if (!sway_device->tablet) { + sway_device->tablet_pad = sway_tablet_pad_create(seat, sway_device); + } + sway_configure_tablet_pad(sway_device->tablet_pad); +} + static struct sway_seat_device *seat_get_device(struct sway_seat *seat, struct sway_input_device *input_device) { struct sway_seat_device *seat_device = NULL; @@ -682,7 +704,7 @@ void seat_configure_device(struct sway_seat *seat, seat_configure_tablet_tool(seat, seat_device); break; case WLR_INPUT_DEVICE_TABLET_PAD: - sway_log(SWAY_DEBUG, "TODO: configure tablet pad"); + seat_configure_tablet_pad(seat, seat_device); break; } } @@ -1079,6 +1101,7 @@ void seat_set_focus_surface(struct sway_seat *seat, seat->has_focus = false; } seat_keyboard_notify_enter(seat, surface); + seat_tablet_pads_notify_enter(seat, surface); } void 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 000000000..b0d4d0c64 --- /dev/null +++ b/sway/input/tablet.c @@ -0,0 +1,345 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include "log.h" +#include "sway/input/cursor.h" +#include "sway/input/seat.h" +#include "sway/input/tablet.h" + +static void handle_pad_tablet_destroy(struct wl_listener *listener, void *data) { + struct sway_tablet_pad *pad = + wl_container_of(listener, pad, tablet_destroy); + + pad->tablet = NULL; + + wl_list_remove(&pad->tablet_destroy.link); + wl_list_init(&pad->tablet_destroy.link); +} + +static void attach_tablet_pad(struct sway_tablet_pad *tablet_pad, + struct sway_tablet *tablet) { + sway_log(SWAY_DEBUG, "Attaching tablet pad \"%s\" to tablet tool \"%s\"", + tablet_pad->seat_device->input_device->wlr_device->name, + tablet->seat_device->input_device->wlr_device->name); + + tablet_pad->tablet = tablet; + + wl_list_remove(&tablet_pad->tablet_destroy.link); + tablet_pad->tablet_destroy.notify = handle_pad_tablet_destroy; + wl_signal_add(&tablet->seat_device->input_device->wlr_device->events.destroy, + &tablet_pad->tablet_destroy); +} + +struct sway_tablet *sway_tablet_create(struct sway_seat *seat, + struct sway_seat_device *device) { + struct sway_tablet *tablet = + calloc(1, sizeof(struct sway_tablet)); + if (!sway_assert(tablet, "could not allocate sway tablet for seat")) { + return NULL; + } + + wl_list_insert(&seat->cursor->tablets, &tablet->link); + + device->tablet = tablet; + tablet->seat_device = device; + + return tablet; +} + +void sway_configure_tablet(struct sway_tablet *tablet) { + struct wlr_input_device *device = + tablet->seat_device->input_device->wlr_device; + struct sway_seat *seat = tablet->seat_device->sway_seat; + + if ((seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER) == 0) { + seat_configure_xcursor(seat); + } + + tablet->tablet_v2 = + wlr_tablet_create(server.tablet_v2, seat->wlr_seat, device); + + /* Search for a sibling tablet pad */ + if (!wlr_input_device_is_libinput(device)) { + /* We can only do this on libinput devices */ + return; + } + + struct libinput_device_group *group = + libinput_device_get_device_group(wlr_libinput_get_device_handle(device)); + struct sway_tablet_pad *tablet_pad; + wl_list_for_each(tablet_pad, &seat->cursor->tablet_pads, link) { + struct wlr_input_device *pad_device = + tablet_pad->seat_device->input_device->wlr_device; + if (!wlr_input_device_is_libinput(pad_device)) { + continue; + } + + struct libinput_device_group *pad_group = + libinput_device_get_device_group(wlr_libinput_get_device_handle(pad_device)); + + if (pad_group == group) { + attach_tablet_pad(tablet_pad, tablet); + break; + } + } +} + +void sway_tablet_destroy(struct sway_tablet *tablet) { + if (!tablet) { + return; + } + wl_list_remove(&tablet->link); + free(tablet); +} + +static void handle_tablet_tool_set_cursor(struct wl_listener *listener, void *data) { + struct sway_tablet_tool *tool = + wl_container_of(listener, tool, set_cursor); + struct wlr_tablet_v2_event_cursor *event = data; + + struct sway_cursor *cursor = tool->seat->cursor; + if (!seatop_allows_set_cursor(cursor->seat)) { + return; + } + + struct wl_client *focused_client = NULL; + struct wlr_surface *focused_surface = + cursor->seat->wlr_seat->pointer_state.focused_surface; + if (focused_surface != NULL) { + focused_client = wl_resource_get_client(focused_surface->resource); + } + + // TODO: check cursor mode + if (focused_client == NULL || + event->seat_client->client != focused_client) { + sway_log(SWAY_DEBUG, "denying request to set cursor from unfocused client"); + return; + } + + cursor_set_image_surface(cursor, event->surface, event->hotspot_x, + event->hotspot_y, focused_client); +} + +static void handle_tablet_tool_destroy(struct wl_listener *listener, void *data) { + struct sway_tablet_tool *tool = + wl_container_of(listener, tool, tool_destroy); + + wl_list_remove(&tool->tool_destroy.link); + wl_list_remove(&tool->set_cursor.link); + + free(tool); +} + +void sway_tablet_tool_configure(struct sway_tablet *tablet, + struct wlr_tablet_tool *wlr_tool) { + struct sway_tablet_tool *tool = + calloc(1, sizeof(struct sway_tablet_tool)); + if (!sway_assert(tool, "could not allocate sway tablet tool for tablet")) { + return; + } + + tool->seat = tablet->seat_device->sway_seat; + tool->tablet = tablet; + tool->tablet_v2_tool = + wlr_tablet_tool_create(server.tablet_v2, + tablet->seat_device->sway_seat->wlr_seat, wlr_tool); + + tool->tool_destroy.notify = handle_tablet_tool_destroy; + wl_signal_add(&wlr_tool->events.destroy, &tool->tool_destroy); + + tool->set_cursor.notify = handle_tablet_tool_set_cursor; + wl_signal_add(&tool->tablet_v2_tool->events.set_cursor, + &tool->set_cursor); + + wlr_tool->data = tool; +} + +static void handle_tablet_pad_attach(struct wl_listener *listener, + void *data) { + struct sway_tablet_pad *pad = wl_container_of(listener, pad, attach); + struct wlr_tablet_tool *wlr_tool = data; + struct sway_tablet_tool *tool = wlr_tool->data; + + if (!tool) { + return; + } + + attach_tablet_pad(pad, tool->tablet); +} + +static void handle_tablet_pad_ring(struct wl_listener *listener, void *data) { + struct sway_tablet_pad *pad = wl_container_of(listener, pad, ring); + struct wlr_event_tablet_pad_ring *event = data; + + if (!pad->current_surface) { + return; + } + + wlr_tablet_v2_tablet_pad_notify_ring(pad->tablet_v2_pad, + event->ring, event->position, + event->source == WLR_TABLET_PAD_RING_SOURCE_FINGER, + event->time_msec); +} + +static void handle_tablet_pad_strip(struct wl_listener *listener, void *data) { + struct sway_tablet_pad *pad = wl_container_of(listener, pad, strip); + struct wlr_event_tablet_pad_strip *event = data; + + if (!pad->current_surface) { + return; + } + + wlr_tablet_v2_tablet_pad_notify_strip(pad->tablet_v2_pad, + event->strip, event->position, + event->source == WLR_TABLET_PAD_STRIP_SOURCE_FINGER, + event->time_msec); +} + +static void handle_tablet_pad_button(struct wl_listener *listener, void *data) { + struct sway_tablet_pad *pad = wl_container_of(listener, pad, button); + struct wlr_event_tablet_pad_button *event = data; + + if (!pad->current_surface) { + return; + } + + wlr_tablet_v2_tablet_pad_notify_mode(pad->tablet_v2_pad, + event->group, event->mode, event->time_msec); + + wlr_tablet_v2_tablet_pad_notify_button(pad->tablet_v2_pad, + event->button, event->time_msec, + (enum zwp_tablet_pad_v2_button_state)event->state); +} + +struct sway_tablet_pad *sway_tablet_pad_create(struct sway_seat *seat, + struct sway_seat_device *device) { + struct sway_tablet_pad *tablet_pad = + calloc(1, sizeof(struct sway_tablet_pad)); + if (!sway_assert(tablet_pad, "could not allocate sway tablet")) { + return NULL; + } + + tablet_pad->seat_device = device; + wl_list_init(&tablet_pad->attach.link); + wl_list_init(&tablet_pad->button.link); + wl_list_init(&tablet_pad->strip.link); + wl_list_init(&tablet_pad->ring.link); + wl_list_init(&tablet_pad->surface_destroy.link); + wl_list_init(&tablet_pad->tablet_destroy.link); + + wl_list_insert(&seat->cursor->tablet_pads, &tablet_pad->link); + + return tablet_pad; +} + +void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) { + struct wlr_input_device *device = + tablet_pad->seat_device->input_device->wlr_device; + struct sway_seat *seat = tablet_pad->seat_device->sway_seat; + + tablet_pad->tablet_v2_pad = + wlr_tablet_pad_create(server.tablet_v2, seat->wlr_seat, device); + + wl_list_remove(&tablet_pad->attach.link); + tablet_pad->attach.notify = handle_tablet_pad_attach; + wl_signal_add(&device->tablet_pad->events.attach_tablet, + &tablet_pad->attach); + + wl_list_remove(&tablet_pad->button.link); + tablet_pad->button.notify = handle_tablet_pad_button; + wl_signal_add(&device->tablet_pad->events.button, &tablet_pad->button); + + wl_list_remove(&tablet_pad->strip.link); + tablet_pad->strip.notify = handle_tablet_pad_strip; + wl_signal_add(&device->tablet_pad->events.strip, &tablet_pad->strip); + + wl_list_remove(&tablet_pad->ring.link); + tablet_pad->ring.notify = handle_tablet_pad_ring; + wl_signal_add(&device->tablet_pad->events.ring, &tablet_pad->ring); + + /* Search for a sibling tablet */ + if (!wlr_input_device_is_libinput(device)) { + /* We can only do this on libinput devices */ + return; + } + + struct libinput_device_group *group = + libinput_device_get_device_group(wlr_libinput_get_device_handle(device)); + struct sway_tablet *tool; + wl_list_for_each(tool, &seat->cursor->tablets, link) { + struct wlr_input_device *tablet = + tool->seat_device->input_device->wlr_device; + if (!wlr_input_device_is_libinput(tablet)) { + continue; + } + + struct libinput_device_group *tablet_group = + libinput_device_get_device_group(wlr_libinput_get_device_handle(tablet)); + + if (tablet_group == group) { + attach_tablet_pad(tablet_pad, tool); + break; + } + } +} + +void sway_tablet_pad_destroy(struct sway_tablet_pad *tablet_pad) { + if (!tablet_pad) { + return; + } + + wl_list_remove(&tablet_pad->link); + wl_list_remove(&tablet_pad->attach.link); + wl_list_remove(&tablet_pad->button.link); + wl_list_remove(&tablet_pad->strip.link); + wl_list_remove(&tablet_pad->ring.link); + wl_list_remove(&tablet_pad->surface_destroy.link); + wl_list_remove(&tablet_pad->tablet_destroy.link); + + free(tablet_pad); +} + +static void handle_pad_tablet_surface_destroy(struct wl_listener *listener, + void *data) { + struct sway_tablet_pad *tablet_pad = + wl_container_of(listener, tablet_pad, surface_destroy); + + wlr_tablet_v2_tablet_pad_notify_leave(tablet_pad->tablet_v2_pad, + tablet_pad->current_surface); + wl_list_remove(&tablet_pad->surface_destroy.link); + wl_list_init(&tablet_pad->surface_destroy.link); + tablet_pad->current_surface = NULL; +} + +void sway_tablet_pad_notify_enter(struct sway_tablet_pad *tablet_pad, + struct wlr_surface *surface) { + if (!tablet_pad || !tablet_pad->tablet) { + return; + } + + if (surface == tablet_pad->current_surface) { + return; + } + + /* Leave current surface */ + if (tablet_pad->current_surface) { + wlr_tablet_v2_tablet_pad_notify_leave(tablet_pad->tablet_v2_pad, + tablet_pad->current_surface); + wl_list_remove(&tablet_pad->surface_destroy.link); + wl_list_init(&tablet_pad->surface_destroy.link); + tablet_pad->current_surface = NULL; + } + + if (!wlr_surface_accepts_tablet_v2(tablet_pad->tablet->tablet_v2, surface)) { + return; + } + + wlr_tablet_v2_tablet_pad_notify_enter(tablet_pad->tablet_v2_pad, + tablet_pad->tablet->tablet_v2, surface); + + tablet_pad->current_surface = surface; + wl_list_remove(&tablet_pad->surface_destroy.link); + tablet_pad->surface_destroy.notify = handle_pad_tablet_surface_destroy; + wl_signal_add(&surface->events.destroy, &tablet_pad->surface_destroy); +} diff --git a/sway/meson.build b/sway/meson.build index 4783c58f6..24628100f 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -31,6 +31,7 @@ sway_sources = files( 'input/seatop_resize_floating.c', 'input/seatop_resize_tiling.c', 'input/switch.c', + 'input/tablet.c', 'config/bar.c', 'config/output.c', diff --git a/sway/server.c b/sway/server.c index aee2cc871..e2d54947f 100644 --- a/sway/server.c +++ b/sway/server.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -86,6 +87,8 @@ bool server_init(struct sway_server *server) { &server->xdg_shell_surface); server->xdg_shell_surface.notify = handle_xdg_shell_surface; + server->tablet_v2 = wlr_tablet_v2_create(server->wl_display); + server->server_decoration_manager = wlr_server_decoration_manager_create(server->wl_display); wlr_server_decoration_manager_set_default_mode(