wayland: Don't clear the cursor on leave events

Stop the frame callback and flag the cursor for a refresh when the pointer re-enters the surface, but don't set a null cursor, as it may have already been set after entering a surface that is part of the window decorations, resulting in an unwanted invisible cursor.

(cherry picked from commit 5e2977709b)
This commit is contained in:
Frank Praznik
2026-01-15 22:51:29 -05:00
parent 66a8454f2d
commit 799d2ca80b
2 changed files with 40 additions and 22 deletions

View File

@@ -73,7 +73,10 @@ typedef struct SDL_WaylandCursorState
Uint64 last_frame_callback_time_ms;
Uint32 current_frame_time_ms;
// 0 or greater if a buffer is attached, -1 if in the reset state.
int current_frame;
SDL_HitTestResult hit_test_result;
} SDL_WaylandCursorState;

View File

@@ -900,7 +900,8 @@ static void Wayland_FreeCursorData(SDL_CursorData *d)
if (seat->pointer.current_cursor == d) {
Wayland_CursorStateDestroyFrameCallback(&seat->pointer.cursor_state);
if (seat->pointer.cursor_state.surface) {
// Custom cursor buffers are about to be destroyed, so ensure they are detached.
if (!d->is_system_cursor && seat->pointer.cursor_state.surface) {
wl_surface_attach(seat->pointer.cursor_state.surface, NULL, 0, 0);
}
@@ -1040,7 +1041,7 @@ static void Wayland_CursorStateSetCursor(SDL_WaylandCursorState *state, const Wa
}
if (cursor) {
if (cursor_data == state->current_cursor) {
if (cursor_data == state->current_cursor && state->current_frame >= 0) {
// Restart the animation sequence if the cursor didn't change.
if (cursor_data->num_frames > 1) {
Wayland_CursorStateResetAnimation(state, true);
@@ -1075,6 +1076,7 @@ static void Wayland_CursorStateSetCursor(SDL_WaylandCursorState *state, const Wa
const enum wp_cursor_shape_device_v1_shape shape = Wayland_GetSystemCursorShape(cursor_data->cursor_data.system.id);
wp_cursor_shape_device_v1_set_shape(state->cursor_shape, serial, shape);
state->current_cursor = cursor_data;
state->current_frame = 0;
return;
}
@@ -1107,6 +1109,7 @@ static void Wayland_CursorStateSetCursor(SDL_WaylandCursorState *state, const Wa
struct wl_buffer *buffer = Wayland_CursorStateGetFrame(state, 0);
wl_surface_attach(state->surface, buffer, 0, 0);
state->current_frame = 0;
if (state->scale != 1.0) {
if (!state->viewport) {
@@ -1156,6 +1159,13 @@ static void Wayland_CursorStateSetCursor(SDL_WaylandCursorState *state, const Wa
}
}
static void Wayland_CursorStateResetCursor(SDL_WaylandCursorState *state)
{
// Stop the frame callback and set the reset status.
Wayland_CursorStateDestroyFrameCallback(state);
state->current_frame = -1;
}
static bool Wayland_ShowCursor(SDL_Cursor *cursor)
{
SDL_VideoDevice *vd = SDL_GetVideoDevice();
@@ -1170,7 +1180,7 @@ static bool Wayland_ShowCursor(SDL_Cursor *cursor)
if (mouse->focus && mouse->focus->internal == seat->pointer.focus) {
Wayland_CursorStateSetCursor(&seat->pointer.cursor_state, &obj, seat->pointer.focus, seat->pointer.enter_serial, cursor);
} else if (!seat->pointer.focus) {
Wayland_CursorStateSetCursor(&seat->pointer.cursor_state, &obj, seat->pointer.focus, seat->pointer.enter_serial, NULL);
Wayland_CursorStateResetCursor(&seat->pointer.cursor_state);
}
SDL_WaylandPenTool *tool;
@@ -1184,7 +1194,7 @@ static bool Wayland_ShowCursor(SDL_Cursor *cursor)
if (tool->focus && (!mouse->focus || mouse->focus->internal == tool->focus)) {
Wayland_CursorStateSetCursor(&tool->cursor_state, &obj, tool->focus, tool->proximity_serial, mouse->cur_cursor);
} else if (!tool->focus) {
Wayland_CursorStateSetCursor(&tool->cursor_state, &obj, tool->focus, tool->proximity_serial, NULL);
Wayland_CursorStateResetCursor(&tool->cursor_state);
}
}
}
@@ -1506,24 +1516,25 @@ void Wayland_SeatUpdatePointerCursor(SDL_WaylandSeat *seat)
.is_pointer = true
};
if (pointer_focus && mouse->cursor_visible) {
if (!seat->pointer.relative_pointer || !mouse->relative_mode_hide_cursor) {
const SDL_HitTestResult rc = pointer_focus->hit_test_result;
if (pointer_focus) {
if (mouse->cursor_visible) {
if (!seat->pointer.relative_pointer || !mouse->relative_mode_hide_cursor) {
const SDL_HitTestResult rc = pointer_focus->hit_test_result;
if (seat->pointer.relative_pointer || rc == SDL_HITTEST_NORMAL || rc == SDL_HITTEST_DRAGGABLE) {
Wayland_CursorStateSetCursor(&seat->pointer.cursor_state, &obj, pointer_focus, seat->pointer.enter_serial, mouse->cur_cursor);
if (seat->pointer.relative_pointer || rc == SDL_HITTEST_NORMAL || rc == SDL_HITTEST_DRAGGABLE) {
Wayland_CursorStateSetCursor(&seat->pointer.cursor_state, &obj, pointer_focus, seat->pointer.enter_serial, mouse->cur_cursor);
} else {
Wayland_CursorStateSetCursor(&seat->pointer.cursor_state, &obj, pointer_focus, seat->pointer.enter_serial, sys_cursors[rc]);
}
} else {
Wayland_CursorStateSetCursor(&seat->pointer.cursor_state, &obj, pointer_focus, seat->pointer.enter_serial, sys_cursors[rc]);
// Hide the cursor in relative mode, unless requested otherwise by the hint.
Wayland_CursorStateSetCursor(&seat->pointer.cursor_state, &obj, pointer_focus, seat->pointer.enter_serial, NULL);
}
} else {
// Hide the cursor in relative mode, unless requested otherwise by the hint.
Wayland_CursorStateSetCursor(&seat->pointer.cursor_state, &obj, pointer_focus, seat->pointer.enter_serial, NULL);
}
} else {
/* The spec states "The cursor actually changes only if the input device focus is one of the
* requesting client's surfaces", so just clear the cursor if the seat has no pointer focus.
*/
Wayland_CursorStateSetCursor(&seat->pointer.cursor_state, &obj, pointer_focus, seat->pointer.enter_serial, NULL);
Wayland_CursorStateResetCursor(&seat->pointer.cursor_state);
}
}
@@ -1536,18 +1547,22 @@ void Wayland_TabletToolUpdateCursor(SDL_WaylandPenTool *tool)
.is_pointer = false
};
if (tool_focus && mouse->cursor_visible) {
// Relative mode is only relevant if the tool sends pointer events.
const bool relative = mouse->pen_mouse_events && (tool_focus->sdlwindow->flags & SDL_WINDOW_MOUSE_RELATIVE_MODE);
if (tool_focus) {
if (mouse->cursor_visible) {
// Relative mode is only relevant if the tool sends pointer events.
const bool relative = mouse->pen_mouse_events && (tool_focus->sdlwindow->flags & SDL_WINDOW_MOUSE_RELATIVE_MODE);
if (!relative || !mouse->relative_mode_hide_cursor) {
Wayland_CursorStateSetCursor(&tool->cursor_state, &obj, tool_focus, tool->proximity_serial, mouse->cur_cursor);
if (!relative || !mouse->relative_mode_hide_cursor) {
Wayland_CursorStateSetCursor(&tool->cursor_state, &obj, tool_focus, tool->proximity_serial, mouse->cur_cursor);
} else {
// Hide the cursor in relative mode, unless requested otherwise by the hint.
Wayland_CursorStateSetCursor(&tool->cursor_state, &obj, tool_focus, tool->proximity_serial, NULL);
}
} else {
// Hide the cursor in relative mode, unless requested otherwise by the hint.
Wayland_CursorStateSetCursor(&tool->cursor_state, &obj, tool_focus, tool->proximity_serial, NULL);
}
} else {
Wayland_CursorStateSetCursor(&tool->cursor_state, &obj, tool_focus, tool->proximity_serial, NULL);
Wayland_CursorStateResetCursor(&tool->cursor_state);
}
}