Przeglądaj źródła

Added fallback support for animated cursors

Sam Lantinga 5 dni temu
rodzic
commit
bd86e85249
3 zmienionych plików z 123 dodań i 14 usunięć
  1. 2 0
      src/events/SDL_events.c
  2. 103 13
      src/events/SDL_mouse.c
  3. 18 1
      src/events/SDL_mouse_c.h

+ 2 - 0
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.

+ 103 - 13
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 {

+ 18 - 1
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);