From 760d2556e1f3ce4123b500ffc4f6d141cfcbca03 Mon Sep 17 00:00:00 2001 From: Trent Waddington 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