Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

443 lines
13 KiB

  1. /******************************************************************************
  2. Copyright (C) Microsoft Corporation 1985-1991. All rights reserved.
  3. Title: TIME.C : MMSYSTEM TIMER API
  4. Version: 1.00
  5. *****************************************************************************/
  6. //
  7. // ***DANGER WARNING****
  8. //
  9. // none of these functions in this file need the default data segment
  10. // so we undefine BUILDDLL, if you write code in this file that needs
  11. // DS == DGROUP be warned!
  12. //
  13. // NOTE: most of this code is interupt time enterable, so we don't want
  14. // it touching DGROUP anyway!
  15. //
  16. #undef BUILDDLL
  17. #include <windows.h>
  18. #include "mmsystem.h"
  19. #include "mmddk.h"
  20. #include "mmsysi.h"
  21. #include "drvr.h"
  22. #include "thunks.h"
  23. #define MIN_RES 1
  24. #define MIN_DELAY 6
  25. //
  26. // Define moveable code for timer interface.
  27. //
  28. #pragma alloc_text( RARE, timeGetDevCaps )
  29. extern SZCODE szTimerDrv[]; // see init.c
  30. DWORD dwLastGetTime = 0; // last TimeGetTime return value can be bigger than system TimeGetTime
  31. DWORD dwRealLastGetTime = 0; // last system TimeGetTime return value
  32. DWORD pfnDelayTimeGetTime = 0; // 32-bit function that sleeps for 1ms and returns if TimeGetTime flag applied
  33. // look in timeTimeGetTime and WOWDelayTimeGetTime in wow32
  34. //
  35. // Define the init code for this file.
  36. //
  37. #pragma alloc_text( INIT, TimeInit )
  38. /****************************************************************************
  39. @doc EXTERNAL
  40. @api UINT | timeGetSystemTime | This function retrieves the system time
  41. in milliseconds. The system time is the time elapsed since
  42. Windows was started.
  43. @parm LPMMTIME | lpTime | Specifies a far pointer to an <t MMTIME> data
  44. structure.
  45. @parm UINT | wSize | Specifies the size of the <t MMTIME> structure.
  46. @rdesc Returns zero.
  47. The system time is returned in the <e MMTIME.ms> field of the <t MMTIME>
  48. structure.
  49. @comm The time is always returned in milliseconds.
  50. @xref timeGetTime
  51. ****************************************************************************/
  52. UINT WINAPI
  53. timeGetSystemTime(
  54. LPMMTIME lpTime,
  55. UINT wSize
  56. )
  57. {
  58. //
  59. // !!!WARNING DS is not setup right!!! see above
  60. //
  61. if (wSize < sizeof(MMTIME))
  62. return TIMERR_STRUCT;
  63. lpTime->u.ms = timeGetTime();
  64. lpTime->wType = TIME_MS;
  65. return TIMERR_NOERROR;
  66. }
  67. /****************************************************************************
  68. @doc EXTERNAL
  69. @api UINT | timeSetEvent | This function sets up a timed callback event.
  70. The event can be a one-time event or a periodic event. Once activated,
  71. the event calls the specified callback function.
  72. @parm UINT | wDelay | Specifies the event period in milliseconds.
  73. If the delay is less than the minimum period supported by the timer,
  74. or greater than the maximum period supported by the timer, the
  75. function returns an error.
  76. @parm UINT | wResolution | Specifies the accuracy of the delay in
  77. milliseconds. The resolution of the timer event increases with
  78. smaller <p wResolution> values. To reduce system overhead, use
  79. the maximum <p wResolution> value appropriate for your application.
  80. @parm LPTIMECALLBACK | lpFunction | Specifies the procedure address of
  81. a callback function that is called once upon expiration of a one-shot
  82. event or periodically upon expiration of periodic events.
  83. @parm DWORD | dwUser | Contains user-supplied callback data.
  84. @parm UINT | wFlags | Specifies the type of timer event, using one of
  85. the following flags:
  86. @flag TIME_ONESHOT | Event occurs once, after <p wPeriod> milliseconds.
  87. @flag TIME_PERIODIC | Event occurs every <p wPeriod> milliseconds.
  88. @rdesc Returns an ID code that identifies the timer event. Returns
  89. NULL if the timer event was not created. The ID code is also passed to
  90. the callback function.
  91. @comm Using this function to generate a high-frequency periodic-delay
  92. event (with a period less than 10 milliseconds) can consume a
  93. significant portion of the system CPU bandwidth. Any call to
  94. <f timeSetEvent> for a periodic-delay timer
  95. must be paired with a call to <f timeKillEvent>.
  96. The callback function must reside in a DLL. You don't have to use
  97. <f MakeProcInstance> to get a procedure-instance address for the callback
  98. function.
  99. @cb void CALLBACK | TimeFunc | <f TimeFunc> is a placeholder for the
  100. application-supplied function name. The actual name must be exported by
  101. including it in the EXPORTS statement of the module-definition file for
  102. the DLL.
  103. @parm UINT | wID | The ID of the timer event. This is the ID returned
  104. by <f timeSetEvent>.
  105. @parm UINT | wMsg | Not used.
  106. @parm DWORD | dwUser | User instance data supplied to the <p dwUser>
  107. parameter of <f timeSetEvent>.
  108. @parm DWORD | dw1 | Not used.
  109. @parm DWORD | dw2 | Not used.
  110. @comm Because the callback is accessed at interrupt time, it must
  111. reside in a DLL, and its code segment must be specified as FIXED
  112. in the module-definition file for the DLL. Any data that the
  113. callback accesses must be in a FIXED data segment as well.
  114. The callback may not make any system calls except for <f PostMessage>,
  115. <f timeGetSystemTime>, <f timeGetTime>, <f timeSetEvent>,
  116. <f timeKillEvent>, <f midiOutShortMsg>,
  117. <f midiOutLongMsg>, and <f OutputDebugStr>.
  118. @xref timeKillEvent timeBeginPeriod timeEndPeriod
  119. ****************************************************************************/
  120. UINT WINAPI
  121. timeSetEvent(
  122. UINT wDelay,
  123. UINT wResolution,
  124. LPTIMECALLBACK lpFunction,
  125. DWORD dwUser,
  126. UINT wFlags
  127. )
  128. {
  129. //
  130. // !!!WARNING DS is not setup right!!! see above
  131. //
  132. TIMEREVENT timerEvent;
  133. V_TCALLBACK(lpFunction, MMSYSERR_INVALPARAM);
  134. //
  135. // the first time this is called init the stacks
  136. // !!!this assumes the first caller will not be at interupt time!!
  137. //
  138. // if (!(WinFlags & WF_ENHANCED))
  139. // timeStackInit();
  140. wDelay = max( MIN_DELAY, wDelay );
  141. wResolution = max( MIN_RES, wResolution );
  142. timerEvent.wDelay = wDelay;
  143. timerEvent.wResolution = wResolution;
  144. timerEvent.lpFunction = lpFunction;
  145. timerEvent.dwUser = dwUser;
  146. timerEvent.wFlags = wFlags;
  147. return (UINT)timeMessage( TDD_SETTIMEREVENT, (LPARAM)(LPVOID)&timerEvent,
  148. (LPARAM)GetCurrentTask() );
  149. }
  150. /****************************************************************************
  151. @doc EXTERNAL
  152. @api UINT | timeGetDevCaps | This function queries the timer device to
  153. determine its capabilities.
  154. @parm LPTIMECAPS | lpTimeCaps | Specifies a far pointer to a
  155. <t TIMECAPS> structure. This structure is filled with information
  156. about the capabilities of the timer device.
  157. @parm UINT | wSize | Specifies the size of the <t TIMECAPS> structure.
  158. @rdesc Returns zero if successful. Returns TIMERR_NOCANDO if it fails
  159. to return the timer device capabilities.
  160. ****************************************************************************/
  161. UINT WINAPI
  162. timeGetDevCaps(
  163. LPTIMECAPS lpTimeCaps,
  164. UINT wSize
  165. )
  166. {
  167. //
  168. // !!!WARNING DS is not setup right!!! see above
  169. //
  170. return (UINT)timeMessage( TDD_GETDEVCAPS, (LPARAM)lpTimeCaps,
  171. (LPARAM)(DWORD)wSize);
  172. }
  173. /******************************Public*Routine******************************\
  174. * timeBeginPeriod
  175. *
  176. * @doc EXTERNAL
  177. *
  178. * @api WORD | timeBeginPeriod | This function sets the minimum (lowest
  179. * number of milliseconds) timer resolution that an application or
  180. * driver is going to use. Call this function immediately before starting
  181. * to use timer-event services, and call <f timeEndPeriod> immediately
  182. * after finishing with the timer-event services.
  183. *
  184. * @parm WORD | wPeriod | Specifies the minimum timer-event resolution
  185. * that the application or driver will use.
  186. *
  187. * @rdesc Returns zero if successful. Returns TIMERR_NOCANDO if the specified
  188. * <p wPeriod> resolution value is out of range.
  189. *
  190. * @xref timeEndPeriod timeSetEvent
  191. *
  192. * @comm For each call to <f timeBeginPeriod>, you must call
  193. * <f timeEndPeriod> with a matching <p wPeriod> value.
  194. * An application or driver can make multiple calls to <f timeBeginPeriod>,
  195. * as long as each <f timeBeginPeriod> call is matched with a
  196. * <f timeEndPeriod> call.
  197. *
  198. *
  199. *
  200. *
  201. * History:
  202. * dd-mm-93 - StephenE - Created
  203. *
  204. \**************************************************************************/
  205. UINT WINAPI
  206. timeBeginPeriod(
  207. UINT uPeriod
  208. )
  209. {
  210. uPeriod = max( MIN_RES, uPeriod );
  211. return (UINT)timeMessage( TDD_BEGINMINPERIOD, (LPARAM)uPeriod, 0L );
  212. }
  213. /******************************Public*Routine******************************\
  214. * timeEndPeriod
  215. *
  216. * @doc EXTERNAL
  217. *
  218. * @api WORD | timeEndPeriod | This function clears a previously set
  219. * minimum (lowest number of milliseconds) timer resolution that an
  220. * application or driver is going to use. Call this function
  221. * immediately after using timer event services.
  222. *
  223. * @parm WORD | wPeriod | Specifies the minimum timer-event resolution
  224. * value specified in the previous call to <f timeBeginPeriod>.
  225. *
  226. * @rdesc Returns zero if successful. Returns TIMERR_NOCANDO if the specified
  227. * <p wPeriod> resolution value is out of range.
  228. *
  229. * @xref timeBeginPeriod timeSetEvent
  230. *
  231. * @comm For each call to <f timeBeginPeriod>, you must call
  232. * <f timeEndPeriod> with a matching <p wPeriod> value.
  233. * An application or driver can make multiple calls to <f timeBeginPeriod>,
  234. * as long as each <f timeBeginPeriod> call is matched with a
  235. * <f timeEndPeriod> call.
  236. *
  237. *
  238. *
  239. * History:
  240. * dd-mm-93 - StephenE - Created
  241. *
  242. \**************************************************************************/
  243. UINT WINAPI
  244. timeEndPeriod(
  245. UINT uPeriod
  246. )
  247. {
  248. uPeriod = max( MIN_RES, uPeriod );
  249. return (UINT)timeMessage( TDD_ENDMINPERIOD, (LPARAM)uPeriod, 0L );
  250. }
  251. /******************************Public*Routine******************************\
  252. *
  253. * timeKillEvent
  254. *
  255. * @doc EXTERNAL
  256. *
  257. * @api WORD | timeKillEvent | This functions destroys a specified timer
  258. * callback event.
  259. *
  260. * @parm WORD | wID | Identifies the event to be destroyed.
  261. *
  262. * @rdesc Returns zero if successful. Returns TIMERR_NOCANDO if the
  263. * specified timer event does not exist.
  264. *
  265. * @comm The timer event ID specified by <p wID> must be an ID
  266. * returned by <f timeSetEvent>.
  267. *
  268. * @xref timeSetEvent
  269. *
  270. *
  271. *
  272. * History:
  273. * dd-mm-93 - StephenE - Created
  274. *
  275. \**************************************************************************/
  276. UINT WINAPI
  277. timeKillEvent(
  278. UINT wID
  279. )
  280. {
  281. if ( 0 == wID ) {
  282. return 0;
  283. }
  284. return (UINT)timeMessage( TDD_KILLTIMEREVENT, (LPARAM)wID, 0L );
  285. }
  286. /******************************Public*Routine******************************\
  287. * timeGetTime
  288. *
  289. * @doc EXTERNAL
  290. *
  291. * @api DWORD | timeGetTime | This function retrieves the system time
  292. * in milliseconds. The system time is the time elapsed since
  293. * Windows was started.
  294. *
  295. * @rdesc The return value is the system time in milliseconds.
  296. *
  297. * @comm The only difference between this function and
  298. * the <f timeGetSystemTime> function is <f timeGetSystemTime>
  299. * uses the standard multimedia time structure <t MMTIME> to return
  300. * the system time. The <f timeGetTime> function has less overhead than
  301. * <f timeGetSystemTime>.
  302. *
  303. * @xref timeGetSystemTime
  304. *
  305. *
  306. * @comment: on faster machines timeGetTime can return the same value
  307. * and some apps will take diff (0) to divide and fault
  308. * to prevent that call DelayTimeGetTime which will check if it is one
  309. * of the known apps that do that and sleep if necessary
  310. *
  311. * History:
  312. * dd-mm-93 - StephenE - Created
  313. *
  314. \**************************************************************************/
  315. DWORD WINAPI
  316. timeGetTime(
  317. void
  318. )
  319. {
  320. DWORD dwGetTime;
  321. DWORD bDelay = 0;
  322. if (pfnDelayTimeGetTime == 0) {
  323. DWORD hmodWow32;
  324. hmodWow32 = LoadLibraryEx32W("wow32.dll", 0, 0);
  325. pfnDelayTimeGetTime = GetProcAddress32W(hmodWow32, "WOWDelayTimeGetTime");
  326. }
  327. RepeatTGT:
  328. dwGetTime = timeMessage( TDD_GETSYSTEMTIME, 0L, 0L );
  329. // check if it wrapped around
  330. if (dwGetTime < dwRealLastGetTime) {
  331. dwLastGetTime = dwRealLastGetTime = dwGetTime;
  332. return dwGetTime;
  333. }
  334. dwRealLastGetTime = dwGetTime;
  335. if (dwGetTime == dwLastGetTime) {
  336. if (!bDelay) {
  337. bDelay = (DWORD) CallProc32W((LPVOID)pfnDelayTimeGetTime,(DWORD)0,(DWORD)0);
  338. if(bDelay) {
  339. goto RepeatTGT;
  340. }
  341. }
  342. else {
  343. dwGetTime = ++dwLastGetTime;
  344. }
  345. }
  346. dwLastGetTime = dwGetTime;
  347. return dwGetTime;
  348. }
  349. /****************************************************************************
  350. @doc INTERNAL
  351. @api BOOL | TimeInit | This function initialises the timer services.
  352. @rdesc The return value is TRUE if the services are initialised, FALSE
  353. if an error occurs.
  354. @comm it is not a FATAL error if a timer driver is not installed, this
  355. routine will allways return TRUE
  356. ****************************************************************************/
  357. BOOL NEAR PASCAL TimeInit(void)
  358. {
  359. OpenDriver(szTimerDrv, NULL, 0L) ;
  360. return TRUE;
  361. }