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