xref: /reactos/dll/win32/winmm/time.c (revision 88300ec3)
1c2c66affSColin Finck /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2c2c66affSColin Finck 
3c2c66affSColin Finck /*
4c2c66affSColin Finck  * MMSYSTEM time functions
5c2c66affSColin Finck  *
6c2c66affSColin Finck  * Copyright 1993 Martin Ayotte
7c2c66affSColin Finck  *
8c2c66affSColin Finck  * This library is free software; you can redistribute it and/or
9c2c66affSColin Finck  * modify it under the terms of the GNU Lesser General Public
10c2c66affSColin Finck  * License as published by the Free Software Foundation; either
11c2c66affSColin Finck  * version 2.1 of the License, or (at your option) any later version.
12c2c66affSColin Finck  *
13c2c66affSColin Finck  * This library is distributed in the hope that it will be useful,
14c2c66affSColin Finck  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15c2c66affSColin Finck  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16c2c66affSColin Finck  * Lesser General Public License for more details.
17c2c66affSColin Finck  *
18c2c66affSColin Finck  * You should have received a copy of the GNU Lesser General Public
19c2c66affSColin Finck  * License along with this library; if not, write to the Free Software
20c2c66affSColin Finck  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21c2c66affSColin Finck  */
22c2c66affSColin Finck 
23c2c66affSColin Finck #include "winemm.h"
24c2c66affSColin Finck 
25c2c66affSColin Finck #ifdef HAVE_SYS_TIME_H
26c2c66affSColin Finck # include <sys/time.h>
27c2c66affSColin Finck #endif
28c2c66affSColin Finck #ifdef HAVE_UNISTD_H
29c2c66affSColin Finck # include <unistd.h>
30c2c66affSColin Finck #endif
31c2c66affSColin Finck 
32c2c66affSColin Finck WINE_DEFAULT_DEBUG_CHANNEL(mmtime);
33c2c66affSColin Finck 
34c2c66affSColin Finck typedef struct tagWINE_TIMERENTRY {
35c2c66affSColin Finck     UINT			wDelay;
36c2c66affSColin Finck     UINT			wResol;
37c2c66affSColin Finck     LPTIMECALLBACK              lpFunc; /* can be lots of things */
38c2c66affSColin Finck     DWORD			dwUser;
39c2c66affSColin Finck     UINT16			wFlags;
40c2c66affSColin Finck     UINT16			wTimerID;
41c2c66affSColin Finck     DWORD			dwTriggerTime;
42c2c66affSColin Finck     struct tagWINE_TIMERENTRY*	lpNext;
43c2c66affSColin Finck } WINE_TIMERENTRY, *LPWINE_TIMERENTRY;
44c2c66affSColin Finck 
45c2c66affSColin Finck static    HANDLE                TIME_hMMTimer;
46c2c66affSColin Finck static    LPWINE_TIMERENTRY 	TIME_TimersList;
47c2c66affSColin Finck static    HANDLE                TIME_hKillEvent;
48c2c66affSColin Finck static    HANDLE                TIME_hWakeEvent;
49c2c66affSColin Finck static    BOOL                  TIME_TimeToDie = TRUE;
50*88300ec3SThomas Brogan static    LARGE_INTEGER         TIME_qpcFreq;
51c2c66affSColin Finck 
52c2c66affSColin Finck /*
53c2c66affSColin Finck  * Some observations on the behavior of winmm on Windows.
54c2c66affSColin Finck  * First, the call to timeBeginPeriod(xx) can never be used
55c2c66affSColin Finck  * to raise the timer resolution, only lower it.
56c2c66affSColin Finck  *
57c2c66affSColin Finck  * Second, a brief survey of a variety of Win 2k and Win X
58c2c66affSColin Finck  * machines showed that a 'standard' (aka default) timer
59c2c66affSColin Finck  * resolution was 1 ms (Win9x is documented as being 1).  However, one
60c2c66affSColin Finck  * machine had a standard timer resolution of 10 ms.
61c2c66affSColin Finck  *
62c2c66affSColin Finck  * Further, if we set our default resolution to 1,
63c2c66affSColin Finck  * the implementation of timeGetTime becomes GetTickCount(),
64c2c66affSColin Finck  * and we can optimize the code to reduce overhead.
65c2c66affSColin Finck  *
66c2c66affSColin Finck  * Additionally, a survey of Event behaviors shows that
67c2c66affSColin Finck  * if we request a Periodic event every 50 ms, then Windows
68c2c66affSColin Finck  * makes sure to trigger that event 20 times in the next
69c2c66affSColin Finck  * second.  If delays prevent that from happening on exact
70c2c66affSColin Finck  * schedule, Windows will trigger the events as close
71c2c66affSColin Finck  * to the original schedule as is possible, and will eventually
72c2c66affSColin Finck  * bring the event triggers back onto a schedule that is
73c2c66affSColin Finck  * consistent with what would have happened if there were
74c2c66affSColin Finck  * no delays.
75c2c66affSColin Finck  *
76c2c66affSColin Finck  *   Jeremy White, October 2004
77c2c66affSColin Finck  */
78c2c66affSColin Finck #define MMSYSTIME_MININTERVAL (1)
79c2c66affSColin Finck #define MMSYSTIME_MAXINTERVAL (65535)
80c2c66affSColin Finck 
81c2c66affSColin Finck 
TIME_TriggerCallBack(LPWINE_TIMERENTRY lpTimer)82c2c66affSColin Finck static	void	TIME_TriggerCallBack(LPWINE_TIMERENTRY lpTimer)
83c2c66affSColin Finck {
84c2c66affSColin Finck     TRACE("%04lx:CallBack => lpFunc=%p wTimerID=%04X dwUser=%08lX dwTriggerTime %ld(delta %ld)\n",
85c2c66affSColin Finck 	  GetCurrentThreadId(), lpTimer->lpFunc, lpTimer->wTimerID, lpTimer->dwUser,
86c2c66affSColin Finck           lpTimer->dwTriggerTime, GetTickCount() - lpTimer->dwTriggerTime);
87c2c66affSColin Finck 
88c2c66affSColin Finck     /* - TimeProc callback that is called here is something strange, under Windows 3.1x it is called
89c2c66affSColin Finck      * 		during interrupt time,  is allowed to execute very limited number of API calls (like
90c2c66affSColin Finck      *	    	PostMessage), and must reside in DLL (therefore uses stack of active application). So I
91c2c66affSColin Finck      *       guess current implementation via SetTimer has to be improved upon.
92c2c66affSColin Finck      */
93c2c66affSColin Finck     switch (lpTimer->wFlags & 0x30) {
94c2c66affSColin Finck     case TIME_CALLBACK_FUNCTION:
95c2c66affSColin Finck 	    (lpTimer->lpFunc)(lpTimer->wTimerID, 0, lpTimer->dwUser, 0, 0);
96c2c66affSColin Finck 	break;
97c2c66affSColin Finck     case TIME_CALLBACK_EVENT_SET:
98c2c66affSColin Finck 	SetEvent((HANDLE)lpTimer->lpFunc);
99c2c66affSColin Finck 	break;
100c2c66affSColin Finck     case TIME_CALLBACK_EVENT_PULSE:
101c2c66affSColin Finck 	PulseEvent((HANDLE)lpTimer->lpFunc);
102c2c66affSColin Finck 	break;
103c2c66affSColin Finck     default:
104c2c66affSColin Finck 	FIXME("Unknown callback type 0x%04x for mmtime callback (%p), ignored.\n",
105c2c66affSColin Finck 	      lpTimer->wFlags, lpTimer->lpFunc);
106c2c66affSColin Finck 	break;
107c2c66affSColin Finck     }
108c2c66affSColin Finck }
109c2c66affSColin Finck 
110c2c66affSColin Finck /**************************************************************************
111c2c66affSColin Finck  *           TIME_MMSysTimeCallback
112c2c66affSColin Finck  */
TIME_MMSysTimeCallback()113c2c66affSColin Finck static DWORD CALLBACK TIME_MMSysTimeCallback()
114c2c66affSColin Finck {
115c2c66affSColin Finck static    int				nSizeLpTimers;
116c2c66affSColin Finck static    LPWINE_TIMERENTRY		lpTimers;
117c2c66affSColin Finck 
118c2c66affSColin Finck     LPWINE_TIMERENTRY   timer, *ptimer, *next_ptimer;
119c2c66affSColin Finck     int			idx;
120c2c66affSColin Finck     DWORD               cur_time;
121c2c66affSColin Finck     DWORD               delta_time;
122c2c66affSColin Finck     DWORD               ret_time = INFINITE;
123c2c66affSColin Finck     DWORD               adjust_time;
124c2c66affSColin Finck 
125c2c66affSColin Finck 
126c2c66affSColin Finck     /* optimize for the most frequent case  - no events */
127c2c66affSColin Finck     if (! TIME_TimersList)
128c2c66affSColin Finck         return(ret_time);
129c2c66affSColin Finck 
130c2c66affSColin Finck     /* since timeSetEvent() and timeKillEvent() can be called
131c2c66affSColin Finck      * from 16 bit code, there are cases where win16 lock is
132c2c66affSColin Finck      * locked upon entering timeSetEvent(), and then the mm timer
133c2c66affSColin Finck      * critical section is locked. This function cannot call the
134c2c66affSColin Finck      * timer callback with the crit sect locked (because callback
135c2c66affSColin Finck      * may need to acquire Win16 lock, thus providing a deadlock
136c2c66affSColin Finck      * situation).
137c2c66affSColin Finck      * To cope with that, we just copy the WINE_TIMERENTRY struct
138c2c66affSColin Finck      * that need to trigger the callback, and call it without the
139c2c66affSColin Finck      * mm timer crit sect locked.
140c2c66affSColin Finck      * the hKillTimeEvent is used to mark the section where we
141c2c66affSColin Finck      * handle the callbacks so we can do synchronous kills.
142c2c66affSColin Finck      * EPP 99/07/13, updated 04/01/10
143c2c66affSColin Finck      */
144c2c66affSColin Finck     idx = 0;
145c2c66affSColin Finck     cur_time = GetTickCount();
146c2c66affSColin Finck 
147c2c66affSColin Finck     EnterCriticalSection(&WINMM_cs);
148c2c66affSColin Finck     for (ptimer = &TIME_TimersList; *ptimer != NULL; ) {
149c2c66affSColin Finck         timer = *ptimer;
150c2c66affSColin Finck         next_ptimer = &timer->lpNext;
151c2c66affSColin Finck         if (cur_time >= timer->dwTriggerTime)
152c2c66affSColin Finck         {
153c2c66affSColin Finck             if (timer->lpFunc) {
154c2c66affSColin Finck                 if (idx == nSizeLpTimers) {
155c2c66affSColin Finck                     if (lpTimers)
156c2c66affSColin Finck                         lpTimers = (LPWINE_TIMERENTRY)
157c2c66affSColin Finck                             HeapReAlloc(GetProcessHeap(), 0, lpTimers,
158c2c66affSColin Finck                                         ++nSizeLpTimers * sizeof(WINE_TIMERENTRY));
159c2c66affSColin Finck                     else
160c2c66affSColin Finck                         lpTimers = (LPWINE_TIMERENTRY)
161c2c66affSColin Finck                         HeapAlloc(GetProcessHeap(), 0,
162c2c66affSColin Finck                                   ++nSizeLpTimers * sizeof(WINE_TIMERENTRY));
163c2c66affSColin Finck                 }
164c2c66affSColin Finck                 lpTimers[idx++] = *timer;
165c2c66affSColin Finck 
166c2c66affSColin Finck             }
167c2c66affSColin Finck 
168c2c66affSColin Finck             /* Update the time after we make the copy to preserve
169c2c66affSColin Finck                the original trigger time    */
170c2c66affSColin Finck             timer->dwTriggerTime += timer->wDelay;
171c2c66affSColin Finck 
172c2c66affSColin Finck             /* TIME_ONESHOT is defined as 0 */
173c2c66affSColin Finck             if (!(timer->wFlags & TIME_PERIODIC))
174c2c66affSColin Finck             {
175c2c66affSColin Finck                 /* unlink timer from timers list */
176c2c66affSColin Finck                 *ptimer = *next_ptimer;
177c2c66affSColin Finck                 HeapFree(GetProcessHeap(), 0, timer);
178c2c66affSColin Finck 
179c2c66affSColin Finck                 /* We don't need to trigger oneshots again */
180c2c66affSColin Finck                 delta_time = INFINITE;
181c2c66affSColin Finck             }
182c2c66affSColin Finck             else
183c2c66affSColin Finck             {
184c2c66affSColin Finck                 /* Compute when this event needs this function
185c2c66affSColin Finck                     to be called again */
186c2c66affSColin Finck                 if (timer->dwTriggerTime <= cur_time)
187c2c66affSColin Finck                     delta_time = 0;
188c2c66affSColin Finck                 else
189c2c66affSColin Finck                     delta_time = timer->dwTriggerTime - cur_time;
190c2c66affSColin Finck             }
191c2c66affSColin Finck         }
192c2c66affSColin Finck         else
193c2c66affSColin Finck             delta_time = timer->dwTriggerTime - cur_time;
194c2c66affSColin Finck 
195c2c66affSColin Finck         /* Determine when we need to return to this function */
196c2c66affSColin Finck         ret_time = min(ret_time, delta_time);
197c2c66affSColin Finck 
198c2c66affSColin Finck         ptimer = next_ptimer;
199c2c66affSColin Finck     }
200c2c66affSColin Finck     if (TIME_hKillEvent) ResetEvent(TIME_hKillEvent);
201c2c66affSColin Finck     LeaveCriticalSection(&WINMM_cs);
202c2c66affSColin Finck 
203c2c66affSColin Finck     while (idx > 0) TIME_TriggerCallBack(&lpTimers[--idx]);
204c2c66affSColin Finck     if (TIME_hKillEvent) SetEvent(TIME_hKillEvent);
205c2c66affSColin Finck 
206c2c66affSColin Finck     /* Finally, adjust the recommended wait time downward
207c2c66affSColin Finck        by the amount of time the processing routines
208c2c66affSColin Finck        actually took */
209c2c66affSColin Finck     adjust_time = GetTickCount() - cur_time;
210c2c66affSColin Finck     if (adjust_time > ret_time)
211c2c66affSColin Finck         ret_time = 0;
212c2c66affSColin Finck     else
213c2c66affSColin Finck         ret_time -= adjust_time;
214c2c66affSColin Finck 
215c2c66affSColin Finck     /* We return the amount of time our caller should sleep
216c2c66affSColin Finck        before needing to check in on us again       */
217c2c66affSColin Finck     return(ret_time);
218c2c66affSColin Finck }
219c2c66affSColin Finck 
220c2c66affSColin Finck /**************************************************************************
221c2c66affSColin Finck  *           TIME_MMSysTimeThread
222c2c66affSColin Finck  */
TIME_MMSysTimeThread(LPVOID arg)223c2c66affSColin Finck static DWORD CALLBACK TIME_MMSysTimeThread(LPVOID arg)
224c2c66affSColin Finck {
225c2c66affSColin Finck     DWORD sleep_time;
226c2c66affSColin Finck     DWORD rc;
227c2c66affSColin Finck 
228c2c66affSColin Finck     TRACE("Starting main winmm thread\n");
229c2c66affSColin Finck 
230c2c66affSColin Finck     /* FIXME:  As an optimization, we could have
231c2c66affSColin Finck                this thread die when there are no more requests
232c2c66affSColin Finck                pending, and then get recreated on the first
233c2c66affSColin Finck                new event; it's not clear if that would be worth
234c2c66affSColin Finck                it or not.                 */
235c2c66affSColin Finck 
236c2c66affSColin Finck     while (! TIME_TimeToDie)
237c2c66affSColin Finck     {
238c2c66affSColin Finck 	sleep_time = TIME_MMSysTimeCallback();
239c2c66affSColin Finck 
240c2c66affSColin Finck         if (sleep_time == 0)
241c2c66affSColin Finck             continue;
242c2c66affSColin Finck 
243c2c66affSColin Finck         rc = WaitForSingleObject(TIME_hWakeEvent, sleep_time);
244c2c66affSColin Finck         if (rc != WAIT_TIMEOUT && rc != WAIT_OBJECT_0)
245c2c66affSColin Finck         {
246c2c66affSColin Finck             FIXME("Unexpected error %ld(%ld) in timer thread\n", rc, GetLastError());
247c2c66affSColin Finck             break;
248c2c66affSColin Finck         }
249c2c66affSColin Finck     }
250c2c66affSColin Finck     TRACE("Exiting main winmm thread\n");
251c2c66affSColin Finck     return 0;
252c2c66affSColin Finck }
253c2c66affSColin Finck 
254c2c66affSColin Finck /**************************************************************************
255c2c66affSColin Finck  * 				TIME_MMTimeStart
256c2c66affSColin Finck  */
TIME_MMTimeStart(void)257c2c66affSColin Finck void	TIME_MMTimeStart(void)
258c2c66affSColin Finck {
259c2c66affSColin Finck     if (!TIME_hMMTimer) {
260c2c66affSColin Finck 	TIME_TimersList = NULL;
261c2c66affSColin Finck         TIME_hWakeEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
262c2c66affSColin Finck         TIME_TimeToDie = FALSE;
263c2c66affSColin Finck 	TIME_hMMTimer = CreateThread(NULL, 0, TIME_MMSysTimeThread, NULL, 0, NULL);
264c2c66affSColin Finck         SetThreadPriority(TIME_hMMTimer, THREAD_PRIORITY_TIME_CRITICAL);
265c2c66affSColin Finck     }
266c2c66affSColin Finck }
267c2c66affSColin Finck 
268c2c66affSColin Finck /**************************************************************************
269c2c66affSColin Finck  * 				TIME_MMTimeStop
270c2c66affSColin Finck  */
TIME_MMTimeStop(void)271c2c66affSColin Finck void	TIME_MMTimeStop(void)
272c2c66affSColin Finck {
273c2c66affSColin Finck     if (TIME_hMMTimer) {
274c2c66affSColin Finck 
275c2c66affSColin Finck         TIME_TimeToDie = TRUE;
276c2c66affSColin Finck         SetEvent(TIME_hWakeEvent);
277c2c66affSColin Finck 
278c2c66affSColin Finck         /* FIXME: in the worst case, we're going to wait 65 seconds here :-( */
279c2c66affSColin Finck 	WaitForSingleObject(TIME_hMMTimer, INFINITE);
280c2c66affSColin Finck 
281c2c66affSColin Finck 	CloseHandle(TIME_hMMTimer);
282c2c66affSColin Finck 	CloseHandle(TIME_hWakeEvent);
283c2c66affSColin Finck 	TIME_hMMTimer = 0;
284c2c66affSColin Finck         TIME_TimersList = NULL;
285c2c66affSColin Finck     }
286c2c66affSColin Finck }
287c2c66affSColin Finck 
288c2c66affSColin Finck /**************************************************************************
289c2c66affSColin Finck  * 				timeGetSystemTime	[WINMM.@]
290c2c66affSColin Finck  */
timeGetSystemTime(LPMMTIME lpTime,UINT wSize)291c2c66affSColin Finck MMRESULT WINAPI timeGetSystemTime(LPMMTIME lpTime, UINT wSize)
292c2c66affSColin Finck {
293c2c66affSColin Finck 
294c2c66affSColin Finck     if (wSize >= sizeof(*lpTime)) {
295c2c66affSColin Finck 	lpTime->wType = TIME_MS;
296c2c66affSColin Finck 	lpTime->u.ms = GetTickCount();
297c2c66affSColin Finck 
298c2c66affSColin Finck     }
299c2c66affSColin Finck 
300c2c66affSColin Finck     return 0;
301c2c66affSColin Finck }
302c2c66affSColin Finck 
303c2c66affSColin Finck /**************************************************************************
304c2c66affSColin Finck  * 				TIME_SetEventInternal	[internal]
305c2c66affSColin Finck  */
TIME_SetEventInternal(UINT wDelay,UINT wResol,LPTIMECALLBACK lpFunc,DWORD dwUser,UINT wFlags)306c2c66affSColin Finck WORD	TIME_SetEventInternal(UINT wDelay, UINT wResol,
307c2c66affSColin Finck                               LPTIMECALLBACK lpFunc, DWORD dwUser, UINT wFlags)
308c2c66affSColin Finck {
309c2c66affSColin Finck     WORD 		wNewID = 0;
310c2c66affSColin Finck     LPWINE_TIMERENTRY	lpNewTimer;
311c2c66affSColin Finck     LPWINE_TIMERENTRY	lpTimer;
312c2c66affSColin Finck 
313c2c66affSColin Finck     TRACE("(%u, %u, %p, %08lX, %04X);\n", wDelay, wResol, lpFunc, dwUser, wFlags);
314c2c66affSColin Finck 
315c2c66affSColin Finck     if (wDelay < MMSYSTIME_MININTERVAL || wDelay > MMSYSTIME_MAXINTERVAL)
316c2c66affSColin Finck 	return 0;
317c2c66affSColin Finck 
318c2c66affSColin Finck     lpNewTimer = HeapAlloc(GetProcessHeap(), 0, sizeof(WINE_TIMERENTRY));
319c2c66affSColin Finck     if (lpNewTimer == NULL)
320c2c66affSColin Finck 	return 0;
321c2c66affSColin Finck 
322c2c66affSColin Finck     TIME_MMTimeStart();
323c2c66affSColin Finck 
324c2c66affSColin Finck     lpNewTimer->wDelay = wDelay;
325c2c66affSColin Finck     lpNewTimer->dwTriggerTime = GetTickCount() + wDelay;
326c2c66affSColin Finck 
327c2c66affSColin Finck     /* FIXME - wResol is not respected, although it is not clear
328c2c66affSColin Finck                that we could change our precision meaningfully  */
329c2c66affSColin Finck     lpNewTimer->wResol = wResol;
330c2c66affSColin Finck     lpNewTimer->lpFunc = lpFunc;
331c2c66affSColin Finck     lpNewTimer->dwUser = dwUser;
332c2c66affSColin Finck     lpNewTimer->wFlags = wFlags;
333c2c66affSColin Finck 
334c2c66affSColin Finck     EnterCriticalSection(&WINMM_cs);
335c2c66affSColin Finck 
336c2c66affSColin Finck     if ((wFlags & TIME_KILL_SYNCHRONOUS) && !TIME_hKillEvent)
337c2c66affSColin Finck         TIME_hKillEvent = CreateEventW(NULL, TRUE, TRUE, NULL);
338c2c66affSColin Finck 
339c2c66affSColin Finck     for (lpTimer = TIME_TimersList; lpTimer != NULL; lpTimer = lpTimer->lpNext) {
340c2c66affSColin Finck 	wNewID = max(wNewID, lpTimer->wTimerID);
341c2c66affSColin Finck     }
342c2c66affSColin Finck 
343c2c66affSColin Finck     lpNewTimer->lpNext = TIME_TimersList;
344c2c66affSColin Finck     TIME_TimersList = lpNewTimer;
345c2c66affSColin Finck     lpNewTimer->wTimerID = wNewID + 1;
346c2c66affSColin Finck 
347c2c66affSColin Finck     LeaveCriticalSection(&WINMM_cs);
348c2c66affSColin Finck 
349c2c66affSColin Finck     /* Wake the service thread in case there is work to be done */
350c2c66affSColin Finck     SetEvent(TIME_hWakeEvent);
351c2c66affSColin Finck 
352c2c66affSColin Finck     TRACE("=> %u\n", wNewID + 1);
353c2c66affSColin Finck 
354c2c66affSColin Finck     return wNewID + 1;
355c2c66affSColin Finck }
356c2c66affSColin Finck 
357c2c66affSColin Finck /**************************************************************************
358c2c66affSColin Finck  * 				timeSetEvent		[WINMM.@]
359c2c66affSColin Finck  */
timeSetEvent(UINT wDelay,UINT wResol,LPTIMECALLBACK lpFunc,DWORD_PTR dwUser,UINT wFlags)360c2c66affSColin Finck MMRESULT WINAPI timeSetEvent(UINT wDelay, UINT wResol, LPTIMECALLBACK lpFunc,
361c2c66affSColin Finck                             DWORD_PTR dwUser, UINT wFlags)
362c2c66affSColin Finck {
363c2c66affSColin Finck     return TIME_SetEventInternal(wDelay, wResol, lpFunc,
364c2c66affSColin Finck                                  dwUser, wFlags);
365c2c66affSColin Finck }
366c2c66affSColin Finck 
367c2c66affSColin Finck /**************************************************************************
368c2c66affSColin Finck  * 				timeKillEvent		[WINMM.@]
369c2c66affSColin Finck  */
timeKillEvent(UINT wID)370c2c66affSColin Finck MMRESULT WINAPI timeKillEvent(UINT wID)
371c2c66affSColin Finck {
372c2c66affSColin Finck     LPWINE_TIMERENTRY   lpSelf = NULL, *lpTimer;
373c2c66affSColin Finck 
374c2c66affSColin Finck     TRACE("(%u)\n", wID);
375c2c66affSColin Finck     EnterCriticalSection(&WINMM_cs);
376c2c66affSColin Finck     /* remove WINE_TIMERENTRY from list */
377c2c66affSColin Finck     for (lpTimer = &TIME_TimersList; *lpTimer; lpTimer = &(*lpTimer)->lpNext) {
378c2c66affSColin Finck 	if (wID == (*lpTimer)->wTimerID) {
379c2c66affSColin Finck             lpSelf = *lpTimer;
380c2c66affSColin Finck             /* unlink timer of id 'wID' */
381c2c66affSColin Finck             *lpTimer = (*lpTimer)->lpNext;
382c2c66affSColin Finck 	    break;
383c2c66affSColin Finck 	}
384c2c66affSColin Finck     }
385c2c66affSColin Finck     LeaveCriticalSection(&WINMM_cs);
386c2c66affSColin Finck 
387c2c66affSColin Finck     if (!lpSelf)
388c2c66affSColin Finck     {
389c2c66affSColin Finck         WARN("wID=%u is not a valid timer ID\n", wID);
390c2c66affSColin Finck         return MMSYSERR_INVALPARAM;
391c2c66affSColin Finck     }
392c2c66affSColin Finck     if (lpSelf->wFlags & TIME_KILL_SYNCHRONOUS)
393c2c66affSColin Finck         WaitForSingleObject(TIME_hKillEvent, INFINITE);
394c2c66affSColin Finck     HeapFree(GetProcessHeap(), 0, lpSelf);
395c2c66affSColin Finck     return TIMERR_NOERROR;
396c2c66affSColin Finck }
397c2c66affSColin Finck 
398c2c66affSColin Finck /**************************************************************************
399c2c66affSColin Finck  * 				timeGetDevCaps		[WINMM.@]
400c2c66affSColin Finck  */
timeGetDevCaps(LPTIMECAPS lpCaps,UINT wSize)401c2c66affSColin Finck MMRESULT WINAPI timeGetDevCaps(LPTIMECAPS lpCaps, UINT wSize)
402c2c66affSColin Finck {
403c2c66affSColin Finck     TRACE("(%p, %u)\n", lpCaps, wSize);
404c2c66affSColin Finck 
405c2c66affSColin Finck     if (lpCaps == 0) {
406c2c66affSColin Finck         WARN("invalid lpCaps\n");
407c2c66affSColin Finck         return TIMERR_NOCANDO;
408c2c66affSColin Finck     }
409c2c66affSColin Finck 
410c2c66affSColin Finck     if (wSize < sizeof(TIMECAPS)) {
411c2c66affSColin Finck         WARN("invalid wSize\n");
412c2c66affSColin Finck         return TIMERR_NOCANDO;
413c2c66affSColin Finck     }
414c2c66affSColin Finck 
415c2c66affSColin Finck     lpCaps->wPeriodMin = MMSYSTIME_MININTERVAL;
416c2c66affSColin Finck     lpCaps->wPeriodMax = MMSYSTIME_MAXINTERVAL;
417c2c66affSColin Finck     return TIMERR_NOERROR;
418c2c66affSColin Finck }
419c2c66affSColin Finck 
420c2c66affSColin Finck /**************************************************************************
421c2c66affSColin Finck  * 				timeBeginPeriod		[WINMM.@]
422c2c66affSColin Finck  */
timeBeginPeriod(UINT wPeriod)423c2c66affSColin Finck MMRESULT WINAPI timeBeginPeriod(UINT wPeriod)
424c2c66affSColin Finck {
425c2c66affSColin Finck     if (wPeriod < MMSYSTIME_MININTERVAL || wPeriod > MMSYSTIME_MAXINTERVAL)
426c2c66affSColin Finck         return TIMERR_NOCANDO;
427c2c66affSColin Finck 
428*88300ec3SThomas Brogan     /*  High resolution timer requested, use QPC */
429*88300ec3SThomas Brogan     if (wPeriod <= 5 && TIME_qpcFreq.QuadPart == 0)
430*88300ec3SThomas Brogan     {
431*88300ec3SThomas Brogan         if (QueryPerformanceFrequency(&TIME_qpcFreq))
432*88300ec3SThomas Brogan             TIME_qpcFreq.QuadPart /= 1000;
433*88300ec3SThomas Brogan     }
434*88300ec3SThomas Brogan 
435c2c66affSColin Finck     if (wPeriod > MMSYSTIME_MININTERVAL)
436c2c66affSColin Finck     {
437c2c66affSColin Finck         WARN("Stub; we set our timer resolution at minimum\n");
438c2c66affSColin Finck     }
439c2c66affSColin Finck 
440c2c66affSColin Finck     return 0;
441c2c66affSColin Finck }
442c2c66affSColin Finck 
443c2c66affSColin Finck /**************************************************************************
444c2c66affSColin Finck  * 				timeEndPeriod		[WINMM.@]
445c2c66affSColin Finck  */
timeEndPeriod(UINT wPeriod)446c2c66affSColin Finck MMRESULT WINAPI timeEndPeriod(UINT wPeriod)
447c2c66affSColin Finck {
448c2c66affSColin Finck     if (wPeriod < MMSYSTIME_MININTERVAL || wPeriod > MMSYSTIME_MAXINTERVAL)
449c2c66affSColin Finck         return TIMERR_NOCANDO;
450c2c66affSColin Finck 
451*88300ec3SThomas Brogan     /*  High resolution timer no longer requested, stop using QPC */
452*88300ec3SThomas Brogan     if (wPeriod <= 5 && TIME_qpcFreq.QuadPart != 0)
453*88300ec3SThomas Brogan         TIME_qpcFreq.QuadPart = 0;
454*88300ec3SThomas Brogan 
455c2c66affSColin Finck     if (wPeriod > MMSYSTIME_MININTERVAL)
456c2c66affSColin Finck     {
457c2c66affSColin Finck         WARN("Stub; we set our timer resolution at minimum\n");
458c2c66affSColin Finck     }
459c2c66affSColin Finck     return 0;
460c2c66affSColin Finck }
461c2c66affSColin Finck 
462c2c66affSColin Finck /**************************************************************************
463c2c66affSColin Finck  * 				timeGetTime    [MMSYSTEM.607]
464c2c66affSColin Finck  * 				timeGetTime    [WINMM.@]
465c2c66affSColin Finck  */
timeGetTime(void)466c2c66affSColin Finck DWORD WINAPI timeGetTime(void)
467c2c66affSColin Finck {
468*88300ec3SThomas Brogan     LARGE_INTEGER perfCount;
469c2c66affSColin Finck #if defined(COMMENTOUTPRIORTODELETING)
470c2c66affSColin Finck     DWORD       count;
471c2c66affSColin Finck 
472c2c66affSColin Finck     /* FIXME: releasing the win16 lock here is a temporary hack (I hope)
473c2c66affSColin Finck      * that lets mciavi.drv run correctly
474c2c66affSColin Finck      */
475c2c66affSColin Finck     if (pFnReleaseThunkLock) pFnReleaseThunkLock(&count);
476c2c66affSColin Finck     if (pFnRestoreThunkLock) pFnRestoreThunkLock(count);
477c2c66affSColin Finck #endif
478*88300ec3SThomas Brogan     /* Use QPC if a high-resolution timer was requested (<= 5ms) */
479*88300ec3SThomas Brogan     if (TIME_qpcFreq.QuadPart != 0)
480*88300ec3SThomas Brogan     {
481*88300ec3SThomas Brogan         QueryPerformanceCounter(&perfCount);
482*88300ec3SThomas Brogan         return (DWORD)(perfCount.QuadPart / TIME_qpcFreq.QuadPart);
483*88300ec3SThomas Brogan     }
484*88300ec3SThomas Brogan     /* Otherwise continue using GetTickCount */
485c2c66affSColin Finck     return GetTickCount();
486c2c66affSColin Finck }
487