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