Added a first-cut version of MapVirtualKeyExW() that has the same
[wine] / dlls / winmm / time.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2
3 /*
4  * MMSYTEM time functions
5  *
6  * Copyright 1993 Martin Ayotte
7  */
8
9 #include <time.h>
10 #include <sys/time.h>
11 #include "windef.h"
12 #include "wingdi.h"
13 #include "winuser.h"
14 #include "winemm.h"
15 #include "services.h"
16 #include "debugtools.h"
17
18 DEFAULT_DEBUG_CHANNEL(mmtime);
19
20 /*
21  * FIXME
22  * We're using "1" as the mininum resolution to the timer,
23  * as Windows 95 does, according to the docs. Maybe it should
24  * depend on the computers resources!
25  */
26 #define MMSYSTIME_MININTERVAL (1)
27 #define MMSYSTIME_MAXINTERVAL (65535)
28
29 /* ### start build ### */
30 extern WORD CALLBACK TIME_CallTo16_word_wwlll(FARPROC16,WORD,WORD,LONG,LONG,LONG);
31 /* ### stop build ### */
32
33 static  void    TIME_TriggerCallBack(LPWINE_TIMERENTRY lpTimer)
34 {
35     TRACE("before CallBack => lpFunc=%p wTimerID=%04X dwUser=%08lX !\n",
36           lpTimer->lpFunc, lpTimer->wTimerID, lpTimer->dwUser);
37         
38     /* - TimeProc callback that is called here is something strange, under Windows 3.1x it is called 
39      *          during interrupt time,  is allowed to execute very limited number of API calls (like
40      *          PostMessage), and must reside in DLL (therefore uses stack of active application). So I 
41      *       guess current implementation via SetTimer has to be improved upon.         
42      */
43     switch (lpTimer->wFlags & 0x30) {
44     case TIME_CALLBACK_FUNCTION:
45         if (lpTimer->wFlags & WINE_TIMER_IS32)
46             ((LPTIMECALLBACK)lpTimer->lpFunc)(lpTimer->wTimerID, 0, lpTimer->dwUser, 0, 0);
47         else
48             TIME_CallTo16_word_wwlll(lpTimer->lpFunc, lpTimer->wTimerID, 0,
49                                      lpTimer->dwUser, 0, 0);
50         break;
51     case TIME_CALLBACK_EVENT_SET:
52         SetEvent((HANDLE)lpTimer->lpFunc);
53         break;
54     case TIME_CALLBACK_EVENT_PULSE:
55         PulseEvent((HANDLE)lpTimer->lpFunc);
56         break;
57     default:
58         FIXME("Unknown callback type 0x%04x for mmtime callback (%p), ignored.\n", 
59               lpTimer->wFlags, lpTimer->lpFunc);
60         break;
61     }
62     TRACE("after CallBack !\n");
63 }
64
65 /**************************************************************************
66  *           TIME_MMSysTimeCallback
67  */
68 static void CALLBACK TIME_MMSysTimeCallback(ULONG_PTR ptr_)
69 {
70     LPWINE_TIMERENTRY   lpTimer, lpNextTimer;
71     LPWINE_MM_IDATA     iData = (LPWINE_MM_IDATA)ptr_;
72     DWORD               delta = GetTickCount() - iData->mmSysTimeMS;
73     int                 idx;
74
75     TRACE("Time delta: %ld\n", delta);
76
77     while (delta >= MMSYSTIME_MININTERVAL) {
78         delta -= MMSYSTIME_MININTERVAL;
79         iData->mmSysTimeMS += MMSYSTIME_MININTERVAL;
80         
81         /* since timeSetEvent() and timeKillEvent() can be called
82          * from 16 bit code, there are cases where win16 lock is
83          * locked upon entering timeSetEvent(), and then the mm timer 
84          * critical section is locked. This function cannot call the
85          * timer callback with the crit sect locked (because callback
86          * may need to acquire Win16 lock, thus providing a deadlock
87          * situation).
88          * To cope with that, we just copy the WINE_TIMERENTRY struct
89          * that need to trigger the callback, and call it without the
90          * mm timer crit sect locked. The bad side of this 
91          * implementation is that, in some cases, the callback may be
92          * invoked *after* a timer has been destroyed...
93          * EPP 99/07/13
94          */
95         idx = 0;
96         
97         EnterCriticalSection(&iData->cs);
98         for (lpTimer = iData->lpTimerList; lpTimer != NULL; ) {
99             lpNextTimer = lpTimer->lpNext;
100             if (lpTimer->uCurTime < MMSYSTIME_MININTERVAL) {
101                 /* since lpTimer->wDelay is >= MININTERVAL, wCurTime value
102                  * shall be correct (>= 0)
103                  */
104                 lpTimer->uCurTime += lpTimer->wDelay - MMSYSTIME_MININTERVAL;
105                 if (lpTimer->lpFunc) {
106                     if (idx == iData->nSizeLpTimers) {
107                         iData->lpTimers = (LPWINE_TIMERENTRY)
108                             HeapReAlloc(GetProcessHeap(), 0, 
109                                         iData->lpTimers, 
110                                         ++iData->nSizeLpTimers * sizeof(WINE_TIMERENTRY));
111                     }
112                     iData->lpTimers[idx++] = *lpTimer;
113                 }
114                 /* TIME_ONESHOT is defined as 0 */
115                 if (!(lpTimer->wFlags & TIME_PERIODIC))
116                     timeKillEvent(lpTimer->wTimerID);
117             } else {
118                 lpTimer->uCurTime -= MMSYSTIME_MININTERVAL;
119             }
120             lpTimer = lpNextTimer;
121         }
122         LeaveCriticalSection(&iData->cs);
123         
124         while (idx > 0) {
125             TIME_TriggerCallBack(&iData->lpTimers[--idx]);
126         }
127     }
128 }
129
130 /**************************************************************************
131  *                              MULTIMEDIA_MMTimeStart          [internal]
132  */
133 static  LPWINE_MM_IDATA MULTIMEDIA_MMTimeStart(void)
134 {
135     LPWINE_MM_IDATA     iData = MULTIMEDIA_GetIData();
136
137     if (IsBadWritePtr(iData, sizeof(WINE_MM_IDATA))) {
138         ERR("iData is not correctly set, please report. Expect failure.\n");
139         return 0;
140     }
141     /* FIXME: the service timer is never killed, even when process is
142      * detached from this DLL
143      */
144     /* one could think it's possible to stop the service thread activity when no more
145      * mm timers are active, but this would require to keep mmSysTimeMS up-to-date
146      * without being incremented within the service thread callback.
147      */
148     if (!iData->hMMTimer) {
149         iData->mmSysTimeMS = GetTickCount();
150         iData->lpTimerList = NULL;
151         /* 10ms seems a reasonable value ?? */
152         iData->hMMTimer = SERVICE_AddTimer(10, TIME_MMSysTimeCallback, (DWORD)iData);
153     }
154
155     return iData;
156 }
157
158 /**************************************************************************
159  *                              timeGetSystemTime       [WINMM.140]
160  */
161 MMRESULT WINAPI timeGetSystemTime(LPMMTIME lpTime, UINT wSize)
162 {
163     TRACE("(%p, %u);\n", lpTime, wSize);
164
165     if (wSize >= sizeof(*lpTime)) {
166         lpTime->wType = TIME_MS;
167         lpTime->u.ms = MULTIMEDIA_MMTimeStart()->mmSysTimeMS;
168
169         TRACE("=> %lu\n", lpTime->u.ms);
170     }
171
172     return 0;
173 }
174
175 /**************************************************************************
176  *                              timeGetSystemTime       [MMSYSTEM.601]
177  */
178 MMRESULT16 WINAPI timeGetSystemTime16(LPMMTIME16 lpTime, UINT16 wSize)
179 {
180     TRACE("(%p, %u);\n", lpTime, wSize);
181
182     if (wSize >= sizeof(*lpTime)) {
183         lpTime->wType = TIME_MS;
184         lpTime->u.ms = MULTIMEDIA_MMTimeStart()->mmSysTimeMS;
185
186         TRACE("=> %lu\n", lpTime->u.ms);
187     }
188
189     return 0;
190 }
191
192 /**************************************************************************
193  *                              timeSetEventInternal    [internal]
194  */
195 static  WORD    timeSetEventInternal(UINT wDelay, UINT wResol,
196                                      FARPROC16 lpFunc, DWORD dwUser, UINT wFlags)
197 {
198     WORD                wNewID = 0;
199     LPWINE_TIMERENTRY   lpNewTimer;
200     LPWINE_TIMERENTRY   lpTimer;
201     LPWINE_MM_IDATA     iData;
202
203     TRACE("(%u, %u, %p, %08lX, %04X);\n", wDelay, wResol, lpFunc, dwUser, wFlags);
204
205     lpNewTimer = (LPWINE_TIMERENTRY)HeapAlloc(GetProcessHeap(), 0, sizeof(WINE_TIMERENTRY));
206     if (lpNewTimer == NULL)
207         return 0;
208
209     if (wDelay < MMSYSTIME_MININTERVAL || wDelay > MMSYSTIME_MAXINTERVAL)
210         return 0;
211
212     iData = MULTIMEDIA_MMTimeStart();
213
214     lpNewTimer->uCurTime = wDelay;
215     lpNewTimer->wDelay = wDelay;
216     lpNewTimer->wResol = wResol;
217     lpNewTimer->lpFunc = lpFunc;
218     lpNewTimer->dwUser = dwUser;
219     lpNewTimer->wFlags = wFlags;
220
221     EnterCriticalSection(&iData->cs);
222
223     for (lpTimer = iData->lpTimerList; lpTimer != NULL; lpTimer = lpTimer->lpNext) {
224         wNewID = max(wNewID, lpTimer->wTimerID);
225     }
226     
227     lpNewTimer->lpNext = iData->lpTimerList;
228     iData->lpTimerList = lpNewTimer;
229     lpNewTimer->wTimerID = wNewID + 1;
230
231     LeaveCriticalSection(&iData->cs);
232
233     TRACE("=> %u\n", wNewID + 1);
234
235     return wNewID + 1;
236 }
237
238 /**************************************************************************
239  *                              timeSetEvent            [MMSYSTEM.602]
240  */
241 MMRESULT WINAPI timeSetEvent(UINT wDelay, UINT wResol, LPTIMECALLBACK lpFunc,
242                              DWORD dwUser, UINT wFlags)
243 {
244     if (wFlags & WINE_TIMER_IS32)
245         WARN("Unknown windows flag... wine internally used.. ooch\n");
246
247     return timeSetEventInternal(wDelay, wResol, (FARPROC16)lpFunc, 
248                                 dwUser, wFlags|WINE_TIMER_IS32);
249 }
250
251 /**************************************************************************
252  *                              timeSetEvent            [MMSYSTEM.602]
253  */
254 MMRESULT16 WINAPI timeSetEvent16(UINT16 wDelay, UINT16 wResol, LPTIMECALLBACK16 lpFunc, 
255                                  DWORD dwUser, UINT16 wFlags)
256 {
257     if (wFlags & WINE_TIMER_IS32)
258         WARN("Unknown windows flag... wine internally used.. ooch\n");
259
260     return timeSetEventInternal(wDelay, wResol, (FARPROC16)lpFunc, 
261                                 dwUser, wFlags & ~WINE_TIMER_IS32);
262 }
263
264 /**************************************************************************
265  *                              timeKillEvent           [WINMM.142]
266  */
267 MMRESULT WINAPI timeKillEvent(UINT wID)
268 {
269     LPWINE_TIMERENTRY*  lpTimer;
270     LPWINE_MM_IDATA     iData = MULTIMEDIA_GetIData();
271     MMRESULT            ret = MMSYSERR_INVALPARAM;
272
273     TRACE("(%u)\n", wID);
274     EnterCriticalSection(&iData->cs);
275     /* remove WINE_TIMERENTRY from list */
276     for (lpTimer = &iData->lpTimerList; *lpTimer; lpTimer = &(*lpTimer)->lpNext) {
277         if (wID == (*lpTimer)->wTimerID) {
278             break;
279         }
280     }
281     LeaveCriticalSection(&iData->cs);
282     
283     if (*lpTimer) {
284         LPWINE_TIMERENTRY       lpTemp = *lpTimer;
285
286         /* unlink timer of id 'wID' */
287         *lpTimer = (*lpTimer)->lpNext;
288         HeapFree(GetProcessHeap(), 0, lpTemp);
289         ret = TIMERR_NOERROR;
290     } else {
291         WARN("wID=%u is not a valid timer ID\n", wID);
292     }
293
294     return ret;
295 }
296
297 /**************************************************************************
298  *                              timeKillEvent           [MMSYSTEM.603]
299  */
300 MMRESULT16 WINAPI timeKillEvent16(UINT16 wID)
301 {
302     return timeKillEvent(wID);
303 }
304
305 /**************************************************************************
306  *                              timeGetDevCaps          [WINMM.139]
307  */
308 MMRESULT WINAPI timeGetDevCaps(LPTIMECAPS lpCaps, UINT wSize)
309 {
310     TRACE("(%p, %u) !\n", lpCaps, wSize);
311
312     lpCaps->wPeriodMin = MMSYSTIME_MININTERVAL;
313     lpCaps->wPeriodMax = MMSYSTIME_MAXINTERVAL;
314     return 0;
315 }
316
317 /**************************************************************************
318  *                              timeGetDevCaps          [MMSYSTEM.604]
319  */
320 MMRESULT16 WINAPI timeGetDevCaps16(LPTIMECAPS16 lpCaps, UINT16 wSize)
321 {
322     TRACE("(%p, %u) !\n", lpCaps, wSize);
323
324     lpCaps->wPeriodMin = MMSYSTIME_MININTERVAL;
325     lpCaps->wPeriodMax = MMSYSTIME_MAXINTERVAL;
326     return 0;
327 }
328
329 /**************************************************************************
330  *                              timeBeginPeriod         [WINMM.137]
331  */
332 MMRESULT WINAPI timeBeginPeriod(UINT wPeriod)
333 {
334     TRACE("(%u) !\n", wPeriod);
335
336     if (wPeriod < MMSYSTIME_MININTERVAL || wPeriod > MMSYSTIME_MAXINTERVAL) 
337         return TIMERR_NOCANDO;
338     return 0;
339 }
340
341 /**************************************************************************
342  *                              timeBeginPeriod16       [MMSYSTEM.605]
343  */
344 MMRESULT16 WINAPI timeBeginPeriod16(UINT16 wPeriod)
345 {
346     TRACE("(%u) !\n", wPeriod);
347
348     if (wPeriod < MMSYSTIME_MININTERVAL || wPeriod > MMSYSTIME_MAXINTERVAL) 
349         return TIMERR_NOCANDO;
350     return 0;
351 }
352
353 /**************************************************************************
354  *                              timeEndPeriod           [WINMM.138]
355  */
356 MMRESULT WINAPI timeEndPeriod(UINT wPeriod)
357 {
358     TRACE("(%u) !\n", wPeriod);
359
360     if (wPeriod < MMSYSTIME_MININTERVAL || wPeriod > MMSYSTIME_MAXINTERVAL) 
361         return TIMERR_NOCANDO;
362     return 0;
363 }
364
365 /**************************************************************************
366  *                              timeEndPeriod16         [MMSYSTEM.606]
367  */
368 MMRESULT16 WINAPI timeEndPeriod16(UINT16 wPeriod)
369 {
370     TRACE("(%u) !\n", wPeriod);
371
372     if (wPeriod < MMSYSTIME_MININTERVAL || wPeriod > MMSYSTIME_MAXINTERVAL) 
373         return TIMERR_NOCANDO;
374     return 0;
375 }
376
377 /**************************************************************************
378  *                              timeGetTime    [MMSYSTEM.607][WINMM.141]
379  */
380 DWORD WINAPI timeGetTime(void)
381 {
382     /* FIXME: releasing the win16 lock here is a temporary hack (I hope)
383      * that lets mciavi.drv run correctly
384      */
385     DWORD count;
386     ReleaseThunkLock(&count);
387     RestoreThunkLock(count);
388     return MULTIMEDIA_MMTimeStart()->mmSysTimeMS;
389 }