Add support for more than one sound card.
[wine] / dlls / comctl32 / animate.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2 /*
3  * Animation control
4  *
5  * Copyright 1998, 1999 Eric Kohl
6  * Copyright 1999 Eric Pouech
7  * Copyright 2005 Dimitrie O. Paun
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  *
23  * NOTES
24  *
25  * This code was audited for completeness against the documented features
26  * of Comctl32.dll version 6.0 on Mar. 15, 2005, by Dimitrie O. Paun.
27  * 
28  * Unless otherwise noted, we believe this code to be complete, as per
29  * the specification mentioned above.
30  * If you discover missing features, or bugs, please note them below.
31  * 
32  * TODO:
33  *   - check for the 'rec ' list in some AVI files
34  */
35
36 #define COM_NO_WINDOWS_H
37 #include <stdarg.h>
38 #include <string.h>
39 #include "windef.h"
40 #include "winbase.h"
41 #include "wingdi.h"
42 #include "winuser.h"
43 #include "winnls.h"
44 #include "commctrl.h"
45 #include "vfw.h"
46 #include "mmsystem.h"
47 #include "comctl32.h"
48 #include "wine/debug.h"
49
50 WINE_DEFAULT_DEBUG_CHANNEL(animate);
51
52 static struct {
53     HMODULE     hModule;
54     HIC         (WINAPI *fnICOpen)(DWORD, DWORD, UINT);
55     LRESULT     (WINAPI *fnICClose)(HIC);
56     LRESULT     (WINAPI *fnICSendMessage)(HIC, UINT, DWORD, DWORD);
57     DWORD       (WINAPIV *fnICDecompress)(HIC,DWORD,LPBITMAPINFOHEADER,LPVOID,LPBITMAPINFOHEADER,LPVOID);
58 } fnIC;
59
60 typedef struct
61 {
62    /* reference to input stream (file or resource) */
63    HGLOBAL              hRes;
64    HMMIO                hMMio;  /* handle to mmio stream */
65    HWND                 hwndSelf;
66    HWND                 hwndNotify;
67    DWORD                dwStyle;
68    /* information on the loaded AVI file */
69    MainAVIHeader        mah;
70    AVIStreamHeader      ash;
71    LPBITMAPINFOHEADER   inbih;
72    LPDWORD              lpIndex;
73    /* data for the decompressor */
74    HIC                  hic;
75    LPBITMAPINFOHEADER   outbih;
76    LPVOID               indata;
77    LPVOID               outdata;
78    /* data for the background mechanism */
79    CRITICAL_SECTION     cs;
80    HANDLE               hStopEvent;
81    HANDLE               hThread;
82    DWORD                threadId;
83    UINT                 uTimer;
84    /* data for playing the file */
85    int                  nFromFrame;
86    int                  nToFrame;
87    int                  nLoop;
88    int                  currFrame;
89    /* tranparency info*/
90    COLORREF             transparentColor;
91    HBRUSH               hbrushBG;
92    HBITMAP              hbmPrevFrame;
93 } ANIMATE_INFO;
94
95 #define ANIMATE_COLOR_NONE      0xffffffff
96
97 static void ANIMATE_Notify(ANIMATE_INFO *infoPtr, UINT notif)
98 {
99     SendMessageW(infoPtr->hwndNotify, WM_COMMAND,
100                  MAKEWPARAM(GetDlgCtrlID(infoPtr->hwndSelf), notif),
101                  (LPARAM)infoPtr->hwndSelf);
102 }
103
104 static BOOL ANIMATE_LoadResW(ANIMATE_INFO *infoPtr, HINSTANCE hInst, LPWSTR lpName)
105 {
106     static const WCHAR aviW[] = { 'A', 'V', 'I', 0 };
107     HRSRC       hrsrc;
108     MMIOINFO    mminfo;
109     LPVOID      lpAvi;
110
111     hrsrc = FindResourceW(hInst, lpName, aviW);
112     if (!hrsrc)
113         return FALSE;
114
115     infoPtr->hRes = LoadResource(hInst, hrsrc);
116     if (!infoPtr->hRes)
117         return FALSE;
118
119     lpAvi = LockResource(infoPtr->hRes);
120     if (!lpAvi)
121         return FALSE;
122
123     memset(&mminfo, 0, sizeof(mminfo));
124     mminfo.fccIOProc = FOURCC_MEM;
125     mminfo.pchBuffer = (LPSTR)lpAvi;
126     mminfo.cchBuffer = SizeofResource(hInst, hrsrc);
127     infoPtr->hMMio = mmioOpenW(NULL, &mminfo, MMIO_READ);
128     if (!infoPtr->hMMio) 
129     {
130         FreeResource(infoPtr->hRes);
131         return FALSE;
132     }
133
134     return TRUE;
135 }
136
137
138 static BOOL ANIMATE_LoadFileW(ANIMATE_INFO *infoPtr, LPWSTR lpName)
139 {
140     infoPtr->hMMio = mmioOpenW(lpName, 0, MMIO_ALLOCBUF | MMIO_READ | MMIO_DENYWRITE);
141
142     return (BOOL)infoPtr->hMMio;
143 }
144
145
146 static BOOL ANIMATE_DoStop(ANIMATE_INFO *infoPtr)
147 {
148     EnterCriticalSection(&infoPtr->cs);
149
150     /* should stop playing */
151     if (infoPtr->hThread)
152     {
153         HANDLE handle = infoPtr->hThread;
154
155         TRACE("stopping animation thread\n");
156         infoPtr->hThread = 0;
157         SetEvent( infoPtr->hStopEvent );
158
159         if (infoPtr->threadId != GetCurrentThreadId())
160         {
161             LeaveCriticalSection(&infoPtr->cs);  /* leave it a chance to run */
162             WaitForSingleObject( handle, INFINITE );
163             TRACE("animation thread stopped\n");
164             EnterCriticalSection(&infoPtr->cs);
165         }
166
167         CloseHandle( handle );
168         CloseHandle( infoPtr->hStopEvent );
169         infoPtr->hStopEvent = 0;
170     }
171     if (infoPtr->uTimer) {
172         KillTimer(infoPtr->hwndSelf, infoPtr->uTimer);
173         infoPtr->uTimer = 0;
174     }
175
176     LeaveCriticalSection(&infoPtr->cs);
177
178     ANIMATE_Notify(infoPtr, ACN_STOP);
179
180     return TRUE;
181 }
182
183
184 static void ANIMATE_Free(ANIMATE_INFO *infoPtr)
185 {
186     if (infoPtr->hMMio) {
187         ANIMATE_DoStop(infoPtr);
188         mmioClose(infoPtr->hMMio, 0);
189         if (infoPtr->hRes) {
190             FreeResource(infoPtr->hRes);
191             infoPtr->hRes = 0;
192         }
193         HeapFree(GetProcessHeap(), 0, infoPtr->lpIndex);
194         infoPtr->lpIndex = NULL;
195         if (infoPtr->hic) {
196             fnIC.fnICClose(infoPtr->hic);
197             infoPtr->hic = 0;
198         }
199         HeapFree(GetProcessHeap(), 0, infoPtr->inbih);
200         infoPtr->inbih = NULL;
201         HeapFree(GetProcessHeap(), 0, infoPtr->outbih);
202         infoPtr->outbih = NULL;
203         HeapFree(GetProcessHeap(), 0, infoPtr->indata);
204         infoPtr->indata = NULL;
205         HeapFree(GetProcessHeap(), 0, infoPtr->outdata);
206         infoPtr->outdata = NULL;
207         if( infoPtr->hbmPrevFrame )
208         {
209             DeleteObject(infoPtr->hbmPrevFrame);
210             infoPtr->hbmPrevFrame = 0;
211         }
212
213         memset(&infoPtr->mah, 0, sizeof(infoPtr->mah));
214         memset(&infoPtr->ash, 0, sizeof(infoPtr->ash));
215         infoPtr->nFromFrame = infoPtr->nToFrame = infoPtr->nLoop = infoPtr->currFrame = 0;
216     }
217     infoPtr->transparentColor = ANIMATE_COLOR_NONE;
218 }
219
220 static void ANIMATE_TransparentBlt(ANIMATE_INFO *infoPtr, HDC hdcDest, HDC hdcSource)
221 {
222     HDC hdcMask;
223     HBITMAP hbmMask;
224     HBITMAP hbmOld;
225
226     /* create a transparency mask */
227     hdcMask = CreateCompatibleDC(hdcDest);
228     hbmMask = CreateBitmap(infoPtr->inbih->biWidth, infoPtr->inbih->biHeight, 1,1,NULL);
229     hbmOld = SelectObject(hdcMask, hbmMask);
230
231     SetBkColor(hdcSource,infoPtr->transparentColor);
232     BitBlt(hdcMask,0,0,infoPtr->inbih->biWidth, infoPtr->inbih->biHeight,hdcSource,0,0,SRCCOPY);
233
234     /* mask the source bitmap */
235     SetBkColor(hdcSource, RGB(0,0,0));
236     SetTextColor(hdcSource, RGB(255,255,255));
237     BitBlt(hdcSource, 0, 0, infoPtr->inbih->biWidth, infoPtr->inbih->biHeight, hdcMask, 0, 0, SRCAND);
238
239     /* mask the destination bitmap */
240     SetBkColor(hdcDest, RGB(255,255,255));
241     SetTextColor(hdcDest, RGB(0,0,0));
242     BitBlt(hdcDest, 0, 0, infoPtr->inbih->biWidth, infoPtr->inbih->biHeight, hdcMask, 0, 0, SRCAND);
243
244     /* combine source and destination */
245     BitBlt(hdcDest,0,0,infoPtr->inbih->biWidth, infoPtr->inbih->biHeight,hdcSource,0,0,SRCPAINT);
246
247     SelectObject(hdcMask, hbmOld);
248     DeleteObject(hbmMask);
249     DeleteDC(hdcMask);
250 }
251
252 static BOOL ANIMATE_PaintFrame(ANIMATE_INFO* infoPtr, HDC hDC)
253 {
254     void* pBitmapData = NULL;
255     LPBITMAPINFO pBitmapInfo = NULL;
256
257     HDC hdcMem;
258     HBITMAP hbmOld;
259
260     int nOffsetX = 0;
261     int nOffsetY = 0;
262
263     int nWidth;
264     int nHeight;
265
266     if (!hDC || !infoPtr->inbih)
267         return TRUE;
268
269     if (infoPtr->dwStyle & ACS_TRANSPARENT)
270         infoPtr->hbrushBG = (HBRUSH)SendMessageW(infoPtr->hwndNotify, WM_CTLCOLORSTATIC,
271                                                  (WPARAM)hDC, (LPARAM)infoPtr->hwndSelf);
272
273     if (infoPtr->hic )
274     {
275         pBitmapData = infoPtr->outdata;
276         pBitmapInfo = (LPBITMAPINFO)infoPtr->outbih;
277
278         nWidth = infoPtr->outbih->biWidth;
279         nHeight = infoPtr->outbih->biHeight;
280     } 
281     else
282     {
283         pBitmapData = infoPtr->indata;
284         pBitmapInfo = (LPBITMAPINFO)infoPtr->inbih;
285
286         nWidth = infoPtr->inbih->biWidth;
287         nHeight = infoPtr->inbih->biHeight;
288     }
289
290     if(!infoPtr->hbmPrevFrame)
291     {
292         infoPtr->hbmPrevFrame=CreateCompatibleBitmap(hDC, nWidth,nHeight );
293     }
294
295     SetDIBits(hDC, infoPtr->hbmPrevFrame, 0, nHeight, pBitmapData, (LPBITMAPINFO)pBitmapInfo, DIB_RGB_COLORS);
296
297     hdcMem = CreateCompatibleDC(hDC);
298     hbmOld = SelectObject(hdcMem, infoPtr->hbmPrevFrame);
299
300     /*
301      * we need to get the transparent color even without ACS_TRANSPARENT,
302      * because the style can be changed later on and the color should always
303      * be obtained in the first frame
304      */
305     if(infoPtr->transparentColor == ANIMATE_COLOR_NONE)
306     {
307         infoPtr->transparentColor = GetPixel(hdcMem,0,0);
308     }
309
310     if(infoPtr->dwStyle & ACS_TRANSPARENT)
311     {
312         HDC hdcFinal = CreateCompatibleDC(hDC);
313         HBITMAP hbmFinal = CreateCompatibleBitmap(hDC,nWidth, nHeight);
314         HBITMAP hbmOld2 = SelectObject(hdcFinal, hbmFinal);
315         RECT rect;
316
317         rect.left = 0;
318         rect.top = 0;
319         rect.right = nWidth;
320         rect.bottom = nHeight;
321
322         if(!infoPtr->hbrushBG)
323             infoPtr->hbrushBG = GetCurrentObject(hDC, OBJ_BRUSH);
324
325         FillRect(hdcFinal, &rect, infoPtr->hbrushBG);
326         ANIMATE_TransparentBlt(infoPtr, hdcFinal, hdcMem);
327
328         SelectObject(hdcFinal, hbmOld2);
329         SelectObject(hdcMem, hbmFinal);
330         DeleteDC(hdcFinal);
331         DeleteObject(infoPtr->hbmPrevFrame);
332         infoPtr->hbmPrevFrame = hbmFinal;
333     }
334
335     if (infoPtr->dwStyle & ACS_CENTER)
336     {
337         RECT rect;
338
339         GetWindowRect(infoPtr->hwndSelf, &rect);
340         nOffsetX = ((rect.right - rect.left) - nWidth)/2;
341         nOffsetY = ((rect.bottom - rect.top) - nHeight)/2;
342     }
343     BitBlt(hDC, nOffsetX, nOffsetY, nWidth, nHeight, hdcMem, 0, 0, SRCCOPY);
344
345     SelectObject(hdcMem, hbmOld);
346     DeleteDC(hdcMem);
347     return TRUE;
348 }
349
350 static BOOL ANIMATE_DrawFrame(ANIMATE_INFO *infoPtr)
351 {
352     HDC         hDC;
353
354     TRACE("Drawing frame %d (loop %d)\n", infoPtr->currFrame, infoPtr->nLoop);
355
356     EnterCriticalSection(&infoPtr->cs);
357
358     mmioSeek(infoPtr->hMMio, infoPtr->lpIndex[infoPtr->currFrame], SEEK_SET);
359     mmioRead(infoPtr->hMMio, infoPtr->indata, infoPtr->ash.dwSuggestedBufferSize);
360
361     if (infoPtr->hic &&
362         fnIC.fnICDecompress(infoPtr->hic, 0, infoPtr->inbih, infoPtr->indata,
363                      infoPtr->outbih, infoPtr->outdata) != ICERR_OK) {
364         LeaveCriticalSection(&infoPtr->cs);
365         WARN("Decompression error\n");
366         return FALSE;
367     }
368
369     if ((hDC = GetDC(infoPtr->hwndSelf)) != 0) {
370         ANIMATE_PaintFrame(infoPtr, hDC);
371         ReleaseDC(infoPtr->hwndSelf, hDC);
372     }
373
374     if (infoPtr->currFrame++ >= infoPtr->nToFrame) {
375         infoPtr->currFrame = infoPtr->nFromFrame;
376         if (infoPtr->nLoop != -1) {
377             if (--infoPtr->nLoop == 0) {
378                 ANIMATE_DoStop(infoPtr);
379             }
380         }
381     }
382     LeaveCriticalSection(&infoPtr->cs);
383
384     return TRUE;
385 }
386
387 static LRESULT ANIMATE_Paint(ANIMATE_INFO *infoPtr, HDC hdc)
388 {
389     PAINTSTRUCT ps;
390     HDC hDC = hdc;
391
392     if (!hdc) hDC = BeginPaint(infoPtr->hwndSelf, &ps);
393
394     EnterCriticalSection(&infoPtr->cs);
395     ANIMATE_PaintFrame(infoPtr, hDC);
396     LeaveCriticalSection(&infoPtr->cs);
397
398     if (!hdc) EndPaint(infoPtr->hwndSelf, &ps);
399
400     return 0;
401 }
402
403 static DWORD CALLBACK ANIMATE_AnimationThread(LPVOID ptr_)
404 {
405     ANIMATE_INFO *infoPtr = (ANIMATE_INFO *)ptr_;
406     HANDLE event;
407     DWORD timeout;
408
409     while(1)
410     {
411         EnterCriticalSection(&infoPtr->cs);
412         ANIMATE_DrawFrame(infoPtr);
413         timeout = infoPtr->mah.dwMicroSecPerFrame;
414         event = infoPtr->hStopEvent;
415         LeaveCriticalSection(&infoPtr->cs);
416
417         /* time is in microseconds, we should convert it to milliseconds */
418         if ((event == 0) || WaitForSingleObject( event, (timeout+500)/1000) == WAIT_OBJECT_0)
419             break;
420     }
421     return TRUE;
422 }
423
424 static LRESULT ANIMATE_Play(ANIMATE_INFO *infoPtr, UINT cRepeat, WORD wFrom, WORD wTo)
425 {
426     /* nothing opened */
427     if (!infoPtr->hMMio)
428         return FALSE;
429
430     if (infoPtr->hThread || infoPtr->uTimer) {
431         TRACE("Already playing\n");
432         return TRUE;
433     }
434
435     infoPtr->nFromFrame = wFrom;
436     infoPtr->nToFrame   = wTo;
437     infoPtr->nLoop      = cRepeat;
438
439     if (infoPtr->nToFrame == 0xFFFF)
440         infoPtr->nToFrame = infoPtr->mah.dwTotalFrames - 1;
441
442     TRACE("(repeat=%d from=%d to=%d);\n",
443           infoPtr->nLoop, infoPtr->nFromFrame, infoPtr->nToFrame);
444
445     if (infoPtr->nFromFrame >= infoPtr->nToFrame ||
446         infoPtr->nToFrame >= infoPtr->mah.dwTotalFrames)
447         return FALSE;
448
449     infoPtr->currFrame = infoPtr->nFromFrame;
450
451     if (infoPtr->dwStyle & ACS_TIMER) 
452     {
453         TRACE("Using a timer\n");
454         /* create a timer to display AVI */
455         infoPtr->uTimer = SetTimer(infoPtr->hwndSelf, 1, 
456                                    infoPtr->mah.dwMicroSecPerFrame / 1000, NULL);
457     } 
458     else 
459     {
460         TRACE("Using an animation thread\n");
461         infoPtr->hStopEvent = CreateEventW( NULL, TRUE, FALSE, NULL );
462         infoPtr->hThread = CreateThread(0, 0, ANIMATE_AnimationThread,
463                                         (LPVOID)infoPtr, 0, &infoPtr->threadId);
464         if(!infoPtr->hThread) return FALSE;
465
466     }
467
468     ANIMATE_Notify(infoPtr, ACN_START);
469
470     return TRUE;
471 }
472
473
474 static BOOL ANIMATE_GetAviInfo(ANIMATE_INFO *infoPtr)
475 {
476     MMCKINFO            ckMainRIFF;
477     MMCKINFO            mmckHead;
478     MMCKINFO            mmckList;
479     MMCKINFO            mmckInfo;
480     DWORD               numFrame;
481     DWORD               insize;
482
483     if (mmioDescend(infoPtr->hMMio, &ckMainRIFF, NULL, 0) != 0) {
484         WARN("Can't find 'RIFF' chunk\n");
485         return FALSE;
486     }
487
488     if ((ckMainRIFF.ckid != FOURCC_RIFF) ||
489         (ckMainRIFF.fccType != mmioFOURCC('A', 'V', 'I', ' '))) {
490         WARN("Can't find 'AVI ' chunk\n");
491         return FALSE;
492     }
493
494     mmckHead.fccType = mmioFOURCC('h', 'd', 'r', 'l');
495     if (mmioDescend(infoPtr->hMMio, &mmckHead, &ckMainRIFF, MMIO_FINDLIST) != 0) {
496         WARN("Can't find 'hdrl' list\n");
497         return FALSE;
498     }
499
500     mmckInfo.ckid = mmioFOURCC('a', 'v', 'i', 'h');
501     if (mmioDescend(infoPtr->hMMio, &mmckInfo, &mmckHead, MMIO_FINDCHUNK) != 0) {
502         WARN("Can't find 'avih' chunk\n");
503         return FALSE;
504     }
505
506     mmioRead(infoPtr->hMMio, (LPSTR)&infoPtr->mah, sizeof(infoPtr->mah));
507
508     TRACE("mah.dwMicroSecPerFrame=%ld\n",       infoPtr->mah.dwMicroSecPerFrame);
509     TRACE("mah.dwMaxBytesPerSec=%ld\n",         infoPtr->mah.dwMaxBytesPerSec);
510     TRACE("mah.dwPaddingGranularity=%ld\n",     infoPtr->mah.dwPaddingGranularity);
511     TRACE("mah.dwFlags=%ld\n",                  infoPtr->mah.dwFlags);
512     TRACE("mah.dwTotalFrames=%ld\n",            infoPtr->mah.dwTotalFrames);
513     TRACE("mah.dwInitialFrames=%ld\n",          infoPtr->mah.dwInitialFrames);
514     TRACE("mah.dwStreams=%ld\n",                infoPtr->mah.dwStreams);
515     TRACE("mah.dwSuggestedBufferSize=%ld\n",    infoPtr->mah.dwSuggestedBufferSize);
516     TRACE("mah.dwWidth=%ld\n",                  infoPtr->mah.dwWidth);
517     TRACE("mah.dwHeight=%ld\n",                 infoPtr->mah.dwHeight);
518
519     mmioAscend(infoPtr->hMMio, &mmckInfo, 0);
520
521     mmckList.fccType = mmioFOURCC('s', 't', 'r', 'l');
522     if (mmioDescend(infoPtr->hMMio, &mmckList, &mmckHead, MMIO_FINDLIST) != 0) {
523         WARN("Can't find 'strl' list\n");
524         return FALSE;
525     }
526
527     mmckInfo.ckid = mmioFOURCC('s', 't', 'r', 'h');
528     if (mmioDescend(infoPtr->hMMio, &mmckInfo, &mmckList, MMIO_FINDCHUNK) != 0) {
529         WARN("Can't find 'strh' chunk\n");
530         return FALSE;
531     }
532
533     mmioRead(infoPtr->hMMio, (LPSTR)&infoPtr->ash, sizeof(infoPtr->ash));
534
535     TRACE("ash.fccType='%c%c%c%c'\n",           LOBYTE(LOWORD(infoPtr->ash.fccType)),
536                                                 HIBYTE(LOWORD(infoPtr->ash.fccType)),
537                                                 LOBYTE(HIWORD(infoPtr->ash.fccType)),
538                                                 HIBYTE(HIWORD(infoPtr->ash.fccType)));
539     TRACE("ash.fccHandler='%c%c%c%c'\n",        LOBYTE(LOWORD(infoPtr->ash.fccHandler)),
540                                                 HIBYTE(LOWORD(infoPtr->ash.fccHandler)),
541                                                 LOBYTE(HIWORD(infoPtr->ash.fccHandler)),
542                                                 HIBYTE(HIWORD(infoPtr->ash.fccHandler)));
543     TRACE("ash.dwFlags=%ld\n",                  infoPtr->ash.dwFlags);
544     TRACE("ash.wPriority=%d\n",                 infoPtr->ash.wPriority);
545     TRACE("ash.wLanguage=%d\n",                 infoPtr->ash.wLanguage);
546     TRACE("ash.dwInitialFrames=%ld\n",          infoPtr->ash.dwInitialFrames);
547     TRACE("ash.dwScale=%ld\n",                  infoPtr->ash.dwScale);
548     TRACE("ash.dwRate=%ld\n",                   infoPtr->ash.dwRate);
549     TRACE("ash.dwStart=%ld\n",                  infoPtr->ash.dwStart);
550     TRACE("ash.dwLength=%ld\n",                 infoPtr->ash.dwLength);
551     TRACE("ash.dwSuggestedBufferSize=%ld\n",    infoPtr->ash.dwSuggestedBufferSize);
552     TRACE("ash.dwQuality=%ld\n",                infoPtr->ash.dwQuality);
553     TRACE("ash.dwSampleSize=%ld\n",             infoPtr->ash.dwSampleSize);
554     TRACE("ash.rcFrame=(%d,%d,%d,%d)\n",        infoPtr->ash.rcFrame.top, infoPtr->ash.rcFrame.left,
555           infoPtr->ash.rcFrame.bottom, infoPtr->ash.rcFrame.right);
556
557     mmioAscend(infoPtr->hMMio, &mmckInfo, 0);
558
559     mmckInfo.ckid = mmioFOURCC('s', 't', 'r', 'f');
560     if (mmioDescend(infoPtr->hMMio, &mmckInfo, &mmckList, MMIO_FINDCHUNK) != 0) {
561         WARN("Can't find 'strh' chunk\n");
562         return FALSE;
563     }
564
565     infoPtr->inbih = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
566     if (!infoPtr->inbih) {
567         WARN("Can't alloc input BIH\n");
568         return FALSE;
569     }
570
571     mmioRead(infoPtr->hMMio, (LPSTR)infoPtr->inbih, mmckInfo.cksize);
572
573     TRACE("bih.biSize=%ld\n",           infoPtr->inbih->biSize);
574     TRACE("bih.biWidth=%ld\n",          infoPtr->inbih->biWidth);
575     TRACE("bih.biHeight=%ld\n",         infoPtr->inbih->biHeight);
576     TRACE("bih.biPlanes=%d\n",          infoPtr->inbih->biPlanes);
577     TRACE("bih.biBitCount=%d\n",        infoPtr->inbih->biBitCount);
578     TRACE("bih.biCompression=%ld\n",    infoPtr->inbih->biCompression);
579     TRACE("bih.biSizeImage=%ld\n",      infoPtr->inbih->biSizeImage);
580     TRACE("bih.biXPelsPerMeter=%ld\n",  infoPtr->inbih->biXPelsPerMeter);
581     TRACE("bih.biYPelsPerMeter=%ld\n",  infoPtr->inbih->biYPelsPerMeter);
582     TRACE("bih.biClrUsed=%ld\n",        infoPtr->inbih->biClrUsed);
583     TRACE("bih.biClrImportant=%ld\n",   infoPtr->inbih->biClrImportant);
584
585     mmioAscend(infoPtr->hMMio, &mmckInfo, 0);
586
587     mmioAscend(infoPtr->hMMio, &mmckList, 0);
588
589 #if 0
590     /* an AVI has 0 or 1 video stream, and to be animated should not contain
591      * an audio stream, so only one strl is allowed
592      */
593     mmckList.fccType = mmioFOURCC('s', 't', 'r', 'l');
594     if (mmioDescend(infoPtr->hMMio, &mmckList, &mmckHead, MMIO_FINDLIST) == 0) {
595         WARN("There should be a single 'strl' list\n");
596         return FALSE;
597     }
598 #endif
599
600     mmioAscend(infoPtr->hMMio, &mmckHead, 0);
601
602     /* no need to read optional JUNK chunk */
603
604     mmckList.fccType = mmioFOURCC('m', 'o', 'v', 'i');
605     if (mmioDescend(infoPtr->hMMio, &mmckList, &ckMainRIFF, MMIO_FINDLIST) != 0) {
606         WARN("Can't find 'movi' list\n");
607         return FALSE;
608     }
609
610     /* FIXME: should handle the 'rec ' LIST when present */
611
612     infoPtr->lpIndex = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
613                                  infoPtr->mah.dwTotalFrames * sizeof(DWORD));
614     if (!infoPtr->lpIndex) 
615         return FALSE;
616
617     numFrame = insize = 0;
618     while (mmioDescend(infoPtr->hMMio, &mmckInfo, &mmckList, 0) == 0 &&
619            numFrame < infoPtr->mah.dwTotalFrames) {
620         infoPtr->lpIndex[numFrame] = mmckInfo.dwDataOffset;
621         if (insize < mmckInfo.cksize)
622             insize = mmckInfo.cksize;
623         numFrame++;
624         mmioAscend(infoPtr->hMMio, &mmckInfo, 0);
625     }
626     if (numFrame != infoPtr->mah.dwTotalFrames) {
627         WARN("Found %ld frames (/%ld)\n", numFrame, infoPtr->mah.dwTotalFrames);
628         return FALSE;
629     }
630     if (insize > infoPtr->ash.dwSuggestedBufferSize) {
631         WARN("insize=%ld suggestedSize=%ld\n", insize, infoPtr->ash.dwSuggestedBufferSize);
632         infoPtr->ash.dwSuggestedBufferSize = insize;
633     }
634
635     infoPtr->indata = HeapAlloc(GetProcessHeap(), 0, infoPtr->ash.dwSuggestedBufferSize);
636     if (!infoPtr->indata) 
637         return FALSE;
638
639     return TRUE;
640 }
641
642
643 static BOOL ANIMATE_GetAviCodec(ANIMATE_INFO *infoPtr)
644 {
645     DWORD       outSize;
646
647     /* check uncompressed AVI */
648     if ((infoPtr->ash.fccHandler == mmioFOURCC('D', 'I', 'B', ' ')) ||
649        (infoPtr->ash.fccHandler == mmioFOURCC('R', 'L', 'E', ' ')) ||
650        (infoPtr->ash.fccHandler == mmioFOURCC(0, 0, 0, 0)))
651     {
652         infoPtr->hic = 0;
653         return TRUE;
654     }
655
656     /* try to get a decompressor for that type */
657     infoPtr->hic = fnIC.fnICOpen(ICTYPE_VIDEO, infoPtr->ash.fccHandler, ICMODE_DECOMPRESS);
658     if (!infoPtr->hic) {
659         WARN("Can't load codec for the file\n");
660         return FALSE;
661     }
662
663     outSize = fnIC.fnICSendMessage(infoPtr->hic, ICM_DECOMPRESS_GET_FORMAT,
664                             (DWORD)infoPtr->inbih, 0L);
665
666     infoPtr->outbih = HeapAlloc(GetProcessHeap(), 0, outSize);
667     if (!infoPtr->outbih)
668         return FALSE;
669
670     if (fnIC.fnICSendMessage(infoPtr->hic, ICM_DECOMPRESS_GET_FORMAT,
671                       (DWORD)infoPtr->inbih, (DWORD)infoPtr->outbih) != outSize) 
672     {
673         WARN("Can't get output BIH\n");
674         return FALSE;
675     }
676
677     infoPtr->outdata = HeapAlloc(GetProcessHeap(), 0, infoPtr->outbih->biSizeImage);
678     if (!infoPtr->outdata) 
679         return FALSE;
680
681     if (fnIC.fnICSendMessage(infoPtr->hic, ICM_DECOMPRESS_BEGIN,
682                       (DWORD)infoPtr->inbih, (DWORD)infoPtr->outbih) != ICERR_OK) {
683         WARN("Can't begin decompression\n");
684         return FALSE;
685     }
686
687     return TRUE;
688 }
689
690
691 static BOOL ANIMATE_OpenW(ANIMATE_INFO *infoPtr, HINSTANCE hInstance, LPWSTR lpszName)
692 {
693     ANIMATE_Free(infoPtr);
694
695     if (!lpszName) 
696     {
697         TRACE("Closing avi!\n");
698         /* installer of thebat! v1.62 requires FALSE here */
699         return (infoPtr->hMMio != 0);
700     }
701
702     if (!hInstance)
703         hInstance = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
704
705     if (HIWORD(lpszName)) 
706     {
707         TRACE("(\"%s\");\n", debugstr_w(lpszName));
708
709         if (!ANIMATE_LoadResW(infoPtr, hInstance, lpszName)) 
710         {
711             TRACE("No AVI resource found!\n");
712             if (!ANIMATE_LoadFileW(infoPtr, lpszName)) 
713             {
714                 WARN("No AVI file found!\n");
715                 return FALSE;
716             }
717         }
718     } 
719     else 
720     {
721         TRACE("(%u);\n", (WORD)(DWORD)lpszName);
722
723         if (!ANIMATE_LoadResW(infoPtr, hInstance, MAKEINTRESOURCEW((INT)lpszName))) 
724         {
725             WARN("No AVI resource found!\n");
726             return FALSE;
727         }
728     }
729
730     if (!ANIMATE_GetAviInfo(infoPtr)) 
731     {
732         WARN("Can't get AVI information\n");
733         ANIMATE_Free(infoPtr);
734         return FALSE;
735     }
736
737     if (!ANIMATE_GetAviCodec(infoPtr)) 
738     {
739         WARN("Can't get AVI Codec\n");
740         ANIMATE_Free(infoPtr);
741         return FALSE;
742     }
743
744     if (!(infoPtr->dwStyle & ACS_CENTER))
745         SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, infoPtr->mah.dwWidth, infoPtr->mah.dwHeight,
746                      SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
747
748     if (infoPtr->dwStyle & ACS_AUTOPLAY) 
749         return ANIMATE_Play(infoPtr, -1, 0, infoPtr->mah.dwTotalFrames - 1);
750
751     return TRUE;
752 }
753
754
755 static BOOL ANIMATE_OpenA(ANIMATE_INFO *infoPtr, HINSTANCE hInstance, LPSTR lpszName)
756 {
757     LPWSTR lpwszName;
758     LRESULT result;
759     INT len;
760
761     if (!HIWORD(lpszName))
762         return ANIMATE_OpenW(infoPtr, hInstance, (LPWSTR)lpszName);
763
764     len = MultiByteToWideChar(CP_ACP, 0, lpszName, -1, NULL, 0);
765     lpwszName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
766     if (!lpwszName) return FALSE;
767     MultiByteToWideChar(CP_ACP, 0, lpszName, -1, lpwszName, len);
768
769     result = ANIMATE_OpenW(infoPtr, hInstance, lpwszName);
770     HeapFree(GetProcessHeap(), 0, lpwszName);
771     return result;
772 }
773
774
775 static BOOL ANIMATE_Stop(ANIMATE_INFO *infoPtr)
776 {
777     /* nothing opened */
778     if (!infoPtr->hMMio)
779         return FALSE;
780
781     ANIMATE_DoStop(infoPtr);
782     return TRUE;
783 }
784
785
786 static BOOL ANIMATE_Create(HWND hWnd, LPCREATESTRUCTW lpcs)
787 {
788     static const WCHAR msvfw32W[] = { 'm', 's', 'v', 'f', 'w', '3', '2', '.', 'd', 'l', 'l', 0 };
789     ANIMATE_INFO *infoPtr;
790
791     if (!fnIC.hModule)
792     {
793         fnIC.hModule = LoadLibraryW(msvfw32W);
794         if (!fnIC.hModule) return FALSE;
795
796         fnIC.fnICOpen        = (void*)GetProcAddress(fnIC.hModule, "ICOpen");
797         fnIC.fnICClose       = (void*)GetProcAddress(fnIC.hModule, "ICClose");
798         fnIC.fnICSendMessage = (void*)GetProcAddress(fnIC.hModule, "ICSendMessage");
799         fnIC.fnICDecompress  = (void*)GetProcAddress(fnIC.hModule, "ICDecompress");
800     }
801
802     /* allocate memory for info structure */
803     infoPtr = (ANIMATE_INFO *)Alloc(sizeof(ANIMATE_INFO));
804     if (!infoPtr) return FALSE;
805
806     /* store crossref hWnd <-> info structure */
807     SetWindowLongPtrW(hWnd, 0, (DWORD_PTR)infoPtr);
808     infoPtr->hwndSelf = hWnd;
809     infoPtr->hwndNotify = lpcs->hwndParent;
810     infoPtr->transparentColor = ANIMATE_COLOR_NONE;
811     infoPtr->hbmPrevFrame = 0;
812     infoPtr->dwStyle = lpcs->style;
813
814     TRACE("Animate style=0x%08lx, parent=%p\n", infoPtr->dwStyle, infoPtr->hwndNotify);
815
816     InitializeCriticalSection(&infoPtr->cs);
817
818     return TRUE;
819 }
820
821
822 static LRESULT ANIMATE_Destroy(ANIMATE_INFO *infoPtr)
823 {
824     /* free avi data */
825     ANIMATE_Free(infoPtr);
826
827     /* free animate info data */
828     SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
829     Free(infoPtr);
830
831     return 0;
832 }
833
834
835 static BOOL ANIMATE_EraseBackground(ANIMATE_INFO *infoPtr, HDC hdc)
836 {
837     RECT rect;
838     HBRUSH hBrush = 0;
839
840     if(infoPtr->dwStyle & ACS_TRANSPARENT)
841     {
842         hBrush = (HBRUSH)SendMessageW(infoPtr->hwndNotify, WM_CTLCOLORSTATIC,
843                                       (WPARAM)hdc, (LPARAM)infoPtr->hwndSelf);
844     }
845
846     GetClientRect(infoPtr->hwndSelf, &rect);
847     FillRect(hdc, &rect, hBrush ? hBrush : GetCurrentObject(hdc, OBJ_BRUSH));
848
849     return TRUE;
850 }
851
852
853 static LRESULT ANIMATE_StyleChanged(ANIMATE_INFO *infoPtr, WPARAM wStyleType, LPSTYLESTRUCT lpss)
854 {
855     TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
856           wStyleType, lpss->styleOld, lpss->styleNew);
857
858     if (wStyleType != GWL_STYLE) return 0;
859   
860     infoPtr->dwStyle = lpss->styleNew;
861
862     InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
863     return 0;
864 }
865
866
867 static LRESULT WINAPI ANIMATE_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
868 {
869     ANIMATE_INFO *infoPtr = (ANIMATE_INFO *)GetWindowLongPtrW(hWnd, 0);
870
871     TRACE("hwnd=%p msg=%x wparam=%x lparam=%lx\n", hWnd, uMsg, wParam, lParam);
872     if (!infoPtr && (uMsg != WM_NCCREATE))
873         return DefWindowProcW(hWnd, uMsg, wParam, lParam);
874     switch (uMsg)
875     {
876     case ACM_OPENA:
877         return ANIMATE_OpenA(infoPtr, (HINSTANCE)wParam, (LPSTR)lParam);
878
879     case ACM_OPENW:
880         return ANIMATE_OpenW(infoPtr, (HINSTANCE)wParam, (LPWSTR)lParam);
881
882     case ACM_PLAY:
883         return ANIMATE_Play(infoPtr, (INT)wParam, LOWORD(lParam), HIWORD(lParam));
884
885     case ACM_STOP:
886         return ANIMATE_Stop(infoPtr);
887
888     case WM_CLOSE:
889         ANIMATE_Free(infoPtr);
890         return 0;
891
892     case WM_NCCREATE:
893         return ANIMATE_Create(hWnd, (LPCREATESTRUCTW)lParam);
894
895     case WM_NCHITTEST:
896         return HTTRANSPARENT;
897
898     case WM_DESTROY:
899         return ANIMATE_Destroy(infoPtr);
900
901     case WM_ERASEBKGND:
902         return ANIMATE_EraseBackground(infoPtr, (HDC)wParam);
903
904     case WM_STYLECHANGED:
905         return ANIMATE_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
906
907     case WM_TIMER:
908         return ANIMATE_DrawFrame(infoPtr);
909
910     case WM_PAINT:
911         /* the animation isn't playing, or has not decompressed
912          * (and displayed) the first frame yet, do default painting
913          */
914         if ((!infoPtr->uTimer && !infoPtr->hThread) || !infoPtr->hbmPrevFrame)
915             return DefWindowProcW(hWnd, uMsg, wParam, lParam);
916         else
917             return ANIMATE_Paint(infoPtr, (HDC)wParam);
918
919     case WM_SIZE:
920         if (infoPtr->dwStyle & ACS_CENTER) 
921             InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
922         return DefWindowProcW(hWnd, uMsg, wParam, lParam);
923
924     default:
925         if ((uMsg >= WM_USER) && (uMsg < WM_APP))
926             ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
927
928         return DefWindowProcW(hWnd, uMsg, wParam, lParam);
929     }
930     return 0;
931 }
932
933 void ANIMATE_Register(void)
934 {
935     WNDCLASSW wndClass;
936
937     ZeroMemory(&wndClass, sizeof(WNDCLASSW));
938     wndClass.style         = CS_GLOBALCLASS | CS_DBLCLKS;
939     wndClass.lpfnWndProc   = ANIMATE_WindowProc;
940     wndClass.cbClsExtra    = 0;
941     wndClass.cbWndExtra    = sizeof(ANIMATE_INFO *);
942     wndClass.hCursor       = LoadCursorW(0, (LPCWSTR)IDC_ARROW);
943     wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
944     wndClass.lpszClassName = ANIMATE_CLASSW;
945
946     RegisterClassW(&wndClass);
947 }
948
949
950 void ANIMATE_Unregister(void)
951 {
952     UnregisterClassW(ANIMATE_CLASSW, NULL);
953 }