[PATCH] Support for animated cursors

Trent Waddington trent.waddington at gmail.com
Mon Aug 27 01:57:29 CDT 2007


Test program at:

    http://rtfm.insomnia.org/~qg/animatedcursor.exe

Probably doesn't support all the possible animated cursors supported
under windows, and correct timing of animation hasn't been attempted.

Trent
-------------- next part --------------
From 760d2556e1f3ce4123b500ffc4f6d141cfcbca03 Mon Sep 17 00:00:00 2001
From: Trent Waddington <trentw at linux.localdomain>
Date: Mon, 27 Aug 2007 16:49:48 +1000
Subject: Support for animated cursors.

---
 dlls/user32/cursoricon.c |  160 +++++++++++++++++++++++++++++++++++++++++++++-
 include/wine/winuser16.h |    1 +
 2 files changed, 159 insertions(+), 2 deletions(-)

diff --git a/dlls/user32/cursoricon.c b/dlls/user32/cursoricon.c
index d528bee..6c54a50 100644
--- a/dlls/user32/cursoricon.c
+++ b/dlls/user32/cursoricon.c
@@ -664,6 +664,106 @@ static CURSORICONFILEDIRENTRY *CURSORICON_FindBestIconFile( CURSORICONFILEDIR *d
     return &dir->idEntries[n];
 }
 
+// functions for decoding animated cursors
+// see: http://www.oreilly.com/www/centers/gff/formats/micriff/
+
+typedef DWORD FOURCC;        // Four-character code
+typedef FOURCC CKID;         // Four-character-code chunk identifier
+typedef DWORD CKSIZE;        // 32-bit unsigned size value
+
+typedef struct {             // Chunk structure
+    CKID     ckID;           // Chunk type identifier
+    CKSIZE   ckSize;         // Chunk size
+    BYTE     ckData[1];      // Chunk data
+} CK;
+
+typedef struct
+{
+    DWORD HeaderSize;   /* Size of the subchunk data in bytes */
+    DWORD NumFrames;    /* Number of icon or cursor frames */
+    DWORD NumSteps;     /* Number of steps in the animation */
+    DWORD Width;        /* Width of frame in pixels */
+    DWORD Height;       /* Height of frame in pixels */
+    DWORD BitCount;     /* Number of bits in the frame pixels */
+    DWORD NumPlanes;    /* Number of color planes in the frame data */
+    DWORD DisplayRate;  /* Default frame display rate */
+    DWORD Flags;        /* File attributes flags */
+} ANIHEADER;
+
+ANIHEADER *anih = NULL;
+LPBYTE *ani_frames = NULL;
+DWORD ani_frame_idx = 0;
+
+static void decodeRIFF(LPBYTE bytes, DWORD size)
+{
+    CK *chunk = (CK*)bytes;
+    CKSIZE sizeWithPad = chunk->ckSize % 2 ? chunk->ckSize + 1 : chunk->ckSize;
+    switch(chunk->ckID) {
+        case 0x46464952:  // RIFF
+            {
+                //DWORD form = *(DWORD*)chunk->ckData;
+                decodeRIFF(chunk->ckData + 4, chunk->ckSize - 4);
+            }
+            break;
+        case 0x5453494c:  // LIST
+            {
+                //DWORD type = *(DWORD*)chunk->ckData;
+                decodeRIFF(chunk->ckData + 4, chunk->ckSize - 4);
+            }
+            break;
+        case 0x68696e61: // anih
+            {
+                anih = (ANIHEADER*)chunk->ckData;
+                ani_frames = (LPBYTE *)HeapAlloc( GetProcessHeap(), 0, sizeof(LPBYTE) * anih->NumFrames );
+            }
+            break;
+        case 0x6e6f6369: // icon
+            {               
+                if (anih && ani_frame_idx < anih->NumFrames)
+                    ani_frames[ani_frame_idx++] = chunk->ckData;
+            }
+            break;
+    }
+    if (sizeWithPad + 8 < size)
+        decodeRIFF(bytes + 8 + sizeWithPad, size - 8 - sizeWithPad);
+}
+
+// frames and nFrames are out parameters, it is the caller's responsibility to 
+// free *frames when done.  Do not use *frames after freeing data.
+void decodeAnimatedCursor(LPBYTE data, DWORD size, LPBYTE **frames, DWORD *nFrames)
+{
+    anih = NULL;
+    ani_frames = NULL;
+    ani_frame_idx = 0;
+
+    decodeRIFF(data, size);
+
+    *nFrames = 0;
+    if (anih) {
+        *frames = ani_frames;
+        *nFrames = anih->NumFrames;
+    }
+}
+
+BOOL isAnimatedCursor(LPBYTE bits)
+{
+    return bits[0] == 'R' && bits[1] == 'I' && bits[2] == 'F' && bits[3] == 'F';
+}
+
+BOOL animate_timer_started = FALSE;
+
+static void CALLBACK animate_cursor(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
+{
+    HCURSOR hCursor = GetCursor();
+
+    if (hCursor) {
+        CURSORICONINFO *info = (CURSORICONINFO*)GlobalLock16( HCURSOR_16(hCursor) );
+        if (info && info->hNext) {
+            SetCursor(HCURSOR_32(info->hNext));
+        }
+    }
+}
+
 /**********************************************************************
  *		CreateIconFromResourceEx (USER32.@)
  *
@@ -699,7 +799,57 @@ HICON WINAPI CreateIconFromResourceEx( LPBYTE bits, UINT cbSize,
 
     if (bIcon)
         bmi = (BITMAPINFO *)bits;
-    else /* get the hotspot */
+    else if (isAnimatedCursor(bits))
+    {
+        HICON hFirstIcon = 0;
+        LPBYTE *frames = NULL;
+        DWORD nFrames;
+        decodeAnimatedCursor(bits, cbSize, &frames, &nFrames);
+
+        if (frames) {
+            DWORD i;
+            HICON hCurIcon = 0;
+
+            for (i = 0; i < nFrames; i++) {
+                CURSORICONFILEDIR *dir;
+                CURSORICONFILEDIRENTRY *entry;
+                LPBYTE bits = frames[i];
+
+                dir = (CURSORICONFILEDIR*) bits;
+                entry = CURSORICON_FindBestCursorFile( dir, width, height, 1 );
+                if (entry) {
+                    HICON hNextIcon = CreateIconFromResourceEx( &bits[entry->dwDIBOffset], entry->dwDIBSize, TRUE, 0x00030000, 0, 0, 0 );
+
+                    if (hNextIcon == NULL) {
+                        // umm, eep
+                        continue;
+                    } else {
+                        if (hCurIcon) {
+                            CURSORICONINFO *info = (CURSORICONINFO *)GlobalLock16( HICON_16(hCurIcon) );
+                            info->hNext = HICON_16(hNextIcon);
+                            GlobalUnlock16( HICON_16(hCurIcon) );
+                        }
+                        hCurIcon = hNextIcon;
+                    }
+                    
+                    if (i == 0)
+                        hFirstIcon = hNextIcon;
+                }
+            }
+
+            if (hCurIcon) {
+                CURSORICONINFO *info = (CURSORICONINFO *)GlobalLock16( HICON_16(hCurIcon) );
+                info->hNext = HICON_16(hFirstIcon);
+                GlobalUnlock16( HICON_16(hCurIcon) );
+            }
+
+            // and cleanup
+            HeapFree( GetProcessHeap(), 0, frames );
+        }
+
+        return hFirstIcon;
+    }
+    else /* get the hotspot */ 
     {
         POINT16 *pt = (POINT16 *)bits;
         hotspot = *pt;
@@ -1464,7 +1614,13 @@ HCURSOR WINAPI SetCursor( HCURSOR hCursor /* [in] Handle of cursor to show */ )
     /* Change the cursor shape only if it is visible */
     if (thread_info->cursor_count >= 0)
     {
-        USER_Driver->pSetCursor( (CURSORICONINFO*)GlobalLock16(HCURSOR_16(hCursor)) );
+        CURSORICONINFO *info = (CURSORICONINFO*)GlobalLock16(HCURSOR_16(hCursor));
+        if (info->hNext && !animate_timer_started) {
+            animate_timer_started = TRUE;
+            SetTimer(NULL, 0, 55, animate_cursor);
+        }
+
+        USER_Driver->pSetCursor( info );
         GlobalUnlock16(HCURSOR_16(hCursor));
     }
     return hOldCursor;
diff --git a/include/wine/winuser16.h b/include/wine/winuser16.h
index 0a05bdd..db7cc32 100644
--- a/include/wine/winuser16.h
+++ b/include/wine/winuser16.h
@@ -156,6 +156,7 @@ typedef struct tagCURSORICONINFO
     WORD    nWidthBytes;
     BYTE    bPlanes;
     BYTE    bBitsPerPixel;
+    HICON16 hNext;
 } CURSORICONINFO;
 
 typedef struct {
-- 
1.4.4.3


More information about the wine-patches mailing list