2 * Implementation of CLSID_SystemClock.
4 * Copyright (C) Hidenori TAKESHIMA <hidenori@a2.ctktv.ne.jp>
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.
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.
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
31 #include "wine/debug.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
34 #include "quartz_private.h"
38 /***************************************************************************
40 * new/delete for CLSID_SystemClock
44 /* can I use offsetof safely? - FIXME? */
45 static QUARTZ_IFEntry IFEntries[] =
47 { &IID_IReferenceClock, offsetof(CSystemClock,refclk)-offsetof(CSystemClock,unk) },
51 static void QUARTZ_DestroySystemClock(IUnknown* punk)
53 CSystemClock_THIS(punk,unk);
55 CSystemClock_UninitIReferenceClock( This );
58 HRESULT QUARTZ_CreateSystemClock(IUnknown* punkOuter,void** ppobj)
63 TRACE("(%p,%p)\n",punkOuter,ppobj);
65 psc = (CSystemClock*)QUARTZ_AllocObj( sizeof(CSystemClock) );
69 QUARTZ_IUnkInit( &psc->unk, punkOuter );
70 hr = CSystemClock_InitIReferenceClock( psc );
73 QUARTZ_FreeObj( psc );
77 psc->unk.pEntries = IFEntries;
78 psc->unk.dwEntries = sizeof(IFEntries)/sizeof(IFEntries[0]);
79 psc->unk.pOnFinalRelease = QUARTZ_DestroySystemClock;
81 *ppobj = (void*)(&psc->unk);
87 /***************************************************************************
89 * CLSID_SystemClock::IReferenceClock
93 #define QUARTZ_MSG_ADDTIMER (WM_APP+0)
94 #define QUARTZ_MSG_REMOVETIMER (WM_APP+1)
95 #define QUARTZ_MSG_EXITTHREAD (WM_APP+2)
98 /****************************************************************************/
100 static QUARTZ_TimerEntry* IReferenceClock_AllocTimerEntry(CSystemClock* This)
102 QUARTZ_TimerEntry* pEntry;
105 pEntry = &This->m_timerEntries[0];
106 for ( dw = 0; dw < WINE_QUARTZ_SYSCLOCK_TIMER_MAX; dw++ )
108 if ( pEntry->hEvent == (HANDLE)NULL )
116 static QUARTZ_TimerEntry* IReferenceClock_SearchTimer(CSystemClock* This, DWORD dwAdvCookie)
118 QUARTZ_TimerEntry* pEntry;
121 pEntry = &This->m_timerEntries[0];
122 for ( dw = 0; dw < WINE_QUARTZ_SYSCLOCK_TIMER_MAX; dw++ )
124 if ( pEntry->hEvent != (HANDLE)NULL &&
125 pEntry->dwAdvCookie == dwAdvCookie )
133 static DWORD IReferenceClock_OnTimerUpdated(CSystemClock* This)
135 QUARTZ_TimerEntry* pEntry;
136 REFERENCE_TIME rtCur;
137 REFERENCE_TIME rtSignal;
138 REFERENCE_TIME rtCount;
142 DWORD dwTimeout = INFINITE;
145 hr = IReferenceClock_GetTime((IReferenceClock*)(&This->refclk),&rtCur);
149 pEntry = &This->m_timerEntries[0];
150 for ( dw = 0; dw < WINE_QUARTZ_SYSCLOCK_TIMER_MAX; dw++ )
152 if ( pEntry->hEvent != (HANDLE)NULL )
154 rtSignal = pEntry->rtStart + pEntry->rtInterval;
155 if ( rtCur >= rtSignal )
157 if ( pEntry->fPeriodic )
159 rtCount = ((rtCur - pEntry->rtStart) / pEntry->rtInterval);
160 lCount = ( rtCount > (REFERENCE_TIME)0x7fffffff ) ?
161 (LONG)0x7fffffff : (LONG)rtCount;
162 if ( !ReleaseSemaphore( pEntry->hEvent, lCount, NULL ) )
166 if ( !ReleaseSemaphore( pEntry->hEvent, 1, NULL ) )
174 TRACE( "signal an event\n" );
175 SetEvent( pEntry->hEvent );
176 pEntry->hEvent = (HANDLE)NULL;
181 rtCount = rtSignal - rtCur;
182 /* [100ns] -> [ms] */
183 rtCount = (rtCount+(REFERENCE_TIME)9999)/(REFERENCE_TIME)10000;
184 dwTimeoutCur = (rtCount >= 0xfffffffe) ? (DWORD)0xfffffffe : (DWORD)rtCount;
185 if ( dwTimeout > dwTimeoutCur )
186 dwTimeout = dwTimeoutCur;
196 DWORD WINAPI IReferenceClock_TimerEntry( LPVOID lpvParam )
198 CSystemClock* This = (CSystemClock*)lpvParam;
203 /* initialize the message queue. */
204 PeekMessageA( &msg, (HWND)NULL, 0, 0, PM_NOREMOVE );
205 /* resume the owner thread. */
206 SetEvent( This->m_hEventInit );
208 TRACE( "Enter message loop.\n" );
211 dwTimeout = INFINITE;
216 dwRes = MsgWaitForMultipleObjects(
222 EnterCriticalSection( &This->m_csClock );
223 dwTimeout = IReferenceClock_OnTimerUpdated(This);
224 LeaveCriticalSection( &This->m_csClock );
225 TRACE( "catch an event / timeout %lu\n", dwTimeout );
227 while ( PeekMessageA( &msg, (HWND)NULL, 0, 0, PM_REMOVE ) )
229 if ( msg.message == WM_QUIT )
232 if ( msg.hwnd != (HWND)NULL )
234 TranslateMessage( &msg );
235 DispatchMessageA( &msg );
239 switch ( msg.message )
241 case QUARTZ_MSG_ADDTIMER:
242 case QUARTZ_MSG_REMOVETIMER:
245 case QUARTZ_MSG_EXITTHREAD:
249 FIXME( "invalid message %04u\n", (unsigned)msg.message );
257 TRACE( "quit thread\n" );
261 /****************************************************************************/
263 static HRESULT WINAPI
264 IReferenceClock_fnQueryInterface(IReferenceClock* iface,REFIID riid,void** ppobj)
266 CSystemClock_THIS(iface,refclk);
268 TRACE("(%p)->()\n",This);
270 return IUnknown_QueryInterface(This->unk.punkControl,riid,ppobj);
274 IReferenceClock_fnAddRef(IReferenceClock* iface)
276 CSystemClock_THIS(iface,refclk);
278 TRACE("(%p)->()\n",This);
280 return IUnknown_AddRef(This->unk.punkControl);
284 IReferenceClock_fnRelease(IReferenceClock* iface)
286 CSystemClock_THIS(iface,refclk);
288 TRACE("(%p)->()\n",This);
290 return IUnknown_Release(This->unk.punkControl);
293 static HRESULT WINAPI
294 IReferenceClock_fnGetTime(IReferenceClock* iface,REFERENCE_TIME* prtTime)
296 CSystemClock_THIS(iface,refclk);
299 TRACE( "(%p)->(%p)\n", This, prtTime );
301 if ( prtTime == NULL )
304 EnterCriticalSection( &This->m_csClock );
306 dwTimeCur = GetTickCount();
307 This->m_rtLast += (REFERENCE_TIME)(DWORD)(dwTimeCur - This->m_dwTimeLast) * (REFERENCE_TIME)10000;
309 This->m_dwTimeLast = dwTimeCur;
311 *prtTime = This->m_rtLast;
313 LeaveCriticalSection( &This->m_csClock );
318 static HRESULT WINAPI
319 IReferenceClock_fnAdviseTime(IReferenceClock* iface,REFERENCE_TIME rtBase,REFERENCE_TIME rtStream,HEVENT hEvent,DWORD_PTR* pdwAdvCookie)
321 CSystemClock_THIS(iface,refclk);
322 QUARTZ_TimerEntry* pEntry;
324 REFERENCE_TIME rtCur;
326 TRACE( "(%p)->()\n", This );
328 if ( pdwAdvCookie == NULL )
330 if ( hEvent == (HANDLE)NULL )
333 EnterCriticalSection( &This->m_csClock );
335 *pdwAdvCookie = (DWORD_PTR)(This->m_dwAdvCookieNext ++);
337 hr = IReferenceClock_GetTime(iface,&rtCur);
340 if ( rtCur >= (rtBase+rtStream) )
347 pEntry = IReferenceClock_AllocTimerEntry(This);
348 if ( pEntry == NULL )
354 pEntry->dwAdvCookie = *pdwAdvCookie;
355 pEntry->fPeriodic = FALSE;
356 pEntry->hEvent = hEvent;
357 pEntry->rtStart = rtBase;
358 pEntry->rtInterval = rtStream;
360 if ( !PostThreadMessageA(
361 This->m_idThreadTimer,
365 pEntry->hEvent = (HANDLE)NULL;
372 LeaveCriticalSection( &This->m_csClock );
377 static HRESULT WINAPI
378 IReferenceClock_fnAdvisePeriodic(IReferenceClock* iface,REFERENCE_TIME rtStart,REFERENCE_TIME rtPeriod,HSEMAPHORE hSemaphore,DWORD_PTR* pdwAdvCookie)
380 CSystemClock_THIS(iface,refclk);
381 QUARTZ_TimerEntry* pEntry;
384 TRACE( "(%p)->()\n", This );
386 if ( pdwAdvCookie == NULL )
388 if ( hSemaphore == (HSEMAPHORE)NULL )
391 EnterCriticalSection( &This->m_csClock );
393 *pdwAdvCookie = (DWORD_PTR)(This->m_dwAdvCookieNext ++);
395 pEntry = IReferenceClock_AllocTimerEntry(This);
396 if ( pEntry == NULL )
402 pEntry->dwAdvCookie = *pdwAdvCookie;
403 pEntry->fPeriodic = TRUE;
404 pEntry->hEvent = (HANDLE)hSemaphore;
405 pEntry->rtStart = rtStart;
406 pEntry->rtInterval = rtPeriod;
408 if ( !PostThreadMessageA(
409 This->m_idThreadTimer,
413 pEntry->hEvent = (HANDLE)NULL;
420 LeaveCriticalSection( &This->m_csClock );
425 static HRESULT WINAPI
426 IReferenceClock_fnUnadvise(IReferenceClock* iface,DWORD_PTR dwAdvCookie)
428 CSystemClock_THIS(iface,refclk);
429 QUARTZ_TimerEntry* pEntry;
431 TRACE( "(%p)->(%lu)\n", This, (DWORD)dwAdvCookie );
433 EnterCriticalSection( &This->m_csClock );
435 pEntry = IReferenceClock_SearchTimer(This,(DWORD)dwAdvCookie);
436 if ( pEntry != NULL )
438 pEntry->hEvent = (HANDLE)NULL;
441 LeaveCriticalSection( &This->m_csClock );
446 static ICOM_VTABLE(IReferenceClock) irefclk =
448 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
449 /* IUnknown fields */
450 IReferenceClock_fnQueryInterface,
451 IReferenceClock_fnAddRef,
452 IReferenceClock_fnRelease,
453 /* IReferenceClock fields */
454 IReferenceClock_fnGetTime,
455 IReferenceClock_fnAdviseTime,
456 IReferenceClock_fnAdvisePeriodic,
457 IReferenceClock_fnUnadvise,
461 HRESULT CSystemClock_InitIReferenceClock( CSystemClock* psc )
466 ICOM_VTBL(&psc->refclk) = &irefclk;
468 InitializeCriticalSection( &psc->m_csClock );
469 psc->m_dwTimeLast = GetTickCount();
470 psc->m_rtLast = (REFERENCE_TIME)0;
471 psc->m_hThreadTimer = (HANDLE)NULL;
472 psc->m_hEventInit = (HANDLE)NULL;
473 psc->m_idThreadTimer = 0;
474 psc->m_dwAdvCookieNext = 1;
475 ZeroMemory( psc->m_timerEntries, sizeof(psc->m_timerEntries) );
477 psc->m_hEventInit = CreateEventA( NULL, TRUE, FALSE, NULL );
478 if ( psc->m_hEventInit == (HANDLE)NULL )
481 psc->m_hThreadTimer = CreateThread(
483 IReferenceClock_TimerEntry,
484 psc, 0, &psc->m_idThreadTimer );
486 if ( psc->m_hThreadTimer == (HANDLE)NULL )
488 CloseHandle( psc->m_hEventInit );
489 psc->m_hEventInit = (HANDLE)NULL;
493 hEvents[0] = psc->m_hEventInit;
494 hEvents[1] = psc->m_hThreadTimer;
495 if ( WaitForMultipleObjects( 2, hEvents, FALSE, INFINITE )
498 CloseHandle( psc->m_hEventInit );
499 psc->m_hEventInit = (HANDLE)NULL;
500 CloseHandle( psc->m_hThreadTimer );
501 psc->m_hThreadTimer = (HANDLE)NULL;
508 DeleteCriticalSection( &psc->m_csClock );
512 void CSystemClock_UninitIReferenceClock( CSystemClock* psc )
516 if ( psc->m_hThreadTimer != (HANDLE)NULL )
518 if ( PostThreadMessageA(
519 psc->m_idThreadTimer,
520 QUARTZ_MSG_EXITTHREAD,
523 WaitForSingleObject( psc->m_hThreadTimer, INFINITE );
525 CloseHandle( psc->m_hThreadTimer );
526 psc->m_hThreadTimer = (HANDLE)NULL;
529 DeleteCriticalSection( &psc->m_csClock );