Improve multi-controller support in HID and Controller Applet (#1453)

* Initial commit

Enable proper LED patterns
Toggle Hotkeys only on focus
Ignore Handheld on Docked mode
Remove PrimaryController
Validate NpadIdType
Rewrite NpadDevices to process config in update loop
Cleanup

* Notify in log periodically when no matched controllers

* Remove duplicate StructArrayHelpers in favor of Common.Memory

Fix struct padding CS0169 warns in Touchscreen

* Remove GTK markup from Controller Applet

Use IList instead of List
Explicit list capacity in 1ms loop
Fix formatting

* Restrict ControllerWindow to show valid controller types

Add selected player name to ControllerWindow title

* ControllerWindow: Fix controller type initial value

NpadDevices: Simplify default battery charge

* Address AcK's comments

Use explicit types and fix formatting

* Remove HashSet for SupportedPlayers

Fixes potential exceptions due to race

* Fix ControllerSupportArg struct packing

Also comes with two revisions of struct for 4/8 players max.
This commit is contained in:
mageven 2020-08-24 02:24:11 +05:30 committed by GitHub
parent 01ff648bdf
commit 27179d0218
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 445 additions and 242 deletions

View file

@ -5,6 +5,7 @@ using Ryujinx.Common.Utilities;
using Ryujinx.Configuration;
using Ryujinx.HLE.FileSystem;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text.Json;
@ -91,6 +92,23 @@ namespace Ryujinx.Ui
_virtualFileSystem = virtualFileSystem;
_inputConfig = ConfigurationState.Instance.Hid.InputConfig.Value.Find(inputConfig => inputConfig.PlayerIndex == _playerIndex);
Title = $"Ryujinx - Controller Settings - {_playerIndex}";
if (_playerIndex == PlayerIndex.Handheld)
{
_controllerType.Append(ControllerType.Handheld.ToString(), "Handheld");
_controllerType.Sensitive = false;
}
else
{
_controllerType.Append(ControllerType.ProController.ToString(), "Pro Controller");
_controllerType.Append(ControllerType.JoyconPair.ToString(), "Joycon Pair");
_controllerType.Append(ControllerType.JoyconLeft.ToString(), "Joycon Left");
_controllerType.Append(ControllerType.JoyconRight.ToString(), "Joycon Right");
}
_controllerType.Active = 0; // Set initial value to first in list.
//Bind Events
_lStickX.Clicked += Button_Pressed;
_lStickY.Clicked += Button_Pressed;
@ -278,7 +296,12 @@ namespace Ryujinx.Ui
switch (config)
{
case KeyboardConfig keyboardConfig:
_controllerType.SetActiveId(keyboardConfig.ControllerType.ToString());
if (!_controllerType.SetActiveId(keyboardConfig.ControllerType.ToString()))
{
_controllerType.SetActiveId(_playerIndex == PlayerIndex.Handheld
? ControllerType.Handheld.ToString()
: ControllerType.ProController.ToString());
}
_lStickUp.Label = keyboardConfig.LeftJoycon.StickUp.ToString();
_lStickDown.Label = keyboardConfig.LeftJoycon.StickDown.ToString();
@ -310,7 +333,12 @@ namespace Ryujinx.Ui
_rSr.Label = keyboardConfig.RightJoycon.ButtonSr.ToString();
break;
case ControllerConfig controllerConfig:
_controllerType.SetActiveId(controllerConfig.ControllerType.ToString());
if (!_controllerType.SetActiveId(controllerConfig.ControllerType.ToString()))
{
_controllerType.SetActiveId(_playerIndex == PlayerIndex.Handheld
? ControllerType.Handheld.ToString()
: ControllerType.ProController.ToString());
}
_lStickX.Label = controllerConfig.LeftJoycon.StickX.ToString();
_invertLStickX.Active = controllerConfig.LeftJoycon.InvertStickX;
@ -894,24 +922,31 @@ namespace Ryujinx.Ui
{
InputConfig inputConfig = GetValues();
var newConfig = new List<InputConfig>();
newConfig.AddRange(ConfigurationState.Instance.Hid.InputConfig.Value);
if (_inputConfig == null && inputConfig != null)
{
ConfigurationState.Instance.Hid.InputConfig.Value.Add(inputConfig);
newConfig.Add(inputConfig);
}
else
{
if (_inputDevice.ActiveId == "disabled")
{
ConfigurationState.Instance.Hid.InputConfig.Value.Remove(_inputConfig);
newConfig.Remove(_inputConfig);
}
else if (inputConfig != null)
{
int index = ConfigurationState.Instance.Hid.InputConfig.Value.IndexOf(_inputConfig);
int index = newConfig.IndexOf(_inputConfig);
ConfigurationState.Instance.Hid.InputConfig.Value[index] = inputConfig;
newConfig[index] = inputConfig;
}
}
// Atomically replace and signal input change.
// NOTE: Do not modify InputConfig.Value directly as other code depends on the on-change event.
ConfigurationState.Instance.Hid.InputConfig.Value = newConfig;
MainWindow.SaveConfig();
Dispose();

View file

@ -138,13 +138,6 @@
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">The controller's type</property>
<property name="active">0</property>
<items>
<item id="Handheld" translatable="yes">Handheld</item>
<item id="ProController" translatable="yes">Pro Controller</item>
<item id="JoyconPair" translatable="yes">Paired Joycons</item>
<item id="JoyconLeft" translatable="yes">Left Joycon</item>
<item id="JoyconRight" translatable="yes">Right Joycon</item>
</items>
<signal name="changed" handler="Controller_Changed" swapped="no"/>
</object>
<packing>

View file

@ -405,9 +405,9 @@ namespace Ryujinx.Ui
});
}
List<GamepadInput> gamepadInputs = new List<GamepadInput>();
List<GamepadInput> gamepadInputs = new List<GamepadInput>(NpadDevices.MaxControllers);
foreach (InputConfig inputConfig in ConfigurationState.Instance.Hid.InputConfig.Value.ToArray())
foreach (InputConfig inputConfig in ConfigurationState.Instance.Hid.InputConfig.Value)
{
ControllerKeys currentButton = 0;
JoystickPosition leftJoystick = new JoystickPosition();
@ -497,18 +497,21 @@ namespace Ryujinx.Ui
});
}
_device.Hid.Npads.SetGamepadsInput(gamepadInputs.ToArray());
_device.Hid.Npads.Update(gamepadInputs);
// Hotkeys
HotkeyButtons currentHotkeyButtons = KeyboardController.GetHotkeyButtons(OpenTK.Input.Keyboard.GetState());
if (currentHotkeyButtons.HasFlag(HotkeyButtons.ToggleVSync) &&
!_prevHotkeyButtons.HasFlag(HotkeyButtons.ToggleVSync))
if(IsFocused)
{
_device.EnableDeviceVsync = !_device.EnableDeviceVsync;
}
// Hotkeys
HotkeyButtons currentHotkeyButtons = KeyboardController.GetHotkeyButtons(OpenTK.Input.Keyboard.GetState());
_prevHotkeyButtons = currentHotkeyButtons;
if (currentHotkeyButtons.HasFlag(HotkeyButtons.ToggleVSync) &&
!_prevHotkeyButtons.HasFlag(HotkeyButtons.ToggleVSync))
{
_device.EnableDeviceVsync = !_device.EnableDeviceVsync;
}
_prevHotkeyButtons = currentHotkeyButtons;
}
//Touchscreen
bool hasTouch = false;

View file

@ -16,6 +16,62 @@ namespace Ryujinx.Ui
_parent = parent;
}
public bool DisplayMessageDialog(ControllerAppletUiArgs args)
{
string playerCount = args.PlayerCountMin == args.PlayerCountMax
? $"exactly {args.PlayerCountMin}"
: $"{args.PlayerCountMin}-{args.PlayerCountMax}";
string message =
$"Application requests <b>{playerCount}</b> player(s) with:\n\n"
+ $"<tt><b>TYPES:</b> {args.SupportedStyles}</tt>\n\n"
+ $"<tt><b>PLAYERS:</b> {string.Join(", ", args.SupportedPlayers)}</tt>\n\n"
+ (args.IsDocked ? "Docked mode set. <tt>Handheld</tt> is also invalid.\n\n" : "")
+ "<i>Please reconfigure Input now and then press OK.</i>";
return DisplayMessageDialog("Controller Applet", message);
}
public bool DisplayMessageDialog(string title, string message)
{
ManualResetEvent dialogCloseEvent = new ManualResetEvent(false);
bool okPressed = false;
Application.Invoke(delegate
{
MessageDialog msgDialog = null;
try
{
msgDialog = new MessageDialog(_parent, DialogFlags.DestroyWithParent, MessageType.Info, ButtonsType.Ok, null)
{
Title = title,
Text = message,
UseMarkup = true
};
msgDialog.SetDefaultSize(400, 0);
msgDialog.Response += (object o, ResponseArgs args) =>
{
if (args.ResponseId == ResponseType.Ok) okPressed = true;
dialogCloseEvent.Set();
msgDialog?.Dispose();
};
msgDialog.Show();
}
catch (Exception e)
{
Logger.Error?.Print(LogClass.Application, $"Error displaying Message Dialog: {e}");
dialogCloseEvent.Set();
}
});
dialogCloseEvent.WaitOne();
return okPressed;
}
public bool DisplayInputDialog(SoftwareKeyboardUiArgs args, out string userText)
{
ManualResetEvent dialogCloseEvent = new ManualResetEvent(false);

View file

@ -507,14 +507,6 @@ namespace Ryujinx.Ui
_windowsMultimediaTimerResolution = new WindowsMultimediaTimerResolution(1);
}
device.Hid.Npads.AddControllers(ConfigurationState.Instance.Hid.InputConfig.Value.Select(inputConfig =>
new HLE.HOS.Services.Hid.ControllerConfig
{
Player = (PlayerIndex)inputConfig.PlayerIndex,
Type = (ControllerType)inputConfig.ControllerType
}
).ToArray());
_glWidget = new GlRenderer(_emulationContext, ConfigurationState.Instance.Logger.GraphicsDebugLevel);
Application.Invoke(delegate