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.master
parent
875edc9c2f
commit
7e420cb6e4
@ -0,0 +1,62 @@
|
|||||||
|
#ifndef _SWAY_INPUT_TABLET_H
|
||||||
|
#define _SWAY_INPUT_TABLET_H
|
||||||
|
#include <wlr/types/wlr_layer_shell_v1.h>
|
||||||
|
|
||||||
|
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
|
@ -0,0 +1,345 @@
|
|||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <wlr/backend/libinput.h>
|
||||||
|
#include <wlr/types/wlr_tablet_v2.h>
|
||||||
|
#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);
|
||||||
|
}
|
Loading…
Reference in new issue