Nerfed/Nerfed.Runtime/Input/Devices/GamePad.cs

225 lines
7.4 KiB
C#

using SDL2;
namespace Nerfed.Runtime;
public class GamePad
{
private const int maxGamePads = 4;
private static readonly GamePad[] gamePads = new GamePad[maxGamePads];
private static readonly GamePad dummy = new GamePad();
public IntPtr Handle { get; private set; }
public bool IsConnected => Handle != IntPtr.Zero;
public int JoystickInstanceId { get; private set; } = -1;
private readonly ButtonState[] buttonStates;
private readonly ButtonState[] lastButtonStates;
static GamePad()
{
for (int i = 0; i < maxGamePads; i++)
{
gamePads[i] = new GamePad();
}
}
private GamePad()
{
int gamePadButtonCount = Enum.GetValues<GamePadButton>().Length;
buttonStates = new ButtonState[gamePadButtonCount];
lastButtonStates = new ButtonState[gamePadButtonCount];
}
public bool IsButtonDown(GamePadButton button)
{
return buttonStates[(int)button] == ButtonState.Pressed;
}
private void Register(IntPtr handle)
{
Handle = handle;
IntPtr joystickHandle = SDL.SDL_GameControllerGetJoystick(handle);
JoystickInstanceId = SDL.SDL_JoystickInstanceID(joystickHandle);
Log.Info("Controller connected");
}
private void UnRegister()
{
Handle = IntPtr.Zero;
JoystickInstanceId = -1;
Log.Info("Controller disconnected");
}
public static GamePad Get(int slot)
{
return slot >= 0 && slot < maxGamePads ? gamePads[slot] : dummy;
}
internal static void Update()
{
for (int i = 0; i < maxGamePads; i++)
{
GamePad gamePad = gamePads[i];
if (gamePad.IsConnected)
{
Array.Copy(gamePad.buttonStates, gamePad.lastButtonStates, gamePad.buttonStates.Length);
}
}
}
internal static void ProcessEvent(ref SDL.SDL_Event ev)
{
switch (ev.type)
{
case SDL.SDL_EventType.SDL_CONTROLLERDEVICEADDED:
ProcessDeviceAdded(ref ev.cdevice);
break;
case SDL.SDL_EventType.SDL_CONTROLLERDEVICEREMOVED:
ProcessDeviceRemoved(ref ev.cdevice);
break;
case SDL.SDL_EventType.SDL_CONTROLLERBUTTONDOWN:
ProcessButtonDown(ref ev.cbutton);
break;
case SDL.SDL_EventType.SDL_CONTROLLERBUTTONUP:
ProcessButtonUp(ref ev.cbutton);
break;
case SDL.SDL_EventType.SDL_CONTROLLERAXISMOTION:
ProcessAxisMotion(ref ev.caxis);
break;
case SDL.SDL_EventType.SDL_CONTROLLERTOUCHPADDOWN:
ProcessTouchPadDown(ref ev.ctouchpad);
break;
case SDL.SDL_EventType.SDL_CONTROLLERTOUCHPADUP:
ProcessTouchPadUp(ref ev.ctouchpad);
break;
case SDL.SDL_EventType.SDL_CONTROLLERTOUCHPADMOTION:
ProcessTouchPadMotion(ref ev.ctouchpad);
break;
case SDL.SDL_EventType.SDL_CONTROLLERSENSORUPDATE:
ProcessSensorUpdate(ref ev.sensor);
break;
}
}
private static void ProcessDeviceAdded(ref SDL.SDL_ControllerDeviceEvent ev)
{
int index = ev.which;
if (SDL.SDL_IsGameController(index) == SDL.SDL_bool.SDL_TRUE)
{
int slot = -1;
for (int i = 0; i < maxGamePads; i++)
{
if (!gamePads[i].IsConnected)
{
slot = i;
break;
}
}
if (slot == -1)
{
Log.Warning("Too many gamepads connected");
return;
}
IntPtr handle = SDL.SDL_GameControllerOpen(index);
if (handle == IntPtr.Zero)
{
Log.Error($"Failed to open gamepad: {SDL.SDL_GetError()}");
return;
}
gamePads[slot].Register(handle);
}
}
private static GamePad GetByJoystickId(int joystickId)
{
for (int slot = 0; slot < maxGamePads; slot++)
{
GamePad gamePad = gamePads[slot];
if (gamePad.IsConnected && gamePad.JoystickInstanceId == joystickId)
{
return gamePad;
}
}
return null;
}
private static void ProcessDeviceRemoved(ref SDL.SDL_ControllerDeviceEvent ev)
{
GamePad gamePad = GetByJoystickId(ev.which);
gamePad?.UnRegister();
}
private static void ProcessButtonDown(ref SDL.SDL_ControllerButtonEvent ev)
{
if (TryConvertButton(ev.button, out GamePadButton button))
{
GamePad gamePad = GetByJoystickId(ev.which);
gamePad.buttonStates[(int)button] = ButtonState.Pressed;
}
}
private static void ProcessButtonUp(ref SDL.SDL_ControllerButtonEvent ev)
{
if (TryConvertButton(ev.button, out GamePadButton button))
{
GamePad gamePad = GetByJoystickId(ev.which);
gamePad.buttonStates[(int)button] = ButtonState.Released;
}
}
private static void ProcessAxisMotion(ref SDL.SDL_ControllerAxisEvent ev)
{
}
private static void ProcessTouchPadDown(ref SDL.SDL_ControllerTouchpadEvent ev)
{
}
private static void ProcessTouchPadUp(ref SDL.SDL_ControllerTouchpadEvent ev)
{
}
private static void ProcessTouchPadMotion(ref SDL.SDL_ControllerTouchpadEvent ev)
{
}
private static void ProcessSensorUpdate(ref SDL.SDL_SensorEvent ev)
{
}
private static bool TryConvertButton(byte rawButton, out GamePadButton button)
{
GamePadButton? result = rawButton switch
{
(byte)SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_A => GamePadButton.A,
(byte)SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_B => GamePadButton.B,
(byte)SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_X => GamePadButton.X,
(byte)SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_Y => GamePadButton.Y,
(byte)SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_BACK => GamePadButton.Back,
(byte)SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_START => GamePadButton.Start,
(byte)SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_LEFTSTICK => GamePadButton.LeftStick,
(byte)SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_RIGHTSTICK => GamePadButton.RightStick,
(byte)SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_LEFTSHOULDER => GamePadButton.LeftShoulder,
(byte)SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_RIGHTSHOULDER => GamePadButton.RightShoulder,
(byte)SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_DPAD_UP => GamePadButton.DPadUp,
(byte)SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_DPAD_DOWN => GamePadButton.DPadDown,
(byte)SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_DPAD_LEFT => GamePadButton.DPadLeft,
(byte)SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_DPAD_RIGHT => GamePadButton.DPadRight,
(byte)SDL.SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_TOUCHPAD => GamePadButton.BigButton,
_ => null
};
button = result.GetValueOrDefault();
return result.HasValue;
}
}