Fixed crashes in new joystick code
authorSam Lantinga <slouken@libsdl.org>
Wed, 28 Nov 2012 11:52:38 -0800
changeset 6712 05f046f5886b
parent 6711 e6355923901d
child 6713 fa17a97389ed
Fixed crashes in new joystick code
src/joystick/SDL_joystick.c
src/joystick/windows/SDL_dxjoystick.c
--- a/src/joystick/SDL_joystick.c	Tue Nov 27 21:40:46 2012 -0800
+++ b/src/joystick/SDL_joystick.c	Wed Nov 28 11:52:38 2012 -0800
@@ -30,7 +30,8 @@
 #include "../events/SDL_events_c.h"
 #endif
 
-SDL_Joystick *SDL_joysticks = NULL;
+static SDL_Joystick *SDL_joysticks = NULL;
+static SDL_Joystick *SDL_updating_joystick = NULL;
 
 int
 SDL_JoystickInit(void)
@@ -386,6 +387,10 @@
         return;
     }
 
+    if (joystick == SDL_updating_joystick) {
+        return;
+    }
+
     SDL_SYS_JoystickClose(joystick);
 	
 	joysticklist = SDL_joysticks;
@@ -432,6 +437,9 @@
 void
 SDL_JoystickQuit(void)
 {
+    /* Make sure we're not getting called in the middle of updating joysticks */
+    SDL_assert(!SDL_updating_joystick);
+
     /* Stop the event polling */
 	while ( SDL_joysticks )
 	{
@@ -588,6 +596,8 @@
 		 */
 		joysticknext = joystick->next;
 
+        SDL_updating_joystick = joystick;
+
         SDL_SYS_JoystickUpdate( joystick );
 
 		if ( joystick->closed && joystick->uncentered )
@@ -595,7 +605,7 @@
 			int i;
 			joystick->uncentered = 0;
 
-            // Tell the app that everything is centered/unpressed... 
+            /* Tell the app that everything is centered/unpressed...  */
             for (i = 0; i < joystick->naxes; i++)
                 SDL_PrivateJoystickAxis(joystick, i, 0);
 
@@ -604,8 +614,16 @@
 
             for (i = 0; i < joystick->nhats; i++)
                 SDL_PrivateJoystickHat(joystick, i, SDL_HAT_CENTERED);
+
 		}
 
+        SDL_updating_joystick = NULL;
+
+        /* If the joystick was closed while updating, free it here */
+        if ( joystick->ref_count <= 0 ) {
+            SDL_JoystickClose(joystick);
+        }
+
 		joystick = joysticknext;
 	}
 
--- a/src/joystick/windows/SDL_dxjoystick.c	Tue Nov 27 21:40:46 2012 -0800
+++ b/src/joystick/windows/SDL_dxjoystick.c	Wed Nov 28 11:52:38 2012 -0800
@@ -498,24 +498,6 @@
 }
 
 
-/*  helper func to create a hidden, message only window for the joystick detect thread
- */
-HWND CreateHiddenJoystickDetectWindow() {
-	WNDCLASSEX wincl;
-	HWND hMessageWindow = 0;
-	SDL_memset( &wincl, 0x0, sizeof(wincl) );
-	wincl.hInstance = GetModuleHandle( NULL );
-	wincl.lpszClassName = L"Message";
-	wincl.lpfnWndProc = SDL_PrivateJoystickDetectProc;      // This function is called by windows
-	wincl.cbSize = sizeof (WNDCLASSEX);
-
-	if (!RegisterClassEx (&wincl))
-		return 0;
-
-	hMessageWindow = (HWND)CreateWindowEx( 0,  L"Message", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL );
-	return hMessageWindow;
-}
-
 DEFINE_GUID(GUID_DEVINTERFACE_USB_DEVICE, 0xA5DCBF10L, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, \
 	0xC0, 0x4F, 0xB9, 0x51, 0xED);
 
@@ -529,12 +511,26 @@
 	HDEVNOTIFY hNotify = 0;
 	DEV_BROADCAST_DEVICEINTERFACE dbh;
 	SDL_bool bOpenedXInputDevices[4];
+	WNDCLASSEX wincl;
 
 	SDL_memset( bOpenedXInputDevices, 0x0, sizeof(bOpenedXInputDevices) );
 
 	result = WIN_CoInitialize();
 
-	messageWindow = CreateHiddenJoystickDetectWindow();
+	SDL_memset( &wincl, 0x0, sizeof(wincl) );
+	wincl.hInstance = GetModuleHandle( NULL );
+	wincl.lpszClassName = L"Message";
+	wincl.lpfnWndProc = SDL_PrivateJoystickDetectProc;      // This function is called by windows
+	wincl.cbSize = sizeof (WNDCLASSEX);
+
+	if (!RegisterClassEx (&wincl))
+	{		
+		SDL_SetError("Failed to create register class for joystick autodetect.",
+		GetLastError());
+		return -1;
+	}
+
+	messageWindow = (HWND)CreateWindowEx( 0,  L"Message", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL );
 	if ( !messageWindow )
 	{
 		SDL_SetError("Failed to create message window for joystick autodetect.",
@@ -611,6 +607,8 @@
 
 	if ( messageWindow )
 		DestroyWindow( messageWindow );
+
+	UnregisterClass( wincl.lpszClassName, wincl.hInstance );
 	messageWindow = 0;
 	WIN_CoUninitialize();
 	return 1;
@@ -720,22 +718,25 @@
 	EnumJoysticksCallback(const DIDEVICEINSTANCE * pdidInstance, VOID * pContext)
 {
 	JoyStick_DeviceData *pNewJoystick;
+	JoyStick_DeviceData *pPrevJoystick = NULL;
 	SDL_bool bXInputDevice;
 	pNewJoystick = *(JoyStick_DeviceData **)pContext;
 	while ( pNewJoystick )
 	{
 		if ( !SDL_memcmp( &pNewJoystick->dxdevice.guidInstance, &pdidInstance->guidInstance, sizeof(pNewJoystick->dxdevice.guidInstance) ) )
 		{
-			if ( SYS_Joystick )
-			{
-				pNewJoystick->pNext = SYS_Joystick;
-			}
-			SYS_Joystick = pNewJoystick;
 			/* if we are replacing the front of the list then update it */
 			if ( pNewJoystick == *(JoyStick_DeviceData **)pContext ) 
 			{
 				*(JoyStick_DeviceData **)pContext = pNewJoystick->pNext;
 			}
+			else if ( pPrevJoystick )
+			{
+				pPrevJoystick->pNext = pNewJoystick->pNext;
+			}
+
+			pNewJoystick->pNext = SYS_Joystick;
+			SYS_Joystick = pNewJoystick;
 
 			s_pKnownJoystickGUIDs[ s_iNewGUID ] = pdidInstance->guidInstance;
 			s_iNewGUID++;
@@ -745,6 +746,7 @@
 				return DIENUM_STOP; 
 		}
 
+		pPrevJoystick = pNewJoystick;
 		pNewJoystick = pNewJoystick->pNext;
 	}
 
@@ -756,17 +758,17 @@
 
 	if ( bXInputDevice )
 	{
-		SDL_memset(&(pNewJoystick->dxdevice), 0x0,
-			sizeof(DIDEVICEINSTANCE));
 		pNewJoystick->bXInputDevice = SDL_TRUE;
 		pNewJoystick->XInputUserId = INVALID_XINPUT_USERID;
 	}
 	else
 	{
 		pNewJoystick->bXInputDevice = SDL_FALSE;
-		SDL_memcpy(&(pNewJoystick->dxdevice), pdidInstance,
-			sizeof(DIDEVICEINSTANCE));
 	}
+	
+	SDL_memcpy(&(pNewJoystick->dxdevice), pdidInstance,
+		sizeof(DIDEVICEINSTANCE));
+
 	pNewJoystick->joystickname = WIN_StringToUTF8(pdidInstance->tszProductName);
 	pNewJoystick->send_add_event = 1;
 	pNewJoystick->nInstanceID = ++s_nInstanceID;