Various cosmetic changes.
[wine] / dlls / quartz / sysclock.c
1 /*
2  * Implementation of CLSID_SystemClock.
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 "uuids.h"
16
17 #include "debugtools.h"
18 DEFAULT_DEBUG_CHANNEL(quartz);
19
20 #include "quartz_private.h"
21 #include "sysclock.h"
22
23
24 /***************************************************************************
25  *
26  *      new/delete for CLSID_SystemClock
27  *
28  */
29
30 /* can I use offsetof safely? - FIXME? */
31 static QUARTZ_IFEntry IFEntries[] =
32 {
33   { &IID_IReferenceClock, offsetof(CSystemClock,refclk)-offsetof(CSystemClock,unk) },
34 };
35
36
37 static void QUARTZ_DestroySystemClock(IUnknown* punk)
38 {
39         CSystemClock_THIS(punk,unk);
40
41         CSystemClock_UninitIReferenceClock( This );
42 }
43
44 HRESULT QUARTZ_CreateSystemClock(IUnknown* punkOuter,void** ppobj)
45 {
46         CSystemClock*   psc;
47         HRESULT hr;
48
49         TRACE("(%p,%p)\n",punkOuter,ppobj);
50
51         psc = (CSystemClock*)QUARTZ_AllocObj( sizeof(CSystemClock) );
52         if ( psc == NULL )
53                 return E_OUTOFMEMORY;
54
55         QUARTZ_IUnkInit( &psc->unk, punkOuter );
56         hr = CSystemClock_InitIReferenceClock( psc );
57         if ( FAILED(hr) )
58         {
59                 QUARTZ_FreeObj( psc );
60                 return hr;
61         }
62
63         psc->unk.pEntries = IFEntries;
64         psc->unk.dwEntries = sizeof(IFEntries)/sizeof(IFEntries[0]);
65         psc->unk.pOnFinalRelease = QUARTZ_DestroySystemClock;
66
67         *ppobj = (void*)(&psc->unk);
68
69         return S_OK;
70 }
71
72
73 /***************************************************************************
74  *
75  *      CLSID_SystemClock::IReferenceClock
76  *
77  */
78
79 #define QUARTZ_MSG_ADDTIMER                     (WM_APP+0)
80 #define QUARTZ_MSG_REMOVETIMER          (WM_APP+1)
81 #define QUARTZ_MSG_EXITTHREAD           (WM_APP+2)
82
83
84 /****************************************************************************/
85
86 static QUARTZ_TimerEntry* IReferenceClock_AllocTimerEntry(CSystemClock* This)
87 {
88         QUARTZ_TimerEntry*      pEntry;
89         DWORD   dw;
90
91         pEntry = &This->m_timerEntries[0];
92         for ( dw = 0; dw < WINE_QUARTZ_SYSCLOCK_TIMER_MAX; dw++ )
93         {
94                 if ( pEntry->hEvent == (HANDLE)NULL )
95                         return pEntry;
96                 pEntry ++;
97         }
98
99         return NULL;
100 }
101
102 static QUARTZ_TimerEntry* IReferenceClock_SearchTimer(CSystemClock* This, DWORD dwAdvCookie)
103 {
104         QUARTZ_TimerEntry*      pEntry;
105         DWORD   dw;
106
107         pEntry = &This->m_timerEntries[0];
108         for ( dw = 0; dw < WINE_QUARTZ_SYSCLOCK_TIMER_MAX; dw++ )
109         {
110                 if ( pEntry->hEvent != (HANDLE)NULL &&
111                          pEntry->dwAdvCookie == dwAdvCookie )
112                         return pEntry;
113                 pEntry ++;
114         }
115
116         return NULL;
117 }
118
119 static DWORD IReferenceClock_OnTimerUpdated(CSystemClock* This)
120 {
121         QUARTZ_TimerEntry*      pEntry;
122         REFERENCE_TIME  rtCur;
123         REFERENCE_TIME  rtSignal;
124         REFERENCE_TIME  rtCount;
125         HRESULT hr;
126         LONG    lCount;
127         DWORD   dw;
128         DWORD   dwTimeout = INFINITE;
129         DWORD   dwTimeoutCur;
130
131         hr = IReferenceClock_GetTime((IReferenceClock*)(&This->refclk),&rtCur);
132         if ( hr != NOERROR )
133                 return INFINITE;
134
135         pEntry = &This->m_timerEntries[0];
136         for ( dw = 0; dw < WINE_QUARTZ_SYSCLOCK_TIMER_MAX; dw++ )
137         {
138                 if ( pEntry->hEvent != (HANDLE)NULL )
139                 {
140                         rtSignal = pEntry->rtStart + pEntry->rtInterval;
141                         if ( rtCur >= rtSignal )
142                         {
143                                 if ( pEntry->fPeriodic )
144                                 {
145                                         rtCount = ((rtCur - pEntry->rtStart) / pEntry->rtInterval);
146                                         lCount = ( rtCount > (REFERENCE_TIME)0x7fffffff ) ?
147                                                 (LONG)0x7fffffff : (LONG)rtCount;
148                                         if ( !ReleaseSemaphore( pEntry->hEvent, lCount, NULL ) )
149                                         {
150                                                 while ( lCount > 0 )
151                                                 {
152                                                         if ( !ReleaseSemaphore( pEntry->hEvent, 1, NULL ) )
153                                                                 break;
154                                                 }
155                                         }
156                                         dwTimeout = 0;
157                                 }
158                                 else
159                                 {
160                                         TRACE( "signal an event\n" );
161                                         SetEvent( pEntry->hEvent );
162                                         pEntry->hEvent = (HANDLE)NULL;
163                                 }
164                         }
165                         else
166                         {
167                                 rtCount = rtSignal - rtCur;
168                                 /* [100ns] -> [ms] */
169                                 rtCount = (rtCount+(REFERENCE_TIME)9999)/(REFERENCE_TIME)10000;
170                                 dwTimeoutCur = (rtCount >= 0xfffffffe) ? (DWORD)0xfffffffe : (DWORD)rtCount;
171                                 if ( dwTimeout > dwTimeoutCur )
172                                         dwTimeout = dwTimeoutCur;
173                         }
174                 }
175                 pEntry ++;
176         }
177
178         return dwTimeout;
179 }
180
181 static
182 DWORD WINAPI IReferenceClock_TimerEntry( LPVOID lpvParam )
183 {
184         CSystemClock*   This = (CSystemClock*)lpvParam;
185         MSG     msg;
186         DWORD   dwRes;
187         DWORD   dwTimeout;
188
189         /* initialize the message queue. */
190         PeekMessageA( &msg, (HWND)NULL, 0, 0, PM_NOREMOVE );
191         /* resume the owner thread. */
192         SetEvent( This->m_hEventInit );
193
194         TRACE( "Enter message loop.\n" );
195
196         /* message loop. */
197         dwTimeout = INFINITE;
198         while ( 1 )
199         {
200                 if ( dwTimeout > 0 )
201                 {
202                         dwRes = MsgWaitForMultipleObjects(
203                                 0, NULL, FALSE,
204                                 dwTimeout,
205                                 QS_ALLEVENTS );
206                 }
207
208                 EnterCriticalSection( &This->m_csClock );
209                 dwTimeout = IReferenceClock_OnTimerUpdated(This);
210                 LeaveCriticalSection( &This->m_csClock );
211                 TRACE( "catch an event / timeout %lu\n", dwTimeout );
212
213                 while ( PeekMessageA( &msg, (HWND)NULL, 0, 0, PM_REMOVE ) )
214                 {
215                         if ( msg.message == WM_QUIT )
216                                 goto quitthread;
217
218                         if ( msg.hwnd != (HWND)NULL )
219                         {
220                                 TranslateMessage( &msg );
221                                 DispatchMessageA( &msg );
222                         }
223                         else
224                         {
225                                 switch ( msg.message )
226                                 {
227                                 case QUARTZ_MSG_ADDTIMER:
228                                 case QUARTZ_MSG_REMOVETIMER:
229                                         dwTimeout = 0;
230                                         break;
231                                 case QUARTZ_MSG_EXITTHREAD:
232                                         PostQuitMessage(0);
233                                         break;
234                                 default:
235                                         FIXME( "invalid message %04u\n", (unsigned)msg.message );
236                                         break;
237                                 }
238                         }
239                 }
240         }
241
242 quitthread:
243         TRACE( "quit thread\n" );
244         return 0;
245 }
246
247 /****************************************************************************/
248
249 static HRESULT WINAPI
250 IReferenceClock_fnQueryInterface(IReferenceClock* iface,REFIID riid,void** ppobj)
251 {
252         CSystemClock_THIS(iface,refclk);
253
254         TRACE("(%p)->()\n",This);
255
256         return IUnknown_QueryInterface(This->unk.punkControl,riid,ppobj);
257 }
258
259 static ULONG WINAPI
260 IReferenceClock_fnAddRef(IReferenceClock* iface)
261 {
262         CSystemClock_THIS(iface,refclk);
263
264         TRACE("(%p)->()\n",This);
265
266         return IUnknown_AddRef(This->unk.punkControl);
267 }
268
269 static ULONG WINAPI
270 IReferenceClock_fnRelease(IReferenceClock* iface)
271 {
272         CSystemClock_THIS(iface,refclk);
273
274         TRACE("(%p)->()\n",This);
275
276         return IUnknown_Release(This->unk.punkControl);
277 }
278
279 static HRESULT WINAPI
280 IReferenceClock_fnGetTime(IReferenceClock* iface,REFERENCE_TIME* prtTime)
281 {
282         CSystemClock_THIS(iface,refclk);
283         DWORD   dwTimeCur;
284
285         TRACE( "(%p)->(%p)\n", This, prtTime );
286
287         if ( prtTime == NULL )
288                 return E_POINTER;
289
290         EnterCriticalSection( &This->m_csClock );
291
292         dwTimeCur = GetTickCount();
293         This->m_rtLast += (REFERENCE_TIME)(DWORD)(dwTimeCur - This->m_dwTimeLast) * (REFERENCE_TIME)10000;
294
295         This->m_dwTimeLast = dwTimeCur;
296
297         *prtTime = This->m_rtLast;
298
299         LeaveCriticalSection( &This->m_csClock );
300
301         return NOERROR;
302 }
303
304 static HRESULT WINAPI
305 IReferenceClock_fnAdviseTime(IReferenceClock* iface,REFERENCE_TIME rtBase,REFERENCE_TIME rtStream,HEVENT hEvent,DWORD_PTR* pdwAdvCookie)
306 {
307         CSystemClock_THIS(iface,refclk);
308         QUARTZ_TimerEntry*      pEntry;
309         HRESULT hr;
310         REFERENCE_TIME  rtCur;
311
312         TRACE( "(%p)->()\n", This );
313
314         if ( pdwAdvCookie == NULL )
315                 return E_POINTER;
316         if ( hEvent == (HANDLE)NULL )
317                 return E_INVALIDARG;
318
319         EnterCriticalSection( &This->m_csClock );
320
321         *pdwAdvCookie = (DWORD_PTR)(This->m_dwAdvCookieNext ++);
322
323         hr = IReferenceClock_GetTime(iface,&rtCur);
324         if ( hr != NOERROR )
325                 goto err;
326         if ( rtCur >= (rtBase+rtStream) )
327         {
328                 SetEvent(hEvent);
329                 hr = NOERROR;
330                 goto err;
331         }
332
333         pEntry = IReferenceClock_AllocTimerEntry(This);
334         if ( pEntry == NULL )
335         {
336                 hr = E_FAIL;
337                 goto err;
338         }
339
340         pEntry->dwAdvCookie = *pdwAdvCookie;
341         pEntry->fPeriodic = FALSE;
342         pEntry->hEvent = hEvent;
343         pEntry->rtStart = rtBase;
344         pEntry->rtInterval = rtStream;
345
346         if ( !PostThreadMessageA(
347                         This->m_idThreadTimer,
348                         QUARTZ_MSG_ADDTIMER,
349                         0, 0 ) )
350         {
351                 pEntry->hEvent = (HANDLE)NULL;
352                 hr = E_FAIL;
353                 goto err;
354         }
355
356         hr = NOERROR;
357 err:
358         LeaveCriticalSection( &This->m_csClock );
359
360         return hr;
361 }
362
363 static HRESULT WINAPI
364 IReferenceClock_fnAdvisePeriodic(IReferenceClock* iface,REFERENCE_TIME rtStart,REFERENCE_TIME rtPeriod,HSEMAPHORE hSemaphore,DWORD_PTR* pdwAdvCookie)
365 {
366         CSystemClock_THIS(iface,refclk);
367         QUARTZ_TimerEntry*      pEntry;
368         HRESULT hr;
369
370         TRACE( "(%p)->()\n", This );
371
372         if ( pdwAdvCookie == NULL )
373                 return E_POINTER;
374         if ( hSemaphore == (HSEMAPHORE)NULL )
375                 return E_INVALIDARG;
376
377         EnterCriticalSection( &This->m_csClock );
378
379         *pdwAdvCookie = (DWORD_PTR)(This->m_dwAdvCookieNext ++);
380
381         pEntry = IReferenceClock_AllocTimerEntry(This);
382         if ( pEntry == NULL )
383         {
384                 hr = E_FAIL;
385                 goto err;
386         }
387
388         pEntry->dwAdvCookie = *pdwAdvCookie;
389         pEntry->fPeriodic = TRUE;
390         pEntry->hEvent = (HANDLE)hSemaphore;
391         pEntry->rtStart = rtStart;
392         pEntry->rtInterval = rtPeriod;
393
394         if ( !PostThreadMessageA(
395                         This->m_idThreadTimer,
396                         QUARTZ_MSG_ADDTIMER,
397                         0, 0 ) )
398         {
399                 pEntry->hEvent = (HANDLE)NULL;
400                 hr = E_FAIL;
401                 goto err;
402         }
403
404         hr = NOERROR;
405 err:
406         LeaveCriticalSection( &This->m_csClock );
407
408         return hr;
409 }
410
411 static HRESULT WINAPI
412 IReferenceClock_fnUnadvise(IReferenceClock* iface,DWORD_PTR dwAdvCookie)
413 {
414         CSystemClock_THIS(iface,refclk);
415         QUARTZ_TimerEntry*      pEntry;
416
417         TRACE( "(%p)->(%lu)\n", This, (DWORD)dwAdvCookie );
418
419         EnterCriticalSection( &This->m_csClock );
420
421         pEntry = IReferenceClock_SearchTimer(This,(DWORD)dwAdvCookie);
422         if ( pEntry != NULL )
423         {
424                 pEntry->hEvent = (HANDLE)NULL;
425         }
426
427         LeaveCriticalSection( &This->m_csClock );
428
429         return NOERROR;
430 }
431
432 static ICOM_VTABLE(IReferenceClock) irefclk =
433 {
434         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
435         /* IUnknown fields */
436         IReferenceClock_fnQueryInterface,
437         IReferenceClock_fnAddRef,
438         IReferenceClock_fnRelease,
439         /* IReferenceClock fields */
440         IReferenceClock_fnGetTime,
441         IReferenceClock_fnAdviseTime,
442         IReferenceClock_fnAdvisePeriodic,
443         IReferenceClock_fnUnadvise,
444 };
445
446
447 HRESULT CSystemClock_InitIReferenceClock( CSystemClock* psc )
448 {
449         HANDLE  hEvents[2];
450
451         TRACE("(%p)\n",psc);
452         ICOM_VTBL(&psc->refclk) = &irefclk;
453
454         InitializeCriticalSection( &psc->m_csClock );
455         psc->m_dwTimeLast = GetTickCount();
456         psc->m_rtLast = (REFERENCE_TIME)0;
457         psc->m_hThreadTimer = (HANDLE)NULL;
458         psc->m_hEventInit = (HANDLE)NULL;
459         psc->m_idThreadTimer = 0;
460         psc->m_dwAdvCookieNext = 1;
461         ZeroMemory( psc->m_timerEntries, sizeof(psc->m_timerEntries) );
462
463         psc->m_hEventInit = CreateEventA( NULL, TRUE, FALSE, NULL );
464         if ( psc->m_hEventInit == (HANDLE)NULL )
465                 goto err;
466
467         psc->m_hThreadTimer = CreateThread(
468                 NULL, 0,
469                 IReferenceClock_TimerEntry,
470                 psc, 0, &psc->m_idThreadTimer );
471
472         if ( psc->m_hThreadTimer == (HANDLE)NULL )
473         {
474                 CloseHandle( psc->m_hEventInit );
475                 psc->m_hEventInit = (HANDLE)NULL;
476                 goto err;
477         }
478
479         hEvents[0] = psc->m_hEventInit;
480         hEvents[1] = psc->m_hThreadTimer;
481         if ( WaitForMultipleObjects( 2, hEvents, FALSE, INFINITE )
482                         != WAIT_OBJECT_0 )
483         {
484                 CloseHandle( psc->m_hEventInit );
485                 psc->m_hEventInit = (HANDLE)NULL;
486                 CloseHandle( psc->m_hThreadTimer );
487                 psc->m_hThreadTimer = (HANDLE)NULL;
488                 goto err;
489         }
490
491         return NOERROR;
492
493 err:
494         DeleteCriticalSection( &psc->m_csClock );
495         return E_FAIL;
496 }
497
498 void CSystemClock_UninitIReferenceClock( CSystemClock* psc )
499 {
500         TRACE("(%p)\n",psc);
501
502         if ( psc->m_hThreadTimer != (HANDLE)NULL )
503         {
504                 if ( PostThreadMessageA(
505                         psc->m_idThreadTimer,
506                         QUARTZ_MSG_EXITTHREAD,
507                         0, 0 ) )
508                 {
509                         WaitForSingleObject( psc->m_hThreadTimer, INFINITE );
510                 }
511                 CloseHandle( psc->m_hThreadTimer );
512                 psc->m_hThreadTimer = (HANDLE)NULL;
513         }
514
515         DeleteCriticalSection( &psc->m_csClock );
516 }