[PATCH] Support for animated cursors

Trent Waddington trent.waddington at gmail.com
Tue Aug 28 06:05:41 CDT 2007


I've fixed the style issues indicated.

I'm now using Xcursor's animated cursor support.

The delay information in the ANI is honored.

The changes to struct tagCURSORICONINFO are removed and, instead, I'm
appending data after the bitmap info.. so Paint Brush can continue
manipulating those bits directly.

Trent
-------------- next part --------------
Style fixes.
Support for animated cursors using Xcursor.
---
 dlls/user32/cursoricon.c |  142 +++++++++++++++++++++------------------------
 dlls/winex11.drv/mouse.c |   82 ++++++++++++++++++++++-----
 include/wine/winuser16.h |    4 +-
 3 files changed, 136 insertions(+), 92 deletions(-)

diff --git a/dlls/user32/cursoricon.c b/dlls/user32/cursoricon.c
index 6c54a50..5ea63fb 100644
--- a/dlls/user32/cursoricon.c
+++ b/dlls/user32/cursoricon.c
@@ -664,17 +664,20 @@ 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/
+/* 
+ * Functions for decoding animated cursors.
+ * Currently doesn't support well ANIs with SEQUENCE or RATE chunks.
+ * 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 DWORD FOURCC;
+typedef FOURCC CKID;
+typedef DWORD CKSIZE;
 
-typedef struct {             // Chunk structure
-    CKID     ckID;           // Chunk type identifier
-    CKSIZE   ckSize;         // Chunk size
-    BYTE     ckData[1];      // Chunk data
+typedef struct {
+    CKID     ckID;
+    CKSIZE   ckSize;
+    BYTE     ckData[1];
 } CK;
 
 typedef struct
@@ -690,47 +693,39 @@ typedef struct
     DWORD Flags;        /* File attributes flags */
 } ANIHEADER;
 
-ANIHEADER *anih = NULL;
-LPBYTE *ani_frames = NULL;
-DWORD ani_frame_idx = 0;
+static ANIHEADER *anih;
+static LPBYTE *ani_frames;
+static DWORD ani_frame_idx;
 
 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;
+    switch(chunk->ckID) 
+    {
+    case 0x46464952:  /* RIFF */
+        decodeRIFF(chunk->ckData + 4, chunk->ckSize - 4);
+        break;
+    case 0x5453494c:  /* LIST */
+        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)
+/* frames and nFrames are out parameters, it is the caller's responsibility to 
+ * free *frames when done.  Do not use *frames after freeing data.
+ */
+static void decodeAnimatedCursor(LPBYTE data, DWORD size, LPBYTE **frames, DWORD *nFrames, DWORD *delay)
 {
     anih = NULL;
     ani_frames = NULL;
@@ -739,31 +734,19 @@ void decodeAnimatedCursor(LPBYTE data, DWORD size, LPBYTE **frames, DWORD *nFram
     decodeRIFF(data, size);
 
     *nFrames = 0;
-    if (anih) {
+    if (anih) 
+    {
         *frames = ani_frames;
         *nFrames = anih->NumFrames;
+        *delay = anih->DisplayRate * 1000 / 60;
     }
 }
 
-BOOL isAnimatedCursor(LPBYTE bits)
+static 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.@)
  *
@@ -804,29 +787,37 @@ HICON WINAPI CreateIconFromResourceEx( LPBYTE bits, UINT cbSize,
         HICON hFirstIcon = 0;
         LPBYTE *frames = NULL;
         DWORD nFrames;
-        decodeAnimatedCursor(bits, cbSize, &frames, &nFrames);
+        DWORD delay;
+        decodeAnimatedCursor(bits, cbSize, &frames, &nFrames, &delay);
 
-        if (frames) {
+        if (frames) 
+        {
             DWORD i;
             HICON hCurIcon = 0;
 
-            for (i = 0; i < nFrames; i++) {
+            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) {
+                if (entry) 
+                {
                     HICON hNextIcon = CreateIconFromResourceEx( &bits[entry->dwDIBOffset], entry->dwDIBSize, TRUE, 0x00030000, 0, 0, 0 );
 
-                    if (hNextIcon == NULL) {
-                        // umm, eep
+                    if (hNextIcon == NULL) 
+                    {
                         continue;
-                    } else {
-                        if (hCurIcon) {
+                    } 
+                    else 
+                    {
+                        if (hCurIcon) 
+                        {
                             CURSORICONINFO *info = (CURSORICONINFO *)GlobalLock16( HICON_16(hCurIcon) );
-                            info->hNext = HICON_16(hNextIcon);
+                            CURSORICONINFO_NEXT(info) = HICON_16(hNextIcon);
+                            CURSORICONINFO_DELAY(info) = delay;
                             GlobalUnlock16( HICON_16(hCurIcon) );
                         }
                         hCurIcon = hNextIcon;
@@ -837,13 +828,7 @@ HICON WINAPI CreateIconFromResourceEx( LPBYTE bits, UINT cbSize,
                 }
             }
 
-            if (hCurIcon) {
-                CURSORICONINFO *info = (CURSORICONINFO *)GlobalLock16( HICON_16(hCurIcon) );
-                info->hNext = HICON_16(hFirstIcon);
-                GlobalUnlock16( HICON_16(hCurIcon) );
-            }
-
-            // and cleanup
+            /* and cleanup */
             HeapFree( GetProcessHeap(), 0, frames );
         }
 
@@ -998,7 +983,8 @@ HICON WINAPI CreateIconFromResourceEx( LPBYTE bits, UINT cbSize,
     sizeAnd = bmpAnd.bmHeight * bmpAnd.bmWidthBytes;
 
     hObj = GlobalAlloc16( GMEM_MOVEABLE,
-                     sizeof(CURSORICONINFO) + sizeXor + sizeAnd );
+                     sizeof(CURSORICONINFO) + sizeXor + sizeAnd + 
+                     sizeof(HICON16) + sizeof(DWORD) );
     if (hObj)
     {
         CURSORICONINFO *info;
@@ -1013,9 +999,13 @@ HICON WINAPI CreateIconFromResourceEx( LPBYTE bits, UINT cbSize,
         info->bBitsPerPixel = bmpXor.bmBitsPixel;
 
         /* Transfer the bitmap bits to the CURSORICONINFO structure */
-
         GetBitmapBits( hAndBits, sizeAnd, (char *)(info + 1) );
         GetBitmapBits( hXorBits, sizeXor, (char *)(info + 1) + sizeAnd );
+
+        /* Set animated sequence info to none */
+        CURSORICONINFO_NEXT(info) = 0;
+        CURSORICONINFO_DELAY(info) = 0;
+
         GlobalUnlock16( hObj );
     }
 
@@ -1615,10 +1605,10 @@ HCURSOR WINAPI SetCursor( HCURSOR hCursor /* [in] Handle of cursor to show */ )
     if (thread_info->cursor_count >= 0)
     {
         CURSORICONINFO *info = (CURSORICONINFO*)GlobalLock16(HCURSOR_16(hCursor));
-        if (info->hNext && !animate_timer_started) {
+        /*if (CURSORICONINFO_NEXT(info) && !animate_timer_started) {
             animate_timer_started = TRUE;
             SetTimer(NULL, 0, 55, animate_cursor);
-        }
+        }*/
 
         USER_Driver->pSetCursor( info );
         GlobalUnlock16(HCURSOR_16(hCursor));
diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c
index 86dad89..a9991e2 100644
--- a/dlls/winex11.drv/mouse.c
+++ b/dlls/winex11.drv/mouse.c
@@ -32,6 +32,9 @@ static void *xcursor_handle;
 MAKE_FUNCPTR(XcursorImageCreate);
 MAKE_FUNCPTR(XcursorImageDestroy);
 MAKE_FUNCPTR(XcursorImageLoadCursor);
+MAKE_FUNCPTR(XcursorImagesCreate);
+MAKE_FUNCPTR(XcursorImagesDestroy);
+MAKE_FUNCPTR(XcursorImagesLoadCursor);
 # undef MAKE_FUNCPTR
 #endif /* SONAME_LIBXCURSOR */
 
@@ -109,6 +112,9 @@ void X11DRV_Xcursor_Init(void)
     LOAD_FUNCPTR(XcursorImageCreate);
     LOAD_FUNCPTR(XcursorImageDestroy);
     LOAD_FUNCPTR(XcursorImageLoadCursor);
+    LOAD_FUNCPTR(XcursorImagesCreate);
+    LOAD_FUNCPTR(XcursorImagesDestroy);
+    LOAD_FUNCPTR(XcursorImagesLoadCursor);
 #undef LOAD_FUNCPTR
 #endif /* SONAME_LIBXCURSOR */
 }
@@ -503,7 +509,6 @@ static XcursorImage *create_cursor_image( CURSORICONINFO *ptr )
     return image;
 }
 
-
 /***********************************************************************
  *              create_xcursor_cursor
  *
@@ -513,6 +518,9 @@ static Cursor create_xcursor_cursor( Display *display, CURSORICONINFO *ptr )
 {
     Cursor cursor;
     XcursorImage *image;
+    XcursorImages *images;
+    int nframes;
+    HICON16 hNext, hNextNext;
 
     if (!ptr) /* Create an empty cursor */
     {
@@ -526,23 +534,67 @@ static Cursor create_xcursor_cursor( Display *display, CURSORICONINFO *ptr )
         return cursor;
     }
 
-    image = create_cursor_image( ptr );
-    if (!image) return 0;
+    nframes = 0;
+    hNext = 0;
+    do {
+        if (hNext == 0)
+            hNext = CURSORICONINFO_NEXT(ptr);
+        else 
+        {
+            CURSORICONINFO *p = GlobalLock16(hNext);
+            if (p)
+            {
+                hNextNext = CURSORICONINFO_NEXT(p);
+                GlobalUnlock16(hNext);
+                hNext = hNextNext;
+            }
+            else
+                hNext = 0;
+        }
+    
+        nframes++;
+    } while (hNext);
 
-    /* Make sure hotspot is valid */
-    image->xhot = ptr->ptHotSpot.x;
-    image->yhot = ptr->ptHotSpot.y;
-    if (image->xhot < 0 || image->xhot >= image->width ||
-        image->yhot < 0 || image->yhot >= image->height)
-    {
-        image->xhot = image->width / 2;
-        image->yhot = image->height / 2;
-    }
+    images = pXcursorImagesCreate( nframes ); 
+
+    hNext = 0;
+    do {
+        CURSORICONINFO *info = ptr;
+        if (hNext)
+        {
+            info = GlobalLock16(hNext);
+            if (!info)
+                break;
+        }
+
+        image = create_cursor_image( info );
+        if (!image) 
+        {
+            pXcursorImagesDestroy( images );
+            return 0;
+        }
+
+        /* Make sure hotspot is valid */
+        image->xhot = info->ptHotSpot.x;
+        image->yhot = info->ptHotSpot.y;
+        if (image->xhot < 0 || image->xhot >= image->width ||
+            image->yhot < 0 || image->yhot >= image->height)
+        {
+            image->xhot = image->width / 2;
+            image->yhot = image->height / 2;
+        }
+
+        image->delay = CURSORICONINFO_DELAY(info);
+
+        images->images[images->nimage++] = image;
 
-    image->delay = 0;
+        hNextNext = CURSORICONINFO_NEXT(info);
+        GlobalUnlock16(hNext);
+        hNext = hNextNext;
+    } while (hNext);
 
-    cursor = pXcursorImageLoadCursor( display, image );
-    pXcursorImageDestroy( image );
+    cursor = pXcursorImagesLoadCursor( display, images );
+    pXcursorImagesDestroy( images );
 
     return cursor;
 }
diff --git a/include/wine/winuser16.h b/include/wine/winuser16.h
index db7cc32..643b67e 100644
--- a/include/wine/winuser16.h
+++ b/include/wine/winuser16.h
@@ -156,8 +156,10 @@ typedef struct tagCURSORICONINFO
     WORD    nWidthBytes;
     BYTE    bPlanes;
     BYTE    bBitsPerPixel;
-    HICON16 hNext;
 } CURSORICONINFO;
+#define CURSORICONINFO_END(x) ((BYTE*)(x) + sizeof(CURSORICONINFO) + (x)->nHeight * (x)->nWidthBytes + (x)->nHeight * (x)->nWidth / 8)
+#define CURSORICONINFO_NEXT(x) *(HICON16*)(CURSORICONINFO_END(x))
+#define CURSORICONINFO_DELAY(x) *(DWORD*)(CURSORICONINFO_END(x) + sizeof(HICON16))
 
 typedef struct {
 	BOOL16		fIcon;
-- 
1.4.4.3


More information about the wine-patches mailing list