windows-nt/Source/XPSP1/NT/base/mvdm/wow16/mmsystem/time.c
2020-09-26 16:20:57 +08:00

444 lines
13 KiB
C

/******************************************************************************
Copyright (C) Microsoft Corporation 1985-1991. All rights reserved.
Title: TIME.C : MMSYSTEM TIMER API
Version: 1.00
*****************************************************************************/
//
// ***DANGER WARNING****
//
// none of these functions in this file need the default data segment
// so we undefine BUILDDLL, if you write code in this file that needs
// DS == DGROUP be warned!
//
// NOTE: most of this code is interupt time enterable, so we don't want
// it touching DGROUP anyway!
//
#undef BUILDDLL
#include <windows.h>
#include "mmsystem.h"
#include "mmddk.h"
#include "mmsysi.h"
#include "drvr.h"
#include "thunks.h"
#define MIN_RES 1
#define MIN_DELAY 6
//
// Define moveable code for timer interface.
//
#pragma alloc_text( RARE, timeGetDevCaps )
extern SZCODE szTimerDrv[]; // see init.c
DWORD dwLastGetTime = 0; // last TimeGetTime return value can be bigger than system TimeGetTime
DWORD dwRealLastGetTime = 0; // last system TimeGetTime return value
DWORD pfnDelayTimeGetTime = 0; // 32-bit function that sleeps for 1ms and returns if TimeGetTime flag applied
// look in timeTimeGetTime and WOWDelayTimeGetTime in wow32
//
// Define the init code for this file.
//
#pragma alloc_text( INIT, TimeInit )
/****************************************************************************
@doc EXTERNAL
@api UINT | timeGetSystemTime | This function retrieves the system time
in milliseconds. The system time is the time elapsed since
Windows was started.
@parm LPMMTIME | lpTime | Specifies a far pointer to an <t MMTIME> data
structure.
@parm UINT | wSize | Specifies the size of the <t MMTIME> structure.
@rdesc Returns zero.
The system time is returned in the <e MMTIME.ms> field of the <t MMTIME>
structure.
@comm The time is always returned in milliseconds.
@xref timeGetTime
****************************************************************************/
UINT WINAPI
timeGetSystemTime(
LPMMTIME lpTime,
UINT wSize
)
{
//
// !!!WARNING DS is not setup right!!! see above
//
if (wSize < sizeof(MMTIME))
return TIMERR_STRUCT;
lpTime->u.ms = timeGetTime();
lpTime->wType = TIME_MS;
return TIMERR_NOERROR;
}
/****************************************************************************
@doc EXTERNAL
@api UINT | timeSetEvent | This function sets up a timed callback event.
The event can be a one-time event or a periodic event. Once activated,
the event calls the specified callback function.
@parm UINT | wDelay | Specifies the event period in milliseconds.
If the delay is less than the minimum period supported by the timer,
or greater than the maximum period supported by the timer, the
function returns an error.
@parm UINT | wResolution | Specifies the accuracy of the delay in
milliseconds. The resolution of the timer event increases with
smaller <p wResolution> values. To reduce system overhead, use
the maximum <p wResolution> value appropriate for your application.
@parm LPTIMECALLBACK | lpFunction | Specifies the procedure address of
a callback function that is called once upon expiration of a one-shot
event or periodically upon expiration of periodic events.
@parm DWORD | dwUser | Contains user-supplied callback data.
@parm UINT | wFlags | Specifies the type of timer event, using one of
the following flags:
@flag TIME_ONESHOT | Event occurs once, after <p wPeriod> milliseconds.
@flag TIME_PERIODIC | Event occurs every <p wPeriod> milliseconds.
@rdesc Returns an ID code that identifies the timer event. Returns
NULL if the timer event was not created. The ID code is also passed to
the callback function.
@comm Using this function to generate a high-frequency periodic-delay
event (with a period less than 10 milliseconds) can consume a
significant portion of the system CPU bandwidth. Any call to
<f timeSetEvent> for a periodic-delay timer
must be paired with a call to <f timeKillEvent>.
The callback function must reside in a DLL. You don't have to use
<f MakeProcInstance> to get a procedure-instance address for the callback
function.
@cb void CALLBACK | TimeFunc | <f TimeFunc> is a placeholder for the
application-supplied function name. The actual name must be exported by
including it in the EXPORTS statement of the module-definition file for
the DLL.
@parm UINT | wID | The ID of the timer event. This is the ID returned
by <f timeSetEvent>.
@parm UINT | wMsg | Not used.
@parm DWORD | dwUser | User instance data supplied to the <p dwUser>
parameter of <f timeSetEvent>.
@parm DWORD | dw1 | Not used.
@parm DWORD | dw2 | Not used.
@comm Because the callback is accessed at interrupt time, it must
reside in a DLL, and its code segment must be specified as FIXED
in the module-definition file for the DLL. Any data that the
callback accesses must be in a FIXED data segment as well.
The callback may not make any system calls except for <f PostMessage>,
<f timeGetSystemTime>, <f timeGetTime>, <f timeSetEvent>,
<f timeKillEvent>, <f midiOutShortMsg>,
<f midiOutLongMsg>, and <f OutputDebugStr>.
@xref timeKillEvent timeBeginPeriod timeEndPeriod
****************************************************************************/
UINT WINAPI
timeSetEvent(
UINT wDelay,
UINT wResolution,
LPTIMECALLBACK lpFunction,
DWORD dwUser,
UINT wFlags
)
{
//
// !!!WARNING DS is not setup right!!! see above
//
TIMEREVENT timerEvent;
V_TCALLBACK(lpFunction, MMSYSERR_INVALPARAM);
//
// the first time this is called init the stacks
// !!!this assumes the first caller will not be at interupt time!!
//
// if (!(WinFlags & WF_ENHANCED))
// timeStackInit();
wDelay = max( MIN_DELAY, wDelay );
wResolution = max( MIN_RES, wResolution );
timerEvent.wDelay = wDelay;
timerEvent.wResolution = wResolution;
timerEvent.lpFunction = lpFunction;
timerEvent.dwUser = dwUser;
timerEvent.wFlags = wFlags;
return (UINT)timeMessage( TDD_SETTIMEREVENT, (LPARAM)(LPVOID)&timerEvent,
(LPARAM)GetCurrentTask() );
}
/****************************************************************************
@doc EXTERNAL
@api UINT | timeGetDevCaps | This function queries the timer device to
determine its capabilities.
@parm LPTIMECAPS | lpTimeCaps | Specifies a far pointer to a
<t TIMECAPS> structure. This structure is filled with information
about the capabilities of the timer device.
@parm UINT | wSize | Specifies the size of the <t TIMECAPS> structure.
@rdesc Returns zero if successful. Returns TIMERR_NOCANDO if it fails
to return the timer device capabilities.
****************************************************************************/
UINT WINAPI
timeGetDevCaps(
LPTIMECAPS lpTimeCaps,
UINT wSize
)
{
//
// !!!WARNING DS is not setup right!!! see above
//
return (UINT)timeMessage( TDD_GETDEVCAPS, (LPARAM)lpTimeCaps,
(LPARAM)(DWORD)wSize);
}
/******************************Public*Routine******************************\
* timeBeginPeriod
*
* @doc EXTERNAL
*
* @api WORD | timeBeginPeriod | This function sets the minimum (lowest
* number of milliseconds) timer resolution that an application or
* driver is going to use. Call this function immediately before starting
* to use timer-event services, and call <f timeEndPeriod> immediately
* after finishing with the timer-event services.
*
* @parm WORD | wPeriod | Specifies the minimum timer-event resolution
* that the application or driver will use.
*
* @rdesc Returns zero if successful. Returns TIMERR_NOCANDO if the specified
* <p wPeriod> resolution value is out of range.
*
* @xref timeEndPeriod timeSetEvent
*
* @comm For each call to <f timeBeginPeriod>, you must call
* <f timeEndPeriod> with a matching <p wPeriod> value.
* An application or driver can make multiple calls to <f timeBeginPeriod>,
* as long as each <f timeBeginPeriod> call is matched with a
* <f timeEndPeriod> call.
*
*
*
*
* History:
* dd-mm-93 - StephenE - Created
*
\**************************************************************************/
UINT WINAPI
timeBeginPeriod(
UINT uPeriod
)
{
uPeriod = max( MIN_RES, uPeriod );
return (UINT)timeMessage( TDD_BEGINMINPERIOD, (LPARAM)uPeriod, 0L );
}
/******************************Public*Routine******************************\
* timeEndPeriod
*
* @doc EXTERNAL
*
* @api WORD | timeEndPeriod | This function clears a previously set
* minimum (lowest number of milliseconds) timer resolution that an
* application or driver is going to use. Call this function
* immediately after using timer event services.
*
* @parm WORD | wPeriod | Specifies the minimum timer-event resolution
* value specified in the previous call to <f timeBeginPeriod>.
*
* @rdesc Returns zero if successful. Returns TIMERR_NOCANDO if the specified
* <p wPeriod> resolution value is out of range.
*
* @xref timeBeginPeriod timeSetEvent
*
* @comm For each call to <f timeBeginPeriod>, you must call
* <f timeEndPeriod> with a matching <p wPeriod> value.
* An application or driver can make multiple calls to <f timeBeginPeriod>,
* as long as each <f timeBeginPeriod> call is matched with a
* <f timeEndPeriod> call.
*
*
*
* History:
* dd-mm-93 - StephenE - Created
*
\**************************************************************************/
UINT WINAPI
timeEndPeriod(
UINT uPeriod
)
{
uPeriod = max( MIN_RES, uPeriod );
return (UINT)timeMessage( TDD_ENDMINPERIOD, (LPARAM)uPeriod, 0L );
}
/******************************Public*Routine******************************\
*
* timeKillEvent
*
* @doc EXTERNAL
*
* @api WORD | timeKillEvent | This functions destroys a specified timer
* callback event.
*
* @parm WORD | wID | Identifies the event to be destroyed.
*
* @rdesc Returns zero if successful. Returns TIMERR_NOCANDO if the
* specified timer event does not exist.
*
* @comm The timer event ID specified by <p wID> must be an ID
* returned by <f timeSetEvent>.
*
* @xref timeSetEvent
*
*
*
* History:
* dd-mm-93 - StephenE - Created
*
\**************************************************************************/
UINT WINAPI
timeKillEvent(
UINT wID
)
{
if ( 0 == wID ) {
return 0;
}
return (UINT)timeMessage( TDD_KILLTIMEREVENT, (LPARAM)wID, 0L );
}
/******************************Public*Routine******************************\
* timeGetTime
*
* @doc EXTERNAL
*
* @api DWORD | timeGetTime | This function retrieves the system time
* in milliseconds. The system time is the time elapsed since
* Windows was started.
*
* @rdesc The return value is the system time in milliseconds.
*
* @comm The only difference between this function and
* the <f timeGetSystemTime> function is <f timeGetSystemTime>
* uses the standard multimedia time structure <t MMTIME> to return
* the system time. The <f timeGetTime> function has less overhead than
* <f timeGetSystemTime>.
*
* @xref timeGetSystemTime
*
*
* @comment: on faster machines timeGetTime can return the same value
* and some apps will take diff (0) to divide and fault
* to prevent that call DelayTimeGetTime which will check if it is one
* of the known apps that do that and sleep if necessary
*
* History:
* dd-mm-93 - StephenE - Created
*
\**************************************************************************/
DWORD WINAPI
timeGetTime(
void
)
{
DWORD dwGetTime;
DWORD bDelay = 0;
if (pfnDelayTimeGetTime == 0) {
DWORD hmodWow32;
hmodWow32 = LoadLibraryEx32W("wow32.dll", 0, 0);
pfnDelayTimeGetTime = GetProcAddress32W(hmodWow32, "WOWDelayTimeGetTime");
}
RepeatTGT:
dwGetTime = timeMessage( TDD_GETSYSTEMTIME, 0L, 0L );
// check if it wrapped around
if (dwGetTime < dwRealLastGetTime) {
dwLastGetTime = dwRealLastGetTime = dwGetTime;
return dwGetTime;
}
dwRealLastGetTime = dwGetTime;
if (dwGetTime == dwLastGetTime) {
if (!bDelay) {
bDelay = (DWORD) CallProc32W((LPVOID)pfnDelayTimeGetTime,(DWORD)0,(DWORD)0);
if(bDelay) {
goto RepeatTGT;
}
}
else {
dwGetTime = ++dwLastGetTime;
}
}
dwLastGetTime = dwGetTime;
return dwGetTime;
}
/****************************************************************************
@doc INTERNAL
@api BOOL | TimeInit | This function initialises the timer services.
@rdesc The return value is TRUE if the services are initialised, FALSE
if an error occurs.
@comm it is not a FATAL error if a timer driver is not installed, this
routine will allways return TRUE
****************************************************************************/
BOOL NEAR PASCAL TimeInit(void)
{
OpenDriver(szTimerDrv, NULL, 0L) ;
return TRUE;
}