From f6fc80804ede9aad905dd554acf5fb3675f958c0 Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Wed, 13 Jul 2022 11:53:09 -0700 Subject: [PATCH] improve gamepad init and support hotplugging --- src/Game.cs | 24 ++++++++++ src/Input/Axis.cs | 9 ++-- src/Input/Gamepad.cs | 53 +++++++++++++---------- src/Input/Input.cs | 47 ++++++++++++++++---- src/Input/Trigger.cs | 9 ++-- src/Input/VirtualButtons/GamepadButton.cs | 12 +++-- 6 files changed, 104 insertions(+), 50 deletions(-) diff --git a/src/Game.cs b/src/Game.cs index acc1f17f..173b8297 100644 --- a/src/Game.cs +++ b/src/Game.cs @@ -205,6 +205,14 @@ namespace MoonWorks case SDL.SDL_EventType.SDL_DROPFILE: HandleFileDrop(_event); break; + + case SDL.SDL_EventType.SDL_CONTROLLERDEVICEADDED: + HandleControllerAdded(_event); + break; + + case SDL.SDL_EventType.SDL_CONTROLLERDEVICEREMOVED: + HandleControllerRemoved(_event); + break; } } } @@ -244,6 +252,22 @@ namespace MoonWorks DropFile(filePath); } + private void HandleControllerAdded(SDL.SDL_Event evt) + { + var index = evt.cdevice.which; + if (SDL.SDL_IsGameController(index) == SDL.SDL_bool.SDL_TRUE) + { + System.Console.WriteLine($"New controller detected!"); + Inputs.AddGamepad(index); + } + } + + private void HandleControllerRemoved(SDL.SDL_Event evt) + { + System.Console.WriteLine($"Controller removal detected!"); + Inputs.RemoveGamepad(evt.cdevice.which); + } + private TimeSpan AdvanceElapsedTime() { long currentTicks = gameTimer.Elapsed.Ticks; diff --git a/src/Input/Axis.cs b/src/Input/Axis.cs index 3fba7bca..e358c384 100644 --- a/src/Input/Axis.cs +++ b/src/Input/Axis.cs @@ -1,4 +1,3 @@ -using System; using MoonWorks.Math; using SDL2; @@ -6,7 +5,7 @@ namespace MoonWorks.Input { public class Axis { - IntPtr GamepadHandle; + public Gamepad Parent { get; } SDL.SDL_GameControllerAxis SDL_Axis; public AxisCode Code { get; private set; } @@ -17,11 +16,11 @@ namespace MoonWorks.Input public float Value { get; private set; } public Axis( - IntPtr gamepadHandle, + Gamepad parent, AxisCode code, SDL.SDL_GameControllerAxis sdlAxis ) { - GamepadHandle = gamepadHandle; + Parent = parent; SDL_Axis = sdlAxis; Code = code; } @@ -29,7 +28,7 @@ namespace MoonWorks.Input internal void Update() { Value = MathHelper.Normalize( - SDL.SDL_GameControllerGetAxis(GamepadHandle, SDL_Axis), + SDL.SDL_GameControllerGetAxis(Parent.Handle, SDL_Axis), short.MinValue, short.MaxValue, -1, 1 ); diff --git a/src/Input/Gamepad.cs b/src/Input/Gamepad.cs index ec01761e..e6d15d9a 100644 --- a/src/Input/Gamepad.cs +++ b/src/Input/Gamepad.cs @@ -8,7 +8,9 @@ namespace MoonWorks.Input public class Gamepad { internal IntPtr Handle; - internal int Index; + internal int JoystickInstanceID; + + public int Slot { get; internal set; } public GamepadButton A { get; } public GamepadButton B { get; } @@ -61,37 +63,40 @@ namespace MoonWorks.Input private VirtualButton[] VirtualButtons; - internal Gamepad(IntPtr handle, int index) + internal Gamepad(IntPtr handle, int slot) { Handle = handle; - Index = index; + Slot = slot; + + IntPtr joystickHandle = SDL.SDL_GameControllerGetJoystick(Handle); + JoystickInstanceID = SDL.SDL_JoystickInstanceID(joystickHandle); AnyPressed = false; - A = new GamepadButton(handle, GamepadButtonCode.A, SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_A); - B = new GamepadButton(handle, GamepadButtonCode.B, SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_B); - X = new GamepadButton(handle, GamepadButtonCode.X, SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_X); - Y = new GamepadButton(handle, GamepadButtonCode.Y, SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_Y); + A = new GamepadButton(this, GamepadButtonCode.A, SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_A); + B = new GamepadButton(this, GamepadButtonCode.B, SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_B); + X = new GamepadButton(this, GamepadButtonCode.X, SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_X); + Y = new GamepadButton(this, GamepadButtonCode.Y, SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_Y); - Back = new GamepadButton(handle, GamepadButtonCode.Back, SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_BACK); - Guide = new GamepadButton(handle, GamepadButtonCode.Guide, SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_GUIDE); - Start = new GamepadButton(handle, GamepadButtonCode.Start, SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_START); + Back = new GamepadButton(this, GamepadButtonCode.Back, SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_BACK); + Guide = new GamepadButton(this, GamepadButtonCode.Guide, SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_GUIDE); + Start = new GamepadButton(this, GamepadButtonCode.Start, SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_START); - LeftStick = new GamepadButton(handle, GamepadButtonCode.LeftStick, SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_LEFTSTICK); - RightStick = new GamepadButton(handle, GamepadButtonCode.RightStick, SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_RIGHTSTICK); + LeftStick = new GamepadButton(this, GamepadButtonCode.LeftStick, SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_LEFTSTICK); + RightStick = new GamepadButton(this, GamepadButtonCode.RightStick, SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_RIGHTSTICK); - LeftShoulder = new GamepadButton(handle, GamepadButtonCode.LeftShoulder, SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_LEFTSHOULDER); - RightShoulder = new GamepadButton(handle, GamepadButtonCode.RightShoulder, SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_RIGHTSHOULDER); + LeftShoulder = new GamepadButton(this, GamepadButtonCode.LeftShoulder, SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_LEFTSHOULDER); + RightShoulder = new GamepadButton(this, GamepadButtonCode.RightShoulder, SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_RIGHTSHOULDER); - DpadUp = new GamepadButton(handle, GamepadButtonCode.DpadUp, SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_DPAD_UP); - DpadDown = new GamepadButton(handle, GamepadButtonCode.DpadDown, SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_DPAD_DOWN); - DpadLeft = new GamepadButton(handle, GamepadButtonCode.DpadLeft, SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_DPAD_LEFT); - DpadRight = new GamepadButton(handle, GamepadButtonCode.DpadRight, SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_DPAD_RIGHT); + DpadUp = new GamepadButton(this, GamepadButtonCode.DpadUp, SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_DPAD_UP); + DpadDown = new GamepadButton(this, GamepadButtonCode.DpadDown, SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_DPAD_DOWN); + DpadLeft = new GamepadButton(this, GamepadButtonCode.DpadLeft, SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_DPAD_LEFT); + DpadRight = new GamepadButton(this, GamepadButtonCode.DpadRight, SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_DPAD_RIGHT); - LeftX = new Axis(handle, AxisCode.LeftX, SDL.SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_LEFTX); - LeftY = new Axis(handle, AxisCode.LeftY, SDL.SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_LEFTY); - RightX = new Axis(handle, AxisCode.RightX, SDL.SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_RIGHTX); - RightY = new Axis(handle, AxisCode.RightY, SDL.SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_RIGHTY); + LeftX = new Axis(this, AxisCode.LeftX, SDL.SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_LEFTX); + LeftY = new Axis(this, AxisCode.LeftY, SDL.SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_LEFTY); + RightX = new Axis(this, AxisCode.RightX, SDL.SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_RIGHTX); + RightY = new Axis(this, AxisCode.RightY, SDL.SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_RIGHTY); LeftXLeft = new AxisButton(LeftX, false); LeftXRight = new AxisButton(LeftX, true); @@ -103,8 +108,8 @@ namespace MoonWorks.Input RightYUp = new AxisButton(RightY, false); RightYDown = new AxisButton(RightY, true); - TriggerLeft = new Trigger(handle, TriggerCode.Left, SDL.SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_TRIGGERLEFT); - TriggerRight = new Trigger(handle, TriggerCode.Right, SDL.SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_TRIGGERRIGHT); + TriggerLeft = new Trigger(this, TriggerCode.Left, SDL.SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_TRIGGERLEFT); + TriggerRight = new Trigger(this, TriggerCode.Right, SDL.SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_TRIGGERRIGHT); TriggerLeftButton = new TriggerButton(TriggerLeft); TriggerRightButton = new TriggerButton(TriggerRight); diff --git a/src/Input/Input.cs b/src/Input/Input.cs index 739a641f..44029240 100644 --- a/src/Input/Input.cs +++ b/src/Input/Input.cs @@ -24,16 +24,10 @@ namespace MoonWorks.Input gamepads = new Gamepad[MAX_GAMEPADS]; - for (var i = 0; i < 4; i += 1) + // initialize dummy controllers + for (var slot = 0; slot < MAX_GAMEPADS; slot += 1) { - if (SDL.SDL_IsGameController(i) == SDL.SDL_bool.SDL_TRUE) - { - gamepads[i] = new Gamepad(SDL.SDL_GameControllerOpen(i), i); - } - else - { - gamepads[i] = new Gamepad(IntPtr.Zero, -1); - } + gamepads[slot] = new Gamepad(IntPtr.Zero, slot); } } @@ -74,14 +68,49 @@ namespace MoonWorks.Input public bool GamepadExists(int slot) { + if (slot < 0 || slot >= MAX_GAMEPADS) + { + return false; + } + return !gamepads[slot].IsDummy; } + // From 0-4 public Gamepad GetGamepad(int slot) { return gamepads[slot]; } + internal void AddGamepad(int index) + { + for (var slot = 0; slot < MAX_GAMEPADS; slot += 1) + { + if (!GamepadExists(slot)) + { + gamepads[slot].Handle = SDL.SDL_GameControllerOpen(index); + System.Console.WriteLine($"Gamepad added to slot {slot}!"); + return; + } + } + + System.Console.WriteLine("Too many gamepads already!"); + } + + internal void RemoveGamepad(int joystickInstanceID) + { + for (int slot = 0; slot < MAX_GAMEPADS; slot += 1) + { + if (joystickInstanceID == gamepads[slot].JoystickInstanceID) + { + SDL.SDL_GameControllerClose(gamepads[slot].Handle); + gamepads[slot].Handle = IntPtr.Zero; + System.Console.WriteLine($"Removing gamepad from slot {slot}!"); + return; + } + } + } + internal static void OnTextInput(char c) { if (TextInput != null) diff --git a/src/Input/Trigger.cs b/src/Input/Trigger.cs index 4a8c826b..dd5ba0e1 100644 --- a/src/Input/Trigger.cs +++ b/src/Input/Trigger.cs @@ -1,4 +1,3 @@ -using System; using MoonWorks.Math; using SDL2; @@ -6,7 +5,7 @@ namespace MoonWorks.Input { public class Trigger { - public IntPtr GamepadHandle; + public Gamepad Parent { get; } public SDL.SDL_GameControllerAxis SDL_Axis; public TriggerCode Code { get; } @@ -17,11 +16,11 @@ namespace MoonWorks.Input public float Value { get; private set; } public Trigger( - IntPtr gamepadHandle, + Gamepad parent, TriggerCode code, SDL.SDL_GameControllerAxis sdlAxis ) { - GamepadHandle = gamepadHandle; + Parent = parent; Code = code; SDL_Axis = sdlAxis; } @@ -29,7 +28,7 @@ namespace MoonWorks.Input internal void Update() { Value = MathHelper.Normalize( - SDL.SDL_GameControllerGetAxis(GamepadHandle, SDL_Axis), + SDL.SDL_GameControllerGetAxis(Parent.Handle, SDL_Axis), 0, short.MaxValue, 0, 1 ); diff --git a/src/Input/VirtualButtons/GamepadButton.cs b/src/Input/VirtualButtons/GamepadButton.cs index 344ee5b4..33c56bd1 100644 --- a/src/Input/VirtualButtons/GamepadButton.cs +++ b/src/Input/VirtualButtons/GamepadButton.cs @@ -1,25 +1,23 @@ -using System; using SDL2; namespace MoonWorks.Input { public class GamepadButton : VirtualButton { - IntPtr GamepadHandle; + public Gamepad Parent { get; } SDL.SDL_GameControllerButton SDL_Button; + public GamepadButtonCode Code { get; } - public GamepadButtonCode Code { get; private set; } - - internal GamepadButton(IntPtr gamepadHandle, GamepadButtonCode code, SDL.SDL_GameControllerButton sdlButton) + internal GamepadButton(Gamepad parent, GamepadButtonCode code, SDL.SDL_GameControllerButton sdlButton) { - GamepadHandle = gamepadHandle; + Parent = parent; Code = code; SDL_Button = sdlButton; } internal override bool CheckPressed() { - return MoonWorks.Conversions.ByteToBool(SDL.SDL_GameControllerGetButton(GamepadHandle, SDL_Button)); + return MoonWorks.Conversions.ByteToBool(SDL.SDL_GameControllerGetButton(Parent.Handle, SDL_Button)); } } }