diff --git a/src/events/SDL_events.c b/src/events/SDL_events.c index bf7cedaf2c..7a50c66817 100644 --- a/src/events/SDL_events.c +++ b/src/events/SDL_events.c @@ -1477,6 +1477,8 @@ void SDL_PumpEventMaintenance(void) } #endif + SDL_UpdateCursorAnimation(); + SDL_UpdateTrays(); SDL_SendPendingSignalEvents(); // in case we had a signal handler fire, etc. diff --git a/src/events/SDL_mouse.c b/src/events/SDL_mouse.c index 9f6ae32819..49b86fd96f 100644 --- a/src/events/SDL_mouse.c +++ b/src/events/SDL_mouse.c @@ -1552,6 +1552,79 @@ SDL_Cursor *SDL_CreateCursor(const Uint8 *data, const Uint8 *mask, int w, int h, return cursor; } +static void SDL_DestroyCursorAnimation(SDL_CursorAnimation *animation) +{ + if (!animation) { + return; + } + + for (int i = 0; i < animation->num_frames; ++i) { + SDL_DestroyCursor(animation->frames[i]); + } + SDL_free(animation->frames); + SDL_free(animation->durations); + SDL_free(animation); +} + +static SDL_CursorAnimation *SDL_CreateCursorAnimation(SDL_CursorFrameInfo *frames, int frame_count, int hot_x, int hot_y) +{ + SDL_CursorAnimation *animation = (SDL_CursorAnimation *)SDL_calloc(1, sizeof(*animation)); + if (!animation) { + return NULL; + } + + animation->frames = (SDL_Cursor **)SDL_calloc(frame_count, sizeof(*animation->frames)); + animation->durations = (Uint32 *)SDL_calloc(frame_count, sizeof(*animation->durations)); + if (!animation->frames || !animation->durations) { + SDL_DestroyCursorAnimation(animation); + return NULL; + } + + for (int i = 0; i < frame_count; ++i) { + animation->frames[i] = SDL_CreateColorCursor(frames[i].surface, hot_x, hot_y); + if (!animation->frames[i]) { + SDL_DestroyCursorAnimation(animation); + return NULL; + } + + animation->durations[i] = frames[i].duration; + } + animation->num_frames = frame_count; + + return animation; +} + +void SDL_UpdateCursorAnimation(void) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + SDL_Cursor *cursor = mouse->cur_cursor; + + if (!cursor || !cursor->animation) { + return; + } + + if (!mouse->focus) { + return; + } + + SDL_CursorAnimation *animation = cursor->animation; + Uint32 duration = animation->durations[animation->current_frame]; + if (!duration) { + // We've reached the stop frame of the animation + return; + } + + Uint64 now = SDL_GetTicks(); + if (now < (animation->last_update + duration)) { + return; + } + + animation->current_frame = (animation->current_frame + 1) % animation->num_frames; + animation->last_update = now; + + SDL_RedrawCursor(); +} + SDL_Cursor *SDL_CreateAnimatedCursor(SDL_CursorFrameInfo *frames, int frame_count, int hot_x, int hot_y) { SDL_Mouse *mouse = SDL_GetMouse(); @@ -1572,18 +1645,6 @@ SDL_Cursor *SDL_CreateAnimatedCursor(SDL_CursorFrameInfo *frames, int frame_coun return NULL; } - // Fall back to a static cursor if the platform doesn't support animated cursors. - if (!mouse->CreateAnimatedCursor) { - // If there is a frame with infinite duration, use it; otherwise, use the first. - for (int i = 0; i < frame_count; ++i) { - if (!frames[i].duration) { - return SDL_CreateColorCursor(frames[i].surface, hot_x, hot_y); - } - } - - return SDL_CreateColorCursor(frames[0].surface, hot_x, hot_y); - } - // Allow specifying the hot spot via properties on the surface SDL_PropertiesID props = SDL_GetSurfaceProperties(frames[0].surface); hot_x = (int)SDL_GetNumberProperty(props, SDL_PROP_SURFACE_HOTSPOT_X_NUMBER, hot_x); @@ -1630,7 +1691,22 @@ SDL_Cursor *SDL_CreateAnimatedCursor(SDL_CursorFrameInfo *frames, int frame_coun temp_frames[i].duration = frames[i].duration; } - cursor = mouse->CreateAnimatedCursor(temp_frames, frame_count, hot_x, hot_y); + if (mouse->CreateAnimatedCursor) { + cursor = mouse->CreateAnimatedCursor(temp_frames, frame_count, hot_x, hot_y); + } else { + SDL_CursorAnimation *animation = SDL_CreateCursorAnimation(temp_frames, frame_count, hot_x, hot_y); + if (!animation) { + goto cleanup; + } + + cursor = (SDL_Cursor *)SDL_calloc(1, sizeof(*cursor)); + if (!cursor) { + SDL_DestroyCursorAnimation(animation); + goto cleanup; + } + cursor->animation = animation; + } + if (cursor) { cursor->next = mouse->cursors; mouse->cursors = cursor; @@ -1729,6 +1805,11 @@ void SDL_RedrawCursor(void) cursor = NULL; } + if (cursor && cursor->animation) { + SDL_CursorAnimation *animation = cursor->animation; + cursor = animation->frames[animation->current_frame]; + } + if (mouse->ShowCursor) { mouse->ShowCursor(cursor); } @@ -1761,6 +1842,11 @@ bool SDL_SetCursor(SDL_Cursor *cursor) return SDL_SetError("Cursor not associated with the current mouse"); } } + if (cursor->animation) { + SDL_CursorAnimation *animation = cursor->animation; + animation->current_frame = 0; + animation->last_update = SDL_GetTicks(); + } mouse->cur_cursor = cursor; } @@ -1814,6 +1900,10 @@ void SDL_DestroyCursor(SDL_Cursor *cursor) mouse->cursors = curr->next; } + if (curr->animation) { + SDL_DestroyCursorAnimation(curr->animation); + } + if (mouse->FreeCursor && curr->internal) { mouse->FreeCursor(curr); } else { diff --git a/src/events/SDL_mouse_c.h b/src/events/SDL_mouse_c.h index 6e049fe841..30ec51d7b6 100644 --- a/src/events/SDL_mouse_c.h +++ b/src/events/SDL_mouse_c.h @@ -31,10 +31,24 @@ typedef struct SDL_CursorData SDL_CursorData; +struct SDL_Cursor; + +typedef struct +{ + Uint64 last_update; + int num_frames; + int current_frame; + struct SDL_Cursor **frames; + Uint32 *durations; +} SDL_CursorAnimation; + struct SDL_Cursor { - struct SDL_Cursor *next; + SDL_CursorAnimation *animation; + SDL_CursorData *internal; + + struct SDL_Cursor *next; }; typedef struct @@ -184,6 +198,9 @@ extern void SDL_SetDefaultCursor(SDL_Cursor *cursor); // Get the preferred default system cursor extern SDL_SystemCursor SDL_GetDefaultSystemCursor(void); +// Update the current cursor animation if needed +extern void SDL_UpdateCursorAnimation(void); + // Set the mouse focus window extern void SDL_SetMouseFocus(SDL_Window *window);