diff --git a/src/video/wayland/SDL_waylandevents_c.h b/src/video/wayland/SDL_waylandevents_c.h index 4ca60a43e9..32abf467d2 100644 --- a/src/video/wayland/SDL_waylandevents_c.h +++ b/src/video/wayland/SDL_waylandevents_c.h @@ -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; diff --git a/src/video/wayland/SDL_waylandmouse.c b/src/video/wayland/SDL_waylandmouse.c index 18d8ccd9e2..61e3e71d80 100644 --- a/src/video/wayland/SDL_waylandmouse.c +++ b/src/video/wayland/SDL_waylandmouse.c @@ -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); } }