diff --git a/BrickController2/BrickController2.Android/Extensions/InputDeviceExtensions.cs b/BrickController2/BrickController2.Android/Extensions/InputDeviceExtensions.cs deleted file mode 100644 index 1ddd31af..00000000 --- a/BrickController2/BrickController2.Android/Extensions/InputDeviceExtensions.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Android.Views; - -namespace BrickController2.Droid.Extensions; - -public static class InputDeviceExtensions -{ - /// - /// Get an unique persistant identifier string for the given inputdevice - /// - /// inputdevice - /// deviceidentifier - public static string GetUniquePersistentDeviceId(this InputDevice? inputDevice) => - // https://developer.android.com/develop/ui/views/touch-and-input/game-controllers/multiple-controllers - // Note: On devices running Android 4.1(API level 16) and higher, you can obtain an input device’s descriptor using getDescriptor(), which returns a unique persistent - // string value for the input device.Unlike a device ID, the descriptor value won't change even if the input device is disconnected, reconnected, or reconfigured. - inputDevice?.Descriptor ?? "NoDescriptor"; -} diff --git a/BrickController2/BrickController2.Android/PlatformServices/GameController/GameControllerService.cs b/BrickController2/BrickController2.Android/PlatformServices/GameController/GameControllerService.cs index 7e2f2988..f92642c2 100644 --- a/BrickController2/BrickController2.Android/PlatformServices/GameController/GameControllerService.cs +++ b/BrickController2/BrickController2.Android/PlatformServices/GameController/GameControllerService.cs @@ -7,7 +7,7 @@ namespace BrickController2.Droid.PlatformServices.GameController { - internal class GameControllerService : GameControllerServiceBase + internal class GameControllerService : GameControllerServiceBase { private readonly InputManager _inputManager; @@ -36,9 +36,9 @@ internal void MainActivityOnInputDeviceAdded(int deviceId) /// deviceId of InputDevice internal void MainActivityOnInputDeviceRemoved(int deviceId) { - if (TryRemove(x => x.Gamepad.Id == deviceId, out var controller)) + if (TryRemove(x => x.ControllerDevice.Id == deviceId, out var controller)) { - _logger.LogInformation("Gamepad has been removed DeviceId:{id}, ControllerId:{controllerId}", + _logger.LogInformation("ControllerDevice has been removed DeviceId:{id}, ControllerId:{controllerId}", deviceId, controller.ControllerId); } } @@ -64,14 +64,14 @@ internal void MainActivityOnInputDeviceChanged(int deviceId) else if (controller != null) { // handle change - remove and then add it again - TryRemove(x => x.Gamepad.Id == deviceId, out _); + TryRemove(x => x.ControllerDevice.Id == deviceId, out _); } AddGameControllerDevice(device); } } - else if (TryRemove(x => x.Gamepad.Id == deviceId, out var controller)) + else if (TryRemove(x => x.ControllerDevice.Id == deviceId, out var controller)) { - _logger.LogInformation("Gamepad has been removed DeviceId:{id}, ControllerId:{controllerId}", + _logger.LogInformation("ControllerDevice has been removed DeviceId:{id}, ControllerId:{controllerId}", deviceId, controller.ControllerId); } } @@ -122,7 +122,7 @@ private void AddGameControllerDevice(InputDevice gamepad) } private bool TryGetControllerByDeviceId(int deviceId, [MaybeNullWhen(false)] out GamepadController controller) - => TryGetController(x => x.Gamepad.Id == deviceId, out controller); + => TryGetController(x => x.ControllerDevice.Id == deviceId, out controller); private static bool TryGetGamepadDevice(int deviceId, [MaybeNullWhen(false)] out InputDevice device) { diff --git a/BrickController2/BrickController2.Android/PlatformServices/GameController/GamepadController.cs b/BrickController2/BrickController2.Android/PlatformServices/GameController/GamepadController.cs index 2e0a3e19..01240f4d 100644 --- a/BrickController2/BrickController2.Android/PlatformServices/GameController/GamepadController.cs +++ b/BrickController2/BrickController2.Android/PlatformServices/GameController/GamepadController.cs @@ -1,5 +1,4 @@ using Android.Views; -using BrickController2.Droid.Extensions; using BrickController2.PlatformServices.GameController; using System; using System.Collections.Generic; @@ -25,11 +24,8 @@ public GamepadController(GameControllerService service, InputDevice gamePad) { // initialize properties Name = GetDisplayName(gamePad); - VendorId = gamePad.VendorId; - ProductId = gamePad.ProductId; ControllerNumber = gamePad.ControllerNumber; ControllerId = GetControllerIdFromNumber(gamePad.ControllerNumber); - UniquePersistantDeviceId = gamePad.GetUniquePersistentDeviceId(); } internal bool OnButtonEvent(KeyEvent e, float buttonValue) diff --git a/BrickController2/BrickController2.WinUI/PlatformServices/GameController/GameControllerService.cs b/BrickController2/BrickController2.WinUI/PlatformServices/GameController/GameControllerService.cs index 296a37ec..f65b70b7 100644 --- a/BrickController2/BrickController2.WinUI/PlatformServices/GameController/GameControllerService.cs +++ b/BrickController2/BrickController2.WinUI/PlatformServices/GameController/GameControllerService.cs @@ -8,7 +8,7 @@ namespace BrickController2.Windows.PlatformServices.GameController; -internal class GameControllerService : GameControllerServiceBase, IGameControllerService +internal class GameControllerService : GameControllerServiceBase, IGameControllerService { private readonly IMainThreadService _mainThreadService; private readonly IDispatcherProvider _dispatcherProvider; @@ -53,9 +53,9 @@ private void Gamepad_GamepadRemoved(object? sender, Gamepad gamepad) // ensure stopped in UI thread _ = _mainThreadService.RunOnMainThread(() => { - if (TryRemove(x => x.Gamepad == gamepad, out var controller)) + if (TryRemove(x => x.ControllerDevice == gamepad, out var controller)) { - _logger.LogInformation("Gamepad has been removed ControllerId:{controllerId}", controller.ControllerId); + _logger.LogInformation("Controller device has been removed ControllerId:{controllerId}", controller.ControllerId); } }); } diff --git a/BrickController2/BrickController2.WinUI/PlatformServices/GameController/GamepadController.cs b/BrickController2/BrickController2.WinUI/PlatformServices/GameController/GamepadController.cs index ef203590..4972f8ba 100644 --- a/BrickController2/BrickController2.WinUI/PlatformServices/GameController/GamepadController.cs +++ b/BrickController2/BrickController2.WinUI/PlatformServices/GameController/GamepadController.cs @@ -24,14 +24,10 @@ internal class GamepadController : GamepadControllerBase public GamepadController(GameControllerService service, Gamepad gamepad, RawGameController rawController, int controllerNumber, IDispatcherTimer timer) : base(service, gamepad) { + Name = rawController.DisplayName; ControllerNumber = controllerNumber; ControllerId = GetControllerIdFromNumber(controllerNumber); - UniquePersistantDeviceId = rawController.NonRoamableId; - Name = rawController.DisplayName; - VendorId = rawController.HardwareVendorId; - ProductId = rawController.HardwareProductId; - _timer = timer; _timer.Interval = DefaultInterval; @@ -55,7 +51,7 @@ public override void Stop() private void Timer_Tick(object? sender, object e) { - var currentReading = Gamepad.GetCurrentReading(); + var currentReading = ControllerDevice.GetCurrentReading(); var currentEvents = currentReading .Enumerate() diff --git a/BrickController2/BrickController2.iOS/PlatformServices/GameController/GameControllerService.cs b/BrickController2/BrickController2.iOS/PlatformServices/GameController/GameControllerService.cs index e138df48..11bfe194 100644 --- a/BrickController2/BrickController2.iOS/PlatformServices/GameController/GameControllerService.cs +++ b/BrickController2/BrickController2.iOS/PlatformServices/GameController/GameControllerService.cs @@ -1,285 +1,124 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using BrickController2.PlatformServices.GameController; +using BrickController2.PlatformServices.GameController; using Foundation; using GameController; - -using static BrickController2.PlatformServices.GameController.GameControllers; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; namespace BrickController2.iOS.PlatformServices.GameController { - public class GameControllerService : IGameControllerService + internal class GameControllerService : GameControllerServiceBase { - private enum GameControllerType - { - Unknown, - Micro, - Standard, - Extended - }; - - private readonly object _lockObject = new object(); - - private readonly IDictionary _lastControllerEventValueMap = new Dictionary(); + private static readonly GCControllerPlayerIndex[] ValidPlayerIndexes = Enum.GetValues(typeof(GCControllerPlayerIndex)) + .Cast() + .Where(i => i != GCControllerPlayerIndex.Unset) + .ToArray(); - private GCController? _gameController; - private event EventHandler? GameControllerEventInternal; private NSObject? _didConnectNotification; private NSObject? _didDisconnectNotification; - public event EventHandler GameControllerEvent + public GameControllerService(ILogger logger) + : base(logger) { - add - { - lock (_lockObject) - { - if (GameControllerEventInternal is null) - { - if (GCController.Controllers.Length == 0) - { - FindController(); - } - else - { - FoundController(); - } - } - - GameControllerEventInternal += value; - } - } - - remove - { - lock (_lockObject) - { - GameControllerEventInternal -= value; - - if (GameControllerEventInternal is null) - { - GCController.StopWirelessControllerDiscovery(); - _didConnectNotification?.Dispose(); - _didDisconnectNotification?.Dispose(); - _didConnectNotification = null; - _didDisconnectNotification = null; - _gameController?.Dispose(); - _gameController = null; - } - } - } } - public event EventHandler? GameControllersChangedEvent; - - public bool IsControllerIdSupported => false; // ToDo: implement ControllerManagement + public override bool IsControllerIdSupported => true; - private void FindController() + protected override void InitializeCurrentControllers() { - lock (_lockObject) + // get all available gamepads + if (GCController.Controllers.Any()) { - _didConnectNotification = GCController.Notifications.ObserveDidConnect((sender, args) => - { - FoundController(); - }); - - GCController.StartWirelessControllerDiscovery(() => { }); + AddDevices(GCController.Controllers); } - } - private void FoundController() - { - lock (_lockObject) + // register GCController events + _didDisconnectNotification = GCController.Notifications.ObserveDidDisconnect((sender, args) => { - _gameController = GCController.Controllers.FirstOrDefault(); - - if (_gameController is not null) + if (args.Notification.Object is GCController controller) { - GCController.StopWirelessControllerDiscovery(); - _didConnectNotification?.Dispose(); - _didConnectNotification = null; - - _didDisconnectNotification = GCController.Notifications.ObserveDidDisconnect((sender, args) => - { - FindController(); - }); - - switch (GetGameControllerType(_gameController)) - { - case GameControllerType.Micro: - SetupMicroGamePad(_gameController.MicroGamepad!); - break; - - case GameControllerType.Standard: -#pragma warning disable CA1422 // Validate platform compatibility - SetupGamePad(_gameController.Gamepad!); -#pragma warning restore CA1422 // Validate platform compatibility - break; - - case GameControllerType.Extended: - SetupExtendedGamePad(_gameController.ExtendedGamepad!); - break; - } + ControllerRemoved(controller); } - } - } - - private GameControllerType GetGameControllerType(GCController controller) - { - try + }); + _didConnectNotification = GCController.Notifications.ObserveDidConnect((sender, args) => { - if (controller.MicroGamepad is not null) + if (args.Notification.Object is GCController controller) { - return GameControllerType.Micro; + ControllerAdded(controller); } - } - catch (InvalidCastException) { } + }); - try - { -#pragma warning disable CA1422 // Validate platform compatibility - if (controller.Gamepad is not null) - { - return GameControllerType.Standard; - } -#pragma warning restore CA1422 // Validate platform compatibility - } - catch (InvalidCastException) { } - - try - { - if (controller.ExtendedGamepad is not null) - { - return GameControllerType.Extended; - } - } - catch (InvalidCastException) { } - - return GameControllerType.Unknown; - } - - private void SetupMicroGamePad(GCMicroGamepad gamePad) - { - SetupDigitalButtonInput(gamePad.ButtonA, "Button_A"); - SetupDigitalButtonInput(gamePad.ButtonX, "Button_X"); - - SetupDPadInput(gamePad.Dpad, "DPad"); + GCController.StartWirelessControllerDiscovery(() => { }); } - private void SetupGamePad(GCGamepad gamePad) - { -#pragma warning disable CA1422 // Validate platform compatibility - SetupDigitalButtonInput(gamePad.ButtonA, "Button_A"); - SetupDigitalButtonInput(gamePad.ButtonB, "Button_B"); - SetupDigitalButtonInput(gamePad.ButtonX, "Button_X"); - SetupDigitalButtonInput(gamePad.ButtonY, "Button_Y"); - - SetupDigitalButtonInput(gamePad.LeftShoulder, "LeftShoulder"); - SetupDigitalButtonInput(gamePad.RightShoulder, "RightShoulder"); - SetupDPadInput(gamePad.DPad, "DPad"); -#pragma warning restore CA1422 // Validate platform compatibility - } - - private void SetupExtendedGamePad(GCExtendedGamepad gamePad) + protected override void RemoveAllControllers() { - SetupDigitalButtonInput(gamePad.ButtonA, "Button_A"); - SetupDigitalButtonInput(gamePad.ButtonB, "Button_B"); - SetupDigitalButtonInput(gamePad.ButtonX, "Button_X"); - SetupDigitalButtonInput(gamePad.ButtonY, "Button_Y"); - - SetupDigitalButtonInput(gamePad.LeftShoulder, "LeftShoulder"); - SetupDigitalButtonInput(gamePad.RightShoulder, "RightShoulder"); + GCController.StopWirelessControllerDiscovery(); + _didConnectNotification?.Dispose(); + _didDisconnectNotification?.Dispose(); + _didConnectNotification = null; + _didDisconnectNotification = null; - SetupAnalogButtonInput(gamePad.LeftTrigger, "LeftTrigger"); - SetupAnalogButtonInput(gamePad.RightTrigger, "RightTrigger"); - - SetupDPadInput(gamePad.DPad, "DPad"); - - SetupJoyInput(gamePad.LeftThumbstick, "LeftThumbStick"); - SetupJoyInput(gamePad.RightThumbstick, "RightThumbStick"); + base.RemoveAllControllers(); } - private void SetupDigitalButtonInput(GCControllerButtonInput button, string name) + private void ControllerRemoved(GCController controller) { - button.ValueChangedHandler = (btn, value, isPressed) => - { - value = isPressed ? 1.0F : 0.0F; - - if (!_lastControllerEventValueMap.ContainsKey(name) || !AreAlmostEqual(_lastControllerEventValueMap[name], value)) - { - // ToDo: find ControllerId - string controllerId = GetControllerIdFromIndex(0); - - _lastControllerEventValueMap[name] = value; - GameControllerEventInternal?.Invoke(this, new GameControllerEventArgs(controllerId, GameControllerEventType.Button, name, value)); - } - }; - } - - private void SetupAnalogButtonInput(GCControllerButtonInput button, string name) - { - button.ValueChangedHandler = (btn, value, isPressed) => + lock (_lockObject) { - value = value < 0.1 ? 0.0F : value; - - if (!_lastControllerEventValueMap.ContainsKey(name) || !AreAlmostEqual(_lastControllerEventValueMap[name], value)) + if (TryRemove(x => x.ControllerDevice == controller, out var controllerDevice)) { - // ToDo: find ControllerId - string controllerId = GetControllerIdFromIndex(0); - - _lastControllerEventValueMap[name] = value; - GameControllerEventInternal?.Invoke(this, new GameControllerEventArgs(controllerId, GameControllerEventType.Axis, name, value)); + _logger.LogInformation("Controller device has been removed ControllerId:{controllerId}", controllerDevice.ControllerId); } - }; + } } - private void SetupDPadInput(GCControllerDirectionPad dPad, string name) + private void ControllerAdded(GCController controller) { - SetupDigitalAxisInput(dPad.XAxis, $"{name}_X"); - SetupDigitalAxisInput(dPad.YAxis, $"{name}_Y"); + AddDevices([controller]); } - private void SetupDigitalAxisInput(GCControllerAxisInput axis, string name) + private void AddDevices(IEnumerable controllers) { - axis.ValueChangedHandler = (ax, value) => + lock (_lockObject) { - if (value < -0.1F) value = -1.0F; - else if (value > 0.1F) value = 1.0F; - else value = 0.0F; - - if (!_lastControllerEventValueMap.ContainsKey(name) || !AreAlmostEqual(_lastControllerEventValueMap[name], value)) + foreach (var controller in controllers) { - // ToDo: find ControllerId - string controllerId = GetControllerIdFromIndex(0); + // If PlayerIndex is unset then assign the next free player index + AssignNextAvailablePlayerIndex(controller); + + // get first unused number and apply it + var newController = new GamepadController(this, controller); - GameControllerEventInternal?.Invoke(this, new GameControllerEventArgs(controllerId, GameControllerEventType.Axis, name, value)); - _lastControllerEventValueMap[name] = value; + AddController(newController); } - }; + } } - private void SetupJoyInput(GCControllerDirectionPad joy, string name) + /// + /// If PlayerIndex is unset then assign the next free player index + /// + /// + private void AssignNextAvailablePlayerIndex(GCController controller) { - SetupAnalogAxisInput(joy.XAxis, $"{name}_X"); - SetupAnalogAxisInput(joy.YAxis, $"{name}_Y"); - } + if (controller.PlayerIndex != GCControllerPlayerIndex.Unset) + return; - private void SetupAnalogAxisInput(GCControllerAxisInput axis, string name) - { - axis.ValueChangedHandler = (ax, value) => - { - value = AdjustControllerValue(value); + var usedIndexes = GCController.Controllers + .Where(c => c.PlayerIndex != GCControllerPlayerIndex.Unset) + .Select(c => c.PlayerIndex) + .ToHashSet(); - if (!_lastControllerEventValueMap.ContainsKey(name) || !AreAlmostEqual(_lastControllerEventValueMap[name], value)) + foreach (var index in ValidPlayerIndexes) + { + if (!usedIndexes.Contains(index)) { - // ToDo: find ControllerId - string controllerId = GetControllerIdFromIndex(0); - - GameControllerEventInternal?.Invoke(this, new GameControllerEventArgs(controllerId, GameControllerEventType.Axis, name, value)); - _lastControllerEventValueMap[name] = value; + controller.PlayerIndex = index; + break; } - }; + } } } } \ No newline at end of file diff --git a/BrickController2/BrickController2.iOS/PlatformServices/GameController/GamepadController.cs b/BrickController2/BrickController2.iOS/PlatformServices/GameController/GamepadController.cs new file mode 100644 index 00000000..d490c26d --- /dev/null +++ b/BrickController2/BrickController2.iOS/PlatformServices/GameController/GamepadController.cs @@ -0,0 +1,223 @@ +using System; +using BrickController2.PlatformServices.GameController; +using GameController; + +using static BrickController2.PlatformServices.GameController.GameControllers; + +using BrickController2.iOS.PlatformServices.GameController; + +internal class GamepadController : GamepadControllerBase, IDisposable +{ + private enum GameControllerType + { + Unknown, + Micro, + Standard, + Extended + }; + + /// + /// Constructor + /// + /// reference to GameControllerService + /// reference to InputDevice + public GamepadController(GameControllerService service, GCController controller) + : base(service, controller) + { + GameControllerType gameControllerType = GetGameControllerType(controller); + + // initialize properties + Name = GetDisplayName(controller, gameControllerType); + ControllerNumber = (int)controller.PlayerIndex; + ControllerId = GetControllerIdFromNumber(ControllerNumber); + + SetupController(controller, gameControllerType); + } + + public void Dispose() + { + ControllerDevice.Dispose(); + } + + private void SetupController(GCController gameController, GameControllerType gameControllerType) + { + switch (gameControllerType) + { + case GameControllerType.Micro: + SetupMicroGamePad(gameController.MicroGamepad!); + break; + + case GameControllerType.Standard: +#pragma warning disable CA1422 // Validate platform compatibility + SetupGamePad(gameController.Gamepad!); +#pragma warning restore CA1422 // Validate platform compatibility + break; + + case GameControllerType.Extended: + SetupExtendedGamePad(gameController.ExtendedGamepad!); + break; + } + } + + private GameControllerType GetGameControllerType(GCController controller) + { + try + { + if (controller.MicroGamepad is not null) + { + return GameControllerType.Micro; + } + } + catch (InvalidCastException) { } + + try + { +#pragma warning disable CA1422 // Validate platform compatibility + if (controller.Gamepad is not null) + { + return GameControllerType.Standard; + } +#pragma warning restore CA1422 // Validate platform compatibility + } + catch (InvalidCastException) { } + + try + { + if (controller.ExtendedGamepad is not null) + { + return GameControllerType.Extended; + } + } + catch (InvalidCastException) { } + + return GameControllerType.Unknown; + } + + private void SetupMicroGamePad(GCMicroGamepad gamePad) + { + SetupDigitalButtonInput(gamePad.ButtonA, "Button_A"); + SetupDigitalButtonInput(gamePad.ButtonX, "Button_X"); + + SetupDPadInput(gamePad.Dpad, "DPad"); + } + + private void SetupGamePad(GCGamepad gamePad) + { +#pragma warning disable CA1422 // Validate platform compatibility + SetupDigitalButtonInput(gamePad.ButtonA, "Button_A"); + SetupDigitalButtonInput(gamePad.ButtonB, "Button_B"); + SetupDigitalButtonInput(gamePad.ButtonX, "Button_X"); + SetupDigitalButtonInput(gamePad.ButtonY, "Button_Y"); + + SetupDigitalButtonInput(gamePad.LeftShoulder, "LeftShoulder"); + SetupDigitalButtonInput(gamePad.RightShoulder, "RightShoulder"); + + SetupDPadInput(gamePad.DPad, "DPad"); +#pragma warning restore CA1422 // Validate platform compatibility + } + + private void SetupExtendedGamePad(GCExtendedGamepad gamePad) + { + SetupDigitalButtonInput(gamePad.ButtonA, "Button_A"); + SetupDigitalButtonInput(gamePad.ButtonB, "Button_B"); + SetupDigitalButtonInput(gamePad.ButtonX, "Button_X"); + SetupDigitalButtonInput(gamePad.ButtonY, "Button_Y"); + + SetupDigitalButtonInput(gamePad.LeftShoulder, "LeftShoulder"); + SetupDigitalButtonInput(gamePad.RightShoulder, "RightShoulder"); + + SetupAnalogButtonInput(gamePad.LeftTrigger, "LeftTrigger"); + SetupAnalogButtonInput(gamePad.RightTrigger, "RightTrigger"); + + SetupDPadInput(gamePad.DPad, "DPad"); + + SetupJoyInput(gamePad.LeftThumbstick, "LeftThumbStick"); + SetupJoyInput(gamePad.RightThumbstick, "RightThumbStick"); + } + + private void SetupDigitalButtonInput(GCControllerButtonInput button, string name) + { + button.ValueChangedHandler = (btn, value, isPressed) => + { + value = isPressed ? BUTTON_PRESSED : BUTTON_RELEASED; + + if (HasValueChanged(name, value)) + { + RaiseEvent(GameControllerEventType.Button, name, value); + } + }; + } + + private void SetupAnalogButtonInput(GCControllerButtonInput button, string name) + { + button.ValueChangedHandler = (btn, value, isPressed) => + { + value = value < 0.1 ? 0.0F : value; + + if (HasValueChanged(name, value)) + { + RaiseEvent(GameControllerEventType.Axis, name, value); + } + }; + } + + private void SetupDPadInput(GCControllerDirectionPad dPad, string name) + { + SetupDigitalAxisInput(dPad.XAxis, $"{name}_X"); + SetupDigitalAxisInput(dPad.YAxis, $"{name}_Y"); + } + + private void SetupDigitalAxisInput(GCControllerAxisInput axis, string name) + { + axis.ValueChangedHandler = (ax, value) => + { + // adjust value + value = value switch + { + < -0.1f => AXIS_MIN_VALUE, + > 0.1f => AXIS_MAX_VALUE, + _ => AXIS_ZERO_VALUE + }; + + if (HasValueChanged(name, value)) + { + RaiseEvent(GameControllerEventType.Axis, name, value); + } + }; + } + + private void SetupJoyInput(GCControllerDirectionPad joy, string name) + { + SetupAnalogAxisInput(joy.XAxis, $"{name}_X"); + SetupAnalogAxisInput(joy.YAxis, $"{name}_Y"); + } + + private void SetupAnalogAxisInput(GCControllerAxisInput axis, string name) + { + axis.ValueChangedHandler = (ax, value) => + { + value = AdjustControllerValue(value); + + if (HasValueChanged(name, value)) + { + RaiseEvent(GameControllerEventType.Axis, name, value); + } + }; + } + + private static string GetDisplayName(GCController controller, GameControllerType gameControllerType) + { + if (!string.IsNullOrEmpty(controller.VendorName)) + { + return controller.VendorName; + } + + return gameControllerType switch + { + GameControllerType.Micro => "Micro Gamepad", + GameControllerType.Standard => "Standard Gamepad", + GameControllerType.Extended => "Extended Gamepad", + _ => "Unknown Gamepad", + }; + } +} \ No newline at end of file diff --git a/BrickController2/BrickController2/PlatformServices/GameController/GameControllerServiceBase.cs b/BrickController2/BrickController2/PlatformServices/GameController/GameControllerServiceBase.cs index a9f08f8d..a37c145a 100644 --- a/BrickController2/BrickController2/PlatformServices/GameController/GameControllerServiceBase.cs +++ b/BrickController2/BrickController2/PlatformServices/GameController/GameControllerServiceBase.cs @@ -10,8 +10,7 @@ namespace BrickController2.PlatformServices.GameController; /// /// Base class for implementation of /// -public abstract class GameControllerServiceBase : IGameControllerServiceInternal - where TGameController : class, IGameController +public abstract class GameControllerServiceBase : IGameControllerServiceInternal { protected readonly object _lockObject = new(); protected readonly ILogger _logger; @@ -21,7 +20,7 @@ public abstract class GameControllerServiceBase : IGameControll /// /// Collection of available gamepads having /// - private readonly List _availableControllers = []; + private readonly List _availableControllers = []; protected GameControllerServiceBase(ILogger logger) { @@ -97,6 +96,8 @@ protected virtual void RemoveAllControllers() foreach (var controller in _availableControllers) { controller.Stop(); + + (controller as IDisposable)?.Dispose(); } _availableControllers.Clear(); // notify removal @@ -119,7 +120,7 @@ protected int GetFirstUnusedControllerNumber() } } - protected void AddController(TGameController controller) + protected void AddController(IGameController controller) { lock (_lockObject) { @@ -130,27 +131,36 @@ protected void AddController(TGameController controller) } } - protected bool TryRemove(Predicate predicate, [MaybeNullWhen(false)] out TGameController controller) + protected bool TryRemove(Predicate predicate, [MaybeNullWhen(false)] out TGameController controller) + where TGameController : class, IGameController { lock (_lockObject) { // remove and stop the controller - if (_availableControllers.Remove(predicate, out controller)) + if (_availableControllers.Remove(x => x is TGameController tc && predicate(tc), out var removed)) { + controller = (TGameController)removed; // safe due to pattern match above controller.Stop(); + // notify removal OnGameControllersChangedEvent(NotifyGameControllersChangedAction.Disconnected, controller); + + (controller as IDisposable)?.Dispose(); + return true; } + + controller = null; return false; } } - protected bool TryGetController(Predicate predicate, [MaybeNullWhen(false)] out TGameController controller) + protected bool TryGetController(Predicate predicate, [MaybeNullWhen(false)] out TGameController controller) + where TGameController : class, IGameController { lock (_lockObject) { - controller = _availableControllers.FirstOrDefault(x => predicate(x)); + controller = _availableControllers.OfType().FirstOrDefault(x => predicate(x)); return controller is not null; } } diff --git a/BrickController2/BrickController2/PlatformServices/GameController/GameControllers.cs b/BrickController2/BrickController2/PlatformServices/GameController/GameControllers.cs index 9ca61de9..a4dfefaf 100644 --- a/BrickController2/BrickController2/PlatformServices/GameController/GameControllers.cs +++ b/BrickController2/BrickController2/PlatformServices/GameController/GameControllers.cs @@ -14,17 +14,6 @@ public static class GameControllers public const float AXIS_MIN_VALUE = - 1.0f; public const float AXIS_MAX_VALUE = 1.0f; - /// - /// Creates an identifier string for the controller from the given index - /// - /// zero-based index - /// Identifier - public static string GetControllerIdFromIndex(int controllerIndex) - { - // controllerIndex == 0 -> "Controller 1" - return $"Controller {controllerIndex + 1}"; - } - /// /// Creates an identifier string for the controller from the given /// diff --git a/BrickController2/BrickController2/PlatformServices/GameController/GamepadControllerBase.cs b/BrickController2/BrickController2/PlatformServices/GameController/GamepadControllerBase.cs index 55a82f59..6948a653 100644 --- a/BrickController2/BrickController2/PlatformServices/GameController/GamepadControllerBase.cs +++ b/BrickController2/BrickController2/PlatformServices/GameController/GamepadControllerBase.cs @@ -4,7 +4,7 @@ namespace BrickController2.PlatformServices.GameController; -public abstract class GamepadControllerBase : IGameController where TGamepad : class +public abstract class GamepadControllerBase : IGameController where TControllerDevice : class { /// stored last value per axis to detect changes private readonly Dictionary _lastAxisValues = []; @@ -13,10 +13,10 @@ public abstract class GamepadControllerBase : IGameController where TG private readonly IGameControllerServiceInternal _controllerService; protected GamepadControllerBase(IGameControllerServiceInternal controllerService, - TGamepad gamepad) + TControllerDevice controllerDevice) { _controllerService = controllerService; - Gamepad = gamepad; + ControllerDevice = controllerDevice; } /// @@ -30,19 +30,15 @@ protected GamepadControllerBase(IGameControllerServiceInternal controllerService public string ControllerId { get; protected init; } = default!; /// - /// Unique and persistant identifier of device + /// DisplayName of the controller /// - public string UniquePersistantDeviceId { get; protected init; } = default!; - public string Name { get; protected init; } = default!; - public int VendorId { get; protected init; } - public int ProductId { get; protected init; } /// - /// Native instance of gamepad + /// Native instance of controller device /// - public TGamepad Gamepad { get; } + public TControllerDevice ControllerDevice { get; } public virtual void Start() { diff --git a/BrickController2/BrickController2/PlatformServices/GameController/IGameController.cs b/BrickController2/BrickController2/PlatformServices/GameController/IGameController.cs index f0c8e60f..61139b65 100644 --- a/BrickController2/BrickController2/PlatformServices/GameController/IGameController.cs +++ b/BrickController2/BrickController2/PlatformServices/GameController/IGameController.cs @@ -18,16 +18,6 @@ public interface IGameController /// string Name { get; } - /// - /// Vendor ID of the game controller. - /// - int VendorId { get; } - - /// - /// Product ID of the game controller. - /// - int ProductId { get; } - /// /// Start the controller and publishing of its events ///