1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
4 
5   This software is provided 'as-is', without any express or implied
6   warranty.  In no event will the authors be held liable for any damages
7   arising from the use of this software.
8 
9   Permission is granted to anyone to use this software for any purpose,
10   including commercial applications, and to alter it and redistribute it
11   freely, subject to the following restrictions:
12 
13   1. The origin of this software must not be misrepresented; you must not
14      claim that you wrote the original software. If you use this software
15      in a product, an acknowledgment in the product documentation would be
16      appreciated but is not required.
17   2. Altered source versions must be plainly marked as such, and must not be
18      misrepresented as being the original software.
19   3. This notice may not be removed or altered from any source distribution.
20 */
21 #include "../../SDL_internal.h"
22 
23 #ifdef SDL_TIMER_WINDOWS
24 
25 #include "../../core/windows/SDL_windows.h"
26 #include <mmsystem.h>
27 
28 #include "SDL_timer.h"
29 #include "SDL_hints.h"
30 
31 
32 /* The first (low-resolution) ticks value of the application */
33 static DWORD start = 0;
34 static BOOL ticks_started = FALSE;
35 
36 /* Store if a high-resolution performance counter exists on the system */
37 static BOOL hires_timer_available;
38 /* The first high-resolution ticks value of the application */
39 static LARGE_INTEGER hires_start_ticks;
40 /* The number of ticks per second of the high-resolution performance counter */
41 static LARGE_INTEGER hires_ticks_per_second;
42 
43 static void
SDL_SetSystemTimerResolution(const UINT uPeriod)44 SDL_SetSystemTimerResolution(const UINT uPeriod)
45 {
46 #ifndef __WINRT__
47     static UINT timer_period = 0;
48 
49     if (uPeriod != timer_period) {
50         if (timer_period) {
51             timeEndPeriod(timer_period);
52         }
53 
54         timer_period = uPeriod;
55 
56         if (timer_period) {
57             timeBeginPeriod(timer_period);
58         }
59     }
60 #endif
61 }
62 
63 static void SDLCALL
SDL_TimerResolutionChanged(void * userdata,const char * name,const char * oldValue,const char * hint)64 SDL_TimerResolutionChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
65 {
66     UINT uPeriod;
67 
68     /* Unless the hint says otherwise, let's have good sleep precision */
69     if (hint && *hint) {
70         uPeriod = SDL_atoi(hint);
71     } else {
72         uPeriod = 1;
73     }
74     if (uPeriod || oldValue != hint) {
75         SDL_SetSystemTimerResolution(uPeriod);
76     }
77 }
78 
79 void
SDL_TicksInit(void)80 SDL_TicksInit(void)
81 {
82     if (ticks_started) {
83         return;
84     }
85     ticks_started = SDL_TRUE;
86 
87     /* if we didn't set a precision, set it high. This affects lots of things
88        on Windows besides the SDL timers, like audio callbacks, etc. */
89     SDL_AddHintCallback(SDL_HINT_TIMER_RESOLUTION,
90                         SDL_TimerResolutionChanged, NULL);
91 
92     /* Set first ticks value */
93     /* QueryPerformanceCounter has had problems in the past, but lots of games
94        use it, so we'll rely on it here.
95      */
96     if (QueryPerformanceFrequency(&hires_ticks_per_second) == TRUE) {
97         hires_timer_available = TRUE;
98         QueryPerformanceCounter(&hires_start_ticks);
99     } else {
100         hires_timer_available = FALSE;
101 #ifndef __WINRT__
102         start = timeGetTime();
103 #endif /* __WINRT__ */
104     }
105 }
106 
107 void
SDL_TicksQuit(void)108 SDL_TicksQuit(void)
109 {
110     SDL_DelHintCallback(SDL_HINT_TIMER_RESOLUTION,
111                         SDL_TimerResolutionChanged, NULL);
112 
113     SDL_SetSystemTimerResolution(0);  /* always release our timer resolution request. */
114 
115     start = 0;
116     ticks_started = SDL_FALSE;
117 }
118 
119 Uint32
SDL_GetTicks(void)120 SDL_GetTicks(void)
121 {
122     DWORD now = 0;
123     LARGE_INTEGER hires_now;
124 
125     if (!ticks_started) {
126         SDL_TicksInit();
127     }
128 
129     if (hires_timer_available) {
130         QueryPerformanceCounter(&hires_now);
131 
132         hires_now.QuadPart -= hires_start_ticks.QuadPart;
133         hires_now.QuadPart *= 1000;
134         hires_now.QuadPart /= hires_ticks_per_second.QuadPart;
135 
136         return (DWORD) hires_now.QuadPart;
137     } else {
138 #ifndef __WINRT__
139         now = timeGetTime();
140 #endif /* __WINRT__ */
141     }
142 
143     return (now - start);
144 }
145 
146 Uint64
SDL_GetPerformanceCounter(void)147 SDL_GetPerformanceCounter(void)
148 {
149     LARGE_INTEGER counter;
150 
151     if (!QueryPerformanceCounter(&counter)) {
152         return SDL_GetTicks();
153     }
154     return counter.QuadPart;
155 }
156 
157 Uint64
SDL_GetPerformanceFrequency(void)158 SDL_GetPerformanceFrequency(void)
159 {
160     LARGE_INTEGER frequency;
161 
162     if (!QueryPerformanceFrequency(&frequency)) {
163         return 1000;
164     }
165     return frequency.QuadPart;
166 }
167 
168 void
SDL_Delay(Uint32 ms)169 SDL_Delay(Uint32 ms)
170 {
171     /* Sleep() is not publicly available to apps in early versions of WinRT.
172      *
173      * Visual C++ 2013 Update 4 re-introduced Sleep() for Windows 8.1 and
174      * Windows Phone 8.1.
175      *
176      * Use the compiler version to determine availability.
177      *
178      * NOTE #1: _MSC_FULL_VER == 180030723 for Visual C++ 2013 Update 3.
179      * NOTE #2: Visual C++ 2013, when compiling for Windows 8.0 and
180      *    Windows Phone 8.0, uses the Visual C++ 2012 compiler to build
181      *    apps and libraries.
182      */
183 #if defined(__WINRT__) && defined(_MSC_FULL_VER) && (_MSC_FULL_VER <= 180030723)
184     static HANDLE mutex = 0;
185     if (!mutex) {
186         mutex = CreateEventEx(0, 0, 0, EVENT_ALL_ACCESS);
187     }
188     WaitForSingleObjectEx(mutex, ms, FALSE);
189 #else
190     if (!ticks_started) {
191         SDL_TicksInit();
192     }
193 
194     Sleep(ms);
195 #endif
196 }
197 
198 #endif /* SDL_TIMER_WINDOWS */
199 
200 /* vi: set ts=4 sw=4 expandtab: */
201