using SDL2; using System; namespace MoonWorks.Input { /// /// The main container class for all input tracking. /// Your Game class will automatically have a reference to this class. /// public class Inputs { public const int MAX_GAMEPADS = 4; /// /// The reference to the Keyboard input abstraction. /// public Keyboard Keyboard { get; } /// /// The reference to the Mouse input abstraction. /// public Mouse Mouse { get; } Gamepad[] Gamepads; public static event Action TextInput; /// /// True if any input on any input device is active. Useful for input remapping. /// public bool AnyPressed { get; private set; } /// /// Contains a reference to an arbitrary VirtualButton that was pressed this frame. Useful for input remapping. /// public VirtualButton AnyPressedButton { get; private set; } public delegate void OnGamepadConnectedFunc(int slot); /// /// Called when a gamepad has been connected. /// /// The slot where the connection occurred. public OnGamepadConnectedFunc OnGamepadConnected = delegate { }; public delegate void OnGamepadDisconnectedFunc(int slot); /// /// Called when a gamepad has been disconnected. /// /// The slot where the disconnection occurred. public OnGamepadDisconnectedFunc OnGamepadDisconnected = delegate { }; internal Inputs() { Keyboard = new Keyboard(); Mouse = new Mouse(); Gamepads = new Gamepad[MAX_GAMEPADS]; // initialize dummy controllers for (var slot = 0; slot < MAX_GAMEPADS; slot += 1) { Gamepads[slot] = new Gamepad(IntPtr.Zero, slot); } } // Assumes that SDL_PumpEvents has been called! internal void Update() { AnyPressed = false; AnyPressedButton = default; // DeviceKind.None Keyboard.Update(); if (Keyboard.AnyPressed) { AnyPressed = true; AnyPressedButton = Keyboard.AnyPressedButton; } Mouse.Update(); if (Mouse.AnyPressed) { AnyPressed = true; AnyPressedButton = Mouse.AnyPressedButton; } foreach (var gamepad in Gamepads) { gamepad.Update(); if (gamepad.AnyPressed) { AnyPressed = true; AnyPressedButton = gamepad.AnyPressedButton; } } } /// /// Returns true if a gamepad is currently connected in the given slot. /// /// Range: 0-3 /// public bool GamepadExists(int slot) { if (slot < 0 || slot >= MAX_GAMEPADS) { return false; } return !Gamepads[slot].IsDummy; } /// /// Gets a gamepad associated with the given slot. /// The first n slots are guaranteed to occupied with gamepads if they are connected. /// If a gamepad does not exist for the given slot, a dummy object with all inputs in default state will be returned. /// You can check if a gamepad is connected in a slot with the GamepadExists function. /// /// Range: 0-3 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)) { var openResult = SDL.SDL_GameControllerOpen(index); if (openResult == 0) { Logger.LogError("Error opening gamepad!"); Logger.LogError(SDL.SDL_GetError()); } else { Gamepads[slot].Register(openResult); Logger.LogInfo($"Gamepad added to slot {slot}!"); if (OnGamepadConnected != null) { OnGamepadConnected(slot); } } return; } } Logger.LogInfo("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].Unregister(); Logger.LogInfo($"Removing gamepad from slot {slot}!"); OnGamepadDisconnected(slot); return; } } } internal static void OnTextInput(char c) { if (TextInput != null) { TextInput(c); } } } }