Don't clobber refcounting in SDL_Init.
authorJørgen P. Tjernø <jorgen@valvesoftware.com>
Tue, 12 Feb 2013 11:47:31 -0800
changeset 6866e74a4b282450
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
     1.1 --- a/Makefile.in	Tue Feb 12 11:47:29 2013 -0800
     1.2 +++ b/Makefile.in	Tue Feb 12 11:47:31 2013 -0800
     1.3 @@ -47,6 +47,7 @@
     1.4  	SDL_assert.h \
     1.5  	SDL_atomic.h \
     1.6  	SDL_audio.h \
     1.7 +        SDL_bits.h \
     1.8  	SDL_blendmode.h \
     1.9  	SDL_clipboard.h \
    1.10  	SDL_cpuinfo.h \
     2.1 --- a/VisualC/SDL/SDL_VS2008.vcproj	Tue Feb 12 11:47:29 2013 -0800
     2.2 +++ b/VisualC/SDL/SDL_VS2008.vcproj	Tue Feb 12 11:47:31 2013 -0800
     2.3 @@ -376,6 +376,10 @@
     2.4  				>
     2.5  			</File>
     2.6  			<File
     2.7 +				RelativePath="..\..\include\SDL_bits.h"
     2.8 +				>
     2.9 +			</File>
    2.10 +			<File
    2.11  				RelativePath="..\..\include\SDL_blendmode.h"
    2.12  				>
    2.13  			</File>
     3.1 --- a/VisualC/SDL/SDL_VS2010.vcxproj	Tue Feb 12 11:47:29 2013 -0800
     3.2 +++ b/VisualC/SDL/SDL_VS2010.vcxproj	Tue Feb 12 11:47:31 2013 -0800
     3.3 @@ -207,6 +207,7 @@
     3.4      <ClInclude Include="..\..\include\SDL_assert.h" />
     3.5      <ClInclude Include="..\..\include\SDL_atomic.h" />
     3.6      <ClInclude Include="..\..\include\SDL_audio.h" />
     3.7 +    <ClInclude Include="..\..\include\SDL_bits.h" />
     3.8      <ClInclude Include="..\..\include\SDL_blendmode.h" />
     3.9      <ClInclude Include="..\..\include\SDL_clipboard.h" />
    3.10      <ClInclude Include="..\..\include\SDL_config.h" />
     4.1 --- a/VisualC/SDL/SDL_VS2012.vcxproj	Tue Feb 12 11:47:29 2013 -0800
     4.2 +++ b/VisualC/SDL/SDL_VS2012.vcxproj	Tue Feb 12 11:47:31 2013 -0800
     4.3 @@ -211,6 +211,7 @@
     4.4      <ClInclude Include="..\..\include\SDL_assert.h" />
     4.5      <ClInclude Include="..\..\include\SDL_atomic.h" />
     4.6      <ClInclude Include="..\..\include\SDL_audio.h" />
     4.7 +    <ClInclude Include="..\..\include\SDL_bits.h" />
     4.8      <ClInclude Include="..\..\include\SDL_blendmode.h" />
     4.9      <ClInclude Include="..\..\include\SDL_clipboard.h" />
    4.10      <ClInclude Include="..\..\include\SDL_config.h" />
    4.11 @@ -454,4 +455,4 @@
    4.12    <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
    4.13    <ImportGroup Label="ExtensionTargets">
    4.14    </ImportGroup>
    4.15 -</Project>
    4.16 \ No newline at end of file
    4.17 +</Project>
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/include/SDL_bits.h	Tue Feb 12 11:47:31 2013 -0800
     5.3 @@ -0,0 +1,102 @@
     5.4 +/*
     5.5 +  Simple DirectMedia Layer
     5.6 +  Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
     5.7 +
     5.8 +  This software is provided 'as-is', without any express or implied
     5.9 +  warranty.  In no event will the authors be held liable for any damages
    5.10 +  arising from the use of this software.
    5.11 +
    5.12 +  Permission is granted to anyone to use this software for any purpose,
    5.13 +  including commercial applications, and to alter it and redistribute it
    5.14 +  freely, subject to the following restrictions:
    5.15 +
    5.16 +  1. The origin of this software must not be misrepresented; you must not
    5.17 +     claim that you wrote the original software. If you use this software
    5.18 +     in a product, an acknowledgment in the product documentation would be
    5.19 +     appreciated but is not required.
    5.20 +  2. Altered source versions must be plainly marked as such, and must not be
    5.21 +     misrepresented as being the original software.
    5.22 +  3. This notice may not be removed or altered from any source distribution.
    5.23 +*/
    5.24 +
    5.25 +/**
    5.26 + *  \file SDL_bits.h
    5.27 + *
    5.28 + *  Functions for fiddling with bits and bitmasks.
    5.29 + */
    5.30 +
    5.31 +#ifndef _SDL_bits_h
    5.32 +#define _SDL_bits_h
    5.33 +
    5.34 +#include "SDL_stdinc.h"
    5.35 +
    5.36 +#include "begin_code.h"
    5.37 +/* Set up for C function definitions, even when using C++ */
    5.38 +#ifdef __cplusplus
    5.39 +/* *INDENT-OFF* */
    5.40 +extern "C" {
    5.41 +/* *INDENT-ON* */
    5.42 +#endif
    5.43 +
    5.44 +/**
    5.45 + *  \file SDL_bits.h
    5.46 + *
    5.47 + *  Uses inline functions for compilers that support them, and static
    5.48 + *  functions for those that do not.  Because these functions become
    5.49 + *  static for compilers that do not support inline functions, this
    5.50 + *  header should only be included in files that actually use them.
    5.51 + */
    5.52 +
    5.53 +/**
    5.54 + *  Get the index of the most significant bit. Result is undefined when called
    5.55 + *  with 0. This operation can also be stated as "count leading zeroes" and
    5.56 + *  "log base 2".
    5.57 + *
    5.58 + *  \return Index of the most significant bit.
    5.59 + */
    5.60 +static __inline__ Sint8
    5.61 +SDL_MostSignificantBitIndex32(Uint32 x)
    5.62 +{
    5.63 +#if defined(__GNUC__)
    5.64 +    /* Count Leading Zeroes builtin in GCC.
    5.65 +     * http://gcc.gnu.org/onlinedocs/gcc-4.3.4/gcc/Other-Builtins.html
    5.66 +     */
    5.67 +    return 31 - __builtin_clz(x);
    5.68 +#else
    5.69 +    /* Based off of Bit Twiddling Hacks by Sean Eron Anderson
    5.70 +     * <seander@cs.stanford.edu>, released in the public domain.
    5.71 +     * http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogLookup
    5.72 +     */
    5.73 +    static const Sint8 LogTable256[256] =
    5.74 +    {
    5.75 +    #define LT(n) n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n
    5.76 +        -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
    5.77 +        LT(4), LT(5), LT(5), LT(6), LT(6), LT(6), LT(6),
    5.78 +        LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7)
    5.79 +    #undef LT
    5.80 +    };
    5.81 +
    5.82 +    register unsigned int t, tt;
    5.83 +
    5.84 +    if (tt = x >> 16)
    5.85 +    {
    5.86 +      return ((t = tt >> 8) ? 24 + LogTable256[t] : 16 + LogTable256[tt]);
    5.87 +    }
    5.88 +    else
    5.89 +    {
    5.90 +      return ((t = x >> 8) ? 8 + LogTable256[t] : LogTable256[x]);
    5.91 +    }
    5.92 +#endif
    5.93 +}
    5.94 +
    5.95 +/* Ends C function definitions when using C++ */
    5.96 +#ifdef __cplusplus
    5.97 +/* *INDENT-OFF* */
    5.98 +}
    5.99 +/* *INDENT-ON* */
   5.100 +#endif
   5.101 +#include "close_code.h"
   5.102 +
   5.103 +#endif /* _SDL_bits_h */
   5.104 +
   5.105 +/* vi: set ts=4 sw=4 expandtab: */
     6.1 --- a/src/SDL.c	Tue Feb 12 11:47:29 2013 -0800
     6.2 +++ b/src/SDL.c	Tue Feb 12 11:47:31 2013 -0800
     6.3 @@ -23,6 +23,7 @@
     6.4  /* Initialization code for SDL */
     6.5  
     6.6  #include "SDL.h"
     6.7 +#include "SDL_bits.h"
     6.8  #include "SDL_revision.h"
     6.9  #include "SDL_fatal.h"
    6.10  #include "SDL_assert_c.h"
    6.11 @@ -42,134 +43,146 @@
    6.12  
    6.13  
    6.14  /* The initialized subsystems */
    6.15 -static Uint32 SDL_initialized = 0;
    6.16  static Uint32 ticks_started = 0;
    6.17  static SDL_bool SDL_bInMainQuit = SDL_FALSE;
    6.18 -static Uint8 SDL_SubsystemRefCount[ 32 ]; // keep a per subsystem init
    6.19 +static Uint8 SDL_SubsystemRefCount[ 32 ];
    6.20  
    6.21 -/* helper func to return the index of the MSB in an int */
    6.22 -int msb32_idx( Uint32 n)
    6.23 +/* Private helper to increment a subsystem's ref counter. */
    6.24 +static void SDL_PrivateSubsystemRefCountIncr(Uint32 subsystem)
    6.25  {
    6.26 -	int b = 0;
    6.27 -	if (!n) return -1;
    6.28 +    int subsystem_index = SDL_MostSignificantBitIndex32(subsystem);
    6.29 +    SDL_assert(SDL_SubsystemRefCount[subsystem_index] < 255);
    6.30 +    ++SDL_SubsystemRefCount[subsystem_index];
    6.31 +}
    6.32  
    6.33 -#define step(x) if (n >= ((Uint32)1) << x) b += x, n >>= x
    6.34 -	step(16); step(8); step(4); step(2); step(1);
    6.35 -#undef step
    6.36 -	return b;
    6.37 +/* Private helper to decrement a subsystem's ref counter. */
    6.38 +void SDL_PrivateSubsystemRefCountDecr(Uint32 subsystem)
    6.39 +{
    6.40 +    int subsystem_index = SDL_MostSignificantBitIndex32(subsystem);
    6.41 +    if (SDL_SubsystemRefCount[subsystem_index] > 0) {
    6.42 +        --SDL_SubsystemRefCount[subsystem_index];
    6.43 +    }
    6.44 +}
    6.45 +
    6.46 +/* Private helper to check if a system needs init. */
    6.47 +static SDL_bool
    6.48 +SDL_PrivateShouldInitSubsystem(Uint32 flags, Uint32 subsystem)
    6.49 +{
    6.50 +    if ((flags & subsystem) == 0) {
    6.51 +      return SDL_FALSE;
    6.52 +    }
    6.53 +
    6.54 +    int subsystem_index = SDL_MostSignificantBitIndex32(subsystem);
    6.55 +    SDL_assert(SDL_SubsystemRefCount[subsystem_index] < 255);
    6.56 +    return (SDL_SubsystemRefCount[subsystem_index] == 0);
    6.57 +}
    6.58 +
    6.59 +/* Private helper to check if a system needs to be quit. */
    6.60 +static SDL_bool
    6.61 +SDL_PrivateShouldQuitSubsystem(Uint32 subsystem) {
    6.62 +    int subsystem_index = SDL_MostSignificantBitIndex32(subsystem);
    6.63 +    if (SDL_SubsystemRefCount[subsystem_index] == 0) {
    6.64 +      return SDL_FALSE;
    6.65 +    }
    6.66 +
    6.67 +    /* If we're in SDL_Quit, we shut down every subsystem, even if refcount
    6.68 +     * isn't zero.
    6.69 +     */
    6.70 +    return SDL_SubsystemRefCount[subsystem_index] == 1 || SDL_bInMainQuit;
    6.71  }
    6.72  
    6.73  int
    6.74  SDL_InitSubSystem(Uint32 flags)
    6.75  {
    6.76  #if !SDL_TIMERS_DISABLED
    6.77 -    /* Initialize the timer subsystem */
    6.78      if (!ticks_started) {
    6.79          SDL_StartTicks();
    6.80          ticks_started = 1;
    6.81      }
    6.82 +#endif
    6.83  
    6.84 -    if ((flags & SDL_INIT_TIMER) ){
    6.85 -		SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_TIMER) ]++;
    6.86 -		SDL_assert( SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_TIMER) ] < 254 );
    6.87 -		if ( !(SDL_initialized & SDL_INIT_TIMER)) {
    6.88 -			if (SDL_TimerInit() < 0) {
    6.89 -				return (-1);
    6.90 -			}
    6.91 -			SDL_initialized |= SDL_INIT_TIMER;
    6.92 -		}
    6.93 -    }
    6.94 +    /* Initialize the timer subsystem */
    6.95 +    if (SDL_PrivateShouldInitSubsystem(flags, SDL_INIT_TIMER)) {
    6.96 +#if !SDL_TIMERS_DISABLED
    6.97 +        if (SDL_TimerInit() < 0) {
    6.98 +            return (-1);
    6.99 +        }
   6.100 +        SDL_PrivateSubsystemRefCountIncr(SDL_INIT_TIMER);
   6.101  #else
   6.102 -    if (flags & SDL_INIT_TIMER) {
   6.103          SDL_SetError("SDL not built with timer support");
   6.104          return (-1);
   6.105 +#endif
   6.106      }
   6.107 -#endif
   6.108  
   6.109 +    /* Initialize the video/event subsystem */
   6.110 +    if (SDL_PrivateShouldInitSubsystem(flags, SDL_INIT_VIDEO)) {
   6.111  #if !SDL_VIDEO_DISABLED
   6.112 -    /* Initialize the video/event subsystem */
   6.113 -    if ((flags & SDL_INIT_VIDEO) ) {
   6.114 -		SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_VIDEO) ]++;
   6.115 -		SDL_assert( SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_VIDEO) ] < 254 );
   6.116 -		if ( !(SDL_initialized & SDL_INIT_VIDEO)) {
   6.117 -			if (SDL_VideoInit(NULL) < 0) {
   6.118 -				return (-1);
   6.119 -			}
   6.120 -			SDL_initialized |= SDL_INIT_VIDEO;
   6.121 -		}
   6.122 -    }
   6.123 +        if (SDL_VideoInit(NULL) < 0) {
   6.124 +            return (-1);
   6.125 +        }
   6.126 +        SDL_PrivateSubsystemRefCountIncr(SDL_INIT_VIDEO);
   6.127  #else
   6.128 -    if (flags & SDL_INIT_VIDEO) {
   6.129          SDL_SetError("SDL not built with video support");
   6.130          return (-1);
   6.131 +#endif
   6.132      }
   6.133 -#endif
   6.134  
   6.135 +    /* Initialize the audio subsystem */
   6.136 +    if (SDL_PrivateShouldInitSubsystem(flags, SDL_INIT_AUDIO)) {
   6.137  #if !SDL_AUDIO_DISABLED
   6.138 -    /* Initialize the audio subsystem */
   6.139 -    if ((flags & SDL_INIT_AUDIO) ) {
   6.140 -		SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_AUDIO) ]++;
   6.141 -		SDL_assert( SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_AUDIO) ] < 254 );
   6.142 -		if ( !(SDL_initialized & SDL_INIT_AUDIO)) {
   6.143 -			if (SDL_AudioInit(NULL) < 0) {
   6.144 -				return (-1);
   6.145 -			}
   6.146 -			SDL_initialized |= SDL_INIT_AUDIO;
   6.147 -		}
   6.148 -    }
   6.149 +        if (SDL_AudioInit(NULL) < 0) {
   6.150 +            return (-1);
   6.151 +        }
   6.152 +        SDL_PrivateSubsystemRefCountIncr(SDL_INIT_AUDIO);
   6.153  #else
   6.154 -    if (flags & SDL_INIT_AUDIO) {
   6.155          SDL_SetError("SDL not built with audio support");
   6.156          return (-1);
   6.157 +#endif
   6.158      }
   6.159 -#endif
   6.160  
   6.161 +    if ((flags & SDL_INIT_GAMECONTROLLER)) {
   6.162 +        // Game controller implies Joystick.
   6.163 +        flags |= SDL_INIT_JOYSTICK;
   6.164 +    }
   6.165 +
   6.166 +    /* Initialize the joystick subsystem */
   6.167 +    if (SDL_PrivateShouldInitSubsystem(flags, SDL_INIT_JOYSTICK)) {
   6.168  #if !SDL_JOYSTICK_DISABLED
   6.169 -    /* Initialize the joystick subsystem */
   6.170 -    if ( ( (flags & SDL_INIT_JOYSTICK)  ) || ((flags & SDL_INIT_GAMECONTROLLER) ) ) { // game controller implies joystick
   6.171 -		SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_JOYSTICK) ]++;
   6.172 -		SDL_assert( SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_JOYSTICK) ] < 254 );
   6.173 -        if ( !(SDL_initialized & SDL_INIT_JOYSTICK) && SDL_JoystickInit() < 0) {
   6.174 +        if (SDL_JoystickInit() < 0) {
   6.175              return (-1);
   6.176          }
   6.177 -
   6.178 -		if ((flags & SDL_INIT_GAMECONTROLLER) ) {
   6.179 -			SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_GAMECONTROLLER) ]++;
   6.180 -			SDL_assert( SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_GAMECONTROLLER) ] < 254 );
   6.181 -			if ( !(SDL_initialized & SDL_INIT_GAMECONTROLLER)) {
   6.182 -				if (SDL_GameControllerInit() < 0) {
   6.183 -					return (-1);
   6.184 -				}
   6.185 -				SDL_initialized |= SDL_INIT_GAMECONTROLLER;
   6.186 -			}
   6.187 -		}
   6.188 -        SDL_initialized |= SDL_INIT_JOYSTICK;
   6.189 -    }
   6.190 +        SDL_PrivateSubsystemRefCountIncr(SDL_INIT_JOYSTICK);
   6.191  #else
   6.192 -    if (flags & SDL_INIT_JOYSTICK) {
   6.193          SDL_SetError("SDL not built with joystick support");
   6.194          return (-1);
   6.195 +#endif
   6.196      }
   6.197 +
   6.198 +    if (SDL_PrivateShouldInitSubsystem(flags, SDL_INIT_GAMECONTROLLER)) {
   6.199 +#if !SDL_JOYSTICK_DISABLED
   6.200 +        if (SDL_GameControllerInit() < 0) {
   6.201 +            return (-1);
   6.202 +        }
   6.203 +        SDL_PrivateSubsystemRefCountIncr(SDL_INIT_GAMECONTROLLER);
   6.204 +#else
   6.205 +        SDL_SetError("SDL not built with joystick support");
   6.206 +        return (-1);
   6.207  #endif
   6.208 +    }
   6.209  
   6.210 +    /* Initialize the haptic subsystem */
   6.211 +    if (SDL_PrivateShouldInitSubsystem(flags, SDL_INIT_HAPTIC)) {
   6.212  #if !SDL_HAPTIC_DISABLED
   6.213 -    /* Initialize the haptic subsystem */
   6.214 -    if ((flags & SDL_INIT_HAPTIC) ) {
   6.215 -		SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_HAPTIC) ]++;
   6.216 -		SDL_assert( SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_HAPTIC) ] < 254 );
   6.217 -		if ( !(SDL_initialized & SDL_INIT_HAPTIC)) {
   6.218 -			if (SDL_HapticInit() < 0) {
   6.219 -				return (-1);
   6.220 -			}
   6.221 -			SDL_initialized |= SDL_INIT_HAPTIC;
   6.222 -		}
   6.223 -    }
   6.224 +        if (SDL_HapticInit() < 0) {
   6.225 +            return (-1);
   6.226 +        }
   6.227 +        SDL_PrivateSubsystemRefCountIncr(SDL_INIT_HAPTIC);
   6.228  #else
   6.229 -    if (flags & SDL_INIT_HAPTIC) {
   6.230          SDL_SetError("SDL not built with haptic (force feedback) support");
   6.231          return (-1);
   6.232 +#endif
   6.233      }
   6.234 -#endif
   6.235 +
   6.236      return (0);
   6.237  }
   6.238  
   6.239 @@ -199,7 +212,6 @@
   6.240          SDL_InstallParachute();
   6.241      }
   6.242  
   6.243 -	SDL_memset( SDL_SubsystemRefCount, 0x0, sizeof(SDL_SubsystemRefCount) );
   6.244      return (0);
   6.245  }
   6.246  
   6.247 @@ -208,62 +220,57 @@
   6.248  {
   6.249      /* Shut down requested initialized subsystems */
   6.250  #if !SDL_JOYSTICK_DISABLED
   6.251 -    if ((flags & SDL_initialized & SDL_INIT_JOYSTICK) || (flags & SDL_initialized & SDL_INIT_GAMECONTROLLER)) {
   6.252 -		if ( (flags & SDL_initialized & SDL_INIT_GAMECONTROLLER) ) {
   6.253 -			SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_GAMECONTROLLER) ]--;
   6.254 -			if ( SDL_bInMainQuit || SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_GAMECONTROLLER) ] == 0 ) {
   6.255 -				SDL_GameControllerQuit();
   6.256 -				SDL_initialized &= ~SDL_INIT_GAMECONTROLLER;
   6.257 -			}
   6.258 +    if ((flags & SDL_INIT_GAMECONTROLLER)) {
   6.259 +        // Game controller implies Joystick.
   6.260 +        flags |= SDL_INIT_JOYSTICK;
   6.261 +
   6.262 +        if (SDL_PrivateShouldQuitSubsystem(SDL_INIT_GAMECONTROLLER)) {
   6.263 +            SDL_GameControllerQuit();
   6.264  		}
   6.265 +        SDL_PrivateSubsystemRefCountDecr(SDL_INIT_GAMECONTROLLER);
   6.266 +    }
   6.267  
   6.268 -		SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_JOYSTICK) ]--;
   6.269 -		if ( SDL_bInMainQuit || SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_JOYSTICK) ] == 0 )
   6.270 -		{
   6.271 +    if ((flags & SDL_INIT_JOYSTICK)) {
   6.272 +        if (SDL_PrivateShouldQuitSubsystem(SDL_INIT_JOYSTICK)) {
   6.273  			SDL_JoystickQuit();
   6.274 -			SDL_initialized &= ~SDL_INIT_JOYSTICK;
   6.275  		}
   6.276 -
   6.277 +        SDL_PrivateSubsystemRefCountDecr(SDL_INIT_JOYSTICK);
   6.278      }
   6.279  #endif
   6.280 +
   6.281  #if !SDL_HAPTIC_DISABLED
   6.282 -    if ((flags & SDL_initialized & SDL_INIT_HAPTIC)) {
   6.283 -		SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_HAPTIC) ]--;
   6.284 -		if ( SDL_bInMainQuit || SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_HAPTIC) ] == 0 )
   6.285 -		{
   6.286 +    if ((flags & SDL_INIT_HAPTIC)) {
   6.287 +        if (SDL_PrivateShouldQuitSubsystem(SDL_INIT_HAPTIC)) {
   6.288  			SDL_HapticQuit();
   6.289 -			SDL_initialized &= ~SDL_INIT_HAPTIC;
   6.290  		}
   6.291 +        SDL_PrivateSubsystemRefCountDecr(SDL_INIT_HAPTIC);
   6.292      }
   6.293  #endif
   6.294 +
   6.295  #if !SDL_AUDIO_DISABLED
   6.296 -    if ((flags & SDL_initialized & SDL_INIT_AUDIO)) {
   6.297 -		SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_AUDIO) ]--;
   6.298 -		if ( SDL_bInMainQuit || SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_AUDIO) ] == 0 )
   6.299 -		{
   6.300 +    if ((flags & SDL_INIT_AUDIO)) {
   6.301 +        if (SDL_PrivateShouldQuitSubsystem(SDL_INIT_AUDIO)) {
   6.302  			SDL_AudioQuit();
   6.303 -			SDL_initialized &= ~SDL_INIT_AUDIO;
   6.304  		}
   6.305 +        SDL_PrivateSubsystemRefCountDecr(SDL_INIT_AUDIO);
   6.306      }
   6.307  #endif
   6.308 +
   6.309  #if !SDL_VIDEO_DISABLED
   6.310 -    if ((flags & SDL_initialized & SDL_INIT_VIDEO)) {
   6.311 -		SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_VIDEO) ]--;
   6.312 -		if ( SDL_bInMainQuit || SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_VIDEO) ] == 0 )
   6.313 -		{
   6.314 +    if ((flags & SDL_INIT_VIDEO)) {
   6.315 +        if (SDL_PrivateShouldQuitSubsystem(SDL_INIT_VIDEO)) {
   6.316  			SDL_VideoQuit();
   6.317 -			SDL_initialized &= ~SDL_INIT_VIDEO;
   6.318  		}
   6.319 +        SDL_PrivateSubsystemRefCountDecr(SDL_INIT_VIDEO);
   6.320      }
   6.321  #endif
   6.322 +
   6.323  #if !SDL_TIMERS_DISABLED
   6.324 -    if ((flags & SDL_initialized & SDL_INIT_TIMER)) {
   6.325 -		SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_TIMER) ]--;
   6.326 -		if ( SDL_bInMainQuit || SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_TIMER) ] == 0 )
   6.327 -		{
   6.328 +    if ((flags & SDL_INIT_TIMER)) {
   6.329 +        if (SDL_PrivateShouldQuitSubsystem(SDL_INIT_TIMER)) {
   6.330  			SDL_TimerQuit();
   6.331 -			SDL_initialized &= ~SDL_INIT_TIMER;
   6.332  		}
   6.333 +        SDL_PrivateSubsystemRefCountDecr(SDL_INIT_TIMER);
   6.334      }
   6.335  #endif
   6.336  }
   6.337 @@ -271,16 +278,33 @@
   6.338  Uint32
   6.339  SDL_WasInit(Uint32 flags)
   6.340  {
   6.341 +    int i;
   6.342 +    int num_subsystems = SDL_arraysize(SDL_SubsystemRefCount);
   6.343 +    Uint32 initialized = 0;
   6.344 +
   6.345      if (!flags) {
   6.346          flags = SDL_INIT_EVERYTHING;
   6.347      }
   6.348 -    return (SDL_initialized & flags);
   6.349 +
   6.350 +    num_subsystems = SDL_min(num_subsystems, SDL_MostSignificantBitIndex32(flags) + 1);
   6.351 +
   6.352 +    /* Iterate over each bit in flags, and check the matching subsystem. */
   6.353 +    for (i = 0; i < num_subsystems; ++i) {
   6.354 +        if ((flags & 1) && SDL_SubsystemRefCount[i] > 0) {
   6.355 +            initialized |= (1 << i);
   6.356 +        }
   6.357 +
   6.358 +        flags >>= 1;
   6.359 +    }
   6.360 +
   6.361 +    return initialized;
   6.362  }
   6.363  
   6.364  void
   6.365  SDL_Quit(void)
   6.366  {
   6.367 -	SDL_bInMainQuit = SDL_TRUE;
   6.368 +    SDL_bInMainQuit = SDL_TRUE;
   6.369 +
   6.370      /* Quit all subsystems */
   6.371  #if defined(__WIN32__)
   6.372      SDL_HelperWindowDestroy();
   6.373 @@ -294,8 +318,12 @@
   6.374      SDL_AssertionsQuit();
   6.375      SDL_LogResetPriorities();
   6.376  
   6.377 -	SDL_memset( SDL_SubsystemRefCount, 0x0, sizeof(SDL_SubsystemRefCount) );
   6.378 -	SDL_bInMainQuit = SDL_FALSE;
   6.379 +    /* Now that every subsystem has been quit, we reset the subsystem refcount
   6.380 +     * and the list of initialized subsystems.
   6.381 +     */
   6.382 +    SDL_memset( SDL_SubsystemRefCount, 0x0, sizeof(SDL_SubsystemRefCount) );
   6.383 +
   6.384 +    SDL_bInMainQuit = SDL_FALSE;
   6.385  }
   6.386  
   6.387  /* Get the library version number */
     7.1 --- a/test/Makefile.in	Tue Feb 12 11:47:29 2013 -0800
     7.2 +++ b/test/Makefile.in	Tue Feb 12 11:47:31 2013 -0800
     7.3 @@ -70,6 +70,7 @@
     7.4  
     7.5  testautomation$(EXE): $(srcdir)/testautomation.c \
     7.6  		      $(srcdir)/testautomation_clipboard.c \
     7.7 +		      $(srcdir)/testautomation_main.c \
     7.8  		      $(srcdir)/testautomation_platform.c \
     7.9  		      $(srcdir)/testautomation_rect.c \
    7.10  		      $(srcdir)/testautomation_render.c \
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/test/testautomation_main.c	Tue Feb 12 11:47:31 2013 -0800
     8.3 @@ -0,0 +1,131 @@
     8.4 +/**
     8.5 + * Automated SDL subsystems management test.
     8.6 + *
     8.7 + * Written by Jørgen Tjernø "jorgenpt"
     8.8 + *
     8.9 + * Released under Public Domain.
    8.10 + */
    8.11 +
    8.12 +#include "SDL.h"
    8.13 +#include "SDL_test.h"
    8.14 +
    8.15 +
    8.16 +/*!
    8.17 + * \brief Tests SDL_Init() and SDL_Quit()
    8.18 + * \sa
    8.19 + * http://wiki.libsdl.org/moin.cgi/SDL_Init
    8.20 + * http://wiki.libsdl.org/moin.cgi/SDL_Quit
    8.21 + */
    8.22 +static int main_testInitQuit (void *arg)
    8.23 +{
    8.24 +    int initialized_subsystems = SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC;
    8.25 +
    8.26 +    SDLTest_AssertCheck( SDL_Init(initialized_subsystems) == 0, "SDL_Init multiple systems." );
    8.27 +
    8.28 +    int enabled_subsystems = SDL_WasInit(initialized_subsystems);
    8.29 +    SDLTest_AssertCheck( enabled_subsystems == initialized_subsystems, "SDL_WasInit(SDL_INIT_EVERYTHING) contains all systems (%i)", enabled_subsystems );
    8.30 +
    8.31 +    SDL_Quit();
    8.32 +
    8.33 +    enabled_subsystems = SDL_WasInit(initialized_subsystems);
    8.34 +    SDLTest_AssertCheck( enabled_subsystems == 0, "SDL_Quit should shut down everything (%i)", enabled_subsystems );
    8.35 +
    8.36 +    return TEST_COMPLETED;
    8.37 +}
    8.38 +
    8.39 +/*!
    8.40 + * \brief Tests SDL_InitSubSystem() and SDL_QuitSubSystem()
    8.41 + * \sa
    8.42 + * http://wiki.libsdl.org/moin.cgi/SDL_Init
    8.43 + * http://wiki.libsdl.org/moin.cgi/SDL_Quit
    8.44 + */
    8.45 +static int main_testInitQuitSubSystem (void *arg)
    8.46 +{
    8.47 +    int i;
    8.48 +    int subsystems[] = { SDL_INIT_JOYSTICK, SDL_INIT_HAPTIC, SDL_INIT_GAMECONTROLLER };
    8.49 +
    8.50 +    for (i = 0; i < SDL_arraysize(subsystems); ++i) {
    8.51 +        int subsystem = subsystems[i];
    8.52 +
    8.53 +        SDLTest_AssertCheck( (SDL_WasInit(subsystem) & subsystem) == 0, "SDL_WasInit(%x) before init should be false", subsystem );
    8.54 +        SDLTest_AssertCheck( SDL_InitSubSystem(subsystem) == 0, "SDL_InitSubSystem(%x)", subsystem );
    8.55 +
    8.56 +        int initialized_system = SDL_WasInit(subsystem);
    8.57 +        SDLTest_AssertCheck( (initialized_system & subsystem) != 0, "SDL_WasInit(%x) should be true (%x)", subsystem, initialized_system );
    8.58 +
    8.59 +        SDL_QuitSubSystem(subsystem);
    8.60 +
    8.61 +        SDLTest_AssertCheck( (SDL_WasInit(subsystem) & subsystem) == 0, "SDL_WasInit(%x) after shutdown should be false", subsystem );
    8.62 +    }
    8.63 +
    8.64 +    return TEST_COMPLETED;
    8.65 +}
    8.66 +
    8.67 +const int joy_and_controller = SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER;
    8.68 +static int main_testImpliedJoystickInit (void *arg)
    8.69 +{
    8.70 +    // First initialize the controller
    8.71 +    SDLTest_AssertCheck( (SDL_WasInit(joy_and_controller) & joy_and_controller) == 0, "SDL_WasInit() before init should be false for joystick & controller" );
    8.72 +    SDLTest_AssertCheck( SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) == 0, "SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER)" );
    8.73 +
    8.74 +    // Then make sure this implicitly initialized the joystick subsystem
    8.75 +    int initialized_system = SDL_WasInit(joy_and_controller);
    8.76 +    SDLTest_AssertCheck( (initialized_system & joy_and_controller) == joy_and_controller, "SDL_WasInit() should be true for joystick & controller (%x)", initialized_system );
    8.77 +
    8.78 +    // Then quit the controller, and make sure that imlicity also quits the
    8.79 +    // joystick subsystem
    8.80 +    SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER);
    8.81 +    initialized_system = SDL_WasInit(joy_and_controller);
    8.82 +    SDLTest_AssertCheck( (initialized_system & joy_and_controller) == 0, "SDL_WasInit() should be false for joystick & controller (%x)", initialized_system );
    8.83 +}
    8.84 +
    8.85 +static int main_testImpliedJoystickQuit (void *arg)
    8.86 +{
    8.87 +    // First initialize the controller and the joystick (explicitly)
    8.88 +    SDLTest_AssertCheck( (SDL_WasInit(joy_and_controller) & joy_and_controller) == 0, "SDL_WasInit() before init should be false for joystick & controller" );
    8.89 +    SDLTest_AssertCheck( SDL_InitSubSystem(SDL_INIT_JOYSTICK) == 0, "SDL_InitSubSystem(SDL_INIT_JOYSTICK)" );
    8.90 +    SDLTest_AssertCheck( SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) == 0, "SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER)" );
    8.91 +
    8.92 +    // Then make sure they're both initialized properly
    8.93 +    int initialized_system = SDL_WasInit(joy_and_controller);
    8.94 +    SDLTest_AssertCheck( (initialized_system & joy_and_controller) == joy_and_controller, "SDL_WasInit() should be true for joystick & controller (%x)", initialized_system );
    8.95 +
    8.96 +    // Then quit the controller, and make sure that it does NOT quit the
    8.97 +    // explicitly initialized joystick subsystem.
    8.98 +    SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER);
    8.99 +    initialized_system = SDL_WasInit(joy_and_controller);
   8.100 +    SDLTest_AssertCheck( (initialized_system & joy_and_controller) == SDL_INIT_JOYSTICK, "SDL_WasInit() should be false for joystick & controller (%x)", initialized_system );
   8.101 +
   8.102 +    SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
   8.103 +}
   8.104 +
   8.105 +static const SDLTest_TestCaseReference mainTest1 =
   8.106 +		{ (SDLTest_TestCaseFp)main_testInitQuit, "main_testInitQuit", "Tests SDL_Init/Quit", TEST_ENABLED};
   8.107 +
   8.108 +static const SDLTest_TestCaseReference mainTest2 =
   8.109 +		{ (SDLTest_TestCaseFp)main_testInitQuitSubSystem, "main_testInitQuitSubSystem", "Tests SDL_InitSubSystem/QuitSubSystem", TEST_ENABLED};
   8.110 +
   8.111 +static const SDLTest_TestCaseReference mainTest3 =
   8.112 +		{ (SDLTest_TestCaseFp)main_testImpliedJoystickInit, "main_testImpliedJoystickInit", "Tests that init for gamecontroller properly implies joystick", TEST_ENABLED};
   8.113 +
   8.114 +static const SDLTest_TestCaseReference mainTest4 =
   8.115 +		{ (SDLTest_TestCaseFp)main_testImpliedJoystickQuit, "main_testImpliedJoystickQuit", "Tests that quit for gamecontroller doesn't quit joystick if you inited it explicitly", TEST_ENABLED};
   8.116 +
   8.117 +/* Sequence of Platform test cases */
   8.118 +static const SDLTest_TestCaseReference *mainTests[] =  {
   8.119 +	&mainTest1,
   8.120 +	&mainTest2,
   8.121 +	&mainTest3,
   8.122 +	&mainTest4,
   8.123 +	NULL
   8.124 +};
   8.125 +
   8.126 +/* Platform test suite (global) */
   8.127 +SDLTest_TestSuiteReference mainTestSuite = {
   8.128 +	"Main",
   8.129 +	NULL,
   8.130 +	mainTests,
   8.131 +	NULL
   8.132 +};
   8.133 +
   8.134 +/* vi: set ts=4 sw=4 expandtab: */
     9.1 --- a/test/testautomation_suites.h	Tue Feb 12 11:47:29 2013 -0800
     9.2 +++ b/test/testautomation_suites.h	Tue Feb 12 11:47:31 2013 -0800
     9.3 @@ -13,6 +13,7 @@
     9.4  extern SDLTest_TestSuiteReference clipboardTestSuite;
     9.5  extern SDLTest_TestSuiteReference eventsTestSuite;
     9.6  extern SDLTest_TestSuiteReference keyboardTestSuite;
     9.7 +extern SDLTest_TestSuiteReference mainTestSuite;
     9.8  extern SDLTest_TestSuiteReference platformTestSuite;
     9.9  extern SDLTest_TestSuiteReference rectTestSuite;
    9.10  extern SDLTest_TestSuiteReference renderTestSuite;
    9.11 @@ -30,6 +31,7 @@
    9.12  	&clipboardTestSuite,
    9.13  	&eventsTestSuite,
    9.14  	&keyboardTestSuite,
    9.15 +	&mainTestSuite,
    9.16  	&platformTestSuite,
    9.17  	&rectTestSuite,
    9.18  	&renderTestSuite,