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