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