| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332 |
- using System;
- using Ryujinx.Common.Logging;
- using Ryujinx.HLE.HOS.Kernel.Threading;
- namespace Ryujinx.HLE.HOS.Services.Hid
- {
- public class NpadDevices : BaseDevice
- {
- internal NpadJoyHoldType JoyHold = NpadJoyHoldType.Vertical;
- internal bool SixAxisActive = false; // TODO: link to hidserver when implemented
- private enum FilterState
- {
- Unconfigured = 0,
- Configured = 1,
- Accepted = 2
- }
- private struct NpadConfig
- {
- public ControllerType ConfiguredType;
- public FilterState State;
- }
- private const int _maxControllers = 9; // Players1-8 and Handheld
- private NpadConfig[] _configuredNpads;
- private ControllerType _supportedStyleSets = ControllerType.ProController |
- ControllerType.JoyconPair |
- ControllerType.JoyconLeft |
- ControllerType.JoyconRight |
- ControllerType.Handheld;
- public ControllerType SupportedStyleSets
- {
- get => _supportedStyleSets;
- set
- {
- if (_supportedStyleSets != value) // Deal with spamming
- {
- _supportedStyleSets = value;
- MatchControllers();
- }
- }
- }
- public PlayerIndex PrimaryController { get; set; } = PlayerIndex.Unknown;
- private KEvent[] _styleSetUpdateEvents;
- private static readonly Array3<BatteryCharge> _fullBattery;
- public NpadDevices(Switch device, bool active = true) : base(device, active)
- {
- _configuredNpads = new NpadConfig[_maxControllers];
- _styleSetUpdateEvents = new KEvent[_maxControllers];
- for (int i = 0; i < _styleSetUpdateEvents.Length; ++i)
- {
- _styleSetUpdateEvents[i] = new KEvent(_device.System.KernelContext);
- }
- _fullBattery[0] = _fullBattery[1] = _fullBattery[2] = BatteryCharge.Percent100;
- }
- public void AddControllers(params ControllerConfig[] configs)
- {
- for (int i = 0; i < configs.Length; ++i)
- {
- PlayerIndex player = configs[i].Player;
- ControllerType controllerType = configs[i].Type;
- if (player > PlayerIndex.Handheld)
- {
- throw new ArgumentOutOfRangeException("Player must be Player1-8 or Handheld");
- }
- if (controllerType == ControllerType.Handheld)
- {
- player = PlayerIndex.Handheld;
- }
- _configuredNpads[(int)player] = new NpadConfig { ConfiguredType = controllerType, State = FilterState.Configured };
- }
- MatchControllers();
- }
- private void MatchControllers()
- {
- PrimaryController = PlayerIndex.Unknown;
- for (int i = 0; i < _configuredNpads.Length; ++i)
- {
- ref NpadConfig config = ref _configuredNpads[i];
- if (config.State == FilterState.Unconfigured)
- {
- continue; // Ignore unconfigured
- }
- if ((config.ConfiguredType & _supportedStyleSets) == 0)
- {
- Logger.PrintWarning(LogClass.Hid, $"ControllerType {config.ConfiguredType} (connected to {(PlayerIndex)i}) not supported by game. Removing...");
- config.State = FilterState.Configured;
- _device.Hid.SharedMemory.Npads[i] = new ShMemNpad(); // Zero it
- continue;
- }
- InitController((PlayerIndex)i, config.ConfiguredType);
- }
- // Couldn't find any matching configuration. Reassign to something that works.
- if (PrimaryController == PlayerIndex.Unknown)
- {
- ControllerType[] npadsTypeList = (ControllerType[])Enum.GetValues(typeof(ControllerType));
- // Skip None Type
- for (int i = 1; i < npadsTypeList.Length; ++i)
- {
- ControllerType controllerType = npadsTypeList[i];
- if ((controllerType & _supportedStyleSets) != 0)
- {
- Logger.PrintWarning(LogClass.Hid, $"No matching controllers found. Reassigning input as ControllerType {controllerType}...");
- InitController(controllerType == ControllerType.Handheld ? PlayerIndex.Handheld : PlayerIndex.Player1, controllerType);
- return;
- }
- }
- Logger.PrintError(LogClass.Hid, "Couldn't find any appropriate controller.");
- }
- }
- internal ref KEvent GetStyleSetUpdateEvent(PlayerIndex player)
- {
- return ref _styleSetUpdateEvents[(int)player];
- }
- private void InitController(PlayerIndex player, ControllerType type)
- {
- if (type == ControllerType.Handheld)
- {
- player = PlayerIndex.Handheld;
- }
- ref ShMemNpad controller = ref _device.Hid.SharedMemory.Npads[(int)player];
- controller = new ShMemNpad(); // Zero it
- // TODO: Allow customizing colors at config
- NpadStateHeader defaultHeader = new NpadStateHeader
- {
- IsHalf = false,
- SingleColorBody = NpadColor.BodyGray,
- SingleColorButtons = NpadColor.ButtonGray,
- LeftColorBody = NpadColor.BodyNeonBlue,
- LeftColorButtons = NpadColor.ButtonGray,
- RightColorBody = NpadColor.BodyNeonRed,
- RightColorButtons = NpadColor.ButtonGray
- };
- controller.SystemProperties = NpadSystemProperties.PowerInfo0Connected |
- NpadSystemProperties.PowerInfo1Connected |
- NpadSystemProperties.PowerInfo2Connected;
- controller.BatteryState = _fullBattery;
- switch (type)
- {
- case ControllerType.ProController:
- defaultHeader.Type = ControllerType.ProController;
- controller.DeviceType = DeviceType.FullKey;
- controller.SystemProperties |= NpadSystemProperties.AbxyButtonOriented |
- NpadSystemProperties.PlusButtonCapability |
- NpadSystemProperties.MinusButtonCapability;
- break;
- case ControllerType.Handheld:
- defaultHeader.Type = ControllerType.Handheld;
- controller.DeviceType = DeviceType.HandheldLeft |
- DeviceType.HandheldRight;
- controller.SystemProperties |= NpadSystemProperties.AbxyButtonOriented |
- NpadSystemProperties.PlusButtonCapability |
- NpadSystemProperties.MinusButtonCapability;
- break;
- case ControllerType.JoyconPair:
- defaultHeader.Type = ControllerType.JoyconPair;
- controller.DeviceType = DeviceType.JoyLeft |
- DeviceType.JoyRight;
- controller.SystemProperties |= NpadSystemProperties.AbxyButtonOriented |
- NpadSystemProperties.PlusButtonCapability |
- NpadSystemProperties.MinusButtonCapability;
- break;
- case ControllerType.JoyconLeft:
- defaultHeader.Type = ControllerType.JoyconLeft;
- defaultHeader.IsHalf = true;
- controller.DeviceType = DeviceType.JoyLeft;
- controller.SystemProperties |= NpadSystemProperties.SlSrButtonOriented |
- NpadSystemProperties.MinusButtonCapability;
- break;
- case ControllerType.JoyconRight:
- defaultHeader.Type = ControllerType.JoyconRight;
- defaultHeader.IsHalf = true;
- controller.DeviceType = DeviceType.JoyRight;
- controller.SystemProperties |= NpadSystemProperties.SlSrButtonOriented |
- NpadSystemProperties.PlusButtonCapability;
- break;
- case ControllerType.Pokeball:
- defaultHeader.Type = ControllerType.Pokeball;
- controller.DeviceType = DeviceType.Palma;
- break;
- }
- controller.Header = defaultHeader;
- if (PrimaryController == PlayerIndex.Unknown)
- {
- PrimaryController = player;
- }
- _configuredNpads[(int)player].State = FilterState.Accepted;
- _styleSetUpdateEvents[(int)player].ReadableEvent.Signal();
- Logger.PrintInfo(LogClass.Hid, $"Connected ControllerType {type} to PlayerIndex {player}");
- }
- private static NpadLayoutsIndex ControllerTypeToLayout(ControllerType controllerType)
- => controllerType switch
- {
- ControllerType.ProController => NpadLayoutsIndex.ProController,
- ControllerType.Handheld => NpadLayoutsIndex.Handheld,
- ControllerType.JoyconPair => NpadLayoutsIndex.JoyDual,
- ControllerType.JoyconLeft => NpadLayoutsIndex.JoyLeft,
- ControllerType.JoyconRight => NpadLayoutsIndex.JoyRight,
- ControllerType.Pokeball => NpadLayoutsIndex.Pokeball,
- _ => NpadLayoutsIndex.SystemExternal
- };
- public void SetGamepadsInput(params GamepadInput[] states)
- {
- UpdateAllEntries();
- for (int i = 0; i < states.Length; ++i)
- {
- SetGamepadState(states[i].PlayerId, states[i].Buttons, states[i].LStick, states[i].RStick);
- }
- }
- private void SetGamepadState(PlayerIndex player, ControllerKeys buttons,
- JoystickPosition leftJoystick, JoystickPosition rightJoystick)
- {
- if (player == PlayerIndex.Auto)
- {
- player = PrimaryController;
- }
- if (player == PlayerIndex.Unknown)
- {
- return;
- }
- if (_configuredNpads[(int)player].State != FilterState.Accepted)
- {
- return;
- }
- ref ShMemNpad currentNpad = ref _device.Hid.SharedMemory.Npads[(int)player];
- ref NpadLayout currentLayout = ref currentNpad.Layouts[(int)ControllerTypeToLayout(currentNpad.Header.Type)];
- ref NpadState currentEntry = ref currentLayout.Entries[(int)currentLayout.Header.LatestEntry];
- currentEntry.Buttons = buttons;
- currentEntry.LStickX = leftJoystick.Dx;
- currentEntry.LStickY = leftJoystick.Dy;
- currentEntry.RStickX = rightJoystick.Dx;
- currentEntry.RStickY = rightJoystick.Dy;
- // Mirror data to Default layout just in case
- ref NpadLayout mainLayout = ref currentNpad.Layouts[(int)NpadLayoutsIndex.SystemExternal];
- mainLayout.Entries[(int)mainLayout.Header.LatestEntry] = currentEntry;
- }
- private void UpdateAllEntries()
- {
- ref Array10<ShMemNpad> controllers = ref _device.Hid.SharedMemory.Npads;
- for (int i = 0; i < controllers.Length; ++i)
- {
- ref Array7<NpadLayout> layouts = ref controllers[i].Layouts;
- for (int l = 0; l < layouts.Length; ++l)
- {
- ref NpadLayout currentLayout = ref layouts[l];
- int currentIndex = UpdateEntriesHeader(ref currentLayout.Header, out int previousIndex);
- ref NpadState currentEntry = ref currentLayout.Entries[currentIndex];
- NpadState previousEntry = currentLayout.Entries[previousIndex];
- currentEntry.SampleTimestamp = previousEntry.SampleTimestamp + 1;
- currentEntry.SampleTimestamp2 = previousEntry.SampleTimestamp2 + 1;
- if (controllers[i].Header.Type == ControllerType.None)
- {
- continue;
- }
- currentEntry.ConnectionState = NpadConnectionState.ControllerStateConnected;
- switch (controllers[i].Header.Type)
- {
- case ControllerType.Handheld:
- case ControllerType.ProController:
- currentEntry.ConnectionState |= NpadConnectionState.ControllerStateWired;
- break;
- case ControllerType.JoyconPair:
- currentEntry.ConnectionState |= NpadConnectionState.JoyLeftConnected |
- NpadConnectionState.JoyRightConnected;
- break;
- case ControllerType.JoyconLeft:
- currentEntry.ConnectionState |= NpadConnectionState.JoyLeftConnected;
- break;
- case ControllerType.JoyconRight:
- currentEntry.ConnectionState |= NpadConnectionState.JoyRightConnected;
- break;
- }
- }
- }
- }
- }
- }
|