Add a stub for GlobalMasterHandle.
[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 <stdlib.h>
10 #include <string.h>
11 #include <time.h>
12 #include "windef.h"
13 #include "wine/winbase16.h" /* GetTaskDS */
14 #include "callback.h"
15 #include "mmsystem.h"
16 #include "xmalloc.h"
17 #include "options.h"
18 #include "debug.h"
19
20 DECLARE_DEBUG_CHANNEL(mmsys)
21 DECLARE_DEBUG_CHANNEL(mmtime)
22
23 #define USE_FAKE_MM_TIMERS
24
25 static MMTIME16 mmSysTimeMS;
26 static MMTIME16 mmSysTimeSMPTE;
27
28 typedef struct tagTIMERENTRY {
29     UINT                        wDelay;
30     UINT                        wResol;
31     FARPROC16                   lpFunc;
32     HINSTANCE                   hInstance;
33     DWORD                       dwUser;
34     UINT                        wFlags;
35     UINT                        wTimerID;
36     UINT                        wCurTime;
37     UINT                        isWin32;
38     struct tagTIMERENTRY*       Next;
39 } TIMERENTRY, *LPTIMERENTRY;
40
41 static LPTIMERENTRY lpTimerList = NULL;
42
43 #ifdef USE_FAKE_MM_TIMERS
44 static DWORD dwLastCBTick = 0;
45 static BOOL bUseFakeTimers = FALSE;
46 static WORD wInCallBackLoop = 0;
47 #endif
48
49 /*
50  * FIXME
51  * We're using "1" as the mininum resolution to the timer,
52  * as Windows 95 does, according to the docs. Maybe it should
53  * depend on the computers resources!
54  */
55 #define MMSYSTIME_MININTERVAL (1)
56 #define MMSYSTIME_MAXINTERVAL (65535)
57
58 static  void    TIME_TriggerCallBack(LPTIMERENTRY lpTimer, DWORD dwCurrent)
59 {
60     lpTimer->wCurTime = lpTimer->wDelay;
61     
62     if (lpTimer->lpFunc != (FARPROC16) NULL) {
63         TRACE(mmtime, "before CallBack16 (%lu)!\n", dwCurrent);
64         TRACE(mmtime, "lpFunc=%p wTimerID=%04X dwUser=%08lX !\n",
65               lpTimer->lpFunc, lpTimer->wTimerID, lpTimer->dwUser);
66         TRACE(mmtime, "hInstance=%04X !\n", lpTimer->hInstance);
67         
68         
69         /* - TimeProc callback that is called here is something strange, under Windows 3.1x it is called 
70          *              during interrupt time,  is allowed to execute very limited number of API calls (like
71          *              PostMessage), and must reside in DLL (therefore uses stack of active application). So I 
72          *       guess current implementation via SetTimer has to be improved upon.             
73          */
74         switch (lpTimer->wFlags & 0x30) {
75         case TIME_CALLBACK_FUNCTION:
76                 if (lpTimer->isWin32)
77                     lpTimer->lpFunc(lpTimer->wTimerID,0,lpTimer->dwUser,0,0);
78                 else
79                     Callbacks->CallTimeFuncProc(lpTimer->lpFunc,
80                                                 lpTimer->wTimerID,0,
81                                                 lpTimer->dwUser,0,0);
82                 break;
83         case TIME_CALLBACK_EVENT_SET:
84                 SetEvent((HANDLE)lpTimer->lpFunc);
85                 break;
86         case TIME_CALLBACK_EVENT_PULSE:
87                 PulseEvent((HANDLE)lpTimer->lpFunc);
88                 break;
89         default:
90                 FIXME(mmtime,"Unknown callback type 0x%04x for mmtime callback (%p),ignored.\n",lpTimer->wFlags,lpTimer->lpFunc);
91                 break;
92         }
93         TRACE(mmtime, "after CallBack16 !\n");
94     }
95     if (lpTimer->wFlags & TIME_ONESHOT)
96         timeKillEvent(lpTimer->wTimerID);
97 }
98
99 /**************************************************************************
100  *           TIME_MMSysTimeCallback
101  */
102 static VOID WINAPI TIME_MMSysTimeCallback( HWND hwnd, UINT msg,
103                                     UINT id, DWORD dwTime )
104 {
105     LPTIMERENTRY lpTimer;
106     
107     mmSysTimeMS.u.ms += MMSYSTIME_MININTERVAL;
108     mmSysTimeSMPTE.u.smpte.frame++;
109     
110 #ifdef USE_FAKE_MM_TIMERS
111     if (bUseFakeTimers)
112         dwLastCBTick = GetTickCount();
113
114     if (!wInCallBackLoop++)
115 #endif
116         for (lpTimer = lpTimerList; lpTimer != NULL; lpTimer = lpTimer->Next) {
117             if (lpTimer->wCurTime < MMSYSTIME_MININTERVAL) {
118                 TIME_TriggerCallBack(lpTimer, dwTime);                  
119             } else {
120                 lpTimer->wCurTime -= MMSYSTIME_MININTERVAL;
121             }
122         }
123 #ifdef USE_FAKE_MM_TIMERS
124     wInCallBackLoop--;
125 #endif
126 }
127
128 /**************************************************************************
129  *                              StartMMTime                     [internal]
130  */
131 static void StartMMTime()
132 {
133     static BOOL         mmTimeStarted = FALSE;
134     
135     if (!mmTimeStarted) {
136         mmTimeStarted = TRUE;
137         mmSysTimeMS.wType = TIME_MS;
138         mmSysTimeMS.u.ms = 0;
139         mmSysTimeSMPTE.wType = TIME_SMPTE;
140         mmSysTimeSMPTE.u.smpte.hour = 0;
141         mmSysTimeSMPTE.u.smpte.min = 0;
142         mmSysTimeSMPTE.u.smpte.sec = 0;
143         mmSysTimeSMPTE.u.smpte.frame = 0;
144         mmSysTimeSMPTE.u.smpte.fps = 0;
145         mmSysTimeSMPTE.u.smpte.dummy = 0;
146         SetTimer( 0, 0, MMSYSTIME_MININTERVAL, TIME_MMSysTimeCallback );
147 #ifdef USE_FAKE_MM_TIMERS
148         bUseFakeTimers = PROFILE_GetWineIniBool("options", "MMFakeTimers", TRUE);
149         TRACE(mmtime, "FakeTimer=%c\n", bUseFakeTimers ? 'Y' : 'N');
150         if (bUseFakeTimers)
151             dwLastCBTick = GetTickCount();
152 #endif
153     }
154 }
155
156 /**************************************************************************
157  *                              timeGetSystemTime       [WINMM.140]
158  */
159 MMRESULT WINAPI timeGetSystemTime(LPMMTIME lpTime, UINT wSize)
160 {
161     TRACE(mmsys, "(%p, %u);\n", lpTime, wSize);
162     StartMMTime();
163     lpTime->wType = TIME_MS;
164     lpTime->u.ms = mmSysTimeMS.u.ms;
165     return 0;
166 }
167
168 /**************************************************************************
169  *                              timeGetSystemTime       [MMSYSTEM.601]
170  */
171 MMRESULT16 WINAPI timeGetSystemTime16(LPMMTIME16 lpTime, UINT16 wSize)
172 {
173     TRACE(mmsys, "(%p, %u);\n", lpTime, wSize);
174     StartMMTime();
175     lpTime->wType = TIME_MS;
176     lpTime->u.ms = mmSysTimeMS.u.ms;
177     return 0;
178 }
179
180 static  WORD    timeSetEventInternal(UINT wDelay,UINT wResol,
181                                      FARPROC16 lpFunc,DWORD dwUser,
182                                      UINT wFlags, UINT16 isWin32)
183 {
184     WORD                wNewID = 0;
185     LPTIMERENTRY        lpNewTimer;
186     LPTIMERENTRY        lpTimer = lpTimerList;
187     
188     TRACE(mmtime, "(%u, %u, %p, %08lX, %04X);\n",
189           wDelay, wResol, lpFunc, dwUser, wFlags);
190     StartMMTime();
191     lpNewTimer = (LPTIMERENTRY)xmalloc(sizeof(TIMERENTRY));
192     if (lpNewTimer == NULL)
193         return 0;
194     while (lpTimer != NULL) {
195         wNewID = MAX(wNewID, lpTimer->wTimerID);
196         lpTimer = lpTimer->Next;
197     }
198     
199     lpNewTimer->Next = lpTimerList;
200     lpTimerList = lpNewTimer;
201     lpNewTimer->wTimerID = wNewID + 1;
202     lpNewTimer->wCurTime = wDelay;
203     lpNewTimer->wDelay = wDelay;
204     lpNewTimer->wResol = wResol;
205     lpNewTimer->lpFunc = lpFunc;
206     lpNewTimer->isWin32 = isWin32;
207     lpNewTimer->hInstance = GetTaskDS16();
208     TRACE(mmtime, "hInstance=%04X !\n", lpNewTimer->hInstance);
209     TRACE(mmtime, "lpFunc=0x%08lx !\n", (DWORD)lpFunc );
210     lpNewTimer->dwUser = dwUser;
211     lpNewTimer->wFlags = wFlags;
212     return lpNewTimer->wTimerID;
213 }
214
215 /**************************************************************************
216  *                              timeSetEvent            [MMSYSTEM.602]
217  */
218 MMRESULT WINAPI timeSetEvent(UINT wDelay,UINT wResol,
219                                  LPTIMECALLBACK lpFunc,DWORD dwUser,
220                                  UINT wFlags)
221 {
222     return timeSetEventInternal(wDelay, wResol, (FARPROC16)lpFunc, 
223                                 dwUser, wFlags, 1);
224 }
225
226 /**************************************************************************
227  *                              timeSetEvent            [MMSYSTEM.602]
228  */
229 MMRESULT16 WINAPI timeSetEvent16(UINT16 wDelay, UINT16 wResol,
230                                  LPTIMECALLBACK16 lpFunc,DWORD dwUser,
231                                  UINT16 wFlags)
232 {
233     return timeSetEventInternal(wDelay, wResol, (FARPROC16)lpFunc, 
234                                 dwUser, wFlags, 0);
235 }
236
237 /**************************************************************************
238  *                              timeKillEvent           [WINMM.142]
239  */
240 MMRESULT WINAPI timeKillEvent(UINT wID)
241 {
242     LPTIMERENTRY*       lpTimer;
243     
244     for (lpTimer = &lpTimerList; *lpTimer; lpTimer = &((*lpTimer)->Next)) {
245         if (wID == (*lpTimer)->wTimerID) {
246             LPTIMERENTRY xlptimer = (*lpTimer)->Next;
247             
248             free(*lpTimer);
249             *lpTimer = xlptimer;
250             return TRUE;
251         }
252     }
253     return 0;
254 }
255
256 /**************************************************************************
257  *                              timeKillEvent           [MMSYSTEM.603]
258  */
259 MMRESULT16 WINAPI timeKillEvent16(UINT16 wID)
260 {
261     return timeKillEvent(wID);
262 }
263
264 /**************************************************************************
265  *                              timeGetDevCaps          [WINMM.139]
266  */
267 MMRESULT WINAPI timeGetDevCaps(LPTIMECAPS lpCaps,UINT wSize)
268 {
269     TRACE(mmtime, "(%p, %u) !\n", lpCaps, wSize);
270     StartMMTime();
271     lpCaps->wPeriodMin = MMSYSTIME_MININTERVAL;
272     lpCaps->wPeriodMax = MMSYSTIME_MAXINTERVAL;
273     return 0;
274 }
275
276 /**************************************************************************
277  *                              timeGetDevCaps          [MMSYSTEM.604]
278  */
279 MMRESULT16 WINAPI timeGetDevCaps16(LPTIMECAPS16 lpCaps, UINT16 wSize)
280 {
281     TRACE(mmtime, "(%p, %u) !\n", lpCaps, wSize);
282     StartMMTime();
283     lpCaps->wPeriodMin = MMSYSTIME_MININTERVAL;
284     lpCaps->wPeriodMax = MMSYSTIME_MAXINTERVAL;
285     return 0;
286 }
287
288 /**************************************************************************
289  *                              timeBeginPeriod         [WINMM.137]
290  */
291 MMRESULT WINAPI timeBeginPeriod(UINT wPeriod)
292 {
293     TRACE(mmtime, "(%u) !\n", wPeriod);
294     StartMMTime();
295     if (wPeriod < MMSYSTIME_MININTERVAL || wPeriod > MMSYSTIME_MAXINTERVAL) 
296         return TIMERR_NOCANDO;
297     return 0;
298 }
299 /**************************************************************************
300  *                              timeBeginPeriod         [MMSYSTEM.605]
301  */
302 MMRESULT16 WINAPI timeBeginPeriod16(UINT16 wPeriod)
303 {
304     TRACE(mmtime, "(%u) !\n", wPeriod);
305     StartMMTime();
306     if (wPeriod < MMSYSTIME_MININTERVAL || wPeriod > MMSYSTIME_MAXINTERVAL) 
307         return TIMERR_NOCANDO;
308     return 0;
309 }
310
311 /**************************************************************************
312  *                              timeEndPeriod           [WINMM.138]
313  */
314 MMRESULT WINAPI timeEndPeriod(UINT wPeriod)
315 {
316     TRACE(mmtime, "(%u) !\n", wPeriod);
317     StartMMTime();
318     if (wPeriod < MMSYSTIME_MININTERVAL || wPeriod > MMSYSTIME_MAXINTERVAL) 
319         return TIMERR_NOCANDO;
320     return 0;
321 }
322
323 /**************************************************************************
324  *                              timeEndPeriod           [MMSYSTEM.606]
325  */
326 MMRESULT16 WINAPI timeEndPeriod16(UINT16 wPeriod)
327 {
328     TRACE(mmtime, "(%u) !\n", wPeriod);
329     if (wPeriod < MMSYSTIME_MININTERVAL || wPeriod > MMSYSTIME_MAXINTERVAL) 
330         return TIMERR_NOCANDO;
331     return 0;
332 }
333
334 /**************************************************************************
335  *                              timeGetTime    [MMSYSTEM.607][WINMM.141]
336  */
337 DWORD WINAPI timeGetTime()
338 {
339     DWORD       dwNewTick = GetTickCount();
340
341     StartMMTime();
342 #ifdef USE_FAKE_MM_TIMERS
343     if (bUseFakeTimers) {
344         if (!wInCallBackLoop++) { 
345             DWORD               dwDelta;
346             
347             
348             if (dwNewTick < dwLastCBTick) {
349                 ERR(mmtime, "dwNewTick(%lu) < dwLastCBTick(%lu)\n", dwNewTick, dwLastCBTick);
350             }
351             dwDelta = dwNewTick - dwLastCBTick;
352             if (dwDelta > MMSYSTIME_MININTERVAL) {
353                 LPTIMERENTRY lpTimer;
354                 
355                 mmSysTimeMS.u.ms += dwDelta; /* FIXME: faked timer */
356                 dwLastCBTick = dwNewTick;
357                 for (lpTimer = lpTimerList; lpTimer != NULL; lpTimer = lpTimer->Next) {
358                     if (lpTimer->wCurTime < dwDelta) {
359                         TIME_TriggerCallBack(lpTimer, dwNewTick);
360                     } else {
361                         lpTimer->wCurTime -= dwDelta;
362                     }
363                 }
364             }
365         }
366         dwNewTick = mmSysTimeMS.u.ms;
367         wInCallBackLoop--;
368     }
369 #endif
370     return dwNewTick;
371 }