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
);