Don't clobber refcounting in SDL_Init.
authorJørgen P. Tjernø <jorgen@valvesoftware.com>
Tue, 12 Feb 2013 11:47:31 -0800
changeset 6866 e74a4b282450
parent 6865 670ebd20759d
child 6867 4c899e841091
Don't clobber refcounting in SDL_Init. - Fixes bug 1712 by not overwriting SDL_SubsystemRefCount in SDL_Init. - Removes the SDL_initialized variable, and makes SDL_SubsystemRefCount the canonical source of truth for whether or not a subsystem has been initialized. - Refactors SDL_InitSubSystem and SDL_QuitSubSystem to use helper functions to manage refcount. - Adds automated tests for SDL_Init/Quit*. - Adds SDL_bits.h which contains SDL_MostSignificantBitIndex.
Makefile.in
VisualC/SDL/SDL_VS2008.vcproj
VisualC/SDL/SDL_VS2010.vcxproj
VisualC/SDL/SDL_VS2012.vcxproj
include/SDL_bits.h
src/SDL.c
test/Makefile.in
test/testautomation_main.c
test/testautomation_suites.h
--- a/Makefile.in	Tue Feb 12 11:47:29 2013 -0800
+++ b/Makefile.in	Tue Feb 12 11:47:31 2013 -0800
@@ -47,6 +47,7 @@
 	SDL_assert.h \
 	SDL_atomic.h \
 	SDL_audio.h \
+        SDL_bits.h \
 	SDL_blendmode.h \
 	SDL_clipboard.h \
 	SDL_cpuinfo.h \
--- a/VisualC/SDL/SDL_VS2008.vcproj	Tue Feb 12 11:47:29 2013 -0800
+++ b/VisualC/SDL/SDL_VS2008.vcproj	Tue Feb 12 11:47:31 2013 -0800
@@ -376,6 +376,10 @@
 				>
 			</File>
 			<File
+				RelativePath="..\..\include\SDL_bits.h"
+				>
+			</File>
+			<File
 				RelativePath="..\..\include\SDL_blendmode.h"
 				>
 			</File>
--- a/VisualC/SDL/SDL_VS2010.vcxproj	Tue Feb 12 11:47:29 2013 -0800
+++ b/VisualC/SDL/SDL_VS2010.vcxproj	Tue Feb 12 11:47:31 2013 -0800
@@ -207,6 +207,7 @@
     <ClInclude Include="..\..\include\SDL_assert.h" />
     <ClInclude Include="..\..\include\SDL_atomic.h" />
     <ClInclude Include="..\..\include\SDL_audio.h" />
+    <ClInclude Include="..\..\include\SDL_bits.h" />
     <ClInclude Include="..\..\include\SDL_blendmode.h" />
     <ClInclude Include="..\..\include\SDL_clipboard.h" />
     <ClInclude Include="..\..\include\SDL_config.h" />
--- a/VisualC/SDL/SDL_VS2012.vcxproj	Tue Feb 12 11:47:29 2013 -0800
+++ b/VisualC/SDL/SDL_VS2012.vcxproj	Tue Feb 12 11:47:31 2013 -0800
@@ -211,6 +211,7 @@
     <ClInclude Include="..\..\include\SDL_assert.h" />
     <ClInclude Include="..\..\include\SDL_atomic.h" />
     <ClInclude Include="..\..\include\SDL_audio.h" />
+    <ClInclude Include="..\..\include\SDL_bits.h" />
     <ClInclude Include="..\..\include\SDL_blendmode.h" />
     <ClInclude Include="..\..\include\SDL_clipboard.h" />
     <ClInclude Include="..\..\include\SDL_config.h" />
@@ -454,4 +455,4 @@
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>
-</Project>
\ No newline at end of file
+</Project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/SDL_bits.h	Tue Feb 12 11:47:31 2013 -0800
@@ -0,0 +1,102 @@
+/*
+  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.
+*/
+
+/**
+ *  \file SDL_bits.h
+ *
+ *  Functions for fiddling with bits and bitmasks.
+ */
+
+#ifndef _SDL_bits_h
+#define _SDL_bits_h
+
+#include "SDL_stdinc.h"
+
+#include "begin_code.h"
+/* Set up for C function definitions, even when using C++ */
+#ifdef __cplusplus
+/* *INDENT-OFF* */
+extern "C" {
+/* *INDENT-ON* */
+#endif
+
+/**
+ *  \file SDL_bits.h
+ *
+ *  Uses inline functions for compilers that support them, and static
+ *  functions for those that do not.  Because these functions become
+ *  static for compilers that do not support inline functions, this
+ *  header should only be included in files that actually use them.
+ */
+
+/**
+ *  Get the index of the most significant bit. Result is undefined when called
+ *  with 0. This operation can also be stated as "count leading zeroes" and
+ *  "log base 2".
+ *
+ *  \return Index of the most significant bit.
+ */
+static __inline__ Sint8
+SDL_MostSignificantBitIndex32(Uint32 x)
+{
+#if defined(__GNUC__)
+    /* Count Leading Zeroes builtin in GCC.
+     * http://gcc.gnu.org/onlinedocs/gcc-4.3.4/gcc/Other-Builtins.html
+     */
+    return 31 - __builtin_clz(x);
+#else
+    /* Based off of Bit Twiddling Hacks by Sean Eron Anderson
+     * <seander@cs.stanford.edu>, released in the public domain.
+     * http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogLookup
+     */
+    static const Sint8 LogTable256[256] =
+    {
+    #define LT(n) n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n
+        -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
+        LT(4), LT(5), LT(5), LT(6), LT(6), LT(6), LT(6),
+        LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7)
+    #undef LT
+    };
+
+    register unsigned int t, tt;
+
+    if (tt = x >> 16)
+    {
+      return ((t = tt >> 8) ? 24 + LogTable256[t] : 16 + LogTable256[tt]);
+    }
+    else
+    {
+      return ((t = x >> 8) ? 8 + LogTable256[t] : LogTable256[x]);
+    }
+#endif
+}
+
+/* Ends C function definitions when using C++ */
+#ifdef __cplusplus
+/* *INDENT-OFF* */
+}
+/* *INDENT-ON* */
+#endif
+#include "close_code.h"
+
+#endif /* _SDL_bits_h */
+
+/* vi: set ts=4 sw=4 expandtab: */
--- a/src/SDL.c	Tue Feb 12 11:47:29 2013 -0800
+++ b/src/SDL.c	Tue Feb 12 11:47:31 2013 -0800
@@ -23,6 +23,7 @@
 /* Initialization code for SDL */
 
 #include "SDL.h"
+#include "SDL_bits.h"
 #include "SDL_revision.h"
 #include "SDL_fatal.h"
 #include "SDL_assert_c.h"
@@ -42,134 +43,146 @@
 
 
 /* The initialized subsystems */
-static Uint32 SDL_initialized = 0;
 static Uint32 ticks_started = 0;
 static SDL_bool SDL_bInMainQuit = SDL_FALSE;
-static Uint8 SDL_SubsystemRefCount[ 32 ]; // keep a per subsystem init
+static Uint8 SDL_SubsystemRefCount[ 32 ];
 
-/* helper func to return the index of the MSB in an int */
-int msb32_idx( Uint32 n)
+/* Private helper to increment a subsystem's ref counter. */
+static void SDL_PrivateSubsystemRefCountIncr(Uint32 subsystem)
+{
+    int subsystem_index = SDL_MostSignificantBitIndex32(subsystem);
+    SDL_assert(SDL_SubsystemRefCount[subsystem_index] < 255);
+    ++SDL_SubsystemRefCount[subsystem_index];
+}
+
+/* Private helper to decrement a subsystem's ref counter. */
+void SDL_PrivateSubsystemRefCountDecr(Uint32 subsystem)
 {
-	int b = 0;
-	if (!n) return -1;
+    int subsystem_index = SDL_MostSignificantBitIndex32(subsystem);
+    if (SDL_SubsystemRefCount[subsystem_index] > 0) {
+        --SDL_SubsystemRefCount[subsystem_index];
+    }
+}
+
+/* Private helper to check if a system needs init. */
+static SDL_bool
+SDL_PrivateShouldInitSubsystem(Uint32 flags, Uint32 subsystem)
+{
+    if ((flags & subsystem) == 0) {
+      return SDL_FALSE;
+    }
 
-#define step(x) if (n >= ((Uint32)1) << x) b += x, n >>= x
-	step(16); step(8); step(4); step(2); step(1);
-#undef step
-	return b;
+    int subsystem_index = SDL_MostSignificantBitIndex32(subsystem);
+    SDL_assert(SDL_SubsystemRefCount[subsystem_index] < 255);
+    return (SDL_SubsystemRefCount[subsystem_index] == 0);
+}
+
+/* Private helper to check if a system needs to be quit. */
+static SDL_bool
+SDL_PrivateShouldQuitSubsystem(Uint32 subsystem) {
+    int subsystem_index = SDL_MostSignificantBitIndex32(subsystem);
+    if (SDL_SubsystemRefCount[subsystem_index] == 0) {
+      return SDL_FALSE;
+    }
+
+    /* If we're in SDL_Quit, we shut down every subsystem, even if refcount
+     * isn't zero.
+     */
+    return SDL_SubsystemRefCount[subsystem_index] == 1 || SDL_bInMainQuit;
 }
 
 int
 SDL_InitSubSystem(Uint32 flags)
 {
 #if !SDL_TIMERS_DISABLED
-    /* Initialize the timer subsystem */
     if (!ticks_started) {
         SDL_StartTicks();
         ticks_started = 1;
     }
-
-    if ((flags & SDL_INIT_TIMER) ){
-		SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_TIMER) ]++;
-		SDL_assert( SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_TIMER) ] < 254 );
-		if ( !(SDL_initialized & SDL_INIT_TIMER)) {
-			if (SDL_TimerInit() < 0) {
-				return (-1);
-			}
-			SDL_initialized |= SDL_INIT_TIMER;
-		}
-    }
-#else
-    if (flags & SDL_INIT_TIMER) {
-        SDL_SetError("SDL not built with timer support");
-        return (-1);
-    }
-#endif
-
-#if !SDL_VIDEO_DISABLED
-    /* Initialize the video/event subsystem */
-    if ((flags & SDL_INIT_VIDEO) ) {
-		SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_VIDEO) ]++;
-		SDL_assert( SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_VIDEO) ] < 254 );
-		if ( !(SDL_initialized & SDL_INIT_VIDEO)) {
-			if (SDL_VideoInit(NULL) < 0) {
-				return (-1);
-			}
-			SDL_initialized |= SDL_INIT_VIDEO;
-		}
-    }
-#else
-    if (flags & SDL_INIT_VIDEO) {
-        SDL_SetError("SDL not built with video support");
-        return (-1);
-    }
 #endif
 
-#if !SDL_AUDIO_DISABLED
-    /* Initialize the audio subsystem */
-    if ((flags & SDL_INIT_AUDIO) ) {
-		SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_AUDIO) ]++;
-		SDL_assert( SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_AUDIO) ] < 254 );
-		if ( !(SDL_initialized & SDL_INIT_AUDIO)) {
-			if (SDL_AudioInit(NULL) < 0) {
-				return (-1);
-			}
-			SDL_initialized |= SDL_INIT_AUDIO;
-		}
-    }
+    /* Initialize the timer subsystem */
+    if (SDL_PrivateShouldInitSubsystem(flags, SDL_INIT_TIMER)) {
+#if !SDL_TIMERS_DISABLED
+        if (SDL_TimerInit() < 0) {
+            return (-1);
+        }
+        SDL_PrivateSubsystemRefCountIncr(SDL_INIT_TIMER);
 #else
-    if (flags & SDL_INIT_AUDIO) {
-        SDL_SetError("SDL not built with audio support");
+        SDL_SetError("SDL not built with timer support");
         return (-1);
+#endif
     }
-#endif
 
-#if !SDL_JOYSTICK_DISABLED
-    /* Initialize the joystick subsystem */
-    if ( ( (flags & SDL_INIT_JOYSTICK)  ) || ((flags & SDL_INIT_GAMECONTROLLER) ) ) { // game controller implies joystick
-		SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_JOYSTICK) ]++;
-		SDL_assert( SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_JOYSTICK) ] < 254 );
-        if ( !(SDL_initialized & SDL_INIT_JOYSTICK) && SDL_JoystickInit() < 0) {
+    /* Initialize the video/event subsystem */
+    if (SDL_PrivateShouldInitSubsystem(flags, SDL_INIT_VIDEO)) {
+#if !SDL_VIDEO_DISABLED
+        if (SDL_VideoInit(NULL) < 0) {
+            return (-1);
+        }
+        SDL_PrivateSubsystemRefCountIncr(SDL_INIT_VIDEO);
+#else
+        SDL_SetError("SDL not built with video support");
+        return (-1);
+#endif
+    }
+
+    /* Initialize the audio subsystem */
+    if (SDL_PrivateShouldInitSubsystem(flags, SDL_INIT_AUDIO)) {
+#if !SDL_AUDIO_DISABLED
+        if (SDL_AudioInit(NULL) < 0) {
             return (-1);
         }
+        SDL_PrivateSubsystemRefCountIncr(SDL_INIT_AUDIO);
+#else
+        SDL_SetError("SDL not built with audio support");
+        return (-1);
+#endif
+    }
 
-		if ((flags & SDL_INIT_GAMECONTROLLER) ) {
-			SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_GAMECONTROLLER) ]++;
-			SDL_assert( SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_GAMECONTROLLER) ] < 254 );
-			if ( !(SDL_initialized & SDL_INIT_GAMECONTROLLER)) {
-				if (SDL_GameControllerInit() < 0) {
-					return (-1);
-				}
-				SDL_initialized |= SDL_INIT_GAMECONTROLLER;
-			}
-		}
-        SDL_initialized |= SDL_INIT_JOYSTICK;
+    if ((flags & SDL_INIT_GAMECONTROLLER)) {
+        // Game controller implies Joystick.
+        flags |= SDL_INIT_JOYSTICK;
     }
+
+    /* Initialize the joystick subsystem */
+    if (SDL_PrivateShouldInitSubsystem(flags, SDL_INIT_JOYSTICK)) {
+#if !SDL_JOYSTICK_DISABLED
+        if (SDL_JoystickInit() < 0) {
+            return (-1);
+        }
+        SDL_PrivateSubsystemRefCountIncr(SDL_INIT_JOYSTICK);
 #else
-    if (flags & SDL_INIT_JOYSTICK) {
         SDL_SetError("SDL not built with joystick support");
         return (-1);
-    }
 #endif
+    }
 
-#if !SDL_HAPTIC_DISABLED
+    if (SDL_PrivateShouldInitSubsystem(flags, SDL_INIT_GAMECONTROLLER)) {
+#if !SDL_JOYSTICK_DISABLED
+        if (SDL_GameControllerInit() < 0) {
+            return (-1);
+        }
+        SDL_PrivateSubsystemRefCountIncr(SDL_INIT_GAMECONTROLLER);
+#else
+        SDL_SetError("SDL not built with joystick support");
+        return (-1);
+#endif
+    }
+
     /* Initialize the haptic subsystem */
-    if ((flags & SDL_INIT_HAPTIC) ) {
-		SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_HAPTIC) ]++;
-		SDL_assert( SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_HAPTIC) ] < 254 );
-		if ( !(SDL_initialized & SDL_INIT_HAPTIC)) {
-			if (SDL_HapticInit() < 0) {
-				return (-1);
-			}
-			SDL_initialized |= SDL_INIT_HAPTIC;
-		}
-    }
+    if (SDL_PrivateShouldInitSubsystem(flags, SDL_INIT_HAPTIC)) {
+#if !SDL_HAPTIC_DISABLED
+        if (SDL_HapticInit() < 0) {
+            return (-1);
+        }
+        SDL_PrivateSubsystemRefCountIncr(SDL_INIT_HAPTIC);
 #else
-    if (flags & SDL_INIT_HAPTIC) {
         SDL_SetError("SDL not built with haptic (force feedback) support");
         return (-1);
+#endif
     }
-#endif
+
     return (0);
 }
 
@@ -199,7 +212,6 @@
         SDL_InstallParachute();
     }
 
-	SDL_memset( SDL_SubsystemRefCount, 0x0, sizeof(SDL_SubsystemRefCount) );
     return (0);
 }
 
@@ -208,62 +220,57 @@
 {
     /* Shut down requested initialized subsystems */
 #if !SDL_JOYSTICK_DISABLED
-    if ((flags & SDL_initialized & SDL_INIT_JOYSTICK) || (flags & SDL_initialized & SDL_INIT_GAMECONTROLLER)) {
-		if ( (flags & SDL_initialized & SDL_INIT_GAMECONTROLLER) ) {
-			SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_GAMECONTROLLER) ]--;
-			if ( SDL_bInMainQuit || SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_GAMECONTROLLER) ] == 0 ) {
-				SDL_GameControllerQuit();
-				SDL_initialized &= ~SDL_INIT_GAMECONTROLLER;
-			}
+    if ((flags & SDL_INIT_GAMECONTROLLER)) {
+        // Game controller implies Joystick.
+        flags |= SDL_INIT_JOYSTICK;
+
+        if (SDL_PrivateShouldQuitSubsystem(SDL_INIT_GAMECONTROLLER)) {
+            SDL_GameControllerQuit();
 		}
+        SDL_PrivateSubsystemRefCountDecr(SDL_INIT_GAMECONTROLLER);
+    }
 
-		SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_JOYSTICK) ]--;
-		if ( SDL_bInMainQuit || SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_JOYSTICK) ] == 0 )
-		{
+    if ((flags & SDL_INIT_JOYSTICK)) {
+        if (SDL_PrivateShouldQuitSubsystem(SDL_INIT_JOYSTICK)) {
 			SDL_JoystickQuit();
-			SDL_initialized &= ~SDL_INIT_JOYSTICK;
 		}
-
+        SDL_PrivateSubsystemRefCountDecr(SDL_INIT_JOYSTICK);
     }
 #endif
+
 #if !SDL_HAPTIC_DISABLED
-    if ((flags & SDL_initialized & SDL_INIT_HAPTIC)) {
-		SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_HAPTIC) ]--;
-		if ( SDL_bInMainQuit || SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_HAPTIC) ] == 0 )
-		{
+    if ((flags & SDL_INIT_HAPTIC)) {
+        if (SDL_PrivateShouldQuitSubsystem(SDL_INIT_HAPTIC)) {
 			SDL_HapticQuit();
-			SDL_initialized &= ~SDL_INIT_HAPTIC;
 		}
+        SDL_PrivateSubsystemRefCountDecr(SDL_INIT_HAPTIC);
     }
 #endif
+
 #if !SDL_AUDIO_DISABLED
-    if ((flags & SDL_initialized & SDL_INIT_AUDIO)) {
-		SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_AUDIO) ]--;
-		if ( SDL_bInMainQuit || SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_AUDIO) ] == 0 )
-		{
+    if ((flags & SDL_INIT_AUDIO)) {
+        if (SDL_PrivateShouldQuitSubsystem(SDL_INIT_AUDIO)) {
 			SDL_AudioQuit();
-			SDL_initialized &= ~SDL_INIT_AUDIO;
 		}
+        SDL_PrivateSubsystemRefCountDecr(SDL_INIT_AUDIO);
     }
 #endif
+
 #if !SDL_VIDEO_DISABLED
-    if ((flags & SDL_initialized & SDL_INIT_VIDEO)) {
-		SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_VIDEO) ]--;
-		if ( SDL_bInMainQuit || SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_VIDEO) ] == 0 )
-		{
+    if ((flags & SDL_INIT_VIDEO)) {
+        if (SDL_PrivateShouldQuitSubsystem(SDL_INIT_VIDEO)) {
 			SDL_VideoQuit();
-			SDL_initialized &= ~SDL_INIT_VIDEO;
 		}
+        SDL_PrivateSubsystemRefCountDecr(SDL_INIT_VIDEO);
     }
 #endif
+
 #if !SDL_TIMERS_DISABLED
-    if ((flags & SDL_initialized & SDL_INIT_TIMER)) {
-		SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_TIMER) ]--;
-		if ( SDL_bInMainQuit || SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_TIMER) ] == 0 )
-		{
+    if ((flags & SDL_INIT_TIMER)) {
+        if (SDL_PrivateShouldQuitSubsystem(SDL_INIT_TIMER)) {
 			SDL_TimerQuit();
-			SDL_initialized &= ~SDL_INIT_TIMER;
 		}
+        SDL_PrivateSubsystemRefCountDecr(SDL_INIT_TIMER);
     }
 #endif
 }
@@ -271,16 +278,33 @@
 Uint32
 SDL_WasInit(Uint32 flags)
 {
+    int i;
+    int num_subsystems = SDL_arraysize(SDL_SubsystemRefCount);
+    Uint32 initialized = 0;
+
     if (!flags) {
         flags = SDL_INIT_EVERYTHING;
     }
-    return (SDL_initialized & flags);
+
+    num_subsystems = SDL_min(num_subsystems, SDL_MostSignificantBitIndex32(flags) + 1);
+
+    /* Iterate over each bit in flags, and check the matching subsystem. */
+    for (i = 0; i < num_subsystems; ++i) {
+        if ((flags & 1) && SDL_SubsystemRefCount[i] > 0) {
+            initialized |= (1 << i);
+        }
+
+        flags >>= 1;
+    }
+
+    return initialized;
 }
 
 void
 SDL_Quit(void)
 {
-	SDL_bInMainQuit = SDL_TRUE;
+    SDL_bInMainQuit = SDL_TRUE;
+
     /* Quit all subsystems */
 #if defined(__WIN32__)
     SDL_HelperWindowDestroy();
@@ -294,8 +318,12 @@
     SDL_AssertionsQuit();
     SDL_LogResetPriorities();
 
-	SDL_memset( SDL_SubsystemRefCount, 0x0, sizeof(SDL_SubsystemRefCount) );
-	SDL_bInMainQuit = SDL_FALSE;
+    /* Now that every subsystem has been quit, we reset the subsystem refcount
+     * and the list of initialized subsystems.
+     */
+    SDL_memset( SDL_SubsystemRefCount, 0x0, sizeof(SDL_SubsystemRefCount) );
+
+    SDL_bInMainQuit = SDL_FALSE;
 }
 
 /* Get the library version number */
--- a/test/Makefile.in	Tue Feb 12 11:47:29 2013 -0800
+++ b/test/Makefile.in	Tue Feb 12 11:47:31 2013 -0800
@@ -70,6 +70,7 @@
 
 testautomation$(EXE): $(srcdir)/testautomation.c \
 		      $(srcdir)/testautomation_clipboard.c \
+		      $(srcdir)/testautomation_main.c \
 		      $(srcdir)/testautomation_platform.c \
 		      $(srcdir)/testautomation_rect.c \
 		      $(srcdir)/testautomation_render.c \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/testautomation_main.c	Tue Feb 12 11:47:31 2013 -0800
@@ -0,0 +1,131 @@
+/**
+ * Automated SDL subsystems management test.
+ *
+ * Written by Jørgen Tjernø "jorgenpt"
+ *
+ * Released under Public Domain.
+ */
+
+#include "SDL.h"
+#include "SDL_test.h"
+
+
+/*!
+ * \brief Tests SDL_Init() and SDL_Quit()
+ * \sa
+ * http://wiki.libsdl.org/moin.cgi/SDL_Init
+ * http://wiki.libsdl.org/moin.cgi/SDL_Quit
+ */
+static int main_testInitQuit (void *arg)
+{
+    int initialized_subsystems = SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC;
+
+    SDLTest_AssertCheck( SDL_Init(initialized_subsystems) == 0, "SDL_Init multiple systems." );
+
+    int enabled_subsystems = SDL_WasInit(initialized_subsystems);
+    SDLTest_AssertCheck( enabled_subsystems == initialized_subsystems, "SDL_WasInit(SDL_INIT_EVERYTHING) contains all systems (%i)", enabled_subsystems );
+
+    SDL_Quit();
+
+    enabled_subsystems = SDL_WasInit(initialized_subsystems);
+    SDLTest_AssertCheck( enabled_subsystems == 0, "SDL_Quit should shut down everything (%i)", enabled_subsystems );
+
+    return TEST_COMPLETED;
+}
+
+/*!
+ * \brief Tests SDL_InitSubSystem() and SDL_QuitSubSystem()
+ * \sa
+ * http://wiki.libsdl.org/moin.cgi/SDL_Init
+ * http://wiki.libsdl.org/moin.cgi/SDL_Quit
+ */
+static int main_testInitQuitSubSystem (void *arg)
+{
+    int i;
+    int subsystems[] = { SDL_INIT_JOYSTICK, SDL_INIT_HAPTIC, SDL_INIT_GAMECONTROLLER };
+
+    for (i = 0; i < SDL_arraysize(subsystems); ++i) {
+        int subsystem = subsystems[i];
+
+        SDLTest_AssertCheck( (SDL_WasInit(subsystem) & subsystem) == 0, "SDL_WasInit(%x) before init should be false", subsystem );
+        SDLTest_AssertCheck( SDL_InitSubSystem(subsystem) == 0, "SDL_InitSubSystem(%x)", subsystem );
+
+        int initialized_system = SDL_WasInit(subsystem);
+        SDLTest_AssertCheck( (initialized_system & subsystem) != 0, "SDL_WasInit(%x) should be true (%x)", subsystem, initialized_system );
+
+        SDL_QuitSubSystem(subsystem);
+
+        SDLTest_AssertCheck( (SDL_WasInit(subsystem) & subsystem) == 0, "SDL_WasInit(%x) after shutdown should be false", subsystem );
+    }
+
+    return TEST_COMPLETED;
+}
+
+const int joy_and_controller = SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER;
+static int main_testImpliedJoystickInit (void *arg)
+{
+    // First initialize the controller
+    SDLTest_AssertCheck( (SDL_WasInit(joy_and_controller) & joy_and_controller) == 0, "SDL_WasInit() before init should be false for joystick & controller" );
+    SDLTest_AssertCheck( SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) == 0, "SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER)" );
+
+    // Then make sure this implicitly initialized the joystick subsystem
+    int initialized_system = SDL_WasInit(joy_and_controller);
+    SDLTest_AssertCheck( (initialized_system & joy_and_controller) == joy_and_controller, "SDL_WasInit() should be true for joystick & controller (%x)", initialized_system );
+
+    // Then quit the controller, and make sure that imlicity also quits the
+    // joystick subsystem
+    SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER);
+    initialized_system = SDL_WasInit(joy_and_controller);
+    SDLTest_AssertCheck( (initialized_system & joy_and_controller) == 0, "SDL_WasInit() should be false for joystick & controller (%x)", initialized_system );
+}
+
+static int main_testImpliedJoystickQuit (void *arg)
+{
+    // First initialize the controller and the joystick (explicitly)
+    SDLTest_AssertCheck( (SDL_WasInit(joy_and_controller) & joy_and_controller) == 0, "SDL_WasInit() before init should be false for joystick & controller" );
+    SDLTest_AssertCheck( SDL_InitSubSystem(SDL_INIT_JOYSTICK) == 0, "SDL_InitSubSystem(SDL_INIT_JOYSTICK)" );
+    SDLTest_AssertCheck( SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) == 0, "SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER)" );
+
+    // Then make sure they're both initialized properly
+    int initialized_system = SDL_WasInit(joy_and_controller);
+    SDLTest_AssertCheck( (initialized_system & joy_and_controller) == joy_and_controller, "SDL_WasInit() should be true for joystick & controller (%x)", initialized_system );
+
+    // Then quit the controller, and make sure that it does NOT quit the
+    // explicitly initialized joystick subsystem.
+    SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER);
+    initialized_system = SDL_WasInit(joy_and_controller);
+    SDLTest_AssertCheck( (initialized_system & joy_and_controller) == SDL_INIT_JOYSTICK, "SDL_WasInit() should be false for joystick & controller (%x)", initialized_system );
+
+    SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
+}
+
+static const SDLTest_TestCaseReference mainTest1 =
+		{ (SDLTest_TestCaseFp)main_testInitQuit, "main_testInitQuit", "Tests SDL_Init/Quit", TEST_ENABLED};
+
+static const SDLTest_TestCaseReference mainTest2 =
+		{ (SDLTest_TestCaseFp)main_testInitQuitSubSystem, "main_testInitQuitSubSystem", "Tests SDL_InitSubSystem/QuitSubSystem", TEST_ENABLED};
+
+static const SDLTest_TestCaseReference mainTest3 =
+		{ (SDLTest_TestCaseFp)main_testImpliedJoystickInit, "main_testImpliedJoystickInit", "Tests that init for gamecontroller properly implies joystick", TEST_ENABLED};
+
+static const SDLTest_TestCaseReference mainTest4 =
+		{ (SDLTest_TestCaseFp)main_testImpliedJoystickQuit, "main_testImpliedJoystickQuit", "Tests that quit for gamecontroller doesn't quit joystick if you inited it explicitly", TEST_ENABLED};
+
+/* Sequence of Platform test cases */
+static const SDLTest_TestCaseReference *mainTests[] =  {
+	&mainTest1,
+	&mainTest2,
+	&mainTest3,
+	&mainTest4,
+	NULL
+};
+
+/* Platform test suite (global) */
+SDLTest_TestSuiteReference mainTestSuite = {
+	"Main",
+	NULL,
+	mainTests,
+	NULL
+};
+
+/* vi: set ts=4 sw=4 expandtab: */
--- a/test/testautomation_suites.h	Tue Feb 12 11:47:29 2013 -0800
+++ b/test/testautomation_suites.h	Tue Feb 12 11:47:31 2013 -0800
@@ -13,6 +13,7 @@
 extern SDLTest_TestSuiteReference clipboardTestSuite;
 extern SDLTest_TestSuiteReference eventsTestSuite;
 extern SDLTest_TestSuiteReference keyboardTestSuite;
+extern SDLTest_TestSuiteReference mainTestSuite;
 extern SDLTest_TestSuiteReference platformTestSuite;
 extern SDLTest_TestSuiteReference rectTestSuite;
 extern SDLTest_TestSuiteReference renderTestSuite;
@@ -30,6 +31,7 @@
 	&clipboardTestSuite,
 	&eventsTestSuite,
 	&keyboardTestSuite,
+	&mainTestSuite,
 	&platformTestSuite,
 	&rectTestSuite,
 	&renderTestSuite,