From 9125b244e7679a4641af02626baf01dc53e2dc6d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20=22sp1rit=22=E2=80=8B?= <[EMAIL REDACTED]>
Date: Wed, 23 Mar 2022 13:46:25 -0400
Subject: [PATCH] wayland: Basic support for zwp_tablet_*v2 protocol
---
src/video/wayland/SDL_waylandevents.c | 364 +++++++
src/video/wayland/SDL_waylandevents_c.h | 33 +
src/video/wayland/SDL_waylandvideo.c | 9 +
src/video/wayland/SDL_waylandvideo.h | 2 +
wayland-protocols/tablet-unstable-v2.xml | 1178 ++++++++++++++++++++++
5 files changed, 1586 insertions(+)
create mode 100644 wayland-protocols/tablet-unstable-v2.xml
diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c
index 55893201bca..85c46f8f1e3 100644
--- a/src/video/wayland/SDL_waylandevents.c
+++ b/src/video/wayland/SDL_waylandevents.c
@@ -40,6 +40,7 @@
#include "xdg-shell-client-protocol.h"
#include "keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h"
#include "text-input-unstable-v3-client-protocol.h"
+#include "tablet-unstable-v2-client-protocol.h"
#ifdef HAVE_LIBDECOR_H
#include <libdecor.h>
@@ -1646,6 +1647,361 @@ Wayland_add_text_input_manager(SDL_VideoData *d, uint32_t id, uint32_t version)
}
}
+static void
+tablet_tool_handle_type(void* data, struct zwp_tablet_tool_v2* tool, uint32_t type)
+{
+ /* unimplemented */
+}
+
+static void
+tablet_tool_handle_hardware_serial(void* data, struct zwp_tablet_tool_v2* tool, uint32_t serial_hi, uint32_t serial_lo)
+{
+ /* unimplemented */
+}
+
+static void
+tablet_tool_handle_hardware_id_wacom(void* data, struct zwp_tablet_tool_v2* tool, uint32_t id_hi, uint32_t id_lo)
+{
+ /* unimplemented */
+}
+
+static void
+tablet_tool_handle_capability(void* data, struct zwp_tablet_tool_v2* tool, uint32_t capability)
+{
+ /* unimplemented */
+}
+
+static void
+tablet_tool_handle_done(void* data, struct zwp_tablet_tool_v2* tool)
+{
+ /* unimplemented */
+}
+
+static void
+tablet_tool_handle_removed(void* data, struct zwp_tablet_tool_v2* tool)
+{
+ /* unimplemented */
+}
+
+static void
+tablet_tool_handle_proximity_in(void* data, struct zwp_tablet_tool_v2* tool, uint32_t serial, struct zwp_tablet_v2* tablet, struct wl_surface* surface)
+{
+ struct SDL_WaylandTabletInput* input = data;
+ SDL_WindowData* window;
+
+ if (!surface) {
+ return;
+ }
+
+ if (!SDL_WAYLAND_own_surface(surface)) {
+ return;
+ }
+
+ window = (SDL_WindowData*)wl_surface_get_user_data(surface);
+
+ if (window) {
+ input->tool_focus = window;
+ input->tool_prox_serial = serial;
+
+ input->is_down = SDL_FALSE;
+
+ input->btn_stylus = SDL_FALSE;
+ input->btn_stylus2 = SDL_FALSE;
+ input->btn_stylus3 = SDL_FALSE;
+
+ SDL_SetMouseFocus(window->sdlwindow);
+ SDL_SetCursor(NULL);
+ }
+}
+
+static void
+tablet_tool_handle_proximity_out(void* data, struct zwp_tablet_tool_v2* tool)
+{
+ struct SDL_WaylandTabletInput* input = data;
+
+ if (input->tool_focus) {
+ SDL_SetMouseFocus(NULL);
+ input->tool_focus = NULL;
+ }
+}
+
+uint32_t
+tablet_tool_btn_to_sdl_button(struct SDL_WaylandTabletInput* input)
+{
+ unsigned int tool_btn = input->btn_stylus3 << 2 | input->btn_stylus2 << 1 | input->btn_stylus << 0;
+ switch (tool_btn) {
+ case 0b000:
+ return SDL_BUTTON_LEFT;
+ case 0b001:
+ return SDL_BUTTON_RIGHT;
+ case 0b010:
+ return SDL_BUTTON_MIDDLE;
+ case 0b100:
+ return SDL_BUTTON_X1;
+ default:
+ return SDL_BUTTON_LEFT;
+ }
+}
+
+static void
+tablet_tool_handle_down(void* data, struct zwp_tablet_tool_v2* tool, uint32_t serial)
+{
+ struct SDL_WaylandTabletInput* input = data;
+ SDL_WindowData* window = input->tool_focus;
+ input->is_down = SDL_TRUE;
+ if (!window) {
+ /* tablet_tool_handle_proximity_out gets called when moving over the libdecoration csd.
+ * that sets input->tool_focus (window) to NULL, but handle_{down,up} events are still
+ * received. To prevent SIGSEGV this returns when this is the case.
+ */
+ return;
+ }
+
+ SDL_SendMouseButton(window->sdlwindow, 0, SDL_PRESSED, tablet_tool_btn_to_sdl_button(input));
+}
+
+static void
+tablet_tool_handle_up(void* data, struct zwp_tablet_tool_v2* tool)
+{
+ struct SDL_WaylandTabletInput* input = data;
+ SDL_WindowData* window = input->tool_focus;
+
+ input->is_down = SDL_FALSE;
+
+ if (!window) {
+ /* tablet_tool_handle_proximity_out gets called when moving over the libdecoration csd.
+ * that sets input->tool_focus (window) to NULL, but handle_{down,up} events are still
+ * received. To prevent SIGSEGV this returns when this is the case.
+ */
+ return;
+ }
+
+ SDL_SendMouseButton(window->sdlwindow, 0, SDL_RELEASED, tablet_tool_btn_to_sdl_button(input));
+}
+
+static void
+tablet_tool_handle_motion(void* data, struct zwp_tablet_tool_v2* tool, wl_fixed_t sx_w, wl_fixed_t sy_w)
+{
+ struct SDL_WaylandTabletInput* input = data;
+ SDL_WindowData* window = input->tool_focus;
+
+ input->sx_w = sx_w;
+ input->sy_w = sy_w;
+ if (input->tool_focus) {
+ const int sx = wl_fixed_to_int(sx_w);
+ const int sy = wl_fixed_to_int(sy_w);
+ SDL_SendMouseMotion(window->sdlwindow, 0, 0, sx, sy);
+ }
+}
+
+static void
+tablet_tool_handle_pressure(void* data, struct zwp_tablet_tool_v2* tool, uint32_t pressure)
+{
+ /* unimplemented */
+}
+
+static void
+tablet_tool_handle_distance(void* data, struct zwp_tablet_tool_v2* tool, uint32_t distance)
+{
+ /* unimplemented */
+}
+
+static void
+tablet_tool_handle_tilt(void* data, struct zwp_tablet_tool_v2* tool, wl_fixed_t xtilt, wl_fixed_t ytilt)
+{
+ /* unimplemented */
+}
+
+static void
+tablet_tool_handle_button(void* data, struct zwp_tablet_tool_v2* tool, uint32_t serial, uint32_t button, uint32_t state)
+{
+ struct SDL_WaylandTabletInput* input = data;
+
+ if (input->is_down) {
+ tablet_tool_handle_up(data, tool);
+ input->is_down = SDL_TRUE;
+ }
+
+ switch (button) {
+ /* see %{_includedir}/linux/input-event-codes.h */
+ case 0x14b: /* BTN_STYLUS */
+ input->btn_stylus = state == ZWP_TABLET_PAD_V2_BUTTON_STATE_PRESSED ? SDL_TRUE : SDL_FALSE;
+ break;
+ case 0x14c: /* BTN_STYLUS2 */
+ input->btn_stylus2 = state == ZWP_TABLET_PAD_V2_BUTTON_STATE_PRESSED ? SDL_TRUE : SDL_FALSE;
+ break;
+ case 0x149: /* BTN_STYLUS3 */
+ input->btn_stylus3 = state == ZWP_TABLET_PAD_V2_BUTTON_STATE_PRESSED ? SDL_TRUE : SDL_FALSE;
+ break;
+ }
+
+ if (input->is_down) {
+ tablet_tool_handle_down(data, tool, serial);
+ }
+}
+
+static void
+tablet_tool_handle_rotation(void* data, struct zwp_tablet_tool_v2* tool, wl_fixed_t degrees)
+{
+ /* unimplemented */
+}
+
+static void
+tablet_tool_handle_slider(void* data, struct zwp_tablet_tool_v2* tool, int32_t position)
+{
+ /* unimplemented */
+}
+
+static void
+tablet_tool_handle_wheel(void* data, struct zwp_tablet_tool_v2* tool, int32_t degrees, int32_t clicks)
+{
+ /* unimplemented */
+}
+
+static void
+tablet_tool_handle_frame(void* data, struct zwp_tablet_tool_v2* tool, uint32_t time)
+{
+ /* unimplemented */
+}
+
+
+static const struct zwp_tablet_tool_v2_listener tablet_tool_listener = {
+ tablet_tool_handle_type,
+ tablet_tool_handle_hardware_serial,
+ tablet_tool_handle_hardware_id_wacom,
+ tablet_tool_handle_capability,
+ tablet_tool_handle_done,
+ tablet_tool_handle_removed,
+ tablet_tool_handle_proximity_in,
+ tablet_tool_handle_proximity_out,
+ tablet_tool_handle_down,
+ tablet_tool_handle_up,
+ tablet_tool_handle_motion,
+ tablet_tool_handle_pressure,
+ tablet_tool_handle_distance,
+ tablet_tool_handle_tilt,
+ tablet_tool_handle_rotation,
+ tablet_tool_handle_slider,
+ tablet_tool_handle_wheel,
+ tablet_tool_handle_button,
+ tablet_tool_handle_frame
+};
+
+struct SDL_WaylandTabletObjectListNode*
+tablet_object_list_new_node(void* object)
+{
+ struct SDL_WaylandTabletObjectListNode* node;
+
+ node = SDL_calloc(1, sizeof *node);
+ if (node == NULL) {
+ return NULL;
+ }
+
+ node->next = NULL;
+ node->object = object;
+
+ return node;
+}
+
+void tablet_object_list_append(struct SDL_WaylandTabletObjectListNode* head, void* object)
+{
+ if (head->object == NULL) {
+ head->object = object;
+ return;
+ }
+
+ while (head->next) {
+ head = head->next;
+ }
+
+ head->next = tablet_object_list_new_node(object);
+}
+
+void tablet_object_list_destroy(struct SDL_WaylandTabletObjectListNode* head, void (*deleter)(void* object))
+{
+ while (head) {
+ struct SDL_WaylandTabletObjectListNode* next = head->next;
+ if (head->object) {
+ (*deleter)(head->object);
+ }
+ SDL_free(head);
+ head = next;
+ }
+}
+
+
+static void
+tablet_seat_handle_tablet_added(void* data, struct zwp_tablet_seat_v2* seat, struct zwp_tablet_v2* tablet)
+{
+ struct SDL_WaylandTabletInput* input = data;
+
+ tablet_object_list_append(input->tablets, tablet);
+}
+
+static void
+tablet_seat_handle_tool_added(void* data, struct zwp_tablet_seat_v2* seat, struct zwp_tablet_tool_v2* tool)
+{
+ struct SDL_WaylandTabletInput* input = data;
+
+ zwp_tablet_tool_v2_add_listener(tool, &tablet_tool_listener, data);
+ zwp_tablet_tool_v2_set_user_data(tool, data);
+
+ tablet_object_list_append(input->tools, tool);
+}
+
+static void
+tablet_seat_handle_pad_added(void* data, struct zwp_tablet_seat_v2* seat, struct zwp_tablet_pad_v2* pad)
+{
+ struct SDL_WaylandTabletInput* input = data;
+
+ tablet_object_list_append(input->pads, pad);
+}
+
+static const struct zwp_tablet_seat_v2_listener tablet_seat_listener = {
+ tablet_seat_handle_tablet_added,
+ tablet_seat_handle_tool_added,
+ tablet_seat_handle_pad_added
+};
+
+void
+Wayland_input_add_tablet(struct SDL_WaylandInput *input, struct SDL_WaylandTabletManager* tablet_manager)
+{
+ struct SDL_WaylandTabletInput* tablet_input;
+
+ if (!tablet_manager || !input || !input->seat) {
+ return;
+ }
+
+ tablet_input = SDL_calloc(1, sizeof *tablet_input);
+ if (tablet_input == NULL) {
+ return;
+ }
+
+ input->tablet = tablet_input;
+
+ tablet_input->seat = (struct SDL_WaylandTabletSeat*)zwp_tablet_manager_v2_get_tablet_seat((struct zwp_tablet_manager_v2*)tablet_manager, input->seat);
+
+ tablet_input->tablets = tablet_object_list_new_node(NULL);
+ tablet_input->tools = tablet_object_list_new_node(NULL);
+ tablet_input->pads = tablet_object_list_new_node(NULL);
+
+ zwp_tablet_seat_v2_add_listener((struct zwp_tablet_seat_v2*)tablet_input->seat, &tablet_seat_listener, tablet_input);
+}
+
+#define TABLET_OBJECT_LIST_DELETER(fun) (void (*)(void*))fun
+void
+Wayland_input_destroy_tablet(struct SDL_WaylandInput* input)
+{
+ tablet_object_list_destroy(input->tablet->pads, TABLET_OBJECT_LIST_DELETER(zwp_tablet_pad_v2_destroy));
+ tablet_object_list_destroy(input->tablet->tools, TABLET_OBJECT_LIST_DELETER(zwp_tablet_tool_v2_destroy));
+ tablet_object_list_destroy(input->tablet->tablets, TABLET_OBJECT_LIST_DELETER(zwp_tablet_v2_destroy));
+
+ zwp_tablet_seat_v2_destroy((struct zwp_tablet_seat_v2*)input->tablet->seat);
+
+ SDL_free(input->tablet);
+ input->tablet = NULL;
+}
+
void
Wayland_display_add_input(SDL_VideoData *d, uint32_t id, uint32_t version)
{
@@ -1671,6 +2027,10 @@ Wayland_display_add_input(SDL_VideoData *d, uint32_t id, uint32_t version)
wl_seat_add_listener(input->seat, &seat_listener, input);
wl_seat_set_user_data(input->seat, input);
+ if (d->tablet_manager) {
+ Wayland_input_add_tablet(input, d->tablet_manager);
+ }
+
WAYLAND_wl_display_flush(d->display);
}
@@ -1711,6 +2071,10 @@ void Wayland_display_destroy_input(SDL_VideoData *d)
wl_touch_destroy(input->touch);
}
+ if (input->tablet) {
+ Wayland_input_destroy_tablet(input);
+ }
+
if (input->seat)
wl_seat_destroy(input->seat);
diff --git a/src/video/wayland/SDL_waylandevents_c.h b/src/video/wayland/SDL_waylandevents_c.h
index 24256a7d024..09e9c2baafa 100644
--- a/src/video/wayland/SDL_waylandevents_c.h
+++ b/src/video/wayland/SDL_waylandevents_c.h
@@ -29,6 +29,34 @@
#include "SDL_waylanddatamanager.h"
#include "SDL_waylandkeyboard.h"
+struct SDL_WaylandTabletSeat;
+
+struct SDL_WaylandTabletObjectListNode {
+ void* object;
+ struct SDL_WaylandTabletObjectListNode* next;
+};
+
+struct SDL_WaylandTabletInput {
+ struct SDL_WaylandTabletSeat* seat;
+
+ struct SDL_WaylandTabletObjectListNode* tablets;
+ struct SDL_WaylandTabletObjectListNode* tools;
+ struct SDL_WaylandTabletObjectListNode* pads;
+
+ SDL_WindowData *tool_focus;
+ uint32_t tool_prox_serial;
+
+ /* Last motion location */
+ wl_fixed_t sx_w;
+ wl_fixed_t sy_w;
+
+ SDL_bool is_down;
+
+ SDL_bool btn_stylus;
+ SDL_bool btn_stylus2;
+ SDL_bool btn_stylus3;
+};
+
typedef struct {
// repeat_rate in range of [1, 1000]
int32_t repeat_rate;
@@ -80,6 +108,8 @@ struct SDL_WaylandInput {
} pointer_curr_axis_info;
SDL_WaylandKeyboardRepeat keyboard_repeat;
+
+ struct SDL_WaylandTabletInput* tablet;
};
extern void Wayland_PumpEvents(_THIS);
@@ -107,6 +137,9 @@ extern void Wayland_display_destroy_relative_pointer_manager(SDL_VideoData *d);
extern int Wayland_input_grab_keyboard(SDL_Window *window, struct SDL_WaylandInput *input);
extern int Wayland_input_ungrab_keyboard(SDL_Window *window);
+extern void Wayland_input_add_tablet(struct SDL_WaylandInput *input, struct SDL_WaylandTabletManager* tablet_manager);
+extern void Wayland_input_destroy_tablet(struct SDL_WaylandInput *input);
+
#endif /* SDL_waylandevents_h_ */
/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c
index c9c28bb9080..b8f9a599738 100644
--- a/src/video/wayland/SDL_waylandvideo.c
+++ b/src/video/wayland/SDL_waylandvideo.c
@@ -52,6 +52,7 @@
#include "idle-inhibit-unstable-v1-client-protocol.h"
#include "xdg-activation-v1-client-protocol.h"
#include "text-input-unstable-v3-client-protocol.h"
+#include "tablet-unstable-v2-client-protocol.h"
#ifdef HAVE_LIBDECOR_H
#include <libdecor.h>
@@ -610,6 +611,11 @@ display_handle_global(void *data, struct wl_registry *registry, uint32_t id,
Wayland_add_data_device_manager(d, id, version);
} else if (SDL_strcmp(interface, "zxdg_decoration_manager_v1") == 0) {
d->decoration_manager = wl_registry_bind(d->registry, id, &zxdg_decoration_manager_v1_interface, 1);
+ } else if (SDL_strcmp(interface, "zwp_tablet_manager_v2") == 0) {
+ d->tablet_manager = wl_registry_bind(d->registry, id, &zwp_tablet_manager_v2_interface, 1);
+ if (d->input) {
+ Wayland_input_add_tablet(d->input, d->tablet_manager);
+ }
#ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
} else if (SDL_strcmp(interface, "qt_touch_extension") == 0) {
@@ -769,6 +775,9 @@ Wayland_VideoQuit(_THIS)
Wayland_touch_destroy(data);
#endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
+ if (data->tablet_manager)
+ zwp_tablet_manager_v2_destroy((struct zwp_tablet_manager_v2*)data->tablet_manager);
+
if (data->data_device_manager)
wl_data_device_manager_destroy(data->data_device_manager);
diff --git a/src/video/wayland/SDL_waylandvideo.h b/src/video/wayland/SDL_waylandvideo.h
index 31168a9d51a..5c658a3c2d5 100644
--- a/src/video/wayland/SDL_waylandvideo.h
+++ b/src/video/wayland/SDL_waylandvideo.h
@@ -34,6 +34,7 @@
struct xkb_context;
struct SDL_WaylandInput;
+struct SDL_WaylandTabletManager;
#ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
struct SDL_WaylandTouch;
@@ -77,6 +78,7 @@ typedef struct {
struct xkb_context *xkb_context;
struct SDL_WaylandInput *input;
+ struct SDL_WaylandTabletManager *tablet_manager;
#ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
struct SDL_WaylandTouch *touch;
diff --git a/wayland-protocols/tablet-unstable-v2.xml b/wayland-protocols/tablet-unstable-v2.xml
new file mode 100644
index 00000000000..b286d964af8
--- /dev/null
+++ b/wayland-protocols/tablet-unstable-v2.xml
@@ -0,0 +1,1178 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="tablet_unstable_v2">
+
+ <copyright>
+ Copyright 2014 © Stephen "Lyude" Chandler Paul
+ Copyright 2015-2016 © Red Hat, Inc.
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation files
+ (the "Software"), to deal in the Software without restriction,
+ including without limitation the rights to use, copy, modify, merge,
+ publish, distribute, sublicense, and/or sell copies of the Software,
+ and to permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+
+ The above copyright notice and this permission notice (including the
+ next paragraph) shall be included in all copies or substantial
+ portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+ </copyright>
+
+ <description summary="Wayland protocol for graphics tablets">
+ This description provides a high-level overview of the interplay between
+ the interfaces defined this protocol. For details, see the protocol
+ specification.
+
+ More than one tablet may exist, and device-specifics matter. Tablets are
+ not represented by a single virtual device like wl_pointer. A client
+ binds to the tablet manager object which is just a proxy object. From
+ that, the client requests wp_tablet_manager.get_tablet_seat(wl_seat)
+ and that returns the actual interface that has all the tablets. With
+ this indirection, we can avoid merging wp_tablet into the actual Wayland
+ protocol, a long-term benefit.
+
+ The wp_tablet_seat sends a "tablet added" event for each tablet
+ connected. That event is followed by descriptive events about the
+ hardware; currently that includes events for name, vid/pid and
+ a wp_tablet.path event that describes a local path. This path can be
+ used to uniquely identify a tablet or get more information through
+ libwacom. Emulated or nested tablets can skip any of those, e.g. a
+ virtual tablet may not have a vid/pid. The sequence of descriptive
+ events is terminated by a wp_tablet.done event to signal that a client
+ may now finalize any initialization for that tablet.
+
+ Events from tablets require a tool in proximity. Tools are also managed
+ by the tablet seat; a "tool added" event is sent whenever a tool is new
+ to the compositor. That event is followed by a number of descriptive
+ events about the hardware; currently that includes capabilities,
+ hardware id and serial number, and tool type. Similar to the tablet
+ interface, a wp_tablet_tool.done event is sent to terminate that initial
+ sequence.
+
+ Any event from a tool happens on the wp_tablet_tool interface. When the
+ tool gets into proximity of the tablet, a proximity_in event is sent on
+ the wp_tablet_tool interface, listing the tablet and the surface. That
+ event is followed by a motion event with the coordinates. After that,
+ it's the usual motion, axis, button, etc. events. The protocol's
+ serialisation means events are grouped by wp_tablet_tool.frame events.
+
+ Two special events (that don't exist in X) are down and up. They signal
+ "tip touching the surface". For tablets without real proximity
+ detection, the sequence is: proximity_in, motion, down, frame.
+
+ When the tool leaves proximity, a proximity_out event is sent. If any
+ button is still down, a button release event is sent before this
+ proximity event. These button events are sent in the same frame as the
+ proximity event to signal to the client that the buttons were held when
+ the tool left proximity.
+
+ If the tool moves out of the surface but stays in proximity (i.e.
+ between windows), compositor-specific grab policies apply. This usually
+ means that the proximity-out is delayed until all buttons are released.
+
+ Moving a tool physically from one tablet to the other has no real effect
+ on the protocol, since we already have the tool object from the "tool
+ added" event. All the information is already there and the proximity
+ events on both tablets are all a client needs to reconstruct what
+ happened.
+
+ Some extra axes are normalized, i.e. the client knows the range as
+ specified in the protocol (e.g. [0, 65535]), the granularity however is
+ unknown. The current normalized axes are pressure, distance, and slider.
+
+ Other extra axes are in physical units as specified in the protocol.
+ The current extra axes with physical units are tilt, rotation and
+ wheel rotation.
+
+ Since tablets work independently of the pointer controlled by the mouse,
+ the focus handling is independent too and controlled by proximity.
+ The wp_tablet_tool.set_cursor request sets a tool-specific cursor.
+ This cursor surface may be the same as the mouse cursor, and it may be
+ the same across tools but it is possible to be more fine-grained. For
+ example, a client may set different cursors for the pen and eraser.
+
+ Tools are generally independent of tablets and it is
+ compositor-specific policy when a tool can be removed. Common approaches
+ will likely include some form of removing a tool when all tablets the
+ tool was used on are removed.
+
+ Warning! The protocol described in this file is experimental and
+ backward incompatible changes may be made. Backward compatible changes
+ may be added together with the corresponding interface version bump.
+ Backward incompatible changes are done by bumping the version number in
+ the protocol and interface names and resetting the interface version.
+ Once the protocol is to be declared stable, the 'z' prefix and the
+ version number in the protocol and interface names are removed and the
+ interface version number is reset.
+ </description>
+
+ <interface name="zwp_tablet_manager_v2" version="1">
+ <description summary="controller object for graphic tablet devices">
+ An object that provides access to the graphics tablets available on this
+ system. All tablets are associated with a seat, to get access to the
+ actual tablets, use wp_tablet_manager.get_tablet_seat.
+ </description>
+
+ <request name="get_tablet_seat">
+ <description summary="get the tablet seat">
+ Get the wp_tablet_seat object for the given seat. This object
+ provides access to all graphics tablets in this seat.
+ </description>
+ <arg name="tablet_seat" type="new_id" interface="zwp_tablet_seat_v2"/>
+ <arg name="seat" type="object" interface="wl_seat" summary="The wl_seat object to retrieve the tablets for" />
+ </request>
+
+ <request name="destroy" type="destructor">
+ <description summary="release the memory for the tablet manager object">
+ Destroy the wp_tablet_manager object. Objects created from this
+ object are unaffected and should be destroyed separately.
+ </description>
+ </request>
+ </interface>
+
+ <interface name="zwp_tablet_seat_v2" version="1">
+ <description summary="controller object for graphic tablet devices of a seat">
+ An object that provides access to the graphics tablets available on this
+ seat. After binding to this interface, the compositor sends a set of
+ wp_tablet_seat.tablet_added and wp_tablet_seat.tool_added events.
+ </description>
+
+ <request name="destroy" type="destructor">
+ <description summary="release the memory for the tablet seat object">
+ Destroy the wp_tablet_seat object. Objects created from this
+ object are unaffected and should be destroyed separately.
+ </description>
+ </request>
+
+ <event name="tablet_added">
+ <description summary="new device notification">
+ This event is sent whenever a new tablet becomes available on this
+ seat. This event only provides the object id of the tablet, any
+ static information about the tablet (device name, vid/pid, etc.) is
+ sent through the wp_tablet interface.
+ </description>
+ <arg name="id" type="new_id" interface="zwp_tablet_v2" summary="the newly added graphics tablet"/>
+ </event>
+
+ <event name="tool_added">
+ <description summary="a new tool has been used with a tablet">
+ This event is sent whenever a tool that has not previously been used
+ with a tablet comes into use. This event only provides the object id
+ of the tool; any static information about the tool (capabilities,
+ type, etc.) is sent through the wp_tablet_tool interface.
+ </description>
+ <arg name="id" type="new_id" interface="zwp_tablet_tool_v2" summary="the newly added tablet tool"/>
+ </event>
+
+ <event name="pad_added">
+ <description summary="new pad notification">
+ This event is sent whenever a new pad is known to the system. Typically,
+ pads are physically attached to tablets and a pad_added event is
+ sent immediately after the wp_tablet_seat.tablet_added.
+ However, some standalone pad devices logically attach to tablets at
+ runtime, and the client must wait for wp_tablet_pad.enter to know
+ the tablet a pad is attached to.
+
+ This event only provides the object id of the pad. All further
+ features (buttons, strips, rings) are sent through the wp_tablet_pad
+ interface.
+ </description>
+ <arg name="id" type="new_id" interface="zwp_tablet_pad_v2" summary="the newly added pad"/>
+ </event>
+ </interface>
+
+ <interface name="zwp_tablet_tool_v2" version="1">
+ <description summary="a physical tablet tool">
+ An object that represents a physical tool that has been, or is
+ currently in use with a tablet in this seat. Each wp_tablet_tool
+ object stays valid until the client destroys it; the compositor
+ reuses the wp_tablet_tool object to indicate that the object's
+ respective physical tool has come into proximity of a tablet again.
+
+ A wp_tablet_tool object's relation to a physical tool depends on the
+ tablet's ability to report serial numbers. If the tablet supports
+ this capability, then the object represents a specific physical tool
+ and can be identified even when used on multiple tablets.
+
+ A tablet tool has a number of static characteristics, e.g. tool type,
+ hardware_serial and capabilities. These capabilities are sent in an
+ event sequence after the wp_tablet_seat.tool_added event before any
+ actual events from this tool. This initial event sequence is
+ terminated by a wp_tablet_tool.done event.
+
+ Tablet tool events are grouped by wp_tablet_tool.frame events.
+ Any events received before a wp_tablet_tool.frame event should be
+ considered part of the same hardware state change.
+ </description>
+
+ <request name="set_cursor">
+ <description summary="set the tablet tool's surface">
+ Sets the surface of the cursor used for this tool on the given
+ tablet. This request only takes effect if the tool is in proximity
+ of one of the requesting client's surfaces or the surface parameter
+ is the current pointer surface. If there was a previous surface set
+ with this request it is replaced. If surface is NULL, the cursor
+ image is hidden.
+
+ The parameters hotspot_x and hotspot_y define the position of the
+ pointer surface relative to the pointer location. Its top-left corner
+ is always at (x, y) - (hotspot_x, hotspot_y), where (x, y) are the
+ coordinates of the pointer location, in surface-local coordinates.
+
+ On surface.attach requests to the pointer surface, hotspot_x and
+ hotspot_y are decremented by the x and y parameters passed to the
+ request. Attach must be confirmed by wl_surface.commit as usual.
+
+ The hotspot can also be updated by passing the currently set pointer
+ surface to this request with new values for hotspot_x and hotspot_y.
+
+ The current and pending input regions of the wl_surface are cleared,
+ and wl_surface.set_input_region is ignored until the wl_surface is no
+ longer used as the cursor. When the use as a cursor ends, the current
+ and pending input regions become undefined, and the wl_surface is
+ unmapped.
+
+ This request gives the surface the role of a wp_tablet_tool cursor. A
+ surface may only ever be used as the cursor surface for one
+ wp_tablet_tool. If the surface already has another role or has
+ previously been used as cursor surface for a different tool, a
+ protocol error is raised.
+ </description>
+ <arg name="serial" type="uint" summary="serial of the enter event"/>
+ <arg name="surface" type="object" interface="wl_surface" allow-null="true"/>
+ <arg name="hotspot_x" type="int" summary="surface-local x coordinate"/>
+ <arg name="hotspot_y" type="int" summary="surface-local y coordinate"/>
+ </request>
+
+ <request name="destroy" type="destructor">
+ <description summary="destroy the tool object">
+ This destroys the client's resource for this tool object.
+ </description>
+ </request>
+
+ <enum name="type">
+ <description summary="a physical tool type">
+ Describes the physical type of a tool. The physical type of a tool
+ generally defines its base usage.
+
+ The mouse tool represents a mouse-shaped tool that is not a relative
+ device but bound to the tablet's surface, providing absolute
+ coordinates.
+
+ The lens tool is a mouse-shaped tool with an attached lens to
+ provide precision focus.
+ </description>
+ <entry name="pen" value="0x140" summary="Pen"/>
+ <entry name="eraser" value="0x141" summary="Eraser"/>
+ <entry name="brush" value="0x142" summary="Brush"/>
+ <entry name="pencil" value="0x143" summary="Pencil"/>
+ <entry name="airbrush" value="0x144" summary="Airbrush"/>
+ <entry name="finger" value="0x145" summary="Finger"/>
+ <entry name="mouse" value="0x146" summary="Mouse"/>
+ <entry name="lens" value="0x147" summary="Lens"/>
+ </enum>
+
+ <event name="type">
+ <description summary="tool type">
+ The tool type is the high-level type of the tool and usually decides
+ the interaction expected from this tool.
+
+ This event is sent in the initial burst of events before the
+ wp_tablet_tool.done event.
+ </description>
+ <arg name="tool_type" type="uint" enum="type" summary="the physical tool type"/>
+ </event>
+
+ <event name="hardware_serial">
+ <description summary="unique hardware serial number of the tool">
+ If the physical tool can be identified by a unique 64-bit serial
+ number, this event notifies the client of this serial number.
+
+ If multiple tablets are available in the same seat and the tool is
+ uniquely identifiable by the serial number,
(Patch may be truncated, please check the link at the top of this post.)