225 lines
7.4 KiB
C#
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;
|
||
|
}
|
||
|
}
|