From 1f09208bc3e151a8fea7f720fb8cf3a33a017f39 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 13 Jul 2013 03:13:41 -0700 Subject: [PATCH] Added a hint to control the Windows timer resolution: SDL_HINT_TIMER_RESOLUTION Added an API to watch hint changes: SDL_AddHintCallback(), SDL_DelHintCallback() You can now dynamically set the joystick background event hint. --- Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj | 4 - include/SDL_hints.h | 45 ++++++- src/SDL_hints.c | 137 ++++++++++++++++---- src/SDL_hints_c.h | 34 ----- src/joystick/SDL_joystick.c | 19 ++- src/timer/windows/SDL_systimer.c | 50 +++++-- src/video/uikit/SDL_uikitappdelegate.m | 12 +- 7 files changed, 212 insertions(+), 89 deletions(-) delete mode 100644 src/SDL_hints_c.h diff --git a/Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj b/Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj index bdad57c6a..08109d084 100755 --- a/Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj +++ b/Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj @@ -67,7 +67,6 @@ 04F7808512FB753F00FC43C0 /* SDL_nullframebuffer.c in Sources */ = {isa = PBXBuildFile; fileRef = 04F7808312FB753F00FC43C0 /* SDL_nullframebuffer.c */; }; 04FFAB8B12E23B8D00BA343D /* SDL_atomic.c in Sources */ = {isa = PBXBuildFile; fileRef = 04FFAB8912E23B8D00BA343D /* SDL_atomic.c */; }; 04FFAB8C12E23B8D00BA343D /* SDL_spinlock.c in Sources */ = {isa = PBXBuildFile; fileRef = 04FFAB8A12E23B8D00BA343D /* SDL_spinlock.c */; }; - 22C905CD13A22646003FE4E4 /* SDL_hints_c.h in Headers */ = {isa = PBXBuildFile; fileRef = 22C905CC13A22646003FE4E4 /* SDL_hints_c.h */; }; 56EA86FB13E9EC2B002E47EB /* SDL_coreaudio.c in Sources */ = {isa = PBXBuildFile; fileRef = 56EA86F913E9EC2B002E47EB /* SDL_coreaudio.c */; }; 56EA86FC13E9EC2B002E47EB /* SDL_coreaudio.h in Headers */ = {isa = PBXBuildFile; fileRef = 56EA86FA13E9EC2B002E47EB /* SDL_coreaudio.h */; }; 56ED04E1118A8EE200A56AA6 /* SDL_power.c in Sources */ = {isa = PBXBuildFile; fileRef = 56ED04E0118A8EE200A56AA6 /* SDL_power.c */; }; @@ -260,7 +259,6 @@ 04F7808312FB753F00FC43C0 /* SDL_nullframebuffer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_nullframebuffer.c; sourceTree = ""; }; 04FFAB8912E23B8D00BA343D /* SDL_atomic.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_atomic.c; sourceTree = ""; }; 04FFAB8A12E23B8D00BA343D /* SDL_spinlock.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_spinlock.c; sourceTree = ""; }; - 22C905CC13A22646003FE4E4 /* SDL_hints_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_hints_c.h; path = ../../src/SDL_hints_c.h; sourceTree = ""; }; 56EA86F913E9EC2B002E47EB /* SDL_coreaudio.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_coreaudio.c; path = coreaudio/SDL_coreaudio.c; sourceTree = ""; }; 56EA86FA13E9EC2B002E47EB /* SDL_coreaudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_coreaudio.h; path = coreaudio/SDL_coreaudio.h; sourceTree = ""; }; 56ED04E0118A8EE200A56AA6 /* SDL_power.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_power.c; path = ../../src/power/SDL_power.c; sourceTree = SOURCE_ROOT; }; @@ -747,7 +745,6 @@ FD99B9D60DD52EDC00FB1D6B /* SDL_fatal.c */, FD99B9D70DD52EDC00FB1D6B /* SDL_fatal.h */, 0442EC5412FE1C3F004C9285 /* SDL_hints.c */, - 22C905CC13A22646003FE4E4 /* SDL_hints_c.h */, 04BAC09B1300C1290055DE28 /* SDL_log.c */, FD99B9D80DD52EDC00FB1D6B /* SDL.c */, ); @@ -967,7 +964,6 @@ 0442EC5012FE1C1E004C9285 /* SDL_render_sw_c.h in Headers */, 0402A85A12FE70C600CECEE3 /* SDL_shaders_gles2.h in Headers */, 04BAC09C1300C1290055DE28 /* SDL_assert_c.h in Headers */, - 22C905CD13A22646003FE4E4 /* SDL_hints_c.h in Headers */, 56EA86FC13E9EC2B002E47EB /* SDL_coreaudio.h in Headers */, 93CB792313FC5E5200BD3E05 /* SDL_uikitviewcontroller.h in Headers */, AA628ADC159369E3005138DD /* SDL_rotate.h in Headers */, diff --git a/include/SDL_hints.h b/include/SDL_hints.h index ac6bf4cff..f27769953 100644 --- a/include/SDL_hints.h +++ b/include/SDL_hints.h @@ -218,13 +218,13 @@ extern "C" { /** * \brief A variable that lets you enable joystick (and gamecontroller) events even when your app is in the background. * - * The default value is "0". - * * The variable can be set to the following values: * "0" - Disable joystick & gamecontroller input events when the * application is in the background. * "1" - Enable joystick & gamecontroller input events when the * application is in the backgroumd. + * + * The default value is "0". This hint may be set at any time. */ #define SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS "SDL_JOYSTICK_ALLOW_BACKGROUND_EVENTS" @@ -240,6 +240,23 @@ extern "C" { #define SDL_HINT_ALLOW_TOPMOST "SDL_ALLOW_TOPMOST" +/** + * \brief A variable that controls the timer resolution, in milliseconds. + * + * The higher resolution the timer, the more frequently the CPU services + * timer interrupts, and the more precise delays are, but this takes up + * power and CPU time. This hint is only used on Windows 7 and earlier. + * + * See this blog post for more information: + * http://randomascii.wordpress.com/2013/07/08/windows-timer-resolution-megawatts-wasted/ + * + * If this variable is set to "0", the system timer resolution is not set. + * + * The default value is "1". This hint may be set at any time. + */ +#define SDL_HINT_TIMER_RESOLUTION "SDL_TIMER_RESOLUTION" + + /** * \brief An enumeration of hint priorities @@ -273,7 +290,6 @@ extern DECLSPEC SDL_bool SDLCALL SDL_SetHintWithPriority(const char *name, extern DECLSPEC SDL_bool SDLCALL SDL_SetHint(const char *name, const char *value); - /** * \brief Get a hint * @@ -281,6 +297,29 @@ extern DECLSPEC SDL_bool SDLCALL SDL_SetHint(const char *name, */ extern DECLSPEC const char * SDLCALL SDL_GetHint(const char *name); +/** + * \brief Add a function to watch a particular hint + * + * \param name The hint to watch + * \param callback The function to call when the hint value changes + * \param userdata A pointer to pass to the callback function + */ +typedef void (*SDL_HintCallback)(void *userdata, const char *name, const char *oldValue, const char *newValue); +extern DECLSPEC void SDLCALL SDL_AddHintCallback(const char *name, + SDL_HintCallback callback, + void *userdata); + +/** + * \brief Remove a function watching a particular hint + * + * \param name The hint being watched + * \param callback The function being called when the hint value changes + * \param userdata A pointer being passed to the callback function + */ +extern DECLSPEC void SDLCALL SDL_DelHintCallback(const char *name, + SDL_HintCallback callback, + void *userdata); + /** * \brief Clear all hints * diff --git a/src/SDL_hints.c b/src/SDL_hints.c index bd9404d46..b00e961cc 100644 --- a/src/SDL_hints.c +++ b/src/SDL_hints.c @@ -21,43 +21,35 @@ #include "SDL_config.h" #include "SDL_hints.h" -#include "SDL_hints_c.h" +#include "SDL_error.h" /* Assuming there aren't many hints set and they aren't being queried in - critical performance paths, we'll just use a linked list here. + critical performance paths, we'll just use linked lists here. */ +typedef struct SDL_HintWatch { + SDL_HintCallback callback; + void *userdata; + struct SDL_HintWatch *next; +} SDL_HintWatch; + typedef struct SDL_Hint { char *name; char *value; SDL_HintPriority priority; - SDL_HintChangedCb callback; + SDL_HintWatch *callbacks; struct SDL_Hint *next; } SDL_Hint; static SDL_Hint *SDL_hints; -SDL_bool -SDL_RegisterHintChangedCb(const char *name, SDL_HintChangedCb hintCb) -{ - SDL_Hint *hint; - - for (hint = SDL_hints; hint; hint = hint->next) { - if (SDL_strcmp(name, hint->name) == 0) { - hint->callback = hintCb; - return SDL_TRUE; - } - } - - return SDL_FALSE; -} - SDL_bool SDL_SetHintWithPriority(const char *name, const char *value, SDL_HintPriority priority) { const char *env; SDL_Hint *hint; + SDL_HintWatch *entry; if (!name || !value) { return SDL_FALSE; @@ -73,12 +65,21 @@ SDL_SetHintWithPriority(const char *name, const char *value, if (priority < hint->priority) { return SDL_FALSE; } - if (SDL_strcmp(hint->value, value) != 0) { - if (hint->callback != NULL) { - (*hint->callback)(name, hint->value, value); + if (!hint->value || !value || SDL_strcmp(hint->value, value) != 0) { + for (entry = hint->callbacks; entry; ) { + /* Save the next entry in case this one is deleted */ + SDL_HintWatch *next = entry->next; + entry->callback(entry->userdata, name, hint->value, value); + entry = next; + } + if (hint->value) { + SDL_free(hint->value); + } + if (value) { + hint->value = SDL_strdup(value); + } else { + hint->value = NULL; } - SDL_free(hint->value); - hint->value = SDL_strdup(value); } hint->priority = priority; return SDL_TRUE; @@ -91,9 +92,9 @@ SDL_SetHintWithPriority(const char *name, const char *value, return SDL_FALSE; } hint->name = SDL_strdup(name); - hint->value = SDL_strdup(value); + hint->value = value ? SDL_strdup(value) : NULL; hint->priority = priority; - hint->callback = NULL; + hint->callbacks = NULL; hint->next = SDL_hints; SDL_hints = hint; return SDL_TRUE; @@ -123,16 +124,100 @@ SDL_GetHint(const char *name) return env; } +void +SDL_AddHintCallback(const char *name, SDL_HintCallback callback, void *userdata) +{ + SDL_Hint *hint; + SDL_HintWatch *entry; + const char *value; + + if (!name || !*name) { + SDL_InvalidParamError("name"); + return; + } + if (!callback) { + SDL_InvalidParamError("callback"); + return; + } + + SDL_DelHintCallback(name, callback, userdata); + + entry = (SDL_HintWatch *)SDL_malloc(sizeof(*entry)); + entry->callback = callback; + entry->userdata = userdata; + + for (hint = SDL_hints; hint; hint = hint->next) { + if (SDL_strcmp(name, hint->name) == 0) { + break; + } + } + if (!hint) { + /* Need to add a hint entry for this watcher */ + hint = (SDL_Hint *)SDL_malloc(sizeof(*hint)); + if (!hint) { + return; + } + hint->name = SDL_strdup(name); + hint->value = NULL; + hint->priority = SDL_HINT_DEFAULT; + hint->callbacks = NULL; + hint->next = SDL_hints; + SDL_hints = hint; + } + + /* Add it to the callbacks for this hint */ + entry->next = hint->callbacks; + hint->callbacks = entry; + + /* Now call it with the current value */ + value = SDL_GetHint(name); + callback(userdata, name, value, value); +} + +void +SDL_DelHintCallback(const char *name, SDL_HintCallback callback, void *userdata) +{ + SDL_Hint *hint; + SDL_HintWatch *entry, *prev; + + for (hint = SDL_hints; hint; hint = hint->next) { + if (SDL_strcmp(name, hint->name) == 0) { + prev = NULL; + for (entry = hint->callbacks; entry; entry = entry->next) { + if (callback == entry->callback && userdata == entry->userdata) { + if (prev) { + prev->next = entry->next; + } else { + hint->callbacks = entry->next; + } + SDL_free(entry); + break; + } + prev = entry; + } + return; + } + } +} + void SDL_ClearHints(void) { SDL_Hint *hint; + SDL_HintWatch *entry; while (SDL_hints) { hint = SDL_hints; SDL_hints = hint->next; SDL_free(hint->name); - SDL_free(hint->value); + if (hint->value) { + SDL_free(hint->value); + } + for (entry = hint->callbacks; entry; ) { + SDL_HintWatch *freeable = entry; + entry = entry->next; + SDL_free(freeable); + } SDL_free(hint); } } diff --git a/src/SDL_hints_c.h b/src/SDL_hints_c.h deleted file mode 100644 index 232e26384..000000000 --- a/src/SDL_hints_c.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2013 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - */ - -#ifndef _SDL_hints_c_h -#define _SDL_hints_c_h - -/** - * \brief A callback function that is optionally called when a hint changes - */ -typedef void (*SDL_HintChangedCb)(const char *name, const char *oldValue, const char *newValue); - -extern SDL_bool SDL_RegisterHintChangedCb(const char *name, SDL_HintChangedCb hintCb); - -#endif /* _SDL_hints_c_h */ - -/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c index 5380acf80..d2eeb6ab6 100644 --- a/src/joystick/SDL_joystick.c +++ b/src/joystick/SDL_joystick.c @@ -36,17 +36,24 @@ static SDL_bool SDL_joystick_allows_background_events = SDL_FALSE; static SDL_Joystick *SDL_joysticks = NULL; static SDL_Joystick *SDL_updating_joystick = NULL; +static void +SDL_JoystickAllowBackgroundEventsChanged(void *userdata, const char *name, const char *oldValue, const char *hint) +{ + if (hint && *hint == '1') { + SDL_joystick_allows_background_events = SDL_TRUE; + } else { + SDL_joystick_allows_background_events = SDL_FALSE; + } +} + int SDL_JoystickInit(void) { - const char *hint; int status; - /* Check to see if we should allow joystick events while in the background */ - hint = SDL_GetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS); - if (hint && *hint == '1') { - SDL_joystick_allows_background_events = SDL_TRUE; - } + /* See if we should allow joystick events while in the background */ + SDL_AddHintCallback(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, + SDL_JoystickAllowBackgroundEventsChanged, NULL); #if !SDL_EVENTS_DISABLED if (SDL_InitSubSystem(SDL_INIT_EVENTS) < 0) { diff --git a/src/timer/windows/SDL_systimer.c b/src/timer/windows/SDL_systimer.c index 33097634e..ce6bd7a70 100644 --- a/src/timer/windows/SDL_systimer.c +++ b/src/timer/windows/SDL_systimer.c @@ -26,8 +26,8 @@ #include #include "SDL_timer.h" +#include "SDL_hints.h" -#define TIME_WRAP_VALUE (~(DWORD)0) /* The first (low-resolution) ticks value of the application */ static DWORD start; @@ -41,6 +41,40 @@ static LARGE_INTEGER hires_start_ticks; static LARGE_INTEGER hires_ticks_per_second; #endif +static void +timeSetPeriod(UINT uPeriod) +{ + static UINT timer_period = 0; + + if (uPeriod != timer_period) { + if (timer_period) { + timeEndPeriod(timer_period); + } + + timer_period = uPeriod; + + if (timer_period) { + timeBeginPeriod(timer_period); + } + } +} + +static void +SDL_TimerResolutionChanged(void *userdata, const char *name, const char *oldValue, const char *hint) +{ + UINT uPeriod; + + /* Unless the hint says otherwise, let's have good sleep precision */ + if (hint && *hint) { + uPeriod = SDL_atoi(hint); + } else { + uPeriod = 1; + } + if (uPeriod || oldValue != hint) { + timeSetPeriod(uPeriod); + } +} + void SDL_StartTicks(void) { @@ -56,16 +90,19 @@ SDL_StartTicks(void) QueryPerformanceCounter(&hires_start_ticks); } else { hires_timer_available = FALSE; - timeBeginPeriod(1); /* use 1 ms timer precision */ + timeSetPeriod(1); /* use 1 ms timer precision */ start = timeGetTime(); } #endif + + SDL_AddHintCallback(SDL_HINT_TIMER_RESOLUTION, + SDL_TimerResolutionChanged, NULL); } Uint32 SDL_GetTicks(void) { - DWORD now, ticks; + DWORD now; #ifndef USE_GETTICKCOUNT LARGE_INTEGER hires_now; #endif @@ -86,12 +123,7 @@ SDL_GetTicks(void) } #endif - if (now < start) { - ticks = (TIME_WRAP_VALUE - start) + now; - } else { - ticks = (now - start); - } - return (ticks); + return (now - start); } Uint64 diff --git a/src/video/uikit/SDL_uikitappdelegate.m b/src/video/uikit/SDL_uikitappdelegate.m index d37a09124..4a3232005 100644 --- a/src/video/uikit/SDL_uikitappdelegate.m +++ b/src/video/uikit/SDL_uikitappdelegate.m @@ -25,7 +25,6 @@ #include "../SDL_sysvideo.h" #include "SDL_assert.h" #include "SDL_hints.h" -#include "../../SDL_hints_c.h" #include "SDL_system.h" #include "SDL_main.h" @@ -69,11 +68,10 @@ int main(int argc, char **argv) return exit_status; } -static void SDL_IdleTimerDisabledChanged(const char *name, const char *oldValue, const char *newValue) +static void +SDL_IdleTimerDisabledChanged(void *userdata, const char *name, const char *oldValue, const char *hint) { - SDL_assert(SDL_strcmp(name, SDL_HINT_IDLE_TIMER_DISABLED) == 0); - - BOOL disable = (*newValue != '0'); + BOOL disable = (hint && *hint != '0'); [UIApplication sharedApplication].idleTimerDisabled = disable; } @@ -218,8 +216,8 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( [[NSFileManager defaultManager] changeCurrentDirectoryPath: [[NSBundle mainBundle] resourcePath]]; /* register a callback for the idletimer hint */ - SDL_SetHint(SDL_HINT_IDLE_TIMER_DISABLED, "0"); - SDL_RegisterHintChangedCb(SDL_HINT_IDLE_TIMER_DISABLED, &SDL_IdleTimerDisabledChanged); + SDL_AddHintCallback(SDL_HINT_IDLE_TIMER_DISABLED, + SDL_IdleTimerDisabledChanged, NULL); SDL_SetMainReady(); [self performSelector:@selector(postFinishLaunch) withObject:nil afterDelay:0.0];