Added a hint to control the Windows timer resolution: SDL_HINT_TIMER_RESOLUTION
authorSam Lantinga <slouken@libsdl.org>
Sat, 13 Jul 2013 03:13:41 -0700
changeset 7432 58908b180ebd
parent 7431 0fc94f315dac
child 7433 8f118396264b
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
include/SDL_hints.h
src/SDL_hints.c
src/SDL_hints_c.h
src/joystick/SDL_joystick.c
src/timer/windows/SDL_systimer.c
src/video/uikit/SDL_uikitappdelegate.m
--- a/Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj	Sat Jul 13 11:06:34 2013 +0800
+++ b/Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj	Sat Jul 13 03:13:41 2013 -0700
@@ -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 = "<group>"; };
 		04FFAB8912E23B8D00BA343D /* SDL_atomic.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_atomic.c; sourceTree = "<group>"; };
 		04FFAB8A12E23B8D00BA343D /* SDL_spinlock.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_spinlock.c; sourceTree = "<group>"; };
-		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 = "<group>"; };
 		56EA86F913E9EC2B002E47EB /* SDL_coreaudio.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_coreaudio.c; path = coreaudio/SDL_coreaudio.c; sourceTree = "<group>"; };
 		56EA86FA13E9EC2B002E47EB /* SDL_coreaudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_coreaudio.h; path = coreaudio/SDL_coreaudio.h; sourceTree = "<group>"; };
 		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 */,
--- a/include/SDL_hints.h	Sat Jul 13 11:06:34 2013 +0800
+++ b/include/SDL_hints.h	Sat Jul 13 03:13:41 2013 -0700
@@ -218,13 +218,13 @@
 /**
  *  \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 @@
 #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_SetHint(const char *name,
                                              const char *value);
 
-
 /**
  *  \brief Get a hint
  *
@@ -282,6 +298,29 @@
 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
  *
  *  This function is called during SDL_Quit() to free stored hints.
--- a/src/SDL_hints.c	Sat Jul 13 11:06:34 2013 +0800
+++ b/src/SDL_hints.c	Sat Jul 13 03:13:41 2013 -0700
@@ -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 @@
             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;
                 }
-                SDL_free(hint->value);
-                hint->value = SDL_strdup(value);
+                if (hint->value) {
+                    SDL_free(hint->value);
+                }
+                if (value) {
+                    hint->value = SDL_strdup(value);
+                } else {
+                    hint->value = NULL;
+                }
             }
             hint->priority = priority;
             return SDL_TRUE;
@@ -91,9 +92,9 @@
         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 @@
     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);
     }
 }
--- a/src/SDL_hints_c.h	Sat Jul 13 11:06:34 2013 +0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-/*
- Simple DirectMedia Layer
- Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
-
- 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: */
--- a/src/joystick/SDL_joystick.c	Sat Jul 13 11:06:34 2013 +0800
+++ b/src/joystick/SDL_joystick.c	Sat Jul 13 03:13:41 2013 -0700
@@ -36,17 +36,24 @@
 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) {
--- a/src/timer/windows/SDL_systimer.c	Sat Jul 13 11:06:34 2013 +0800
+++ b/src/timer/windows/SDL_systimer.c	Sat Jul 13 03:13:41 2013 -0700
@@ -26,8 +26,8 @@
 #include <mmsystem.h>
 
 #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_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 @@
         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 @@
     }
 #endif
 
-    if (now < start) {
-        ticks = (TIME_WRAP_VALUE - start) + now;
-    } else {
-        ticks = (now - start);
-    }
-    return (ticks);
+    return (now - start);
 }
 
 Uint64
--- a/src/video/uikit/SDL_uikitappdelegate.m	Sat Jul 13 11:06:34 2013 +0800
+++ b/src/video/uikit/SDL_uikitappdelegate.m	Sat Jul 13 03:13:41 2013 -0700
@@ -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 @@
     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 @@
     [[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];