/* * MMSYTEM time functions * * Copyright 1993 Martin Ayotte */ #include #include #include #include "windows.h" #include "win.h" #include "ldt.h" #include "module.h" #include "callback.h" #include "user.h" #include "driver.h" #include "mmsystem.h" #include "debug.h" #include "xmalloc.h" static BOOL32 mmTimeStarted = FALSE; static MMTIME16 mmSysTimeMS; static MMTIME16 mmSysTimeSMPTE; /* this is used to avoid infinite loop in timeGetTime, because of the faked multimedia timers, and will desappear as soon as the timers are implemented correctly. */ static int time_called=0; typedef struct tagTIMERENTRY { UINT32 wDelay; UINT32 wResol; FARPROC16 lpFunc; HINSTANCE32 hInstance; DWORD dwUser; UINT32 wFlags; UINT32 wTimerID; UINT32 wCurTime; UINT32 iswin32; struct tagTIMERENTRY *Next; DWORD triggertime; } TIMERENTRY, *LPTIMERENTRY; static LPTIMERENTRY lpTimerList = NULL; /* * FIXME * We're using "1" as the mininum resolution to the timer, * as Windows 95 does, according to the docs. Maybe it should * depend on the computers resources! */ #define MMSYSTIME_MININTERVAL (1) #define MMSYSTIME_MAXINTERVAL (65535) /************************************************************************** * check_MMtimers */ static VOID check_MMtimers() { LPTIMERENTRY lpTimer = lpTimerList; DWORD curtick = GetTickCount(); while (lpTimer != NULL) { if (lpTimer->triggertime <= curtick) { lpTimer->wCurTime = lpTimer->wDelay; if (lpTimer->lpFunc != (FARPROC16) NULL) { TRACE(mmtime, "before CallBack16 !\n"); TRACE(mmtime, "lpFunc=%p wTimerID=%04X dwUser=%08lX !\n", lpTimer->lpFunc, lpTimer->wTimerID, lpTimer->dwUser); TRACE(mmtime, "hInstance=%04X !\n", lpTimer->hInstance); /* - TimeProc callback that is called here is something strange, under Windows 3.1x it is called * during interrupt time, is allowed to execute very limited number of API calls (like * PostMessage), and must reside in DLL (therefore uses stack of active application). So I * guess current implementation via SetTimer has to be improved upon. */ if (lpTimer->iswin32) lpTimer->lpFunc(lpTimer->wTimerID,0,lpTimer->dwUser,0,0); else Callbacks->CallTimeFuncProc(lpTimer->lpFunc, lpTimer->wTimerID,0, lpTimer->dwUser,0,0 ); TRACE(mmtime, "after CallBack16 !\n"); } if (lpTimer->wFlags & TIME_ONESHOT) timeKillEvent32(lpTimer->wTimerID); } lpTimer = lpTimer->Next; } } /************************************************************************** * TIME_MMSysTimeCallback */ static VOID TIME_MMSysTimeCallback( HWND32 hwnd, UINT32 msg, UINT32 id, DWORD dwTime ) { LPTIMERENTRY lpTimer = lpTimerList; mmSysTimeMS.u.ms += MMSYSTIME_MININTERVAL; mmSysTimeSMPTE.u.smpte.frame++; while (lpTimer != NULL) { lpTimer->wCurTime--; if (lpTimer->wCurTime == 0) { lpTimer->wCurTime = lpTimer->wDelay; if (lpTimer->lpFunc != (FARPROC16) NULL) { TRACE(mmtime, "before CallBack16 !\n"); TRACE(mmtime, "lpFunc=%p wTimerID=%04X dwUser=%08lX !\n", lpTimer->lpFunc, lpTimer->wTimerID, lpTimer->dwUser); TRACE(mmtime, "hInstance=%04X !\n", lpTimer->hInstance); /* This is wrong (lpFunc is NULL all the time) lpFunc = MODULE_GetEntryPoint( lpTimer->hInstance, MODULE_GetOrdinal(lpTimer->hInstance,"TimerCallBack" )); TRACE(mmtime, "lpFunc=%08lx !\n", lpFunc); */ /* - TimeProc callback that is called here is something strange, under Windows 3.1x it is called * during interrupt time, is allowed to execute very limited number of API calls (like * PostMessage), and must reside in DLL (therefore uses stack of active application). So I * guess current implementation via SetTimer has to be improved upon. */ if (lpTimer->iswin32) lpTimer->lpFunc(lpTimer->wTimerID,0,lpTimer->dwUser,0,0); else Callbacks->CallTimeFuncProc(lpTimer->lpFunc, lpTimer->wTimerID,0, lpTimer->dwUser,0,0 ); TRACE(mmtime, "after CallBack16 !\n"); fflush(stdout); } if (lpTimer->wFlags & TIME_ONESHOT) timeKillEvent32(lpTimer->wTimerID); } lpTimer = lpTimer->Next; } } /************************************************************************** * StartMMTime [internal] */ static void StartMMTime() { if (!mmTimeStarted) { mmTimeStarted = TRUE; mmSysTimeMS.wType = TIME_MS; mmSysTimeMS.u.ms = 0; mmSysTimeSMPTE.wType = TIME_SMPTE; mmSysTimeSMPTE.u.smpte.hour = 0; mmSysTimeSMPTE.u.smpte.min = 0; mmSysTimeSMPTE.u.smpte.sec = 0; mmSysTimeSMPTE.u.smpte.frame = 0; mmSysTimeSMPTE.u.smpte.fps = 0; mmSysTimeSMPTE.u.smpte.dummy = 0; SetTimer32( 0, 1, MMSYSTIME_MININTERVAL, TIME_MMSysTimeCallback ); } } /************************************************************************** * timeGetSystemTime [WINMM.140] */ MMRESULT32 WINAPI timeGetSystemTime32(LPMMTIME32 lpTime, UINT32 wSize) { TRACE(mmsys, "(%p, %u);\n", lpTime, wSize); if (!mmTimeStarted) StartMMTime(); lpTime->wType = TIME_MS; lpTime->u.ms = mmSysTimeMS.u.ms; return 0; } /************************************************************************** * timeGetSystemTime [MMSYSTEM.601] */ MMRESULT16 WINAPI timeGetSystemTime16(LPMMTIME16 lpTime, UINT16 wSize) { TRACE(mmsys, "(%p, %u);\n", lpTime, wSize); if (!mmTimeStarted) StartMMTime(); lpTime->wType = TIME_MS; lpTime->u.ms = mmSysTimeMS.u.ms; return 0; } /************************************************************************** * timeSetEvent [MMSYSTEM.602] */ MMRESULT32 WINAPI timeSetEvent32(UINT32 wDelay,UINT32 wResol, LPTIMECALLBACK32 lpFunc,DWORD dwUser, UINT32 wFlags) { WORD wNewID = 0; LPTIMERENTRY lpNewTimer; LPTIMERENTRY lpTimer = lpTimerList; TRACE(mmtime, "(%u, %u, %p, %08lX, %04X);\n", wDelay, wResol, lpFunc, dwUser, wFlags); if (!mmTimeStarted) StartMMTime(); lpNewTimer = (LPTIMERENTRY)xmalloc(sizeof(TIMERENTRY)); if (lpNewTimer == NULL) return 0; while (lpTimer != NULL) { wNewID = MAX(wNewID, lpTimer->wTimerID); lpTimer = lpTimer->Next; } lpNewTimer->Next = lpTimerList; lpTimerList = lpNewTimer; lpNewTimer->wTimerID = wNewID + 1; lpNewTimer->wCurTime = wDelay; lpNewTimer->triggertime = wDelay+GetTickCount(); lpNewTimer->wDelay = wDelay; lpNewTimer->wResol = wResol; lpNewTimer->lpFunc = (FARPROC16) lpFunc; lpNewTimer->iswin32 = 1; lpNewTimer->hInstance = GetTaskDS(); TRACE(mmtime, "hInstance=%04X !\n", lpNewTimer->hInstance); TRACE(mmtime, "lpFunc=%p !\n", lpFunc); lpNewTimer->dwUser = dwUser; lpNewTimer->wFlags = wFlags; return lpNewTimer->wTimerID; } /************************************************************************** * timeSetEvent [MMSYSTEM.602] */ MMRESULT16 WINAPI timeSetEvent16(UINT16 wDelay, UINT16 wResol, LPTIMECALLBACK16 lpFunc,DWORD dwUser, UINT16 wFlags) { WORD wNewID = 0; LPTIMERENTRY lpNewTimer; LPTIMERENTRY lpTimer = lpTimerList; TRACE(mmtime, "(%u, %u, %p, %08lX, %04X);\n", wDelay, wResol, lpFunc, dwUser, wFlags); if (!mmTimeStarted) StartMMTime(); lpNewTimer = (LPTIMERENTRY)xmalloc(sizeof(TIMERENTRY)); if (lpNewTimer == NULL) return 0; while (lpTimer != NULL) { wNewID = MAX(wNewID, lpTimer->wTimerID); lpTimer = lpTimer->Next; } lpNewTimer->Next = lpTimerList; lpTimerList = lpNewTimer; lpNewTimer->wTimerID = wNewID + 1; lpNewTimer->wCurTime = wDelay; lpNewTimer->wDelay = wDelay; lpNewTimer->triggertime = wDelay+GetTickCount(); lpNewTimer->wResol = wResol; lpNewTimer->lpFunc = (FARPROC16) lpFunc; lpNewTimer->iswin32 = 0; lpNewTimer->hInstance = GetTaskDS(); TRACE(mmtime, "hInstance=%04X !\n", lpNewTimer->hInstance); TRACE(mmtime, "(lpFunc)=%p !\n", PTR_SEG_TO_LIN(lpFunc)); lpNewTimer->dwUser = dwUser; lpNewTimer->wFlags = wFlags; return lpNewTimer->wTimerID; } /************************************************************************** * timeKillEvent [WINMM.142] */ MMRESULT32 WINAPI timeKillEvent32(UINT32 wID) { LPTIMERENTRY xlptimer,*lpTimer = &lpTimerList; while (*lpTimer) { if (wID == (*lpTimer)->wTimerID) { xlptimer = (*lpTimer)->Next; free(*lpTimer); *lpTimer = xlptimer; return TRUE; } lpTimer = &((*lpTimer)->Next); } return 0; } /************************************************************************** * timeKillEvent [MMSYSTEM.603] */ MMRESULT16 WINAPI timeKillEvent16(UINT16 wID) { return timeKillEvent32(wID); } /************************************************************************** * timeGetDevCaps [WINMM.139] */ MMRESULT32 WINAPI timeGetDevCaps32(LPTIMECAPS32 lpCaps,UINT32 wSize) { TRACE(mmtime, "(%p, %u) !\n", lpCaps, wSize); if (!mmTimeStarted) StartMMTime(); lpCaps->wPeriodMin = MMSYSTIME_MININTERVAL; lpCaps->wPeriodMax = MMSYSTIME_MAXINTERVAL; return 0; } /************************************************************************** * timeGetDevCaps [MMSYSTEM.604] */ MMRESULT16 WINAPI timeGetDevCaps16(LPTIMECAPS16 lpCaps, UINT16 wSize) { TRACE(mmtime, "(%p, %u) !\n", lpCaps, wSize); if (!mmTimeStarted) StartMMTime(); lpCaps->wPeriodMin = MMSYSTIME_MININTERVAL; lpCaps->wPeriodMax = MMSYSTIME_MAXINTERVAL; return 0; } /************************************************************************** * timeBeginPeriod [WINMM.137] */ MMRESULT32 WINAPI timeBeginPeriod32(UINT32 wPeriod) { TRACE(mmtime, "(%u) !\n", wPeriod); if (!mmTimeStarted) StartMMTime(); if (wPeriod < MMSYSTIME_MININTERVAL || wPeriod > MMSYSTIME_MAXINTERVAL) return TIMERR_NOCANDO; return 0; } /************************************************************************** * timeBeginPeriod [MMSYSTEM.605] */ MMRESULT16 WINAPI timeBeginPeriod16(UINT16 wPeriod) { TRACE(mmtime, "(%u) !\n", wPeriod); if (!mmTimeStarted) StartMMTime(); if (wPeriod < MMSYSTIME_MININTERVAL || wPeriod > MMSYSTIME_MAXINTERVAL) return TIMERR_NOCANDO; return 0; } /************************************************************************** * timeEndPeriod [WINMM.138] */ MMRESULT32 WINAPI timeEndPeriod32(UINT32 wPeriod) { TRACE(mmtime, "(%u) !\n", wPeriod); if (wPeriod < MMSYSTIME_MININTERVAL || wPeriod > MMSYSTIME_MAXINTERVAL) return TIMERR_NOCANDO; return 0; } /************************************************************************** * timeEndPeriod [MMSYSTEM.606] */ MMRESULT16 WINAPI timeEndPeriod16(UINT16 wPeriod) { TRACE(mmtime, "(%u) !\n", wPeriod); if (wPeriod < MMSYSTIME_MININTERVAL || wPeriod > MMSYSTIME_MAXINTERVAL) return TIMERR_NOCANDO; return 0; } /************************************************************************** * timeGetTime [MMSYSTEM.607][WINMM.141] */ DWORD WINAPI timeGetTime() { /* FIXME all this function should have to be is long result; struct timeval time; dprintf_mmtime(stddeb, "timeGetTime(); !\n"); gettimeofday(&time, 0); result = (((long)time.tv_sec * (long)1000) + ((long)time.tv_usec / (long)1000)); but the multimedia timers are not implemented correctly, so all the rest is a workaround to fake them. */ static DWORD lasttick=0; DWORD newtick; TRACE(mmtime, "!\n"); if (!mmTimeStarted) StartMMTime(); newtick = GetTickCount(); mmSysTimeMS.u.ms+=newtick-lasttick; /* FIXME: faked timer */ if (newtick!=lasttick) if (!time_called) { /* to avoid infinite recursion if timeGetTime is called inside check_MMtimers */ time_called++; check_MMtimers(); time_called--; } //check_MMtimers(); lasttick = newtick; TRACE(mmtime, "Time = %ld\n",mmSysTimeMS.u.ms); return mmSysTimeMS.u.ms; }