comctl32: Avoid leak of hShell32 (Coverity).
[wine] / dlls / mciqtz32 / mciqtz.c
1 /*
2  * DirectShow MCI Driver
3  *
4  * Copyright 2009 Christian Costa
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <stdarg.h>
22 #include "windef.h"
23 #include "winbase.h"
24 #include "winuser.h"
25 #include "mmddk.h"
26 #include "wine/debug.h"
27 #include "mciqtz_private.h"
28 #include "digitalv.h"
29 #include "wownt32.h"
30
31 WINE_DEFAULT_DEBUG_CHANNEL(mciqtz);
32
33 static DWORD MCIQTZ_mciClose(UINT, DWORD, LPMCI_GENERIC_PARMS);
34 static DWORD MCIQTZ_mciStop(UINT, DWORD, LPMCI_GENERIC_PARMS);
35
36 /*======================================================================*
37  *                          MCI QTZ implementation                      *
38  *======================================================================*/
39
40 static HINSTANCE MCIQTZ_hInstance = 0;
41
42 /***********************************************************************
43  *              DllMain (MCIQTZ.0)
44  */
45 BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID fImpLoad)
46 {
47     switch (fdwReason) {
48     case DLL_PROCESS_ATTACH:
49         DisableThreadLibraryCalls(hInstDLL);
50         MCIQTZ_hInstance = hInstDLL;
51         break;
52     }
53     return TRUE;
54 }
55
56 /**************************************************************************
57  *                              MCIQTZ_mciGetOpenDev            [internal]
58  */
59 static WINE_MCIQTZ* MCIQTZ_mciGetOpenDev(UINT wDevID)
60 {
61     WINE_MCIQTZ* wma = (WINE_MCIQTZ*)mciGetDriverData(wDevID);
62
63     if (!wma) {
64         WARN("Invalid wDevID=%u\n", wDevID);
65         return NULL;
66     }
67     return wma;
68 }
69
70 /**************************************************************************
71  *                              MCIQTZ_drvOpen                  [internal]
72  */
73 static DWORD MCIQTZ_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp)
74 {
75     WINE_MCIQTZ* wma;
76     static const WCHAR mciAviWStr[] = {'M','C','I','A','V','I',0};
77
78     TRACE("(%s, %p)\n", debugstr_w(str), modp);
79
80     /* session instance */
81     if (!modp)
82         return 0xFFFFFFFF;
83
84     wma = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIQTZ));
85     if (!wma)
86         return 0;
87
88     modp->wType = MCI_DEVTYPE_DIGITAL_VIDEO;
89     wma->wDevID = modp->wDeviceID;
90     modp->wCustomCommandTable = wma->command_table = mciLoadCommandResource(MCIQTZ_hInstance, mciAviWStr, 0);
91     mciSetDriverData(wma->wDevID, (DWORD_PTR)wma);
92
93     return modp->wDeviceID;
94 }
95
96 /**************************************************************************
97  *                              MCIQTZ_drvClose         [internal]
98  */
99 static DWORD MCIQTZ_drvClose(DWORD dwDevID)
100 {
101     WINE_MCIQTZ* wma;
102
103     TRACE("(%04x)\n", dwDevID);
104
105     wma = MCIQTZ_mciGetOpenDev(dwDevID);
106
107     if (wma) {
108         /* finish all outstanding things */
109         MCIQTZ_mciClose(dwDevID, MCI_WAIT, NULL);
110
111         mciFreeCommandResource(wma->command_table);
112         mciSetDriverData(dwDevID, 0);
113         HeapFree(GetProcessHeap(), 0, wma);
114         return 1;
115     }
116
117     return (dwDevID == 0xFFFFFFFF) ? 1 : 0;
118 }
119
120 /**************************************************************************
121  *                              MCIQTZ_drvConfigure             [internal]
122  */
123 static DWORD MCIQTZ_drvConfigure(DWORD dwDevID)
124 {
125     WINE_MCIQTZ* wma;
126
127     TRACE("(%04x)\n", dwDevID);
128
129     wma = MCIQTZ_mciGetOpenDev(dwDevID);
130     if (!wma)
131         return 0;
132
133     MCIQTZ_mciStop(dwDevID, MCI_WAIT, NULL);
134
135     MessageBoxA(0, "Sample QTZ Wine Driver !", "MM-Wine Driver", MB_OK);
136
137     return 1;
138 }
139
140 /***************************************************************************
141  *                              MCIQTZ_mciOpen                  [internal]
142  */
143 static DWORD MCIQTZ_mciOpen(UINT wDevID, DWORD dwFlags,
144                             LPMCI_DGV_OPEN_PARMSW lpOpenParms)
145 {
146     WINE_MCIQTZ* wma;
147     HRESULT hr;
148     DWORD style = 0;
149     RECT rc = { 0, 0, 0, 0 };
150
151     TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpOpenParms);
152
153     if (!lpOpenParms)
154         return MCIERR_NULL_PARAMETER_BLOCK;
155
156     wma = MCIQTZ_mciGetOpenDev(wDevID);
157     if (!wma)
158         return MCIERR_INVALID_DEVICE_ID;
159
160     MCIQTZ_mciStop(wDevID, MCI_WAIT, NULL);
161
162     hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
163     wma->uninit = SUCCEEDED(hr);
164
165     hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, &IID_IGraphBuilder, (LPVOID*)&wma->pgraph);
166     if (FAILED(hr)) {
167         TRACE("Cannot create filtergraph (hr = %x)\n", hr);
168         goto err;
169     }
170
171     hr = IGraphBuilder_QueryInterface(wma->pgraph, &IID_IMediaControl, (LPVOID*)&wma->pmctrl);
172     if (FAILED(hr)) {
173         TRACE("Cannot get IMediaControl interface (hr = %x)\n", hr);
174         goto err;
175     }
176
177     hr = IGraphBuilder_QueryInterface(wma->pgraph, &IID_IMediaSeeking, (void**)&wma->seek);
178     if (FAILED(hr)) {
179         TRACE("Cannot get IMediaSeeking interface (hr = %x)\n", hr);
180         goto err;
181     }
182
183     hr = IGraphBuilder_QueryInterface(wma->pgraph, &IID_IMediaEvent, (void**)&wma->mevent);
184     if (FAILED(hr)) {
185         TRACE("Cannot get IMediaEvent interface (hr = %x)\n", hr);
186         goto err;
187     }
188
189     hr = IGraphBuilder_QueryInterface(wma->pgraph, &IID_IVideoWindow, (void**)&wma->vidwin);
190     if (FAILED(hr)) {
191         TRACE("Cannot get IVideoWindow interface (hr = %x)\n", hr);
192         goto err;
193     }
194
195     hr = IGraphBuilder_QueryInterface(wma->pgraph, &IID_IBasicVideo, (void**)&wma->vidbasic);
196     if (FAILED(hr)) {
197         TRACE("Cannot get IBasicVideo interface (hr = %x)\n", hr);
198         goto err;
199     }
200
201     if (!(dwFlags & MCI_OPEN_ELEMENT) || (dwFlags & MCI_OPEN_ELEMENT_ID)) {
202         TRACE("Wrong dwFlags %x\n", dwFlags);
203         goto err;
204     }
205
206     if (!lpOpenParms->lpstrElementName || !lpOpenParms->lpstrElementName[0]) {
207         TRACE("Invalid filename specified\n");
208         goto err;
209     }
210
211     TRACE("Open file %s\n", debugstr_w(lpOpenParms->lpstrElementName));
212
213     hr = IGraphBuilder_RenderFile(wma->pgraph, lpOpenParms->lpstrElementName, NULL);
214     if (FAILED(hr)) {
215         TRACE("Cannot render file (hr = %x)\n", hr);
216         goto err;
217     }
218
219     IVideoWindow_put_AutoShow(wma->vidwin, OAFALSE);
220     IVideoWindow_put_Visible(wma->vidwin, OAFALSE);
221     if (dwFlags & MCI_DGV_OPEN_WS)
222         style = lpOpenParms->dwStyle;
223     if (dwFlags & MCI_DGV_OPEN_PARENT) {
224         IVideoWindow_put_MessageDrain(wma->vidwin, (OAHWND)lpOpenParms->hWndParent);
225         IVideoWindow_put_WindowState(wma->vidwin, SW_HIDE);
226         IVideoWindow_put_WindowStyle(wma->vidwin, style|WS_CHILD);
227         IVideoWindow_put_Owner(wma->vidwin, (OAHWND)lpOpenParms->hWndParent);
228         GetClientRect(lpOpenParms->hWndParent, &rc);
229         IVideoWindow_SetWindowPosition(wma->vidwin, rc.left, rc.top, rc.right - rc.top, rc.bottom - rc.top);
230         wma->parent = (HWND)lpOpenParms->hWndParent;
231     }
232     else if (style)
233         IVideoWindow_put_WindowStyle(wma->vidwin, style);
234     IBasicVideo_GetVideoSize(wma->vidbasic, &rc.right, &rc.bottom);
235     wma->opened = TRUE;
236
237     if (dwFlags & MCI_NOTIFY)
238         mciDriverNotify(HWND_32(LOWORD(lpOpenParms->dwCallback)), wDevID, MCI_NOTIFY_SUCCESSFUL);
239
240     return 0;
241
242 err:
243     if (wma->vidbasic)
244         IBasicVideo_Release(wma->vidbasic);
245     wma->vidbasic = NULL;
246     if (wma->seek)
247         IMediaSeeking_Release(wma->seek);
248     wma->seek = NULL;
249     if (wma->vidwin)
250         IVideoWindow_Release(wma->vidwin);
251     wma->vidwin = NULL;
252     if (wma->pgraph)
253         IGraphBuilder_Release(wma->pgraph);
254     wma->pgraph = NULL;
255     if (wma->mevent)
256         IMediaEvent_Release(wma->mevent);
257     wma->mevent = NULL;
258     if (wma->pmctrl)
259         IMediaControl_Release(wma->pmctrl);
260     wma->pmctrl = NULL;
261
262     if (wma->uninit)
263         CoUninitialize();
264     wma->uninit = 0;
265
266     return MCIERR_INTERNAL;
267 }
268
269 /***************************************************************************
270  *                              MCIQTZ_mciClose                 [internal]
271  */
272 static DWORD MCIQTZ_mciClose(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
273 {
274     WINE_MCIQTZ* wma;
275
276     TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
277
278     wma = MCIQTZ_mciGetOpenDev(wDevID);
279     if (!wma)
280         return MCIERR_INVALID_DEVICE_ID;
281
282     MCIQTZ_mciStop(wDevID, MCI_WAIT, NULL);
283
284     if (wma->opened) {
285         IVideoWindow_Release(wma->vidwin);
286         IBasicVideo_Release(wma->vidbasic);
287         IMediaSeeking_Release(wma->seek);
288         IMediaEvent_Release(wma->mevent);
289         IGraphBuilder_Release(wma->pgraph);
290         IMediaControl_Release(wma->pmctrl);
291         if (wma->uninit)
292             CoUninitialize();
293         wma->opened = FALSE;
294     }
295
296     return 0;
297 }
298
299 /***************************************************************************
300  *                              MCIQTZ_mciPlay                  [internal]
301  */
302 static DWORD MCIQTZ_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
303 {
304     WINE_MCIQTZ* wma;
305     HRESULT hr;
306     REFERENCE_TIME time1 = 0, time2 = 0;
307     GUID format;
308     DWORD pos1;
309
310     TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
311
312     if (!lpParms)
313         return MCIERR_NULL_PARAMETER_BLOCK;
314
315     wma = MCIQTZ_mciGetOpenDev(wDevID);
316     if (!wma)
317         return MCIERR_INVALID_DEVICE_ID;
318
319     IMediaSeeking_GetTimeFormat(wma->seek, &format);
320     if (dwFlags & MCI_FROM) {
321         if (IsEqualGUID(&format, &TIME_FORMAT_MEDIA_TIME))
322             time1 = lpParms->dwFrom * 10000;
323         else
324             time1 = lpParms->dwFrom;
325         pos1 = AM_SEEKING_AbsolutePositioning;
326     } else
327         pos1 = AM_SEEKING_NoPositioning;
328     if (dwFlags & MCI_TO) {
329         if (IsEqualGUID(&format, &TIME_FORMAT_MEDIA_TIME))
330             time2 = lpParms->dwTo * 10000;
331         else
332             time2 = lpParms->dwTo;
333     } else
334         IMediaSeeking_GetDuration(wma->seek, &time2);
335     IMediaSeeking_SetPositions(wma->seek, &time1, pos1, &time2, AM_SEEKING_AbsolutePositioning);
336
337     hr = IMediaControl_Run(wma->pmctrl);
338     if (FAILED(hr)) {
339         TRACE("Cannot run filtergraph (hr = %x)\n", hr);
340         return MCIERR_INTERNAL;
341     }
342
343     IVideoWindow_put_Visible(wma->vidwin, OATRUE);
344
345     if (dwFlags & MCI_NOTIFY)
346         mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)), wDevID, MCI_NOTIFY_SUCCESSFUL);
347     return 0;
348 }
349
350 /***************************************************************************
351  *                              MCIQTZ_mciSeek                  [internal]
352  */
353 static DWORD MCIQTZ_mciSeek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
354 {
355     WINE_MCIQTZ* wma;
356     HRESULT hr;
357     LONGLONG newpos;
358
359     TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
360
361     if (!lpParms)
362         return MCIERR_NULL_PARAMETER_BLOCK;
363
364     wma = MCIQTZ_mciGetOpenDev(wDevID);
365     if (!wma)
366         return MCIERR_INVALID_DEVICE_ID;
367
368     MCIQTZ_mciStop(wDevID, MCI_WAIT, NULL);
369
370     if (dwFlags & MCI_SEEK_TO_START) {
371         newpos = 0;
372     } else if (dwFlags & MCI_SEEK_TO_END) {
373         FIXME("MCI_SEEK_TO_END not implemented yet\n");
374         return MCIERR_INTERNAL;
375     } else if (dwFlags & MCI_TO) {
376         FIXME("MCI_TO not implemented yet\n");
377         return MCIERR_INTERNAL;
378     } else {
379         WARN("dwFlag doesn't tell where to seek to...\n");
380         return MCIERR_MISSING_PARAMETER;
381     }
382
383     hr = IMediaSeeking_SetPositions(wma->seek, &newpos, AM_SEEKING_AbsolutePositioning, NULL, AM_SEEKING_NoPositioning);
384     if (FAILED(hr)) {
385         FIXME("Cannot set position (hr = %x)\n", hr);
386         return MCIERR_INTERNAL;
387     }
388
389     if (dwFlags & MCI_NOTIFY)
390         mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)), wDevID, MCI_NOTIFY_SUCCESSFUL);
391
392     return 0;
393 }
394
395 /***************************************************************************
396  *                              MCIQTZ_mciStop                  [internal]
397  */
398 static DWORD MCIQTZ_mciStop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
399 {
400     WINE_MCIQTZ* wma;
401     HRESULT hr;
402
403     TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
404
405     wma = MCIQTZ_mciGetOpenDev(wDevID);
406     if (!wma)
407         return MCIERR_INVALID_DEVICE_ID;
408
409     if (!wma->opened)
410         return 0;
411
412     hr = IMediaControl_Stop(wma->pmctrl);
413     if (FAILED(hr)) {
414         TRACE("Cannot stop filtergraph (hr = %x)\n", hr);
415         return MCIERR_INTERNAL;
416     }
417
418     if (!wma->parent)
419         IVideoWindow_put_Visible(wma->vidwin, OAFALSE);
420
421     return 0;
422 }
423
424 /***************************************************************************
425  *                              MCIQTZ_mciPause                 [internal]
426  */
427 static DWORD MCIQTZ_mciPause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
428 {
429     WINE_MCIQTZ* wma;
430     HRESULT hr;
431
432     TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
433
434     wma = MCIQTZ_mciGetOpenDev(wDevID);
435     if (!wma)
436         return MCIERR_INVALID_DEVICE_ID;
437
438     hr = IMediaControl_Pause(wma->pmctrl);
439     if (FAILED(hr)) {
440         TRACE("Cannot pause filtergraph (hr = %x)\n", hr);
441         return MCIERR_INTERNAL;
442     }
443
444     return 0;
445 }
446
447 /***************************************************************************
448  *                              MCIQTZ_mciGetDevCaps            [internal]
449  */
450 static DWORD MCIQTZ_mciGetDevCaps(UINT wDevID, DWORD dwFlags, LPMCI_GETDEVCAPS_PARMS lpParms)
451 {
452     WINE_MCIQTZ* wma;
453
454     TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
455
456     if (!lpParms)
457         return MCIERR_NULL_PARAMETER_BLOCK;
458
459     wma = MCIQTZ_mciGetOpenDev(wDevID);
460     if (!wma)
461         return MCIERR_INVALID_DEVICE_ID;
462
463     if (!(dwFlags & MCI_GETDEVCAPS_ITEM))
464         return MCIERR_MISSING_PARAMETER;
465
466     switch (lpParms->dwItem) {
467         case MCI_GETDEVCAPS_CAN_RECORD:
468             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
469             TRACE("MCI_GETDEVCAPS_CAN_RECORD = %08x\n", lpParms->dwReturn);
470             break;
471         case MCI_GETDEVCAPS_HAS_AUDIO:
472             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
473             TRACE("MCI_GETDEVCAPS_HAS_AUDIO = %08x\n", lpParms->dwReturn);
474             break;
475         case MCI_GETDEVCAPS_HAS_VIDEO:
476             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
477             TRACE("MCI_GETDEVCAPS_HAS_VIDEO = %08x\n", lpParms->dwReturn);
478             break;
479         case MCI_GETDEVCAPS_DEVICE_TYPE:
480             lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_DIGITAL_VIDEO, MCI_DEVTYPE_DIGITAL_VIDEO);
481             TRACE("MCI_GETDEVCAPS_DEVICE_TYPE = %08x\n", lpParms->dwReturn);
482             break;
483         case MCI_GETDEVCAPS_USES_FILES:
484             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
485             TRACE("MCI_GETDEVCAPS_USES_FILES = %08x\n", lpParms->dwReturn);
486             break;
487         case MCI_GETDEVCAPS_COMPOUND_DEVICE:
488             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
489             TRACE("MCI_GETDEVCAPS_COMPOUND_DEVICE = %08x\n", lpParms->dwReturn);
490             break;
491         case MCI_GETDEVCAPS_CAN_EJECT:
492             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
493             TRACE("MCI_GETDEVCAPS_EJECT = %08x\n", lpParms->dwReturn);
494             break;
495         case MCI_GETDEVCAPS_CAN_PLAY:
496             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
497             TRACE("MCI_GETDEVCAPS_CAN_PLAY = %08x\n", lpParms->dwReturn);
498             break;
499         case MCI_GETDEVCAPS_CAN_SAVE:
500             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
501             TRACE("MCI_GETDEVCAPS_CAN_SAVE = %08x\n", lpParms->dwReturn);
502             break;
503         case MCI_DGV_GETDEVCAPS_CAN_REVERSE:
504             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
505             TRACE("MCI_DGV_GETDEVCAPS_CAN_REVERSE = %08x\n", lpParms->dwReturn);
506             break;
507         case MCI_DGV_GETDEVCAPS_CAN_STRETCH:
508             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE); /* FIXME */
509             TRACE("MCI_DGV_GETDEVCAPS_CAN_STRETCH = %08x\n", lpParms->dwReturn);
510             break;
511         case MCI_DGV_GETDEVCAPS_CAN_LOCK:
512             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
513             TRACE("MCI_DGV_GETDEVCAPS_CAN_LOCK = %08x\n", lpParms->dwReturn);
514             break;
515         case MCI_DGV_GETDEVCAPS_CAN_FREEZE:
516             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
517             TRACE("MCI_DGV_GETDEVCAPS_CAN_FREEZE = %08x\n", lpParms->dwReturn);
518             break;
519         case MCI_DGV_GETDEVCAPS_CAN_STR_IN:
520             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
521             TRACE("MCI_DGV_GETDEVCAPS_CAN_STRETCH_INPUT = %08x\n", lpParms->dwReturn);
522             break;
523         case MCI_DGV_GETDEVCAPS_HAS_STILL:
524             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
525             TRACE("MCI_DGV_GETDEVCAPS_HAS_STILL = %08x\n", lpParms->dwReturn);
526             break;
527         case MCI_DGV_GETDEVCAPS_CAN_TEST:
528             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE); /* FIXME */
529             TRACE("MCI_DGV_GETDEVCAPS_CAN_TEST = %08x\n", lpParms->dwReturn);
530             break;
531         case MCI_DGV_GETDEVCAPS_MAX_WINDOWS:
532             lpParms->dwReturn = 1;
533             TRACE("MCI_DGV_GETDEVCAPS_MAX_WINDOWS = %u\n", lpParms->dwReturn);
534             return 0;
535         default:
536             WARN("Unknown capability %08x\n", lpParms->dwItem);
537             /* Fall through */
538         case MCI_DGV_GETDEVCAPS_MAXIMUM_RATE: /* unknown to w2k */
539         case MCI_DGV_GETDEVCAPS_MINIMUM_RATE: /* unknown to w2k */
540             return MCIERR_UNSUPPORTED_FUNCTION;
541     }
542
543     return MCI_RESOURCE_RETURNED;
544 }
545
546 /***************************************************************************
547  *                              MCIQTZ_mciSet                   [internal]
548  */
549 static DWORD MCIQTZ_mciSet(UINT wDevID, DWORD dwFlags, LPMCI_DGV_SET_PARMS lpParms)
550 {
551     WINE_MCIQTZ* wma;
552
553     TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
554
555     if (!lpParms)
556         return MCIERR_NULL_PARAMETER_BLOCK;
557
558     wma = MCIQTZ_mciGetOpenDev(wDevID);
559     if (!wma)
560         return MCIERR_INVALID_DEVICE_ID;
561
562     if (dwFlags & MCI_SET_TIME_FORMAT) {
563         switch (lpParms->dwTimeFormat) {
564             case MCI_FORMAT_MILLISECONDS:
565                 TRACE("MCI_SET_TIME_FORMAT = MCI_FORMAT_MILLISECONDS\n");
566                 wma->time_format = MCI_FORMAT_MILLISECONDS;
567                 break;
568             case MCI_FORMAT_FRAMES:
569                 TRACE("MCI_SET_TIME_FORMAT = MCI_FORMAT_FRAMES\n");
570                 wma->time_format = MCI_FORMAT_FRAMES;
571                 break;
572             default:
573                 WARN("Bad time format %u\n", lpParms->dwTimeFormat);
574                 return MCIERR_BAD_TIME_FORMAT;
575         }
576     }
577
578     if (dwFlags & MCI_SET_DOOR_OPEN)
579         FIXME("MCI_SET_DOOR_OPEN not implemented yet\n");
580     if (dwFlags & MCI_SET_DOOR_CLOSED)
581         FIXME("MCI_SET_DOOR_CLOSED not implemented yet\n");
582     if (dwFlags & MCI_SET_AUDIO)
583         FIXME("MCI_SET_AUDIO not implemented yet\n");
584     if (dwFlags & MCI_SET_VIDEO)
585         FIXME("MCI_SET_VIDEO not implemented yet\n");
586     if (dwFlags & MCI_SET_ON)
587         FIXME("MCI_SET_ON not implemented yet\n");
588     if (dwFlags & MCI_SET_OFF)
589         FIXME("MCI_SET_OFF not implemented yet\n");
590     if (dwFlags & MCI_SET_AUDIO_LEFT)
591         FIXME("MCI_SET_AUDIO_LEFT not implemented yet\n");
592     if (dwFlags & MCI_SET_AUDIO_RIGHT)
593         FIXME("MCI_SET_AUDIO_RIGHT not implemented yet\n");
594
595     if (dwFlags & ~0x7f03 /* All MCI_SET flags mask */)
596         ERR("Unknown flags %08x\n", dwFlags & ~0x7f03);
597
598     return 0;
599 }
600
601 /***************************************************************************
602  *                              MCIQTZ_mciStatus                [internal]
603  */
604 static DWORD MCIQTZ_mciStatus(UINT wDevID, DWORD dwFlags, LPMCI_DGV_STATUS_PARMSW lpParms)
605 {
606     WINE_MCIQTZ* wma;
607     HRESULT hr;
608
609     TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
610
611     if (!lpParms)
612         return MCIERR_NULL_PARAMETER_BLOCK;
613
614     wma = MCIQTZ_mciGetOpenDev(wDevID);
615     if (!wma)
616         return MCIERR_INVALID_DEVICE_ID;
617
618     if (!(dwFlags & MCI_STATUS_ITEM)) {
619         WARN("No status item specified\n");
620         return MCIERR_UNRECOGNIZED_COMMAND;
621     }
622
623     switch (lpParms->dwItem) {
624         case MCI_STATUS_LENGTH: {
625             LONGLONG duration = -1;
626             GUID format;
627             switch (wma->time_format) {
628                 case MCI_FORMAT_MILLISECONDS: format = TIME_FORMAT_MEDIA_TIME; break;
629                 case MCI_FORMAT_FRAMES: format = TIME_FORMAT_FRAME; break;
630                 default: ERR("Unhandled format %x\n", wma->time_format); break;
631             }
632             hr = IMediaSeeking_SetTimeFormat(wma->seek, &format);
633             if (FAILED(hr)) {
634                 FIXME("Cannot set time format (hr = %x)\n", hr);
635                 lpParms->dwReturn = 0;
636                 break;
637             }
638             hr = IMediaSeeking_GetDuration(wma->seek, &duration);
639             if (FAILED(hr) || duration < 0) {
640                 FIXME("Cannot read duration (hr = %x)\n", hr);
641                 lpParms->dwReturn = 0;
642             } else if (wma->time_format != MCI_FORMAT_MILLISECONDS)
643                 lpParms->dwReturn = duration;
644             else
645                 lpParms->dwReturn = duration / 10000;
646             break;
647         }
648         case MCI_STATUS_POSITION: {
649             REFERENCE_TIME curpos;
650
651             hr = IMediaSeeking_GetCurrentPosition(wma->seek, &curpos);
652             if (FAILED(hr)) {
653                 FIXME("Cannot get position (hr = %x)\n", hr);
654                 return MCIERR_INTERNAL;
655             }
656             lpParms->dwReturn = curpos / 10000;
657             break;
658         }
659         case MCI_STATUS_NUMBER_OF_TRACKS:
660             FIXME("MCI_STATUS_NUMBER_OF_TRACKS not implemented yet\n");
661             return MCIERR_UNRECOGNIZED_COMMAND;
662         case MCI_STATUS_MODE: {
663             LONG state = State_Stopped;
664             IMediaControl_GetState(wma->pmctrl, -1, &state);
665             if (state == State_Stopped)
666                 lpParms->dwReturn = MCI_MODE_STOP;
667             else if (state == State_Running) {
668                 LONG code;
669                 LONG_PTR p1, p2;
670
671                 lpParms->dwReturn = MCI_MODE_PLAY;
672
673                 do {
674                     hr = IMediaEvent_GetEvent(wma->mevent, &code, &p1, &p2, 0);
675                     if (hr == S_OK && code == EC_COMPLETE){
676                         lpParms->dwReturn = MCI_MODE_STOP;
677                         IMediaControl_Stop(wma->pmctrl);
678                     }
679                 } while (hr == S_OK);
680
681             } else if (state == State_Paused)
682                 lpParms->dwReturn = MCI_MODE_PAUSE;
683             break;
684         }
685         case MCI_STATUS_MEDIA_PRESENT:
686             FIXME("MCI_STATUS_MEDIA_PRESENT not implemented yet\n");
687             return MCIERR_UNRECOGNIZED_COMMAND;
688         case MCI_STATUS_TIME_FORMAT:
689             lpParms->dwReturn = wma->time_format;
690             break;
691         case MCI_STATUS_READY:
692             FIXME("MCI_STATUS_READY not implemented yet\n");
693             return MCIERR_UNRECOGNIZED_COMMAND;
694         case MCI_STATUS_CURRENT_TRACK:
695             FIXME("MCI_STATUS_CURRENT_TRACK not implemented yet\n");
696             return MCIERR_UNRECOGNIZED_COMMAND;
697         default:
698             FIXME("Unknown command %08X\n", lpParms->dwItem);
699             return MCIERR_UNRECOGNIZED_COMMAND;
700     }
701
702     if (dwFlags & MCI_NOTIFY)
703         mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)), wDevID, MCI_NOTIFY_SUCCESSFUL);
704
705     return 0;
706 }
707
708 /***************************************************************************
709  *                              MCIQTZ_mciWhere                 [internal]
710  */
711 static DWORD MCIQTZ_mciWhere(UINT wDevID, DWORD dwFlags, LPMCI_DGV_RECT_PARMS lpParms)
712 {
713     WINE_MCIQTZ* wma;
714     HRESULT hr;
715     HWND hWnd;
716     RECT rc;
717     DWORD ret = MCIERR_UNRECOGNIZED_COMMAND;
718
719     TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
720
721     if (!lpParms)
722         return MCIERR_NULL_PARAMETER_BLOCK;
723
724     wma = MCIQTZ_mciGetOpenDev(wDevID);
725     if (!wma)
726         return MCIERR_INVALID_DEVICE_ID;
727
728     hr = IVideoWindow_get_Owner(wma->vidwin, (OAHWND*)&hWnd);
729     if (FAILED(hr)) {
730         TRACE("No video stream, returning no window error\n");
731         return MCIERR_NO_WINDOW;
732     }
733
734     if (dwFlags & MCI_DGV_WHERE_SOURCE) {
735         if (dwFlags & MCI_DGV_WHERE_MAX)
736             FIXME("MCI_DGV_WHERE_SOURCE_MAX stub %s\n", wine_dbgstr_rect(&rc));
737         IBasicVideo_GetSourcePosition(wma->vidbasic, &rc.left, &rc.top, &rc.right, &rc.bottom);
738         TRACE("MCI_DGV_WHERE_SOURCE %s\n", wine_dbgstr_rect(&rc));
739     }
740     if (dwFlags & MCI_DGV_WHERE_DESTINATION) {
741         if (dwFlags & MCI_DGV_WHERE_MAX)
742             FIXME("MCI_DGV_WHERE_DESTINATION_MAX stub %s\n", wine_dbgstr_rect(&rc));
743         IBasicVideo_GetDestinationPosition(wma->vidbasic, &rc.left, &rc.top, &rc.right, &rc.bottom);
744         TRACE("MCI_DGV_WHERE_DESTINATION %s\n", wine_dbgstr_rect(&rc));
745     }
746     if (dwFlags & MCI_DGV_WHERE_FRAME) {
747         if (dwFlags & MCI_DGV_WHERE_MAX)
748             FIXME("MCI_DGV_WHERE_FRAME_MAX not supported yet\n");
749         else
750             FIXME("MCI_DGV_WHERE_FRAME not supported yet\n");
751         goto out;
752     }
753     if (dwFlags & MCI_DGV_WHERE_VIDEO) {
754         if (dwFlags & MCI_DGV_WHERE_MAX)
755             FIXME("MCI_DGV_WHERE_VIDEO_MAX not supported yet\n");
756         else
757             FIXME("MCI_DGV_WHERE_VIDEO not supported yet\n");
758         goto out;
759     }
760     if (dwFlags & MCI_DGV_WHERE_WINDOW) {
761         if (dwFlags & MCI_DGV_WHERE_MAX) {
762             GetWindowRect(GetDesktopWindow(), &rc);
763             rc.right -= rc.left;
764             rc.bottom -= rc.top;
765             TRACE("MCI_DGV_WHERE_WINDOW_MAX %s\n", wine_dbgstr_rect(&rc));
766         } else {
767             IVideoWindow_GetWindowPosition(wma->vidwin, &rc.left, &rc.top, &rc.right, &rc.bottom);
768             TRACE("MCI_DGV_WHERE_WINDOW %s\n", wine_dbgstr_rect(&rc));
769         }
770     }
771     ret = 0;
772 out:
773     lpParms->rc = rc;
774     return ret;
775 }
776
777 /***************************************************************************
778  *                              MCIQTZ_mciWindow                [internal]
779  */
780 static DWORD MCIQTZ_mciWindow(UINT wDevID, DWORD dwFlags, LPMCI_DGV_WINDOW_PARMSW lpParms)
781 {
782     WINE_MCIQTZ *wma = MCIQTZ_mciGetOpenDev(wDevID);
783
784     TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
785
786     if (!lpParms)
787         return MCIERR_NULL_PARAMETER_BLOCK;
788     if (!wma)
789         return MCIERR_INVALID_DEVICE_ID;
790     if (dwFlags & MCI_TEST)
791         return 0;
792
793     if (dwFlags & MCI_DGV_WINDOW_HWND && (IsWindow(lpParms->hWnd) || !lpParms->hWnd)) {
794         LONG visible = OATRUE;
795         LONG style = 0;
796         TRACE("Setting hWnd to %p\n", lpParms->hWnd);
797         IVideoWindow_get_Visible(wma->vidwin, &visible);
798         IVideoWindow_put_Visible(wma->vidwin, OAFALSE);
799         IVideoWindow_get_WindowStyle(wma->vidwin, &style);
800         style &= ~WS_CHILD;
801         if (lpParms->hWnd)
802             IVideoWindow_put_WindowStyle(wma->vidwin, style|WS_CHILD);
803         else
804             IVideoWindow_put_WindowStyle(wma->vidwin, style);
805         IVideoWindow_put_Owner(wma->vidwin, (OAHWND)lpParms->hWnd);
806         IVideoWindow_put_MessageDrain(wma->vidwin, (OAHWND)lpParms->hWnd);
807         IVideoWindow_put_Visible(wma->vidwin, visible);
808         wma->parent = lpParms->hWnd;
809     }
810     if (dwFlags & MCI_DGV_WINDOW_STATE) {
811         TRACE("Setting nCmdShow to %d\n", lpParms->nCmdShow);
812         IVideoWindow_put_WindowState(wma->vidwin, lpParms->nCmdShow);
813     }
814     if (dwFlags & MCI_DGV_WINDOW_TEXT) {
815         TRACE("Setting caption to %s\n", debugstr_w(lpParms->lpstrText));
816         IVideoWindow_put_Caption(wma->vidwin, lpParms->lpstrText);
817     }
818     return 0;
819 }
820
821 /******************************************************************************
822  *              MCIAVI_mciUpdate            [internal]
823  */
824 static DWORD MCIQTZ_mciUpdate(UINT wDevID, DWORD dwFlags, LPMCI_DGV_UPDATE_PARMS lpParms)
825 {
826     WINE_MCIQTZ *wma;
827     DWORD res = 0;
828
829     TRACE("%04x, %08x, %p\n", wDevID, dwFlags, lpParms);
830
831     if (!lpParms)
832         return MCIERR_NULL_PARAMETER_BLOCK;
833
834     wma = MCIQTZ_mciGetOpenDev(wDevID);
835     if (!wma)
836         return MCIERR_INVALID_DEVICE_ID;
837
838     if (dwFlags & MCI_DGV_UPDATE_HDC) {
839         LONG state, size;
840         BYTE *data;
841         BITMAPINFO *info;
842         HRESULT hr;
843         RECT src, dest;
844         LONG visible = OATRUE;
845
846         res = MCIERR_INTERNAL;
847         IMediaControl_GetState(wma->pmctrl, -1, &state);
848         if (state == State_Running)
849             return MCIERR_UNSUPPORTED_FUNCTION;
850         /* If in stopped state, nothing has been drawn to screen
851          * moving to pause, which is needed for the old dib renderer, will result
852          * in a single frame drawn, so hide the window here */
853         IVideoWindow_get_Visible(wma->vidwin, &visible);
854         if (wma->parent)
855             IVideoWindow_put_Visible(wma->vidwin, OAFALSE);
856         /* FIXME: Should we check the original state and restore it? */
857         IMediaControl_Pause(wma->pmctrl);
858         IMediaControl_GetState(wma->pmctrl, -1, &state);
859         if (FAILED(hr = IBasicVideo_GetCurrentImage(wma->vidbasic, &size, NULL))) {
860             WARN("Could not get image size (hr = %x)\n", hr);
861             goto out;
862         }
863         data = HeapAlloc(GetProcessHeap(), 0, size);
864         info = (BITMAPINFO*)data;
865         IBasicVideo_GetCurrentImage(wma->vidbasic, &size, (LONG*)data);
866         data += info->bmiHeader.biSize;
867
868         IBasicVideo_GetSourcePosition(wma->vidbasic, &src.left, &src.top, &src.right, &src.bottom);
869         IBasicVideo_GetDestinationPosition(wma->vidbasic, &dest.left, &dest.top, &dest.right, &dest.bottom);
870         StretchDIBits(lpParms->hDC,
871               dest.left, dest.top, dest.right + dest.left, dest.bottom + dest.top,
872               src.left, src.top, src.right + src.left, src.bottom + src.top,
873               data, info, DIB_RGB_COLORS, SRCCOPY);
874         HeapFree(GetProcessHeap(), 0, data);
875         res = 0;
876 out:
877         if (wma->parent)
878             IVideoWindow_put_Visible(wma->vidwin, visible);
879     }
880     else if (dwFlags)
881         FIXME("Unhandled flags %x\n", dwFlags);
882     return res;
883 }
884
885 /***************************************************************************
886  *                              MCIQTZ_mciSetAudio              [internal]
887  */
888 static DWORD MCIQTZ_mciSetAudio(UINT wDevID, DWORD dwFlags, LPMCI_DGV_SETAUDIO_PARMSW lpParms)
889 {
890     WINE_MCIQTZ *wma;
891
892     FIXME("(%04x, %08x, %p) : stub\n", wDevID, dwFlags, lpParms);
893
894     if (!lpParms)
895         return MCIERR_NULL_PARAMETER_BLOCK;
896
897     wma = MCIQTZ_mciGetOpenDev(wDevID);
898     if (!wma)
899         return MCIERR_INVALID_DEVICE_ID;
900
901     MCIQTZ_mciStop(wDevID, MCI_WAIT, NULL);
902
903     return 0;
904 }
905
906 /*======================================================================*
907  *                          MCI QTZ entry points                        *
908  *======================================================================*/
909
910 /**************************************************************************
911  *                              DriverProc (MCIQTZ.@)
912  */
913 LRESULT CALLBACK MCIQTZ_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
914                                    LPARAM dwParam1, LPARAM dwParam2)
915 {
916     TRACE("(%08lX, %p, %08X, %08lX, %08lX)\n",
917           dwDevID, hDriv, wMsg, dwParam1, dwParam2);
918
919     switch (wMsg) {
920         case DRV_LOAD:                  return 1;
921         case DRV_FREE:                  return 1;
922         case DRV_OPEN:                  return MCIQTZ_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
923         case DRV_CLOSE:                 return MCIQTZ_drvClose(dwDevID);
924         case DRV_ENABLE:                return 1;
925         case DRV_DISABLE:               return 1;
926         case DRV_QUERYCONFIGURE:        return 1;
927         case DRV_CONFIGURE:             return MCIQTZ_drvConfigure(dwDevID);
928         case DRV_INSTALL:               return DRVCNF_RESTART;
929         case DRV_REMOVE:                return DRVCNF_RESTART;
930     }
931
932     /* session instance */
933     if (dwDevID == 0xFFFFFFFF)
934         return 1;
935
936     switch (wMsg) {
937         case MCI_OPEN_DRIVER:   return MCIQTZ_mciOpen      (dwDevID, dwParam1, (LPMCI_DGV_OPEN_PARMSW)     dwParam2);
938         case MCI_CLOSE_DRIVER:  return MCIQTZ_mciClose     (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)       dwParam2);
939         case MCI_PLAY:          return MCIQTZ_mciPlay      (dwDevID, dwParam1, (LPMCI_PLAY_PARMS)          dwParam2);
940         case MCI_SEEK:          return MCIQTZ_mciSeek      (dwDevID, dwParam1, (LPMCI_SEEK_PARMS)          dwParam2);
941         case MCI_STOP:          return MCIQTZ_mciStop      (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)       dwParam2);
942         case MCI_PAUSE:         return MCIQTZ_mciPause     (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)       dwParam2);
943         case MCI_GETDEVCAPS:    return MCIQTZ_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS)    dwParam2);
944         case MCI_SET:           return MCIQTZ_mciSet       (dwDevID, dwParam1, (LPMCI_DGV_SET_PARMS)       dwParam2);
945         case MCI_STATUS:        return MCIQTZ_mciStatus    (dwDevID, dwParam1, (LPMCI_DGV_STATUS_PARMSW)   dwParam2);
946         case MCI_WHERE:         return MCIQTZ_mciWhere     (dwDevID, dwParam1, (LPMCI_DGV_RECT_PARMS)      dwParam2);
947         /* Digital Video specific */
948         case MCI_SETAUDIO:      return MCIQTZ_mciSetAudio  (dwDevID, dwParam1, (LPMCI_DGV_SETAUDIO_PARMSW) dwParam2);
949         case MCI_UPDATE:
950             return MCIQTZ_mciUpdate(dwDevID, dwParam1, (LPMCI_DGV_UPDATE_PARMS)dwParam2);
951         case MCI_WINDOW:
952             return MCIQTZ_mciWindow(dwDevID, dwParam1, (LPMCI_DGV_WINDOW_PARMSW)dwParam2);
953         case MCI_PUT:
954         case MCI_RECORD:
955         case MCI_RESUME:
956         case MCI_INFO:
957         case MCI_LOAD:
958         case MCI_SAVE:
959         case MCI_FREEZE:
960         case MCI_REALIZE:
961         case MCI_UNFREEZE:
962         case MCI_STEP:
963         case MCI_COPY:
964         case MCI_CUT:
965         case MCI_DELETE:
966         case MCI_PASTE:
967         case MCI_CUE:
968         /* Digital Video specific */
969         case MCI_CAPTURE:
970         case MCI_MONITOR:
971         case MCI_RESERVE:
972         case MCI_SIGNAL:
973         case MCI_SETVIDEO:
974         case MCI_QUALITY:
975         case MCI_LIST:
976         case MCI_UNDO:
977         case MCI_CONFIGURE:
978         case MCI_RESTORE:
979             FIXME("Unimplemented command [%08X]\n", wMsg);
980             break;
981         case MCI_SPIN:
982         case MCI_ESCAPE:
983             WARN("Unsupported command [%08X]\n", wMsg);
984             break;
985         case MCI_OPEN:
986         case MCI_CLOSE:
987             FIXME("Shouldn't receive a MCI_OPEN or CLOSE message\n");
988             break;
989         default:
990             TRACE("Sending msg [%08X] to default driver proc\n", wMsg);
991             return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
992     }
993
994     return MCIERR_UNRECOGNIZED_COMMAND;
995 }