[SDL2] Fixed joystick hotplugging
authorStefanos A. <stapostol@gmail.com>
Mon, 30 Dec 2013 14:24:48 +0000 (15:24 +0100)
committerStefanos A. <stapostol@gmail.com>
Mon, 30 Dec 2013 14:24:48 +0000 (15:24 +0100)
SDL2 uses a weird system of device ids and instance ids to report
joystick events, where the ADDED event uses a device id and the rest use
instance ids.

The SDL2 joystick driver is now fixed to correctly distinguish between
the two, which fixes hotplugging support for joystick devices.

Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs

index 1d61602..e284cc5 100644 (file)
@@ -56,10 +56,11 @@ namespace OpenTK.Platform.SDL2
             }
         }
 
+        int last_joystick_instance = 0;
         int last_controllers_instance = 0;
 
         readonly List<JoystickDevice> joysticks = new List<JoystickDevice>(4);
-        readonly Dictionary<int, int> sdl_joyid_to_joysticks = new Dictionary<int, int>();
+        readonly Dictionary<int, int> sdl_instanceid_to_joysticks = new Dictionary<int, int>();
         readonly List<Sdl2GamePad> controllers = new List<Sdl2GamePad>(4);
         readonly Dictionary<int, int> sdl_instanceid_to_controllers = new Dictionary<int, int>();
 
@@ -113,6 +114,11 @@ namespace OpenTK.Platform.SDL2
             return id >= 0 && id < joysticks.Count;
         }
 
+        bool IsJoystickInstanceValid(int instance_id)
+        {
+            return sdl_instanceid_to_joysticks.ContainsKey(instance_id);
+        }
+
         bool IsControllerValid(int id)
         {
             return id >= 0 && id < controllers.Count;
@@ -217,28 +223,41 @@ namespace OpenTK.Platform.SDL2
                         IntPtr handle = SDL.JoystickOpen(id);
                         if (handle != IntPtr.Zero)
                         {
+                            int device_id = id;
+                            int instance_id = last_joystick_instance++;
+
                             JoystickDevice<Sdl2JoystickDetails> joystick = OpenJoystick(id);
                             if (joystick != null)
                             {
                                 joystick.Details.IsConnected = true;
-                                if (!sdl_joyid_to_joysticks.ContainsKey(id))
+                                if (device_id < joysticks.Count)
+                                {
+                                    joysticks[device_id] = joystick;
+                                }
+                                else
                                 {
-                                    sdl_joyid_to_joysticks.Add(id, joysticks.Count);
-                                    joysticks.Add(null);
+                                    joysticks.Add(joystick);
                                 }
-                                int index = sdl_joyid_to_joysticks[id];
-                                joysticks[index] = joystick;
+
+                                sdl_instanceid_to_joysticks.Add(instance_id, device_id);
                             }
                         }
                     }
                     break;
 
                 case EventType.JOYDEVICEREMOVED:
+                    if (IsJoystickInstanceValid(id))
                     {
-                        int index = sdl_joyid_to_joysticks[id];
-                        JoystickDevice<Sdl2JoystickDetails> joystick = (JoystickDevice<Sdl2JoystickDetails>)joysticks[index];
+                        int instance_id = sdl_instanceid_to_joysticks[id];
+                        sdl_instanceid_to_joysticks.Remove(id);
+
+                        JoystickDevice<Sdl2JoystickDetails> joystick = (JoystickDevice<Sdl2JoystickDetails>)joysticks[instance_id];
                         joystick.Details.IsConnected = false;
                     }
+                    else
+                    {
+                        Debug.Print("[SDL2] Invalid joystick id {0} in {1}", id, ev.Type);
+                    }
                     break;
             }
         }
@@ -246,9 +265,9 @@ namespace OpenTK.Platform.SDL2
         public void ProcessJoystickEvent(JoyAxisEvent ev)
         {
             int id = ev.Which;
-            if (IsJoystickValid(id))
+            if (IsJoystickInstanceValid(id))
             {
-                int index = sdl_joyid_to_joysticks[id];
+                int index = sdl_instanceid_to_joysticks[id];
                 JoystickDevice<Sdl2JoystickDetails> joystick = (JoystickDevice<Sdl2JoystickDetails>)joysticks[index];
                 float value = ev.Value * RangeMultiplier;
                 joystick.SetAxis((JoystickAxis)ev.Axis, value);
@@ -262,9 +281,9 @@ namespace OpenTK.Platform.SDL2
         public void ProcessJoystickEvent(JoyBallEvent ev)
         {
             int id = ev.Which;
-            if (IsJoystickValid(id))
+            if (IsJoystickInstanceValid(id))
             {
-                int index = sdl_joyid_to_joysticks[id];
+                int index = sdl_instanceid_to_joysticks[id];
                 JoystickDevice<Sdl2JoystickDetails> joystick = (JoystickDevice<Sdl2JoystickDetails>)joysticks[index];
                 // Todo: does it make sense to support balls?
             }
@@ -277,9 +296,9 @@ namespace OpenTK.Platform.SDL2
         public void ProcessJoystickEvent(JoyButtonEvent ev)
         {
             int id = ev.Which;
-            if (IsJoystickValid(id))
+            if (IsJoystickInstanceValid(id))
             {
-                int index = sdl_joyid_to_joysticks[id];
+                int index = sdl_instanceid_to_joysticks[id];
                 JoystickDevice<Sdl2JoystickDetails> joystick = (JoystickDevice<Sdl2JoystickDetails>)joysticks[index];
                 joystick.SetButton((JoystickButton)ev.Button, ev.State == State.Pressed);
             }
@@ -292,9 +311,9 @@ namespace OpenTK.Platform.SDL2
         public void ProcessJoystickEvent(JoyHatEvent ev)
         {
             int id = ev.Which;
-            if (IsJoystickValid(id))
+            if (IsJoystickInstanceValid(id))
             {
-                int index = sdl_joyid_to_joysticks[id];
+                int index = sdl_instanceid_to_joysticks[id];
                 JoystickDevice<Sdl2JoystickDetails> joystick = (JoystickDevice<Sdl2JoystickDetails>)joysticks[index];
                 // Todo: map hat to an extra axis
             }
@@ -307,6 +326,11 @@ namespace OpenTK.Platform.SDL2
         public void ProcessControllerEvent(ControllerDeviceEvent ev)
         {
             int id = ev.Which;
+            if (id < 0)
+            {
+                Debug.Print("[SDL2] Invalid controller id {0} in {1}", id, ev.Type);
+                return;
+            }
 
             switch (ev.Type)
             {
@@ -355,18 +379,26 @@ namespace OpenTK.Platform.SDL2
                     break;
 
                 case EventType.CONTROLLERDEVICEREMOVED:
+                    if (IsControllerInstanceValid(id))
                     {
-                        int instance_id = id;
-                        if (IsControllerInstanceValid(id))
-                        {
-                            controllers[id].State.SetConnected(false);
-                            sdl_instanceid_to_controllers.Remove(instance_id);
-                        }
-                        break;
+                        controllers[id].State.SetConnected(false);
+                        sdl_instanceid_to_controllers.Remove(id);
+                    }
+                    else
+                    {
+                        Debug.Print("[SDL2] Invalid game controller instance {0} in {1}", id, ev.Type);
                     }
+                    break;
 
                 case EventType.CONTROLLERDEVICEREMAPPED:
-                    // Todo: what should we do in this case?
+                    if (IsControllerInstanceValid(id))
+                    {
+                        // Todo: what should we do in this case?
+                    }
+                    else
+                    {
+                        Debug.Print("[SDL2] Invalid game controller instance {0} in {1}", id, ev.Type);
+                    }
                     break;
             }
          }
@@ -453,7 +485,7 @@ namespace OpenTK.Platform.SDL2
             if (IsJoystickValid(index))
             {
                 JoystickDevice<Sdl2JoystickDetails> joystick =
-                    (JoystickDevice<Sdl2JoystickDetails>)joysticks[sdl_joyid_to_joysticks[index]];
+                    (JoystickDevice<Sdl2JoystickDetails>)joysticks[index];
 
                 for (int i = 0; i < joystick.Axis.Count; i++)
                 {
@@ -465,7 +497,7 @@ namespace OpenTK.Platform.SDL2
                     state.SetButton(JoystickButton.Button0 + i, joystick.Button[i]);
                 }
 
-                state.SetIsConnected(true);
+                state.SetIsConnected(joystick.Details.IsConnected);
             }
 
             return state;
@@ -476,12 +508,12 @@ namespace OpenTK.Platform.SDL2
             if (IsJoystickValid(index))
             {
                 JoystickDevice<Sdl2JoystickDetails> joystick =
-                    (JoystickDevice<Sdl2JoystickDetails>)joysticks[sdl_joyid_to_joysticks[index]];
+                    (JoystickDevice<Sdl2JoystickDetails>)joysticks[index];
 
                 return new JoystickCapabilities(
                     joystick.Axis.Count,
                     joystick.Button.Count,
-                    true);
+                    joystick.Details.IsConnected);
             }
             return new JoystickCapabilities();
         }