* Slap *some* sense into our header inclusions.
[reactos.git] / reactos / dll / directx / quartz / systemclock.c
1 /*
2 * Implementation of IReferenceClock
3 *
4 * Copyright 2004 Raphael Junqueira
5 *
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.
10 *
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.
15 *
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21 #include "quartz_private.h"
22
23 #include <wine/debug.h>
24 //#include "wine/unicode.h"
25 //#include <assert.h>
26
27 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
28
29 typedef struct SystemClockAdviseEntry SystemClockAdviseEntry;
30 struct SystemClockAdviseEntry {
31 SystemClockAdviseEntry* next;
32 SystemClockAdviseEntry* prev;
33
34 HANDLE hEvent;
35 REFERENCE_TIME rtBaseTime;
36 REFERENCE_TIME rtIntervalTime;
37 };
38
39 typedef struct SystemClockImpl {
40 const IReferenceClockVtbl *lpVtbl;
41 LONG ref;
42
43 /** IReferenceClock */
44 HANDLE adviseThread;
45 DWORD adviseThreadId;
46 BOOL adviseThreadActive;
47 REFERENCE_TIME lastRefTime;
48 DWORD lastTimeTickCount;
49 CRITICAL_SECTION safe;
50
51 SystemClockAdviseEntry* pSingleShotAdvise;
52 SystemClockAdviseEntry* pPeriodicAdvise;
53 } SystemClockImpl;
54
55
56 static void QUARTZ_RemoveAviseEntryFromQueue(SystemClockImpl* This, SystemClockAdviseEntry* pEntry) {
57 if (pEntry->prev) pEntry->prev->next = pEntry->next;
58 if (pEntry->next) pEntry->next->prev = pEntry->prev;
59 if (This->pSingleShotAdvise == pEntry) This->pSingleShotAdvise = pEntry->next;
60 if (This->pPeriodicAdvise == pEntry) This->pPeriodicAdvise = pEntry->next;
61 }
62
63 static void QUARTZ_InsertAviseEntryFromQueue(SystemClockImpl* This, SystemClockAdviseEntry* pEntry, SystemClockAdviseEntry** pQueue) {
64 SystemClockAdviseEntry* prev_it = NULL;
65 SystemClockAdviseEntry* it = NULL;
66 REFERENCE_TIME bornTime = pEntry->rtBaseTime + pEntry->rtIntervalTime;
67
68 for (it = *pQueue; NULL != it && (it->rtBaseTime + it->rtIntervalTime) < bornTime; it = it->next) {
69 prev_it = it;
70 }
71 if (NULL == prev_it) {
72 pEntry->prev = NULL;
73 if (NULL != (*pQueue)) pEntry->next = (*pQueue)->next;
74 /*assert( NULL == pEntry->next->prev );*/
75 if (NULL != pEntry->next) pEntry->next->prev = pEntry;
76 (*pQueue) = pEntry;
77 } else {
78 pEntry->prev = prev_it;
79 pEntry->next = prev_it->next;
80 prev_it->next = pEntry;
81 if (NULL != pEntry->next) pEntry->next->prev = pEntry;
82 }
83 }
84
85 #define MAX_REFTIME (REFERENCE_TIME)(0x7FFFFFFFFFFFFFFF)
86 #define ADVISE_EXIT (WM_APP + 0)
87 #define ADVISE_REMOVE (WM_APP + 2)
88 #define ADVISE_ADD_SINGLESHOT (WM_APP + 4)
89 #define ADVISE_ADD_PERIODIC (WM_APP + 8)
90
91 static DWORD WINAPI SystemClockAdviseThread(LPVOID lpParam) {
92 SystemClockImpl* This = lpParam;
93 DWORD timeOut = INFINITE;
94 DWORD tmpTimeOut;
95 MSG msg;
96 HRESULT hr;
97 REFERENCE_TIME curTime;
98 SystemClockAdviseEntry* it = NULL;
99
100 TRACE("(%p): Main Loop\n", This);
101
102 while (TRUE) {
103 if (timeOut > 0) MsgWaitForMultipleObjects(0, NULL, FALSE, timeOut, QS_POSTMESSAGE|QS_SENDMESSAGE|QS_TIMER);
104
105 EnterCriticalSection(&This->safe);
106 /*timeOut = IReferenceClock_OnTimerUpdated(This); */
107 hr = IReferenceClock_GetTime((IReferenceClock*) This, &curTime);
108 if (FAILED(hr)) {
109 timeOut = INFINITE;
110 goto outrefresh;
111 }
112
113 /** First SingleShots Advice: sorted list */
114 for (it = This->pSingleShotAdvise; NULL != it && (it->rtBaseTime + it->rtIntervalTime) <= curTime; it = it->next) {
115 /** send event ... */
116 SetEvent(it->hEvent);
117 /** ... and Release it */
118 QUARTZ_RemoveAviseEntryFromQueue(This, it);
119 CoTaskMemFree(it);
120 }
121 if (NULL != it) timeOut = (DWORD) ((it->rtBaseTime + it->rtIntervalTime) - curTime) / (REFERENCE_TIME)10000;
122
123 /** Now Periodics Advice: semi sorted list (sort cannot be used) */
124 for (it = This->pPeriodicAdvise; NULL != it; it = it->next) {
125 if (it->rtBaseTime <= curTime) {
126 DWORD nPeriods = (DWORD) ((curTime - it->rtBaseTime) / it->rtIntervalTime);
127 /** Release the semaphore ... */
128 ReleaseSemaphore(it->hEvent, nPeriods, NULL);
129 /** ... and refresh time */
130 it->rtBaseTime += nPeriods * it->rtIntervalTime;
131 /*assert( it->rtBaseTime + it->rtIntervalTime < curTime );*/
132 }
133 tmpTimeOut = (DWORD) ((it->rtBaseTime + it->rtIntervalTime) - curTime) / (REFERENCE_TIME)10000;
134 if (timeOut > tmpTimeOut) timeOut = tmpTimeOut;
135 }
136
137 outrefresh:
138 LeaveCriticalSection(&This->safe);
139
140 while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) {
141 /** if hwnd we suppose that is a windows event ... */
142 if (NULL != msg.hwnd) {
143 TranslateMessage(&msg);
144 DispatchMessageA(&msg);
145 } else {
146 switch (msg.message) {
147 case WM_QUIT:
148 case ADVISE_EXIT:
149 goto outofthread;
150 case ADVISE_ADD_SINGLESHOT:
151 case ADVISE_ADD_PERIODIC:
152 /** set timeout to 0 to do a rescan now */
153 timeOut = 0;
154 break;
155 case ADVISE_REMOVE:
156 /** hmmmm what we can do here ... */
157 timeOut = INFINITE;
158 break;
159 default:
160 ERR("Unhandled message %u. Critical Path\n", msg.message);
161 break;
162 }
163 }
164 }
165 }
166
167 outofthread:
168 TRACE("(%p): Exiting\n", This);
169 return 0;
170 }
171 /*static DWORD WINAPI SystemClockAdviseThread(LPVOID lpParam) { */
172
173 static BOOL SystemClockPostMessageToAdviseThread(SystemClockImpl* This, UINT iMsg) {
174 if (FALSE == This->adviseThreadActive) {
175 BOOL res;
176 This->adviseThread = CreateThread(NULL, 0, SystemClockAdviseThread, This, 0, &This->adviseThreadId);
177 if (NULL == This->adviseThread) return FALSE;
178 SetThreadPriority(This->adviseThread, THREAD_PRIORITY_TIME_CRITICAL);
179 This->adviseThreadActive = TRUE;
180 while(1) {
181 res = PostThreadMessageA(This->adviseThreadId, iMsg, 0, 0);
182 /* Let the thread creates its message queue (with MsgWaitForMultipleObjects call) by yielding and retrying */
183 if (!res && (GetLastError() == ERROR_INVALID_THREAD_ID))
184 Sleep(0);
185 else
186 break;
187 }
188 return res;
189 }
190 return PostThreadMessageA(This->adviseThreadId, iMsg, 0, 0);
191 }
192
193 static ULONG WINAPI SystemClockImpl_AddRef(IReferenceClock* iface) {
194 SystemClockImpl *This = (SystemClockImpl *)iface;
195 ULONG ref = InterlockedIncrement(&This->ref);
196
197 TRACE("(%p): AddRef from %d\n", This, ref - 1);
198
199 return ref;
200 }
201
202 static HRESULT WINAPI SystemClockImpl_QueryInterface(IReferenceClock* iface, REFIID riid, void** ppobj) {
203 SystemClockImpl *This = (SystemClockImpl *)iface;
204 TRACE("(%p, %s,%p)\n", This, debugstr_guid(riid), ppobj);
205
206 if (IsEqualIID (riid, &IID_IUnknown) ||
207 IsEqualIID (riid, &IID_IReferenceClock)) {
208 SystemClockImpl_AddRef(iface);
209 *ppobj = This;
210 return S_OK;
211 }
212
213 *ppobj = NULL;
214 WARN("(%p, %s,%p): not found\n", This, debugstr_guid(riid), ppobj);
215 return E_NOINTERFACE;
216 }
217
218 static ULONG WINAPI SystemClockImpl_Release(IReferenceClock* iface) {
219 SystemClockImpl *This = (SystemClockImpl *)iface;
220 ULONG ref = InterlockedDecrement(&This->ref);
221 TRACE("(%p): ReleaseRef to %d\n", This, ref);
222 if (ref == 0) {
223 if (SystemClockPostMessageToAdviseThread(This, ADVISE_EXIT)) {
224 WaitForSingleObject(This->adviseThread, INFINITE);
225 CloseHandle(This->adviseThread);
226 }
227 This->safe.DebugInfo->Spare[0] = 0;
228 DeleteCriticalSection(&This->safe);
229 CoTaskMemFree(This);
230 }
231 return ref;
232 }
233
234 static HRESULT WINAPI SystemClockImpl_GetTime(IReferenceClock* iface, REFERENCE_TIME* pTime) {
235 SystemClockImpl *This = (SystemClockImpl *)iface;
236 DWORD curTimeTickCount;
237 HRESULT hr = S_OK;
238
239 TRACE("(%p, %p)\n", This, pTime);
240
241 if (NULL == pTime) {
242 return E_POINTER;
243 }
244
245 curTimeTickCount = GetTickCount();
246
247 EnterCriticalSection(&This->safe);
248 if (This->lastTimeTickCount == curTimeTickCount) hr = S_FALSE;
249 This->lastRefTime += (REFERENCE_TIME) (DWORD) (curTimeTickCount - This->lastTimeTickCount) * (REFERENCE_TIME) 10000;
250 This->lastTimeTickCount = curTimeTickCount;
251 *pTime = This->lastRefTime;
252 LeaveCriticalSection(&This->safe);
253 return hr;
254 }
255
256 static HRESULT WINAPI SystemClockImpl_AdviseTime(IReferenceClock* iface, REFERENCE_TIME rtBaseTime, REFERENCE_TIME rtStreamTime, HEVENT hEvent, DWORD_PTR* pdwAdviseCookie) {
257 SystemClockImpl *This = (SystemClockImpl *)iface;
258 SystemClockAdviseEntry* pEntry = NULL;
259
260 TRACE("(%p, 0x%s, 0x%s, %ld, %p)\n", This, wine_dbgstr_longlong(rtBaseTime),
261 wine_dbgstr_longlong(rtStreamTime), hEvent, pdwAdviseCookie);
262
263 if (!hEvent) {
264 return E_INVALIDARG;
265 }
266 if (0 >= rtBaseTime + rtStreamTime) {
267 return E_INVALIDARG;
268 }
269 if (NULL == pdwAdviseCookie) {
270 return E_POINTER;
271 }
272 pEntry = CoTaskMemAlloc(sizeof(SystemClockAdviseEntry));
273 if (NULL == pEntry) {
274 return E_OUTOFMEMORY;
275 }
276 ZeroMemory(pEntry, sizeof(SystemClockAdviseEntry));
277
278 pEntry->hEvent = (HANDLE) hEvent;
279 pEntry->rtBaseTime = rtBaseTime + rtStreamTime;
280 pEntry->rtIntervalTime = 0;
281
282 EnterCriticalSection(&This->safe);
283 QUARTZ_InsertAviseEntryFromQueue(This, pEntry, &This->pSingleShotAdvise);
284 LeaveCriticalSection(&This->safe);
285
286 SystemClockPostMessageToAdviseThread(This, ADVISE_ADD_SINGLESHOT);
287
288 *pdwAdviseCookie = (DWORD_PTR) (pEntry);
289 return S_OK;
290 }
291
292 static HRESULT WINAPI SystemClockImpl_AdvisePeriodic(IReferenceClock* iface, REFERENCE_TIME rtStartTime, REFERENCE_TIME rtPeriodTime, HSEMAPHORE hSemaphore, DWORD_PTR* pdwAdviseCookie) {
293 SystemClockImpl *This = (SystemClockImpl *)iface;
294 SystemClockAdviseEntry* pEntry = NULL;
295
296 TRACE("(%p, 0x%s, 0x%s, %ld, %p)\n", This, wine_dbgstr_longlong(rtStartTime),
297 wine_dbgstr_longlong(rtPeriodTime), hSemaphore, pdwAdviseCookie);
298
299 if (!hSemaphore) {
300 return E_INVALIDARG;
301 }
302 if (0 >= rtStartTime || 0 >= rtPeriodTime) {
303 return E_INVALIDARG;
304 }
305 if (NULL == pdwAdviseCookie) {
306 return E_POINTER;
307 }
308 pEntry = CoTaskMemAlloc(sizeof(SystemClockAdviseEntry));
309 if (NULL == pEntry) {
310 return E_OUTOFMEMORY;
311 }
312 ZeroMemory(pEntry, sizeof(SystemClockAdviseEntry));
313
314 pEntry->hEvent = (HANDLE) hSemaphore;
315 pEntry->rtBaseTime = rtStartTime;
316 pEntry->rtIntervalTime = rtPeriodTime;
317
318 EnterCriticalSection(&This->safe);
319 QUARTZ_InsertAviseEntryFromQueue(This, pEntry, &This->pPeriodicAdvise);
320 LeaveCriticalSection(&This->safe);
321
322 SystemClockPostMessageToAdviseThread(This, ADVISE_ADD_PERIODIC);
323
324 *pdwAdviseCookie = (DWORD_PTR) (pEntry);
325 return S_OK;
326 }
327
328 static HRESULT WINAPI SystemClockImpl_Unadvise(IReferenceClock* iface, DWORD_PTR dwAdviseCookie) {
329 SystemClockImpl *This = (SystemClockImpl *)iface;
330 SystemClockAdviseEntry* pEntry = NULL;
331 SystemClockAdviseEntry* it = NULL;
332 HRESULT ret = S_OK;
333 TRACE("(%p, %lu)\n", This, dwAdviseCookie);
334
335 pEntry = (SystemClockAdviseEntry*) dwAdviseCookie;
336
337 EnterCriticalSection(&This->safe);
338 for (it = This->pPeriodicAdvise; NULL != it && it != pEntry; it = it->next) ;
339 if (it != pEntry) {
340 for (it = This->pSingleShotAdvise; NULL != it && it != pEntry; it = it->next) ;
341 if (it != pEntry) {
342 ret = S_FALSE;
343 goto out;
344 }
345 }
346
347 QUARTZ_RemoveAviseEntryFromQueue(This, pEntry);
348 CoTaskMemFree(pEntry);
349
350 SystemClockPostMessageToAdviseThread(This, ADVISE_REMOVE);
351
352 out:
353 LeaveCriticalSection(&This->safe);
354 return ret;
355 }
356
357 static const IReferenceClockVtbl SystemClock_Vtbl =
358 {
359 SystemClockImpl_QueryInterface,
360 SystemClockImpl_AddRef,
361 SystemClockImpl_Release,
362 SystemClockImpl_GetTime,
363 SystemClockImpl_AdviseTime,
364 SystemClockImpl_AdvisePeriodic,
365 SystemClockImpl_Unadvise
366 };
367
368 HRESULT QUARTZ_CreateSystemClock(IUnknown * pUnkOuter, LPVOID * ppv) {
369 SystemClockImpl* obj = NULL;
370
371 TRACE("(%p,%p)\n", ppv, pUnkOuter);
372
373 obj = CoTaskMemAlloc(sizeof(SystemClockImpl));
374 if (NULL == obj) {
375 *ppv = NULL;
376 return E_OUTOFMEMORY;
377 }
378 ZeroMemory(obj, sizeof(SystemClockImpl));
379
380 obj->lpVtbl = &SystemClock_Vtbl;
381 obj->ref = 0; /* will be inited by QueryInterface */
382
383 obj->lastTimeTickCount = GetTickCount();
384 InitializeCriticalSection(&obj->safe);
385 obj->safe.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": SystemClockImpl.safe");
386
387 return SystemClockImpl_QueryInterface((IReferenceClock*) obj, &IID_IReferenceClock, ppv);
388 }