Implement ResetDC and PHYSICALOFFSET[X|Y] devcaps.
[wine] / dlls / quartz / fgevent.c
1 /*
2  * CLSID_FilterGraph event handling.
3  *
4  * Copyright (C) Hidenori TAKESHIMA <hidenori@a2.ctktv.ne.jp>
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22
23 #include "windef.h"
24 #include "winbase.h"
25 #include "wingdi.h"
26 #include "winuser.h"
27 #include "winerror.h"
28 #include "strmif.h"
29 #include "control.h"
30 #include "evcode.h"
31 #include "uuids.h"
32 #include "vfwmsgs.h"
33
34 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
36
37 #include "quartz_private.h"
38 #include "fgraph.h"
39
40 #define EVENTQUEUE_BLOCKSIZE    2
41 #define EVENTQUEUE_MAX                  1024
42
43 struct FilterGraph_MEDIAEVENT
44 {
45         long            lEventCode;
46         LONG_PTR        lParam1;
47         LONG_PTR        lParam2;
48 };
49
50
51 static HRESULT FGEVENT_KeepEvent(
52         BOOL bKeep,
53         long lEventCode, LONG_PTR lParam1, LONG_PTR lParam2 )
54 {
55         switch ( lEventCode )
56         {
57         /*case EC_COMPLETE:*/
58         case EC_USERABORT:
59                 break;
60         case EC_ERRORABORT:
61                 break;
62         case EC_TIME:
63                 break;
64         /*case EC_REPAINT:*/
65         case EC_STREAM_ERROR_STOPPED:
66                 break;
67         case EC_STREAM_ERROR_STILLPLAYING:
68                 break;
69         case EC_ERROR_STILLPLAYING:
70                 break;
71         case EC_PALETTE_CHANGED:
72                 break;
73         case EC_VIDEO_SIZE_CHANGED:
74                 break;
75         case EC_QUALITY_CHANGE:
76                 break;
77         /*case EC_SHUTTING_DOWN:*/
78         case EC_CLOCK_CHANGED:
79                 break;
80
81         case EC_OPENING_FILE:
82                 break;
83         case EC_BUFFERING_DATA:
84                 break;
85         case EC_FULLSCREEN_LOST:
86                 if ( bKeep )
87                 {
88                         if ( ((IBaseFilter*)lParam2) != NULL )
89                                 IBaseFilter_AddRef( (IBaseFilter*)lParam2 );
90                 }
91                 else
92                 {
93                         if ( ((IBaseFilter*)lParam2) != NULL )
94                                 IBaseFilter_Release( (IBaseFilter*)lParam2 );
95                 }
96                 break;
97         /*case EC_ACTIVATE:*/
98         /*case EC_NEED_RESTART:*/
99         /*case EC_WINDOW_DESTROYED:*/
100         /*case EC_DISPLAY_CHANGED:*/
101         /*case EC_STARVATION:*/
102         /*case EC_OLE_EVENT:*/
103         /*case EC_NOTIFY_WINDOW:*/
104         /*case EC_STREAM_CONTROL_STOPPED:*/
105         /*case EC_STREAM_CONTROL_STARTED:*/
106         /*case EC_END_OF_SEGMENT:*/
107         /*case EC_SEGMENT_STARTED:*/
108         case EC_LENGTH_CHANGED:
109                 break;
110
111         default:
112                 if ( lEventCode < EC_USER )
113                 {
114                         FIXME( "unknown system event %08lx\n", lEventCode );
115                         return E_INVALIDARG;
116                 }
117                 TRACE( "user event %08lx\n", lEventCode );
118                 break;
119         }
120
121         return NOERROR;
122 }
123
124 /***************************************************************************
125  *
126  *      CLSID_FilterGraph::IMediaEvent[Ex]
127  *
128  */
129
130 static HRESULT WINAPI
131 IMediaEventEx_fnQueryInterface(IMediaEventEx* iface,REFIID riid,void** ppobj)
132 {
133         CFilterGraph_THIS(iface,mediaevent);
134
135         TRACE("(%p)->()\n",This);
136
137         return IUnknown_QueryInterface(This->unk.punkControl,riid,ppobj);
138 }
139
140 static ULONG WINAPI
141 IMediaEventEx_fnAddRef(IMediaEventEx* iface)
142 {
143         CFilterGraph_THIS(iface,mediaevent);
144
145         TRACE("(%p)->()\n",This);
146
147         return IUnknown_AddRef(This->unk.punkControl);
148 }
149
150 static ULONG WINAPI
151 IMediaEventEx_fnRelease(IMediaEventEx* iface)
152 {
153         CFilterGraph_THIS(iface,mediaevent);
154
155         TRACE("(%p)->()\n",This);
156
157         return IUnknown_Release(This->unk.punkControl);
158 }
159
160 static HRESULT WINAPI
161 IMediaEventEx_fnGetTypeInfoCount(IMediaEventEx* iface,UINT* pcTypeInfo)
162 {
163         CFilterGraph_THIS(iface,mediaevent);
164
165         FIXME("(%p)->()\n",This);
166
167         return E_NOTIMPL;
168 }
169
170 static HRESULT WINAPI
171 IMediaEventEx_fnGetTypeInfo(IMediaEventEx* iface,UINT iTypeInfo, LCID lcid, ITypeInfo** ppobj)
172 {
173         CFilterGraph_THIS(iface,mediaevent);
174
175         FIXME("(%p)->()\n",This);
176
177         return E_NOTIMPL;
178 }
179
180 static HRESULT WINAPI
181 IMediaEventEx_fnGetIDsOfNames(IMediaEventEx* iface,REFIID riid, LPOLESTR* ppwszName, UINT cNames, LCID lcid, DISPID* pDispId)
182 {
183         CFilterGraph_THIS(iface,mediaevent);
184
185         FIXME("(%p)->()\n",This);
186
187         return E_NOTIMPL;
188 }
189
190 static HRESULT WINAPI
191 IMediaEventEx_fnInvoke(IMediaEventEx* iface,DISPID DispId, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarRes, EXCEPINFO* pExcepInfo, UINT* puArgErr)
192 {
193         CFilterGraph_THIS(iface,mediaevent);
194
195         FIXME("(%p)->()\n",This);
196
197         return E_NOTIMPL;
198 }
199
200
201 static HRESULT WINAPI
202 IMediaEventEx_fnGetEventHandle(IMediaEventEx* iface,OAEVENT* hEvent)
203 {
204         CFilterGraph_THIS(iface,mediaevent);
205
206         TRACE("(%p)->()\n",This);
207
208         *hEvent = (OAEVENT)This->m_hMediaEvent;
209
210         return NOERROR;
211 }
212
213 static HRESULT WINAPI
214 IMediaEventEx_fnGetEvent(IMediaEventEx* iface,long* plEventCode,LONG_PTR* plParam1,LONG_PTR* plParam2,long lTimeOut)
215 {
216         CFilterGraph_THIS(iface,mediaevent);
217         ULONG cQueued;
218         DWORD dw;
219         DWORD dwStart;
220         HRESULT hr;
221         FilterGraph_MEDIAEVENT* pEvent;
222
223         TRACE("(%p)->(%p,%p,%p,%ld)\n",This,plEventCode,
224                 plParam1,plParam2,lTimeOut);
225
226         if ( plEventCode == NULL || plParam1 == NULL || plParam2 == NULL )
227                 return E_POINTER;
228
229         while ( 1 )
230         {
231                 dwStart = GetTickCount();
232                 dw = WaitForSingleObject( This->m_hMediaEvent, lTimeOut );
233                 if ( dw == WAIT_TIMEOUT )
234                         return VFW_E_TIMEOUT;
235                 if ( dw != WAIT_OBJECT_0 )
236                         return E_FAIL;
237
238                 EnterCriticalSection( &This->m_csMediaEvents );
239                 hr = S_FALSE;
240                 if ( This->m_cbMediaEventsMax > 0 )
241                 {
242                         cQueued =
243                                 (This->m_cbMediaEventsMax +
244                                  This->m_cbMediaEventsPut - This->m_cbMediaEventsGet) %
245                                         This->m_cbMediaEventsMax;
246                         if ( cQueued > 0 )
247                         {
248                                 pEvent = &This->m_pMediaEvents[This->m_cbMediaEventsGet];
249                                 *plEventCode = pEvent->lEventCode;
250                                 *plParam1 = pEvent->lParam1;
251                                 *plParam2 = pEvent->lParam2;
252                                 This->m_cbMediaEventsGet = (This->m_cbMediaEventsGet + 1) %
253                                                 This->m_cbMediaEventsMax;
254
255                                 hr = NOERROR;
256                                 if ( This->m_cbMediaEventsPut == This->m_cbMediaEventsGet )
257                                         ResetEvent( This->m_hMediaEvent );
258                         }
259                 }
260                 LeaveCriticalSection( &This->m_csMediaEvents );
261
262                 if ( hr != S_FALSE )
263                         return hr;
264                 if ( lTimeOut != INFINITE )
265                 {
266                         lTimeOut -= GetTickCount() - dwStart;
267                         if ( lTimeOut < 0 )
268                                 return VFW_E_TIMEOUT;
269                 }
270         }
271 }
272
273 static HRESULT WINAPI
274 IMediaEventEx_fnWaitForCompletion(IMediaEventEx* iface,long lTimeOut,long* plEventCode)
275 {
276         CFilterGraph_THIS(iface,mediaevent);
277         HRESULT hr;
278         long lEventCode;
279         LONG_PTR lParam1;
280         LONG_PTR lParam2;
281         DWORD dwTimePrev;
282         DWORD dwTimeCur;
283
284         TRACE("(%p)->(%ld,%p)\n",This,lTimeOut,plEventCode);
285
286         if ( plEventCode == NULL )
287                 return E_POINTER;
288         *plEventCode = 0;
289
290         dwTimePrev = GetTickCount();
291
292         while ( 1 )
293         {
294                 hr = IMediaEventEx_GetEvent(
295                                 CFilterGraph_IMediaEventEx(This),
296                                 &lEventCode,&lParam1,&lParam2,lTimeOut);
297                 if ( hr == VFW_E_TIMEOUT )
298                         hr = E_ABORT;
299                 if ( hr != NOERROR )
300                         return hr;
301                 IMediaEventEx_FreeEventParams(
302                                 CFilterGraph_IMediaEventEx(This),
303                                 lEventCode,lParam1,lParam2);
304
305                 if ( lEventCode == EC_COMPLETE ||
306                          lEventCode == EC_ERRORABORT ||
307                          lEventCode == EC_USERABORT )
308                 {
309                         *plEventCode = lEventCode;
310                         return NOERROR;
311                 }
312
313                 if ( lTimeOut != INFINITE )
314                 {
315                         dwTimeCur = GetTickCount();
316                         lTimeOut -= dwTimeCur - dwTimePrev;
317                         dwTimePrev = dwTimeCur;
318                         if ( lTimeOut < 0 )
319                                 return E_ABORT;
320                 }
321         }
322 }
323
324 static HRESULT WINAPI
325 IMediaEventEx_fnCancelDefaultHandling(IMediaEventEx* iface,long lEventCode)
326 {
327         CFilterGraph_THIS(iface,mediaevent);
328
329         FIXME("(%p)->() stub!\n",This);
330
331         return E_NOTIMPL;
332 }
333
334 static HRESULT WINAPI
335 IMediaEventEx_fnRestoreDefaultHandling(IMediaEventEx* iface,long lEventCode)
336 {
337         CFilterGraph_THIS(iface,mediaevent);
338
339         FIXME("(%p)->() stub!\n",This);
340
341         return E_NOTIMPL;
342 }
343
344 static HRESULT WINAPI
345 IMediaEventEx_fnFreeEventParams(IMediaEventEx* iface,long lEventCode,LONG_PTR lParam1,LONG_PTR lParam2)
346 {
347         CFilterGraph_THIS(iface,mediaevent);
348
349         TRACE("(%p)->(%08lx,%08x,%08x)\n",This,lEventCode,lParam1,lParam2);
350
351         return FGEVENT_KeepEvent( FALSE, lEventCode, lParam1, lParam2 );
352 }
353
354 static HRESULT WINAPI
355 IMediaEventEx_fnSetNotifyWindow(IMediaEventEx* iface,OAHWND hwnd,long message,LONG_PTR lParam)
356 {
357         CFilterGraph_THIS(iface,mediaevent);
358
359         TRACE("(%p)->(%08x,%08lx,%08x)\n",This,hwnd,message,lParam);
360
361         EnterCriticalSection( &This->m_csMediaEvents );
362         This->m_hwndEventNotify = (HWND)hwnd;
363         This->m_lEventNotifyMsg = message;
364         This->m_lEventNotifyParam = lParam;
365         LeaveCriticalSection( &This->m_csMediaEvents );
366
367         return NOERROR;
368 }
369
370 static HRESULT WINAPI
371 IMediaEventEx_fnSetNotifyFlags(IMediaEventEx* iface,long lNotifyFlags)
372 {
373         CFilterGraph_THIS(iface,mediaevent);
374
375         TRACE("(%p)->(%ld)\n",This,lNotifyFlags);
376
377         if ( lNotifyFlags != 0 && lNotifyFlags != 1 )
378                 return E_INVALIDARG;
379
380         EnterCriticalSection( &This->m_csMediaEvents );
381         This->m_lEventNotifyFlags = lNotifyFlags;
382         LeaveCriticalSection( &This->m_csMediaEvents );
383
384         return NOERROR;
385 }
386
387 static HRESULT WINAPI
388 IMediaEventEx_fnGetNotifyFlags(IMediaEventEx* iface,long* plNotifyFlags)
389 {
390         CFilterGraph_THIS(iface,mediaevent);
391
392         TRACE("(%p)->(%p)\n",This,plNotifyFlags);
393
394         if ( plNotifyFlags == NULL )
395                 return E_POINTER;
396
397         EnterCriticalSection( &This->m_csMediaEvents );
398         *plNotifyFlags = This->m_lEventNotifyFlags;
399         LeaveCriticalSection( &This->m_csMediaEvents );
400
401         return NOERROR;
402 }
403
404
405
406 static ICOM_VTABLE(IMediaEventEx) imediaevent =
407 {
408         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
409         /* IUnknown fields */
410         IMediaEventEx_fnQueryInterface,
411         IMediaEventEx_fnAddRef,
412         IMediaEventEx_fnRelease,
413         /* IDispatch fields */
414         IMediaEventEx_fnGetTypeInfoCount,
415         IMediaEventEx_fnGetTypeInfo,
416         IMediaEventEx_fnGetIDsOfNames,
417         IMediaEventEx_fnInvoke,
418         /* IMediaEvent fields */
419         IMediaEventEx_fnGetEventHandle,
420         IMediaEventEx_fnGetEvent,
421         IMediaEventEx_fnWaitForCompletion,
422         IMediaEventEx_fnCancelDefaultHandling,
423         IMediaEventEx_fnRestoreDefaultHandling,
424         IMediaEventEx_fnFreeEventParams,
425         /* IMediaEventEx fields */
426         IMediaEventEx_fnSetNotifyWindow,
427         IMediaEventEx_fnSetNotifyFlags,
428         IMediaEventEx_fnGetNotifyFlags,
429 };
430
431
432 HRESULT CFilterGraph_InitIMediaEventEx( CFilterGraph* pfg )
433 {
434         TRACE("(%p)\n",pfg);
435         ICOM_VTBL(&pfg->mediaevent) = &imediaevent;
436
437         pfg->m_hMediaEvent = CreateEventA( NULL, TRUE, FALSE, NULL );
438         if ( pfg->m_hMediaEvent == (HANDLE)NULL )
439                 return E_OUTOFMEMORY;
440
441         InitializeCriticalSection( &pfg->m_csMediaEvents );
442         pfg->m_pMediaEvents = NULL;
443         pfg->m_cbMediaEventsPut = 0;
444         pfg->m_cbMediaEventsGet = 0;
445         pfg->m_cbMediaEventsMax = 0;
446         pfg->m_hwndEventNotify = (HWND)NULL;
447         pfg->m_lEventNotifyMsg = 0;
448         pfg->m_lEventNotifyParam = 0;
449         pfg->m_lEventNotifyFlags = 0;
450
451         return NOERROR;
452 }
453
454 void CFilterGraph_UninitIMediaEventEx( CFilterGraph* pfg )
455 {
456         HRESULT hr;
457         long lEventCode;
458         LONG_PTR lParam1;
459         LONG_PTR lParam2;
460
461         TRACE("(%p)\n",pfg);
462
463         while ( 1 )
464         {
465                 hr = IMediaEventEx_GetEvent(
466                                 CFilterGraph_IMediaEventEx(pfg),
467                                 &lEventCode,&lParam1,&lParam2,0);
468                 if ( hr != NOERROR )
469                         break;
470                 IMediaEventEx_FreeEventParams(
471                                 CFilterGraph_IMediaEventEx(pfg),
472                                 lEventCode,lParam1,lParam2);
473         }
474
475         if ( pfg->m_pMediaEvents != NULL )
476         {
477                 QUARTZ_FreeMem( pfg->m_pMediaEvents );
478                 pfg->m_pMediaEvents = NULL;
479         }
480
481         DeleteCriticalSection( &pfg->m_csMediaEvents );
482         CloseHandle( pfg->m_hMediaEvent );
483 }
484
485 /***************************************************************************
486  *
487  *      CLSID_FilterGraph::IMediaEventSink
488  *
489  */
490
491 static HRESULT WINAPI
492 IMediaEventSink_fnQueryInterface(IMediaEventSink* iface,REFIID riid,void** ppobj)
493 {
494         CFilterGraph_THIS(iface,mediaeventsink);
495
496         TRACE("(%p)->()\n",This);
497
498         return IUnknown_QueryInterface(This->unk.punkControl,riid,ppobj);
499 }
500
501 static ULONG WINAPI
502 IMediaEventSink_fnAddRef(IMediaEventSink* iface)
503 {
504         CFilterGraph_THIS(iface,mediaeventsink);
505
506         TRACE("(%p)->()\n",This);
507
508         return IUnknown_AddRef(This->unk.punkControl);
509 }
510
511 static ULONG WINAPI
512 IMediaEventSink_fnRelease(IMediaEventSink* iface)
513 {
514         CFilterGraph_THIS(iface,mediaeventsink);
515
516         TRACE("(%p)->()\n",This);
517
518         return IUnknown_Release(This->unk.punkControl);
519 }
520
521 static HRESULT WINAPI
522 IMediaEventSink_fnNotify(IMediaEventSink* iface,long lEventCode,LONG_PTR lParam1,LONG_PTR lParam2)
523 {
524         CFilterGraph_THIS(iface,mediaeventsink);
525         HRESULT hr = NOERROR;
526         ULONG cQueued;
527         ULONG cTemp;
528         FilterGraph_MEDIAEVENT* pEvent;
529
530         TRACE("(%p)->(%08lx,%08x,%08x) stub!\n",This,lEventCode,lParam1,lParam2);
531
532         EnterCriticalSection( &This->m_csMediaEvents );
533
534         /* allocate a new entry. */
535         if ( This->m_cbMediaEventsMax == 0 )
536                 cQueued = 0;
537         else
538                 cQueued =
539                         (This->m_cbMediaEventsMax +
540                          This->m_cbMediaEventsPut - This->m_cbMediaEventsGet) %
541                                 This->m_cbMediaEventsMax;
542
543         if ( (cQueued + 1) >= This->m_cbMediaEventsMax )
544         {
545                 if ( This->m_cbMediaEventsMax >= EVENTQUEUE_MAX )
546                 {
547                         hr = E_FAIL;
548                         goto end;
549                 }
550                 pEvent = (FilterGraph_MEDIAEVENT*)
551                         QUARTZ_AllocMem( sizeof(FilterGraph_MEDIAEVENT) *
552                                 (This->m_cbMediaEventsMax+EVENTQUEUE_BLOCKSIZE) );
553                 if ( pEvent == NULL )
554                 {
555                         hr = E_OUTOFMEMORY;
556                         goto end;
557                 }
558                 if ( cQueued > 0 )
559                 {
560                         if ( (This->m_cbMediaEventsGet + cQueued) >=
561                                 This->m_cbMediaEventsMax )
562                         {
563                                 cTemp = This->m_cbMediaEventsMax - This->m_cbMediaEventsGet;
564                                 memcpy(
565                                         pEvent,
566                                         &This->m_pMediaEvents[This->m_cbMediaEventsGet],
567                                         sizeof(FilterGraph_MEDIAEVENT) * cTemp );
568                                 memcpy(
569                                         pEvent + cTemp,
570                                         &This->m_pMediaEvents[0],
571                                         sizeof(FilterGraph_MEDIAEVENT) * (cQueued - cTemp) );
572                         }
573                         else
574                         {
575                                 memcpy(
576                                         pEvent,
577                                         &This->m_pMediaEvents[This->m_cbMediaEventsGet],
578                                         sizeof(FilterGraph_MEDIAEVENT) * cQueued );
579                         }
580                         QUARTZ_FreeMem( This->m_pMediaEvents );
581                 }
582                 This->m_pMediaEvents = pEvent;
583                 This->m_cbMediaEventsMax += EVENTQUEUE_BLOCKSIZE;
584                 This->m_cbMediaEventsPut = cQueued;
585                 This->m_cbMediaEventsGet = 0;
586         }
587
588         /* duplicate params if necessary. */
589         hr = FGEVENT_KeepEvent( TRUE, lEventCode, lParam1, lParam2 );
590         if ( FAILED(hr) )
591                 goto end;
592
593         /* add to the queue. */
594         pEvent = &This->m_pMediaEvents[This->m_cbMediaEventsPut];
595         pEvent->lEventCode = lEventCode;
596         pEvent->lParam1 = lParam1;
597         pEvent->lParam2 = lParam2;
598         This->m_cbMediaEventsPut =
599                 (This->m_cbMediaEventsPut + 1) % This->m_cbMediaEventsMax;
600
601         SetEvent( This->m_hMediaEvent );
602         if ( This->m_hwndEventNotify != (HWND)NULL &&
603                  This->m_lEventNotifyFlags == 0 )
604         {
605                 PostMessageA(
606                         This->m_hwndEventNotify,
607                         This->m_lEventNotifyMsg,
608                         (WPARAM)0,
609                         (LPARAM)This->m_lEventNotifyParam );
610         }
611
612         hr = NOERROR;
613 end:
614         LeaveCriticalSection( &This->m_csMediaEvents );
615
616         return hr;
617 }
618
619
620 static ICOM_VTABLE(IMediaEventSink) imediaeventsink =
621 {
622         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
623         /* IUnknown fields */
624         IMediaEventSink_fnQueryInterface,
625         IMediaEventSink_fnAddRef,
626         IMediaEventSink_fnRelease,
627         /* IMediaEventSink fields */
628         IMediaEventSink_fnNotify,
629 };
630
631
632
633 HRESULT CFilterGraph_InitIMediaEventSink( CFilterGraph* pfg )
634 {
635         TRACE("(%p)\n",pfg);
636         ICOM_VTBL(&pfg->mediaeventsink) = &imediaeventsink;
637
638         return NOERROR;
639 }
640
641 void CFilterGraph_UninitIMediaEventSink( CFilterGraph* pfg )
642 {
643         TRACE("(%p)\n",pfg);
644 }
645