- Fix hotspot handling
[wine/multimedia.git] / dlls / quartz / systemclock.c
blob50a38f7c36432feec4c2399767be7048f10f88e6
1 /*
2 * Implementation of IReferenceClock
4 * Copyright 2004 Raphael Junqueira
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
21 #define COM_NO_WINDOWS_H
22 #include "quartz_private.h"
24 #include "wine/debug.h"
25 #include "wine/unicode.h"
26 #include "uuids.h"
27 #include <assert.h>
29 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
31 typedef struct SystemClockAdviseEntry SystemClockAdviseEntry;
32 struct SystemClockAdviseEntry {
33 SystemClockAdviseEntry* next;
34 SystemClockAdviseEntry* prev;
36 HANDLE hEvent;
37 REFERENCE_TIME rtBaseTime;
38 REFERENCE_TIME rtIntervalTime;
41 typedef struct SystemClockImpl {
42 IReferenceClockVtbl *lpVtbl;
43 ULONG ref;
45 /** IReferenceClock */
46 HANDLE adviseThread;
47 DWORD adviseThreadId;
48 BOOL adviseThreadActive;
49 REFERENCE_TIME lastRefTime;
50 DWORD lastTimeTickCount;
51 CRITICAL_SECTION safe;
53 SystemClockAdviseEntry* pSingleShotAdvise;
54 SystemClockAdviseEntry* pPeriodicAdvise;
55 } SystemClockImpl;
58 static void QUARTZ_RemoveAviseEntryFromQueue(SystemClockImpl* This, SystemClockAdviseEntry* pEntry) {
59 if (pEntry->prev) pEntry->prev->next = pEntry->next;
60 if (pEntry->next) pEntry->next->prev = pEntry->prev;
61 if (This->pSingleShotAdvise == pEntry) This->pSingleShotAdvise = pEntry->next;
62 if (This->pPeriodicAdvise == pEntry) This->pPeriodicAdvise = pEntry->next;
65 static void QUARTZ_InsertAviseEntryFromQueue(SystemClockImpl* This, SystemClockAdviseEntry* pEntry, SystemClockAdviseEntry** pQueue) {
66 SystemClockAdviseEntry* prev_it = NULL;
67 SystemClockAdviseEntry* it = NULL;
68 REFERENCE_TIME bornTime = pEntry->rtBaseTime + pEntry->rtIntervalTime;
70 for (it = *pQueue; NULL != it && (it->rtBaseTime + it->rtIntervalTime) < bornTime; it = it->next) {
71 prev_it = it;
73 if (NULL == prev_it) {
74 pEntry->prev = NULL;
75 if (NULL != (*pQueue)) pEntry->next = (*pQueue)->next;
76 /*assert( NULL == pEntry->next->prev );*/
77 if (NULL != pEntry->next) pEntry->next->prev = pEntry;
78 (*pQueue) = pEntry;
79 } else {
80 pEntry->prev = prev_it;
81 pEntry->next = prev_it->next;
82 prev_it->next = pEntry;
83 if (NULL != pEntry->next) pEntry->next->prev = pEntry;
87 #define MAX_REFTIME (REFERENCE_TIME)(0x7FFFFFFFFFFFFFFF)
88 #define ADVISE_EXIT (WM_APP + 0)
89 #define ADVISE_REMOVE (WM_APP + 2)
90 #define ADVISE_ADD_SINGLESHOT (WM_APP + 4)
91 #define ADVISE_ADD_PERIODIC (WM_APP + 8)
93 static DWORD WINAPI SystemClockAdviseThread(LPVOID lpParam) {
94 SystemClockImpl* This = (SystemClockImpl*) lpParam;
95 DWORD timeOut = INFINITE;
96 DWORD tmpTimeOut;
97 MSG msg;
98 HRESULT hr;
99 REFERENCE_TIME curTime;
100 SystemClockAdviseEntry* it = NULL;
102 TRACE("(%p): Main Loop\n", This);
104 while (TRUE) {
105 if (timeOut > 0) MsgWaitForMultipleObjects(0, NULL, FALSE, timeOut, QS_POSTMESSAGE|QS_SENDMESSAGE|QS_TIMER);
107 EnterCriticalSection(&This->safe);
108 /*timeOut = IReferenceClock_OnTimerUpdated(This); */
109 hr = IReferenceClock_GetTime((IReferenceClock*) This, &curTime);
110 if (FAILED(hr)) {
111 timeOut = INFINITE;
112 goto outrefresh;
115 /** First SingleShots Advice: sorted list */
116 for (it = This->pSingleShotAdvise; NULL != it && it->rtBaseTime <= curTime; it = it->next) {
117 /** send event ... */
118 SetEvent((HANDLE) it->hEvent);
119 /** ... and Release it */
120 QUARTZ_RemoveAviseEntryFromQueue(This, it);
121 HeapFree(GetProcessHeap(), 0, it);
123 if (NULL != it) timeOut = (DWORD) (curTime - (it->rtBaseTime + it->rtIntervalTime));
125 /** Now Periodics Advice: semi sorted list (sort cannot be used) */
126 for (it = This->pPeriodicAdvise; NULL != it; it = it->next) {
127 if (it->rtBaseTime <= curTime) {
128 DWORD nPeriods = (DWORD) ((curTime - it->rtBaseTime) / it->rtIntervalTime);
129 /** Release the semaphore ... */
130 ReleaseSemaphore((HANDLE) it->hEvent, nPeriods, NULL);
131 /** ... and refresh time */
132 it->rtBaseTime += it->rtIntervalTime;
133 /*assert( it->rtBaseTime + it->rtIntervalTime < curTime );*/
135 tmpTimeOut = (DWORD) (curTime - (it->rtBaseTime + it->rtIntervalTime));
136 if (timeOut > tmpTimeOut) timeOut = tmpTimeOut;
139 outrefresh:
140 LeaveCriticalSection(&This->safe);
142 while (TRUE == PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) {
143 /** if hwnd we suppose that is a windows event ... */
144 if (NULL != msg.hwnd) {
145 TranslateMessage(&msg);
146 DispatchMessageA(&msg);
147 } else {
148 switch (msg.message) {
149 case WM_QUIT:
150 case ADVISE_EXIT:
151 goto outofthread;
152 break;
153 case ADVISE_ADD_SINGLESHOT:
154 case ADVISE_ADD_PERIODIC:
155 /** set timeout to 0 to do a rescan now */
156 timeOut = 0;
157 break;
158 case ADVISE_REMOVE:
159 /** hmmmm what we can do here ... */
160 timeOut = INFINITE;
161 break;
162 default:
163 ERR("Unhandled message %u. Critical Path\n", msg.message);
164 break;
170 outofthread:
171 TRACE("(%p): Exiting\n", This);
172 return 0;
174 /*static DWORD WINAPI SystemClockAdviseThread(LPVOID lpParam) { */
176 static BOOL SystemClockPostMessageToAdviseThread(SystemClockImpl* This, UINT iMsg) {
177 if (FALSE == This->adviseThreadActive) {
178 This->adviseThread = CreateThread(NULL, 0, SystemClockAdviseThread, This, 0, &This->adviseThreadId);
179 if (NULL == This->adviseThread) return FALSE;
180 SetThreadPriority(This->adviseThread, THREAD_PRIORITY_TIME_CRITICAL);
181 This->adviseThreadActive = TRUE;
183 return PostThreadMessageA(This->adviseThreadId, iMsg, 0, 0);
186 IReferenceClockVtbl SystemClock_Vtbl;
188 static ULONG WINAPI SystemClockImpl_AddRef(IReferenceClock* iface) {
189 ICOM_THIS(SystemClockImpl,iface);
190 TRACE("(%p): AddRef from %ld\n", This, This->ref);
191 return ++(This->ref);
194 static HRESULT WINAPI SystemClockImpl_QueryInterface(IReferenceClock* iface, REFIID riid, void** ppobj) {
195 ICOM_THIS(SystemClockImpl,iface);
196 TRACE("(%p, %s,%p)\n", This, debugstr_guid(riid), ppobj);
198 if (IsEqualIID (riid, &IID_IUnknown) ||
199 IsEqualIID (riid, &IID_IReferenceClock)) {
200 SystemClockImpl_AddRef(iface);
201 *ppobj = This;
202 return S_OK;
205 WARN("(%p, %s,%p): not found\n", This, debugstr_guid(riid), ppobj);
206 return E_NOINTERFACE;
209 static ULONG WINAPI SystemClockImpl_Release(IReferenceClock* iface) {
210 ICOM_THIS(SystemClockImpl,iface);
211 ULONG ref = --This->ref;
212 TRACE("(%p): ReleaseRef to %ld\n", This, This->ref);
213 if (ref == 0) {
214 if (SystemClockPostMessageToAdviseThread(This, ADVISE_EXIT)) {
215 WaitForSingleObject(This->adviseThread, INFINITE);
216 CloseHandle(This->adviseThread);
218 DeleteCriticalSection(&This->safe);
219 HeapFree(GetProcessHeap(), 0, This);
221 return ref;
224 static HRESULT WINAPI SystemClockImpl_GetTime(IReferenceClock* iface, REFERENCE_TIME* pTime) {
225 ICOM_THIS(SystemClockImpl,iface);
226 DWORD curTimeTickCount;
227 HRESULT hr = S_OK;
229 TRACE("(%p, %p)\n", This, pTime);
231 if (NULL == pTime) {
232 return E_POINTER;
235 curTimeTickCount = GetTickCount();
237 EnterCriticalSection(&This->safe);
238 /** TODO: safe this not using * 10000 */
239 This->lastRefTime += (REFERENCE_TIME) (DWORD) (curTimeTickCount - This->lastTimeTickCount) * (REFERENCE_TIME) 10000;
240 This->lastTimeTickCount = curTimeTickCount;
241 LeaveCriticalSection(&This->safe);
243 *pTime = This->lastRefTime;
244 if (This->lastTimeTickCount == curTimeTickCount) hr = S_FALSE;
245 This->lastTimeTickCount = curTimeTickCount;
246 return hr;
249 static HRESULT WINAPI SystemClockImpl_AdviseTime(IReferenceClock* iface, REFERENCE_TIME rtBaseTime, REFERENCE_TIME rtStreamTime, HEVENT hEvent, DWORD_PTR* pdwAdviseCookie) {
250 ICOM_THIS(SystemClockImpl,iface);
251 SystemClockAdviseEntry* pEntry = NULL;
253 TRACE("(%p, %lld, %lld, %ld, %p)\n", This, rtBaseTime, rtStreamTime, hEvent, pdwAdviseCookie);
255 if ((HEVENT) 0 == hEvent) {
256 return E_INVALIDARG;
258 if (0 >= rtBaseTime + rtStreamTime) {
259 return E_INVALIDARG;
261 if (NULL == pdwAdviseCookie) {
262 return E_POINTER;
264 pEntry = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SystemClockAdviseEntry));
265 if (NULL == pEntry) {
266 return E_OUTOFMEMORY;
269 pEntry->hEvent = (HANDLE) hEvent;
270 pEntry->rtBaseTime = rtBaseTime + rtStreamTime;
271 pEntry->rtIntervalTime = 0;
273 EnterCriticalSection(&This->safe);
274 QUARTZ_InsertAviseEntryFromQueue(This, pEntry, &This->pSingleShotAdvise);
275 LeaveCriticalSection(&This->safe);
277 SystemClockPostMessageToAdviseThread(This, ADVISE_ADD_SINGLESHOT);
279 *pdwAdviseCookie = (DWORD_PTR) (pEntry);
280 return S_OK;
283 static HRESULT WINAPI SystemClockImpl_AdvisePeriodic(IReferenceClock* iface, REFERENCE_TIME rtStartTime, REFERENCE_TIME rtPeriodTime, HSEMAPHORE hSemaphore, DWORD_PTR* pdwAdviseCookie) {
284 ICOM_THIS(SystemClockImpl,iface);
285 SystemClockAdviseEntry* pEntry = NULL;
287 TRACE("(%p, %lld, %lld, %ld, %p)\n", This, rtStartTime, rtPeriodTime, hSemaphore, pdwAdviseCookie);
289 if ((HSEMAPHORE) 0 == hSemaphore) {
290 return E_INVALIDARG;
292 if (0 >= rtStartTime || 0 >= rtPeriodTime) {
293 return E_INVALIDARG;
295 if (NULL == pdwAdviseCookie) {
296 return E_POINTER;
298 pEntry = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SystemClockAdviseEntry));
299 if (NULL == pEntry) {
300 return E_OUTOFMEMORY;
303 pEntry->hEvent = (HANDLE) hSemaphore;
304 pEntry->rtBaseTime = rtStartTime;
305 pEntry->rtIntervalTime = rtPeriodTime;
307 EnterCriticalSection(&This->safe);
308 QUARTZ_InsertAviseEntryFromQueue(This, pEntry, &This->pPeriodicAdvise);
309 LeaveCriticalSection(&This->safe);
311 SystemClockPostMessageToAdviseThread(This, ADVISE_ADD_PERIODIC);
313 *pdwAdviseCookie = (DWORD_PTR) (pEntry);
314 return S_OK;
317 static HRESULT WINAPI SystemClockImpl_Unadvise(IReferenceClock* iface, DWORD_PTR dwAdviseCookie) {
318 ICOM_THIS(SystemClockImpl,iface);
319 SystemClockAdviseEntry* pEntry = NULL;
320 SystemClockAdviseEntry* it = NULL;
321 HRESULT ret = S_OK;
322 TRACE("(%p, %lu)\n", This, dwAdviseCookie);
324 pEntry = (SystemClockAdviseEntry*) dwAdviseCookie;
326 EnterCriticalSection(&This->safe);
327 for (it = This->pPeriodicAdvise; NULL != it && it != pEntry; it = it->next) ;
328 if (it != pEntry) {
329 for (it = This->pSingleShotAdvise; NULL != it && it != pEntry; it = it->next) ;
330 if (it != pEntry) {
331 ret = S_FALSE;
332 goto out;
336 QUARTZ_RemoveAviseEntryFromQueue(This, pEntry);
337 HeapFree(GetProcessHeap(), 0, pEntry);
339 SystemClockPostMessageToAdviseThread(This, ADVISE_REMOVE);
341 out:
342 LeaveCriticalSection(&This->safe);
343 return ret;
346 IReferenceClockVtbl SystemClock_Vtbl =
348 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
349 SystemClockImpl_QueryInterface,
350 SystemClockImpl_AddRef,
351 SystemClockImpl_Release,
352 SystemClockImpl_GetTime,
353 SystemClockImpl_AdviseTime,
354 SystemClockImpl_AdvisePeriodic,
355 SystemClockImpl_Unadvise
358 HRESULT QUARTZ_CreateSystemClock(IUnknown * pUnkOuter, LPVOID * ppv) {
359 SystemClockImpl* obj = NULL;
361 TRACE("(%p,%p)\n", ppv, pUnkOuter);
363 obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IReferenceClock));
364 if (NULL == obj) {
365 *ppv = NULL;
366 return E_OUTOFMEMORY;
368 obj->lpVtbl = &SystemClock_Vtbl;
369 obj->ref = 0; /* will be inited by QueryInterface */
371 obj->lastTimeTickCount = GetTickCount();
372 InitializeCriticalSection(&obj->safe);
374 return SystemClockImpl_QueryInterface((IReferenceClock*) obj, &IID_IReferenceClock, ppv);