From 6a510d6174e853f47e0dfee1d7a258aaa777968e Mon Sep 17 00:00:00 2001
From: Frank Praznik <[EMAIL REDACTED]>
Date: Wed, 29 Oct 2025 12:34:55 -0400
Subject: [PATCH] wayland: Set tablet cursors separately from pointer cursors
Some compositors don't implicitly use the pointer cursor when the tablet cursor is not set, and the presence of a tablet doesn't necessarily guarantee pointer capability. Set the cursor for tablet tools independently of pointer cursors.
This required refactoring of cursor state handling, as well as some tablet related structures.
---
src/video/wayland/SDL_waylandevents.c | 210 ++++++-----
src/video/wayland/SDL_waylandevents_c.h | 78 +++-
src/video/wayland/SDL_waylandmouse.c | 465 +++++++++++++-----------
src/video/wayland/SDL_waylandmouse.h | 8 +-
4 files changed, 425 insertions(+), 336 deletions(-)
diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c
index 46c8877d8c78b..b69116a07e603 100644
--- a/src/video/wayland/SDL_waylandevents.c
+++ b/src/video/wayland/SDL_waylandevents.c
@@ -373,8 +373,15 @@ void Wayland_DisplayInitPointerGestureManager(SDL_VideoData *display)
static void Wayland_SeatCreateCursorShape(SDL_WaylandSeat *seat)
{
if (seat->display->cursor_shape_manager) {
- if (seat->pointer.wl_pointer && !seat->pointer.cursor_shape) {
- seat->pointer.cursor_shape = wp_cursor_shape_manager_v1_get_pointer(seat->display->cursor_shape_manager, seat->pointer.wl_pointer);
+ if (seat->pointer.wl_pointer && !seat->pointer.cursor_state.cursor_shape) {
+ seat->pointer.cursor_state.cursor_shape = wp_cursor_shape_manager_v1_get_pointer(seat->display->cursor_shape_manager, seat->pointer.wl_pointer);
+ }
+
+ SDL_WaylandPenTool *tool;
+ wl_list_for_each(tool, &seat->tablet.tool_list, link) {
+ if (!tool->cursor_state.cursor_shape) {
+ tool->cursor_state.cursor_shape = wp_cursor_shape_manager_v1_get_tablet_tool_v2(seat->display->cursor_shape_manager, tool->wltool);
+ }
}
}
}
@@ -770,7 +777,7 @@ static void pointer_dispatch_absolute_motion(SDL_WaylandSeat *seat)
if (rc != window_data->hit_test_result) {
window_data->hit_test_result = rc;
- Wayland_SeatUpdateCursor(seat);
+ Wayland_SeatUpdatePointerCursor(seat);
}
}
}
@@ -852,7 +859,7 @@ static void pointer_handle_enter(void *data, struct wl_pointer *pointer,
* window that already has focus, as the focus change sequence
* won't be run.
*/
- Wayland_SeatUpdateCursor(seat);
+ Wayland_SeatUpdatePointerCursor(seat);
}
}
@@ -892,7 +899,7 @@ static void pointer_handle_leave(void *data, struct wl_pointer *pointer,
}
Wayland_SeatUpdatePointerGrab(seat);
- Wayland_SeatUpdateCursor(seat);
+ Wayland_SeatUpdatePointerCursor(seat);
}
static bool Wayland_ProcessHitTest(SDL_WaylandSeat *seat, Uint32 serial)
@@ -1269,7 +1276,7 @@ static void pointer_handle_frame(void *data, struct wl_pointer *pointer)
* window that already has focus, as the focus change sequence
* won't be run.
*/
- Wayland_SeatUpdateCursor(seat);
+ Wayland_SeatUpdatePointerCursor(seat);
}
}
@@ -2354,7 +2361,7 @@ static const struct wl_keyboard_listener keyboard_listener = {
static void Wayland_SeatDestroyPointer(SDL_WaylandSeat *seat, bool send_event)
{
- Wayland_SeatDestroyCursorFrameCallback(seat);
+ Wayland_CursorStateRelease(&seat->pointer.cursor_state);
// End any active gestures.
if (seat->pointer.gesture_focus) {
@@ -2388,18 +2395,6 @@ static void Wayland_SeatDestroyPointer(SDL_WaylandSeat *seat, bool send_event)
zwp_pointer_gesture_pinch_v1_destroy(seat->pointer.gesture_pinch);
}
- if (seat->pointer.cursor_state.surface) {
- wl_surface_destroy(seat->pointer.cursor_state.surface);
- }
-
- if (seat->pointer.cursor_state.viewport) {
- wp_viewport_destroy(seat->pointer.cursor_state.viewport);
- }
-
- if (seat->pointer.cursor_shape) {
- wp_cursor_shape_device_v1_destroy(seat->pointer.cursor_shape);
- }
-
if (seat->pointer.wl_pointer) {
if (wl_pointer_get_version(seat->pointer.wl_pointer) >= WL_POINTER_RELEASE_SINCE_VERSION) {
wl_pointer_release(seat->pointer.wl_pointer);
@@ -3274,23 +3269,6 @@ void Wayland_DisplayCreateTextInputManager(SDL_VideoData *d, uint32_t id)
}
// Pen/Tablet support...
-
-typedef struct SDL_WaylandPenTool // a stylus, etc, on a tablet.
-{
- SDL_PenID instance_id;
- SDL_PenInfo info;
- SDL_Window *tool_focus;
- struct zwp_tablet_tool_v2 *wltool;
- float x;
- float y;
- bool frame_motion_set;
- float frame_axes[SDL_PEN_AXIS_COUNT];
- Uint32 frame_axes_set;
- int frame_pen_down;
- int frame_buttons[3];
- struct wl_list link;
-} SDL_WaylandPenTool;
-
static void tablet_tool_handle_type(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t type)
{
SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
@@ -3342,7 +3320,9 @@ static void tablet_tool_handle_removed(void *data, struct zwp_tablet_tool_v2 *to
if (sdltool->instance_id) {
SDL_RemovePenDevice(0, sdltool->instance_id);
}
- zwp_tablet_tool_v2_destroy(tool);
+
+ Wayland_CursorStateRelease(&sdltool->cursor_state);
+ zwp_tablet_tool_v2_destroy(sdltool->wltool);
WAYLAND_wl_list_remove(&sdltool->link);
SDL_free(sdltool);
}
@@ -3351,79 +3331,70 @@ static void tablet_tool_handle_proximity_in(void *data, struct zwp_tablet_tool_v
{
SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
SDL_WindowData *windowdata = surface ? Wayland_GetWindowDataForOwnedSurface(surface) : NULL;
- sdltool->tool_focus = windowdata ? windowdata->sdlwindow : NULL;
+ sdltool->focus = windowdata;
+ sdltool->proximity_serial = serial;
+ sdltool->frame.have_proximity_in = true;
- SDL_assert(sdltool->instance_id == 0); // shouldn't be added at this point.
- if (sdltool->info.subtype != SDL_PEN_TYPE_UNKNOWN) { // don't tell SDL about it if we don't know its role.
- sdltool->instance_id = SDL_AddPenDevice(0, NULL, &sdltool->info, sdltool);
- }
-
- // According to the docs, this should be followed by a motion event, where we'll send our SDL events.
+ // According to the docs, this should be followed by a frame event, where we'll send our SDL events.
}
static void tablet_tool_handle_proximity_out(void *data, struct zwp_tablet_tool_v2 *tool)
{
SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
- sdltool->tool_focus = NULL;
-
- if (sdltool->instance_id) {
- SDL_RemovePenDevice(0, sdltool->instance_id);
- sdltool->instance_id = 0;
- }
+ sdltool->frame.have_proximity_out = true;
}
static void tablet_tool_handle_down(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t serial)
{
SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
- sdltool->frame_pen_down = 1;
+ sdltool->frame.tool_state = WAYLAND_TABLET_TOOL_STATE_DOWN;
}
static void tablet_tool_handle_up(void *data, struct zwp_tablet_tool_v2 *tool)
{
SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
- sdltool->frame_pen_down = 0;
+ sdltool->frame.tool_state = WAYLAND_TABLET_TOOL_STATE_UP;
}
static void tablet_tool_handle_motion(void *data, struct zwp_tablet_tool_v2 *tool, wl_fixed_t sx_w, wl_fixed_t sy_w)
{
SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
- SDL_Window *window = sdltool->tool_focus;
- if (window) {
- const SDL_WindowData *windowdata = window->internal;
- sdltool->x = (float)(wl_fixed_to_double(sx_w) * windowdata->pointer_scale.x);
- sdltool->y = (float)(wl_fixed_to_double(sy_w) * windowdata->pointer_scale.y);
- sdltool->frame_motion_set = true;
+ SDL_WindowData *windowdata = sdltool->focus;
+ if (windowdata) {
+ sdltool->frame.x = (float)(wl_fixed_to_double(sx_w) * windowdata->pointer_scale.x);
+ sdltool->frame.y = (float)(wl_fixed_to_double(sy_w) * windowdata->pointer_scale.y);
+ sdltool->frame.have_motion = true;
}
}
static void tablet_tool_handle_pressure(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t pressure)
{
SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
- sdltool->frame_axes[SDL_PEN_AXIS_PRESSURE] = ((float) pressure) / 65535.0f;
- sdltool->frame_axes_set |= (1u << SDL_PEN_AXIS_PRESSURE);
+ sdltool->frame.axes[SDL_PEN_AXIS_PRESSURE] = ((float) pressure) / 65535.0f;
+ sdltool->frame.axes_set |= (1u << SDL_PEN_AXIS_PRESSURE);
if (pressure) {
- sdltool->frame_axes[SDL_PEN_AXIS_DISTANCE] = 0.0f;
- sdltool->frame_axes_set |= (1u << SDL_PEN_AXIS_DISTANCE);
+ sdltool->frame.axes[SDL_PEN_AXIS_DISTANCE] = 0.0f;
+ sdltool->frame.axes_set |= (1u << SDL_PEN_AXIS_DISTANCE);
}
}
static void tablet_tool_handle_distance(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t distance)
{
SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
- sdltool->frame_axes[SDL_PEN_AXIS_DISTANCE] = ((float) distance) / 65535.0f;
- sdltool->frame_axes_set |= (1u << SDL_PEN_AXIS_DISTANCE);
+ sdltool->frame.axes[SDL_PEN_AXIS_DISTANCE] = ((float) distance) / 65535.0f;
+ sdltool->frame.axes_set |= (1u << SDL_PEN_AXIS_DISTANCE);
if (distance) {
- sdltool->frame_axes[SDL_PEN_AXIS_PRESSURE] = 0.0f;
- sdltool->frame_axes_set |= (1u << SDL_PEN_AXIS_PRESSURE);
+ sdltool->frame.axes[SDL_PEN_AXIS_PRESSURE] = 0.0f;
+ sdltool->frame.axes_set |= (1u << SDL_PEN_AXIS_PRESSURE);
}
}
static void tablet_tool_handle_tilt(void *data, struct zwp_tablet_tool_v2 *tool, wl_fixed_t xtilt, wl_fixed_t ytilt)
{
SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
- sdltool->frame_axes[SDL_PEN_AXIS_XTILT] = (float)(wl_fixed_to_double(xtilt));
- sdltool->frame_axes[SDL_PEN_AXIS_YTILT] = (float)(wl_fixed_to_double(ytilt));
- sdltool->frame_axes_set |= (1u << SDL_PEN_AXIS_XTILT) | (1u << SDL_PEN_AXIS_YTILT);
+ sdltool->frame.axes[SDL_PEN_AXIS_XTILT] = (float)(wl_fixed_to_double(xtilt));
+ sdltool->frame.axes[SDL_PEN_AXIS_YTILT] = (float)(wl_fixed_to_double(ytilt));
+ sdltool->frame.axes_set |= (1u << SDL_PEN_AXIS_XTILT) | (1u << SDL_PEN_AXIS_YTILT);
}
static void tablet_tool_handle_button(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t serial, uint32_t button, uint32_t state)
@@ -3446,23 +3417,23 @@ static void tablet_tool_handle_button(void *data, struct zwp_tablet_tool_v2 *too
return; // don't care about this button, I guess.
}
- SDL_assert((sdlbutton >= 1) && (sdlbutton <= SDL_arraysize(sdltool->frame_buttons)));
- sdltool->frame_buttons[sdlbutton-1] = (state == ZWP_TABLET_PAD_V2_BUTTON_STATE_PRESSED) ? 1 : 0;
+ SDL_assert((sdlbutton >= 1) && (sdlbutton <= SDL_arraysize(sdltool->frame.buttons)));
+ sdltool->frame.buttons[sdlbutton-1] = (state == ZWP_TABLET_PAD_V2_BUTTON_STATE_PRESSED) ? 1 : 0;
}
static void tablet_tool_handle_rotation(void *data, struct zwp_tablet_tool_v2 *tool, wl_fixed_t degrees)
{
SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
const float rotation = (float)(wl_fixed_to_double(degrees));
- sdltool->frame_axes[SDL_PEN_AXIS_ROTATION] = (rotation > 180.0f) ? (rotation - 360.0f) : rotation; // map to -180.0f ... 179.0f range
- sdltool->frame_axes_set |= (1u << SDL_PEN_AXIS_ROTATION);
+ sdltool->frame.axes[SDL_PEN_AXIS_ROTATION] = (rotation > 180.0f) ? (rotation - 360.0f) : rotation; // map to -180.0f ... 179.0f range
+ sdltool->frame.axes_set |= (1u << SDL_PEN_AXIS_ROTATION);
}
static void tablet_tool_handle_slider(void *data, struct zwp_tablet_tool_v2 *tool, int32_t position)
{
SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
- sdltool->frame_axes[SDL_PEN_AXIS_SLIDER] = position / 65535.f;
- sdltool->frame_axes_set |= (1u << SDL_PEN_AXIS_SLIDER);
+ sdltool->frame.axes[SDL_PEN_AXIS_SLIDER] = position / 65535.f;
+ sdltool->frame.axes_set |= (1u << SDL_PEN_AXIS_SLIDER);
}
static void tablet_tool_handle_wheel(void *data, struct zwp_tablet_tool_v2 *tool, int32_t degrees, int32_t clicks)
@@ -3474,51 +3445,66 @@ static void tablet_tool_handle_frame(void *data, struct zwp_tablet_tool_v2 *tool
{
SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
- if (!sdltool->instance_id) {
- return; // Not a pen we report on.
+ const Uint64 timestamp = Wayland_AdjustEventTimestampBase(Wayland_EventTimestampMSToNS(time));
+ SDL_Window *window = sdltool->focus ? sdltool->focus->sdlwindow : NULL;
+
+ if (sdltool->frame.have_proximity_in) {
+ SDL_assert(sdltool->instance_id == 0); // shouldn't be added at this point.
+ if (sdltool->info.subtype != SDL_PEN_TYPE_UNKNOWN) { // don't tell SDL about it if we don't know its role.
+ sdltool->instance_id = SDL_AddPenDevice(timestamp, NULL, &sdltool->info, sdltool);
+ Wayland_TabletToolUpdateCursor(sdltool);
+ }
}
- const Uint64 timestamp = Wayland_AdjustEventTimestampBase(Wayland_EventTimestampMSToNS(time));
const SDL_PenID instance_id = sdltool->instance_id;
- SDL_Window *window = sdltool->tool_focus;
+
+ if (!instance_id) {
+ return; // Not a pen we report on.
+ }
+
+ // !!! FIXME: Should hit testing be done if pens generate pointer motion?
// I don't know if this is necessary (or makes sense), but send motion before pen downs, but after pen ups, so you don't get unexpected lines drawn.
- if (sdltool->frame_motion_set && (sdltool->frame_pen_down != -1)) {
- if (sdltool->frame_pen_down) {
- SDL_SendPenMotion(timestamp, instance_id, window, sdltool->x, sdltool->y);
+ if (sdltool->frame.have_motion && sdltool->frame.tool_state) {
+ if (sdltool->frame.tool_state == WAYLAND_TABLET_TOOL_STATE_UP) {
+ SDL_SendPenMotion(timestamp, instance_id, window, sdltool->frame.x, sdltool->frame.y);
SDL_SendPenTouch(timestamp, instance_id, window, false, true); // !!! FIXME: how do we know what tip is in use?
} else {
SDL_SendPenTouch(timestamp, instance_id, window, false, false); // !!! FIXME: how do we know what tip is in use?
- SDL_SendPenMotion(timestamp, instance_id, window, sdltool->x, sdltool->y);
+ SDL_SendPenMotion(timestamp, instance_id, window, sdltool->frame.x, sdltool->frame.y);
}
} else {
- if (sdltool->frame_pen_down != -1) {
- SDL_SendPenTouch(timestamp, instance_id, window, false, (sdltool->frame_pen_down != 0)); // !!! FIXME: how do we know what tip is in use?
+ if (sdltool->frame.tool_state) {
+ SDL_SendPenTouch(timestamp, instance_id, window, false, sdltool->frame.tool_state == WAYLAND_TABLET_TOOL_STATE_DOWN); // !!! FIXME: how do we know what tip is in use?
}
- if (sdltool->frame_motion_set) {
- SDL_SendPenMotion(timestamp, instance_id, window, sdltool->x, sdltool->y);
+ if (sdltool->frame.have_motion) {
+ SDL_SendPenMotion(timestamp, instance_id, window, sdltool->frame.x, sdltool->frame.y);
}
}
for (SDL_PenAxis i = 0; i < SDL_PEN_AXIS_COUNT; i++) {
- if (sdltool->frame_axes_set & (1u << i)) {
- SDL_SendPenAxis(timestamp, instance_id, window, i, sdltool->frame_axes[i]);
+ if (sdltool->frame.axes_set & (1u << i)) {
+ SDL_SendPenAxis(timestamp, instance_id, window, i, sdltool->frame.axes[i]);
}
}
- for (int i = 0; i < SDL_arraysize(sdltool->frame_buttons); i++) {
- const int state = sdltool->frame_buttons[i];
- if (state != -1) {
- SDL_SendPenButton(timestamp, instance_id, window, (Uint8)(i + 1), (state != 0));
- sdltool->frame_buttons[i] = -1;
+ for (int i = 0; i < SDL_arraysize(sdltool->frame.buttons); i++) {
+ const int state = sdltool->frame.buttons[i];
+ if (state) {
+ SDL_SendPenButton(timestamp, instance_id, window, (Uint8)(i + 1), state == WAYLAND_TABLET_TOOL_BUTTON_DOWN);
}
}
- // reset for next frame.
- sdltool->frame_pen_down = -1;
- sdltool->frame_motion_set = false;
- sdltool->frame_axes_set = 0;
+ if (sdltool->frame.have_proximity_out) {
+ sdltool->focus = NULL;
+ Wayland_TabletToolUpdateCursor(sdltool);
+ SDL_RemovePenDevice(timestamp, sdltool->instance_id);
+ sdltool->instance_id = 0;
+ }
+
+ // Reset for the next frame.
+ SDL_zero(sdltool->frame);
}
static const struct zwp_tablet_tool_v2_listener tablet_tool_listener = {
@@ -3558,10 +3544,11 @@ static void tablet_seat_handle_tool_added(void *data, struct zwp_tablet_seat_v2
sdltool->wltool = tool;
sdltool->info.max_tilt = -1.0f;
sdltool->info.num_buttons = -1;
- sdltool->frame_pen_down = -1;
- for (int i = 0; i < SDL_arraysize(sdltool->frame_buttons); i++) {
- sdltool->frame_buttons[i] = -1;
+
+ if (seat->display->cursor_shape_manager) {
+ sdltool->cursor_state.cursor_shape = wp_cursor_shape_manager_v1_get_tablet_tool_v2(seat->display->cursor_shape_manager, tool);
}
+
WAYLAND_wl_list_insert(&seat->tablet.tool_list, &sdltool->link);
// this will send a bunch of zwp_tablet_tool_v2 events right up front to tell
@@ -3598,6 +3585,8 @@ void Wayland_DisplayInitTabletManager(SDL_VideoData *display)
static void Wayland_remove_all_pens_callback(SDL_PenID instance_id, void *handle, void *userdata)
{
SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) handle;
+
+ Wayland_CursorStateRelease(&sdltool->cursor_state);
zwp_tablet_tool_v2_destroy(sdltool->wltool);
SDL_free(sdltool);
}
@@ -3605,10 +3594,10 @@ static void Wayland_remove_all_pens_callback(SDL_PenID instance_id, void *handle
static void Wayland_SeatDestroyTablet(SDL_WaylandSeat *seat, bool send_events)
{
if (send_events) {
- SDL_WaylandPenTool *pen, *temp;
- wl_list_for_each_safe (pen, temp, &seat->tablet.tool_list, link) {
+ SDL_WaylandPenTool *tool, *temp;
+ wl_list_for_each_safe (tool, temp, &seat->tablet.tool_list, link) {
// Remove all tools for this seat, sending PROXIMITY_OUT events.
- tablet_tool_handle_removed(pen, pen->wltool);
+ tablet_tool_handle_removed(tool, tool->wltool);
}
} else {
// Shutting down, just delete everything.
@@ -3675,8 +3664,13 @@ void Wayland_DisplayRemoveWindowReferencesFromSeats(SDL_VideoData *display, SDL_
SDL_WaylandPenTool *tool;
wl_list_for_each (tool, &seat->tablet.tool_list, link) {
- if (tool->tool_focus == window->sdlwindow) {
- tablet_tool_handle_proximity_out(tool, tool->wltool);
+ if (tool->focus == window) {
+ tool->focus = NULL;
+ Wayland_TabletToolUpdateCursor(tool);
+ if (tool->instance_id) {
+ SDL_RemovePenDevice(0, tool->instance_id);
+ tool->instance_id = 0;
+ }
}
}
}
@@ -3811,7 +3805,7 @@ void Wayland_SeatUpdatePointerGrab(SDL_WaylandSeat *seat)
seat->pointer.locked_pointer = NULL;
// Update the cursor after destroying a relative move lock.
- Wayland_SeatUpdateCursor(seat);
+ Wayland_SeatUpdatePointerCursor(seat);
}
if (seat->pointer.wl_pointer) {
@@ -3831,7 +3825,7 @@ void Wayland_SeatUpdatePointerGrab(SDL_WaylandSeat *seat)
zwp_locked_pointer_v1_add_listener(seat->pointer.locked_pointer, &locked_pointer_listener, seat);
// Ensure that the relative pointer is hidden, if required.
- Wayland_SeatUpdateCursor(seat);
+ Wayland_SeatUpdatePointerCursor(seat);
}
// Locked the cursor for relative mode, nothing more to do.
diff --git a/src/video/wayland/SDL_waylandevents_c.h b/src/video/wayland/SDL_waylandevents_c.h
index a178d129f4dff..57d58368f22ff 100644
--- a/src/video/wayland/SDL_waylandevents_c.h
+++ b/src/video/wayland/SDL_waylandevents_c.h
@@ -56,6 +56,66 @@ typedef struct
char text[8];
} SDL_WaylandKeyboardRepeat;
+typedef struct SDL_WaylandCursorState
+{
+ SDL_CursorData *current_cursor;
+ struct wp_cursor_shape_device_v1 *cursor_shape;
+ struct wl_surface *surface;
+ struct wp_viewport *viewport;
+
+ double scale;
+
+ // Pointer to the internal data for system cursors.
+ void *system_cursor_handle;
+
+ // The cursor animation thread lock must be held when modifying this.
+ struct wl_callback *frame_callback;
+
+ Uint64 last_frame_callback_time_ms;
+ Uint32 current_frame_time_ms;
+ int current_frame;
+ SDL_HitTestResult hit_test_result;
+} SDL_WaylandCursorState;
+
+typedef struct SDL_WaylandPenTool // a stylus, etc, on a tablet.
+{
+ SDL_PenID instance_id;
+ SDL_PenInfo info;
+ SDL_WindowData *focus;
+ struct zwp_tablet_tool_v2 *wltool;
+ Uint32 proximity_serial;
+
+ struct
+ {
+ float x;
+ float y;
+
+ float axes[SDL_PEN_AXIS_COUNT];
+ Uint32 axes_set;
+
+ enum
+ {
+ WAYLAND_TABLET_TOOL_BUTTON_NONE = 0,
+ WAYLAND_TABLET_TOOL_BUTTON_DOWN,
+ WAYLAND_TABLET_TOOL_BUTTON_UP
+ } buttons[3];
+
+ enum
+ {
+ WAYLAND_TABLET_TOOL_STATE_NONE = 0,
+ WAYLAND_TABLET_TOOL_STATE_DOWN,
+ WAYLAND_TABLET_TOOL_STATE_UP
+ } tool_state;
+
+ bool have_motion;
+ bool have_proximity_in;
+ bool have_proximity_out;
+ } frame;
+
+ SDL_WaylandCursorState cursor_state;
+ struct wl_list link;
+} SDL_WaylandPenTool;
+
typedef struct SDL_WaylandSeat
{
SDL_VideoData *display;
@@ -120,7 +180,6 @@ typedef struct SDL_WaylandSeat
struct wl_pointer *wl_pointer;
struct zwp_relative_pointer_v1 *relative_pointer;
struct zwp_input_timestamps_v1 *timestamps;
- struct wp_cursor_shape_device_v1 *cursor_shape;
struct zwp_locked_pointer_v1 *locked_pointer;
struct zwp_confined_pointer_v1 *confined_pointer;
struct zwp_pointer_gesture_pinch_v1 *gesture_pinch;
@@ -176,22 +235,7 @@ typedef struct SDL_WaylandSeat
Uint64 timestamp_ns;
} pending_frame;
- // Cursor state
- struct
- {
- struct wl_surface *surface;
- struct wp_viewport *viewport;
-
- // Animation state for cursors
- void *cursor_handle;
-
- // The cursor animation thread lock must be held when modifying this.
- struct wl_callback *frame_callback;
-
- Uint64 last_frame_callback_time_ms;
- Uint32 current_frame_time_ms;
- int current_frame;
- } cursor_state;
+ SDL_WaylandCursorState cursor_state;
} pointer;
struct
diff --git a/src/video/wayland/SDL_waylandmouse.c b/src/video/wayland/SDL_waylandmouse.c
index bae09f9ff6035..348e92295cc6e 100644
--- a/src/video/wayland/SDL_waylandmouse.c
+++ b/src/video/wayland/SDL_waylandmouse.c
@@ -42,6 +42,7 @@
#include "pointer-constraints-unstable-v1-client-protocol.h"
#include "viewporter-client-protocol.h"
#include "pointer-warp-v1-client-protocol.h"
+#include "tablet-v2-client-protocol.h"
#include "../../SDL_hints_c.h"
@@ -296,48 +297,40 @@ static void Wayland_DBusFinishCursorProperties(void)
#endif
-static CustomCursorImage *Wayland_GetScaledCustomCursorImage(SDL_CursorData *data, int frame_index, double scale)
+static struct wl_buffer *Wayland_CursorStateGetFrame(SDL_WaylandCursorState *state, int frame_index)
{
- const int offset = data->cursor_data.custom.images_per_frame * frame_index;
-
- /* Find the closest image. Images that are larger than the
- * desired size are preferred over images that are smaller.
- */
- CustomCursorImage *closest = NULL;
- int desired_w = (int)SDL_round(data->cursor_data.custom.width * scale);
- int desired_h = (int)SDL_round(data->cursor_data.custom.height * scale);
- int desired_size = desired_w * desired_h;
- int closest_distance = -1;
- int closest_size = -1;
- for (int i = 0; i < data->cursor_data.custom.images_per_frame && closest_distance && data->cursor_data.custom.images[offset + i].buffer; ++i) {
- CustomCursorImage *candidate = &data->cursor_data.custom.images[offset + i];
- int size = candidate->width * candidate->height;
- int delta_w = candidate->width - desired_w;
- int delta_h = candidate->height - desired_h;
- int distance = (delta_w * delta_w) + (delta_h * delta_h);
- if (closest_distance < 0 || distance < closest_distance ||
- (size > desired_size && closest_size < desired_size)) {
- closest = candidate;
- closest_distance = distance;
- closest_size = size;
- }
- }
-
- return closest;
-}
-
-static struct wl_buffer *Wayland_SeatGetCursorFrame(SDL_WaylandSeat *seat, int frame_index)
-{
- SDL_CursorData *data = seat->pointer.current_cursor;
+ SDL_CursorData *data = state->current_cursor;
if (data) {
if (!data->is_system_cursor) {
- const double scale = seat->pointer.focus ? seat->pointer.focus->scale_factor : 1.0;
- CustomCursorImage *image = Wayland_GetScaledCustomCursorImage(data, frame_index, scale);
+ const int offset = data->cursor_data.custom.images_per_frame * frame_index;
+
+ /* Find the closest image. Images that are larger than the
+ * desired size are preferred over images that are smaller.
+ */
+ CustomCursorImage *closest = NULL;
+ int desired_w = (int)SDL_round(data->cursor_data.custom.width * state->scale);
+ int desired_h = (int)SDL_round(data->cursor_data.custom.height * state->scale);
+ int desired_size = desired_w * desired_h;
+ int closest_distance = -1;
+ int closest_size = -1;
+ for (int i = 0; i < data->cursor_data.custom.images_per_frame && closest_distance && data->cursor_data.custom.images[offset + i].buffer; ++i) {
+ CustomCursorImage *candidate = &data->cursor_data.custom.images[offset + i];
+ int size = candidate->width * candidate->height;
+ int delta_w = candidate->width - desired_w;
+ int delta_h = candidate->height - desired_h;
+ int distance = (delta_w * delta_w) + (delta_h * delta_h);
+ if (closest_distance < 0 || distance < closest_distance ||
+ (size > desired_size && closest_size < desired_size)) {
+ closest = candidate;
+ closest_distance = distance;
+ closest_size = size;
+ }
+ }
- return image ? image->buffer : NULL;
+ return closest ? closest->buffer : NULL;
} else {
- return ((Wayland_CachedSystemCursor *)(seat->pointer.cursor_state.cursor_handle))->buffers[frame_index];
+ return ((Wayland_CachedSystemCursor *)(state->system_cursor_handle))->buffers[frame_index];
}
}
@@ -509,23 +502,23 @@ static const struct wl_callback_listener cursor_frame_listener = {
static void cursor_frame_done(void *data, struct wl_callback *cb, uint32_t time)
{
- SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data;
- if (!seat->pointer.current_cursor) {
+ SDL_WaylandCursorState *state = (SDL_WaylandCursorState *)data;
+ if (!state->current_cursor) {
return;
}
- Uint32 *frames = seat->pointer.current_cursor->frame_durations_ms;
- SDL_CursorData *c = seat->pointer.current_cursor;
+ Uint32 *frames = state->current_cursor->frame_durations_ms;
+ SDL_CursorData *c = state->current_cursor;
const Uint64 now = SDL_GetTicks();
- const Uint32 elapsed = (now - seat->pointer.cursor_state.last_frame_callback_time_ms) % c->total_duration_ms;
+ const Uint32 elapsed = (now - state->last_frame_callback_time_ms) % c->total_duration_ms;
Uint32 advance = 0;
- int next = seat->pointer.cursor_state.current_frame;
+ int next = state->current_frame;
- seat->pointer.cursor_state.current_frame_time_ms += elapsed;
+ state->current_frame_time_ms += elapsed;
// Calculate the next frame based on the elapsed duration.
- for (Uint32 t = frames[next]; t <= seat->pointer.cursor_state.current_frame_time_ms; t += frames[next]) {
+ for (Uint32 t = frames[next]; t <= state->current_frame_time_ms; t += frames[next]) {
next = (next + 1) % c->num_frames;
advance = t;
@@ -536,52 +529,52 @@ static void cursor_frame_done(void *data, struct wl_callback *cb, uint32_t time)
}
wl_callback_destroy(cb);
- seat->pointer.cursor_state.frame_callback = NULL;
+ state->frame_callback = NULL;
// Don't queue another callback if this frame time is infinite.
if (frames[next]) {
- seat->pointer.cursor_state.frame_callback = wl_surface_frame(seat->pointer.cursor_state.surface);
- wl_callback_add_listener(seat->pointer.cursor_state.frame_callback, &cursor_frame_listener, data);
+ state->frame_callback = wl_surface_frame(state->surface);
+ wl_callback_add_listener(state->frame_callback, &cursor_frame_listener, data);
}
- seat->pointer.cursor_state.current_frame_time_ms -= advance;
- seat->pointer.cursor_state.last_frame_callback_time_ms = now;
- seat->pointer.cursor_state.current_frame = next;
+ state->current_frame_time_ms -= advance;
+ state->last_frame_callback_time_ms = now;
+ state->current_frame = next;
- struct wl_buffer *buffer = Wayland_SeatGetCursorFrame(seat, next);
- wl_surface_attach(seat->pointer.cursor_state.surface, buffer, 0, 0);
+ struct wl_buffer *buffer = Wayland_CursorStateGetFrame(state, next);
+ wl_surface_attach(state->surface, buffer, 0, 0);
- if (wl_surface_get_version(seat->pointer.cursor_state.surface) >= WL_SURFACE_DAMAGE_BUFFER_SINCE_VERSION) {
- wl_surface_damage_buffer(seat->pointer.cursor_state.surface, 0, 0, SDL_MAX_SINT32, SDL_MAX_SINT32);
+ if (wl_surface_get_version(state->surface) >= WL_SURFACE_DAMAGE_BUFFER_SINCE_VERSION) {
+ wl_surface_damage_buffer(state->surface, 0, 0, SDL_MAX_SINT32, SDL_MAX_SINT32);
} else {
- wl_surface_damage(seat->pointer.cursor_state.surface, 0, 0, SDL_MAX_SINT32, SDL_MAX_SINT32);
+ wl_surface_damage(state->surface, 0, 0, SDL_MAX_SINT32, SDL_MAX_SINT32);
}
- wl_surface_commit(seat->pointer.cursor_state.surface);
+ wl_surface_commit(state->surface);
}
-void Wayland_SeatSetCursorFrameCallback(SDL_WaylandSeat *seat)
+void Wayland_CursorStateSetFrameCallback(SDL_WaylandCursorState *state, void *userdata)
{
if (cursor_thread_context.lock) {
SDL_LockMutex(cursor_thread_context.lock);
}
- seat->pointer.cursor_state.frame_callback = wl_surface_frame(seat->pointer.cursor_state.surface);
- wl_callback_add_listener(seat->pointer.cursor_state.frame_callback, &cursor_frame_listener, seat);
+ state->frame_callback = wl_surface_frame(state->surface);
+ wl_callback_add_listener(state->frame_callback, &cursor_frame_listener, userdata);
(Patch may be truncated, please check the link at the top of this post.)