Add raw screenshot gamepad button

This triggers the same raw screenshot as the keyboard hotkey,
i.e. the game is not informed of the intent to screenshot,
so it cannot add watermarks etc.

The button can be configured using both the regular UI and GTK.

Includes English and German translations.

An alternate approach is proposed in draft PR #4503:
Allow triggering all hotkeys using the gamepad.

Re: #5952
This commit is contained in:
Willi Schinmeyer 2024-09-06 12:58:31 +02:00
parent ca59c3f499
commit 30d7253e26
18 changed files with 197 additions and 2 deletions

View file

@ -3,6 +3,7 @@ namespace Ryujinx.Common.Configuration.Hid
public class LeftJoyconCommonConfig<TButton> public class LeftJoyconCommonConfig<TButton>
{ {
public TButton ButtonMinus { get; set; } public TButton ButtonMinus { get; set; }
public TButton ButtonScreenshot { get; set; }
public TButton ButtonL { get; set; } public TButton ButtonL { get; set; }
public TButton ButtonZl { get; set; } public TButton ButtonZl { get; set; }
public TButton ButtonSl { get; set; } public TButton ButtonSl { get; set; }

View file

@ -166,6 +166,7 @@ namespace Ryujinx.Input.GTK3
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadLeft, (Key)_configuration.LeftJoycon.DpadLeft)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadLeft, (Key)_configuration.LeftJoycon.DpadLeft));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadRight, (Key)_configuration.LeftJoycon.DpadRight)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadRight, (Key)_configuration.LeftJoycon.DpadRight));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Minus, (Key)_configuration.LeftJoycon.ButtonMinus)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Minus, (Key)_configuration.LeftJoycon.ButtonMinus));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Misc1, (Key)_configuration.LeftJoycon.ButtonScreenshot));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftShoulder, (Key)_configuration.LeftJoycon.ButtonL)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftShoulder, (Key)_configuration.LeftJoycon.ButtonL));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftTrigger, (Key)_configuration.LeftJoycon.ButtonZl)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftTrigger, (Key)_configuration.LeftJoycon.ButtonZl));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger0, (Key)_configuration.LeftJoycon.ButtonSr)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger0, (Key)_configuration.LeftJoycon.ButtonSr));

View file

@ -15,6 +15,7 @@ using Ryujinx.UI.Common.Helper;
using Ryujinx.UI.Widgets; using Ryujinx.UI.Widgets;
using SkiaSharp; using SkiaSharp;
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@ -73,11 +74,14 @@ namespace Ryujinx.UI
private HideCursorMode _hideCursorMode; private HideCursorMode _hideCursorMode;
private readonly InputManager _inputManager; private readonly InputManager _inputManager;
private readonly IKeyboard _keyboardInterface; private readonly IKeyboard _keyboardInterface;
private readonly List<IGamepad> _gamepadInterfaces;
private readonly GraphicsDebugLevel _glLogLevel; private readonly GraphicsDebugLevel _glLogLevel;
private string _gpuBackendName; private string _gpuBackendName;
private string _gpuDriverName; private string _gpuDriverName;
private bool _isMouseInClient; private bool _isMouseInClient;
private int _gamepadsChanged; // use atomically via Interlocked
public RendererWidgetBase(InputManager inputManager, GraphicsDebugLevel glLogLevel) public RendererWidgetBase(InputManager inputManager, GraphicsDebugLevel glLogLevel)
{ {
var mouseDriver = new GTK3MouseDriver(this); var mouseDriver = new GTK3MouseDriver(this);
@ -88,6 +92,13 @@ namespace Ryujinx.UI
TouchScreenManager = _inputManager.CreateTouchScreenManager(); TouchScreenManager = _inputManager.CreateTouchScreenManager();
_keyboardInterface = (IKeyboard)_inputManager.KeyboardDriver.GetGamepad("0"); _keyboardInterface = (IKeyboard)_inputManager.KeyboardDriver.GetGamepad("0");
_gamepadInterfaces = new List<IGamepad>();
_inputManager.GamepadDriver.OnGamepadConnected += GamepadConnected;
_inputManager.GamepadDriver.OnGamepadDisconnected += GamepadDisconnected;
RefreshGamepads();
WaitEvent = new ManualResetEvent(false); WaitEvent = new ManualResetEvent(false);
_glLogLevel = glLogLevel; _glLogLevel = glLogLevel;
@ -649,6 +660,11 @@ namespace Ryujinx.UI
}); });
} }
if (Interlocked.Exchange(ref _gamepadsChanged, 0) == 1)
{
RefreshGamepads();
}
NpadManager.Update(ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat()); NpadManager.Update(ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat());
if ((Toplevel as MainWindow).IsFocused) if ((Toplevel as MainWindow).IsFocused)
@ -767,7 +783,8 @@ namespace Ryujinx.UI
state |= KeyboardHotkeyState.ToggleVSync; state |= KeyboardHotkeyState.ToggleVSync;
} }
if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot)) if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot) ||
ButtonPressedOnAnyGamepad(GamepadButtonInputId.Misc1))
{ {
state |= KeyboardHotkeyState.Screenshot; state |= KeyboardHotkeyState.Screenshot;
} }
@ -809,5 +826,37 @@ namespace Ryujinx.UI
return state; return state;
} }
private void GamepadConnected(string id)
{
Interlocked.Exchange(ref _gamepadsChanged, 1);
}
private void GamepadDisconnected(string id)
{
Interlocked.Exchange(ref _gamepadsChanged, 1);
}
private void RefreshGamepads()
{
_gamepadInterfaces.Clear();
foreach (string id in _inputManager.GamepadDriver.GamepadsIds)
{
_gamepadInterfaces.Add(_inputManager.GamepadDriver.GetGamepad(id));
}
}
private bool ButtonPressedOnAnyGamepad(GamepadButtonInputId button)
{
foreach (IGamepad gamepad in _gamepadInterfaces)
{
if (gamepad.IsPressed(button))
{
return true;
}
}
return false;
}
} }
} }

View file

@ -84,6 +84,7 @@ namespace Ryujinx.UI.Windows
[GUI] ToggleButton _dpadLeft; [GUI] ToggleButton _dpadLeft;
[GUI] ToggleButton _dpadRight; [GUI] ToggleButton _dpadRight;
[GUI] ToggleButton _minus; [GUI] ToggleButton _minus;
[GUI] ToggleButton _screenshot;
[GUI] ToggleButton _l; [GUI] ToggleButton _l;
[GUI] ToggleButton _zL; [GUI] ToggleButton _zL;
[GUI] ToggleButton _rStick; [GUI] ToggleButton _rStick;
@ -165,6 +166,7 @@ namespace Ryujinx.UI.Windows
_dpadLeft.Clicked += Button_Pressed; _dpadLeft.Clicked += Button_Pressed;
_dpadRight.Clicked += Button_Pressed; _dpadRight.Clicked += Button_Pressed;
_minus.Clicked += Button_Pressed; _minus.Clicked += Button_Pressed;
_screenshot.Clicked += Button_Pressed;
_l.Clicked += Button_Pressed; _l.Clicked += Button_Pressed;
_zL.Clicked += Button_Pressed; _zL.Clicked += Button_Pressed;
_lSl.Clicked += Button_Pressed; _lSl.Clicked += Button_Pressed;
@ -400,6 +402,7 @@ namespace Ryujinx.UI.Windows
_dpadLeft.Label = "Unbound"; _dpadLeft.Label = "Unbound";
_dpadRight.Label = "Unbound"; _dpadRight.Label = "Unbound";
_minus.Label = "Unbound"; _minus.Label = "Unbound";
_screenshot.Label = "Unbound";
_l.Label = "Unbound"; _l.Label = "Unbound";
_zL.Label = "Unbound"; _zL.Label = "Unbound";
_lSl.Label = "Unbound"; _lSl.Label = "Unbound";
@ -460,6 +463,7 @@ namespace Ryujinx.UI.Windows
_dpadLeft.Label = keyboardConfig.LeftJoycon.DpadLeft.ToString(); _dpadLeft.Label = keyboardConfig.LeftJoycon.DpadLeft.ToString();
_dpadRight.Label = keyboardConfig.LeftJoycon.DpadRight.ToString(); _dpadRight.Label = keyboardConfig.LeftJoycon.DpadRight.ToString();
_minus.Label = keyboardConfig.LeftJoycon.ButtonMinus.ToString(); _minus.Label = keyboardConfig.LeftJoycon.ButtonMinus.ToString();
_screenshot.Label = keyboardConfig.LeftJoycon.ButtonScreenshot.ToString();
_l.Label = keyboardConfig.LeftJoycon.ButtonL.ToString(); _l.Label = keyboardConfig.LeftJoycon.ButtonL.ToString();
_zL.Label = keyboardConfig.LeftJoycon.ButtonZl.ToString(); _zL.Label = keyboardConfig.LeftJoycon.ButtonZl.ToString();
_lSl.Label = keyboardConfig.LeftJoycon.ButtonSl.ToString(); _lSl.Label = keyboardConfig.LeftJoycon.ButtonSl.ToString();
@ -498,6 +502,7 @@ namespace Ryujinx.UI.Windows
_dpadLeft.Label = controllerConfig.LeftJoycon.DpadLeft.ToString(); _dpadLeft.Label = controllerConfig.LeftJoycon.DpadLeft.ToString();
_dpadRight.Label = controllerConfig.LeftJoycon.DpadRight.ToString(); _dpadRight.Label = controllerConfig.LeftJoycon.DpadRight.ToString();
_minus.Label = controllerConfig.LeftJoycon.ButtonMinus.ToString(); _minus.Label = controllerConfig.LeftJoycon.ButtonMinus.ToString();
_screenshot.Label = controllerConfig.LeftJoycon.ButtonScreenshot.ToString();
_l.Label = controllerConfig.LeftJoycon.ButtonL.ToString(); _l.Label = controllerConfig.LeftJoycon.ButtonL.ToString();
_zL.Label = controllerConfig.LeftJoycon.ButtonZl.ToString(); _zL.Label = controllerConfig.LeftJoycon.ButtonZl.ToString();
_lSl.Label = controllerConfig.LeftJoycon.ButtonSl.ToString(); _lSl.Label = controllerConfig.LeftJoycon.ButtonSl.ToString();
@ -566,6 +571,7 @@ namespace Ryujinx.UI.Windows
Enum.TryParse(_dpadLeft.Label, out Key lDPadLeft); Enum.TryParse(_dpadLeft.Label, out Key lDPadLeft);
Enum.TryParse(_dpadRight.Label, out Key lDPadRight); Enum.TryParse(_dpadRight.Label, out Key lDPadRight);
Enum.TryParse(_minus.Label, out Key lButtonMinus); Enum.TryParse(_minus.Label, out Key lButtonMinus);
Enum.TryParse(_screenshot.Label, out Key lButtonScreenshot);
Enum.TryParse(_l.Label, out Key lButtonL); Enum.TryParse(_l.Label, out Key lButtonL);
Enum.TryParse(_zL.Label, out Key lButtonZl); Enum.TryParse(_zL.Label, out Key lButtonZl);
Enum.TryParse(_lSl.Label, out Key lButtonSl); Enum.TryParse(_lSl.Label, out Key lButtonSl);
@ -597,6 +603,7 @@ namespace Ryujinx.UI.Windows
LeftJoycon = new LeftJoyconCommonConfig<Key> LeftJoycon = new LeftJoyconCommonConfig<Key>
{ {
ButtonMinus = lButtonMinus, ButtonMinus = lButtonMinus,
ButtonScreenshot = lButtonScreenshot,
ButtonL = lButtonL, ButtonL = lButtonL,
ButtonZl = lButtonZl, ButtonZl = lButtonZl,
ButtonSl = lButtonSl, ButtonSl = lButtonSl,
@ -643,6 +650,7 @@ namespace Ryujinx.UI.Windows
Enum.TryParse(_lStick.Label, out ConfigStickInputId lStick); Enum.TryParse(_lStick.Label, out ConfigStickInputId lStick);
Enum.TryParse(_lStickButton.Label, out ConfigGamepadInputId lStickButton); Enum.TryParse(_lStickButton.Label, out ConfigGamepadInputId lStickButton);
Enum.TryParse(_minus.Label, out ConfigGamepadInputId lButtonMinus); Enum.TryParse(_minus.Label, out ConfigGamepadInputId lButtonMinus);
Enum.TryParse(_screenshot.Label, out ConfigGamepadInputId lButtonScreenshot);
Enum.TryParse(_l.Label, out ConfigGamepadInputId lButtonL); Enum.TryParse(_l.Label, out ConfigGamepadInputId lButtonL);
Enum.TryParse(_zL.Label, out ConfigGamepadInputId lButtonZl); Enum.TryParse(_zL.Label, out ConfigGamepadInputId lButtonZl);
Enum.TryParse(_lSl.Label, out ConfigGamepadInputId lButtonSl); Enum.TryParse(_lSl.Label, out ConfigGamepadInputId lButtonSl);
@ -710,6 +718,7 @@ namespace Ryujinx.UI.Windows
LeftJoycon = new LeftJoyconCommonConfig<ConfigGamepadInputId> LeftJoycon = new LeftJoyconCommonConfig<ConfigGamepadInputId>
{ {
ButtonMinus = lButtonMinus, ButtonMinus = lButtonMinus,
ButtonScreenshot = lButtonScreenshot,
ButtonL = lButtonL, ButtonL = lButtonL,
ButtonZl = lButtonZl, ButtonZl = lButtonZl,
ButtonSl = lButtonSl, ButtonSl = lButtonSl,
@ -997,6 +1006,7 @@ namespace Ryujinx.UI.Windows
DpadLeft = Key.Left, DpadLeft = Key.Left,
DpadRight = Key.Right, DpadRight = Key.Right,
ButtonMinus = Key.Minus, ButtonMinus = Key.Minus,
ButtonScreenshot = Key.Unbound, // keyboard already has a default screenshot configured via ConfigurationState.Hid.Hotkeys.Screenshot, so no gamepad mapping needed
ButtonL = Key.E, ButtonL = Key.E,
ButtonZl = Key.Q, ButtonZl = Key.Q,
ButtonSl = Key.Unbound, ButtonSl = Key.Unbound,
@ -1057,6 +1067,7 @@ namespace Ryujinx.UI.Windows
DpadLeft = ConfigGamepadInputId.DpadLeft, DpadLeft = ConfigGamepadInputId.DpadLeft,
DpadRight = ConfigGamepadInputId.DpadRight, DpadRight = ConfigGamepadInputId.DpadRight,
ButtonMinus = ConfigGamepadInputId.Minus, ButtonMinus = ConfigGamepadInputId.Minus,
ButtonScreenshot = ConfigGamepadInputId.Misc1,
ButtonL = ConfigGamepadInputId.LeftShoulder, ButtonL = ConfigGamepadInputId.LeftShoulder,
ButtonZl = ConfigGamepadInputId.LeftTrigger, ButtonZl = ConfigGamepadInputId.LeftTrigger,
ButtonSl = ConfigGamepadInputId.Unbound, ButtonSl = ConfigGamepadInputId.Unbound,

View file

@ -475,6 +475,31 @@
<property name="top_attach">4</property> <property name="top_attach">4</property>
</packing> </packing>
</child> </child>
<child>
<object class="GtkLabel">
<property name="width_request">80</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Screenshot</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">6</property>
</packing>
</child>
<child>
<object class="GtkToggleButton" id="_screenshot">
<property name="label" translatable="yes"> </property>
<property name="width_request">70</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">6</property>
</packing>
</child>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>

View file

@ -161,6 +161,7 @@ namespace Ryujinx.Headless.SDL2
DpadLeft = Key.Left, DpadLeft = Key.Left,
DpadRight = Key.Right, DpadRight = Key.Right,
ButtonMinus = Key.Minus, ButtonMinus = Key.Minus,
ButtonScreenshot = Key.Unbound, // keyboard already has a default screenshot configured via ConfigurationState.Hid.Hotkeys.Screenshot, so no gamepad mapping needed
ButtonL = Key.E, ButtonL = Key.E,
ButtonZl = Key.Q, ButtonZl = Key.Q,
ButtonSl = Key.Unbound, ButtonSl = Key.Unbound,
@ -221,6 +222,7 @@ namespace Ryujinx.Headless.SDL2
DpadLeft = ConfigGamepadInputId.DpadLeft, DpadLeft = ConfigGamepadInputId.DpadLeft,
DpadRight = ConfigGamepadInputId.DpadRight, DpadRight = ConfigGamepadInputId.DpadRight,
ButtonMinus = ConfigGamepadInputId.Minus, ButtonMinus = ConfigGamepadInputId.Minus,
ButtonScreenshot = ConfigGamepadInputId.Misc1,
ButtonL = ConfigGamepadInputId.LeftShoulder, ButtonL = ConfigGamepadInputId.LeftShoulder,
ButtonZl = ConfigGamepadInputId.LeftTrigger, ButtonZl = ConfigGamepadInputId.LeftTrigger,
ButtonSl = ConfigGamepadInputId.Unbound, ButtonSl = ConfigGamepadInputId.Unbound,

View file

@ -244,6 +244,7 @@ namespace Ryujinx.Input.SDL2
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadLeft, (GamepadButtonInputId)_configuration.LeftJoycon.DpadLeft)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadLeft, (GamepadButtonInputId)_configuration.LeftJoycon.DpadLeft));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadRight, (GamepadButtonInputId)_configuration.LeftJoycon.DpadRight)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadRight, (GamepadButtonInputId)_configuration.LeftJoycon.DpadRight));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Minus, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonMinus)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Minus, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonMinus));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Misc1, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonScreenshot));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftShoulder, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonL)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftShoulder, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonL));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftTrigger, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonZl)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftTrigger, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonZl));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger0, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonSr)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger0, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonSr));

View file

@ -372,6 +372,7 @@ namespace Ryujinx.Input.SDL2
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadLeft, (Key)_configuration.LeftJoycon.DpadLeft)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadLeft, (Key)_configuration.LeftJoycon.DpadLeft));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadRight, (Key)_configuration.LeftJoycon.DpadRight)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadRight, (Key)_configuration.LeftJoycon.DpadRight));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Minus, (Key)_configuration.LeftJoycon.ButtonMinus)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Minus, (Key)_configuration.LeftJoycon.ButtonMinus));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Misc1, (Key)_configuration.LeftJoycon.ButtonScreenshot));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftShoulder, (Key)_configuration.LeftJoycon.ButtonL)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftShoulder, (Key)_configuration.LeftJoycon.ButtonL));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftTrigger, (Key)_configuration.LeftJoycon.ButtonZl)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftTrigger, (Key)_configuration.LeftJoycon.ButtonZl));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger0, (Key)_configuration.LeftJoycon.ButtonSr)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger0, (Key)_configuration.LeftJoycon.ButtonSr));

View file

@ -888,6 +888,7 @@ namespace Ryujinx.UI.Common.Configuration
DpadLeft = Key.Left, DpadLeft = Key.Left,
DpadRight = Key.Right, DpadRight = Key.Right,
ButtonMinus = Key.Minus, ButtonMinus = Key.Minus,
ButtonScreenshot = Key.Unbound, // keyboard already has a default screenshot configured via ConfigurationState.Hid.Hotkeys.Screenshot, so no gamepad mapping needed
ButtonL = Key.E, ButtonL = Key.E,
ButtonZl = Key.Q, ButtonZl = Key.Q,
ButtonSl = Key.Unbound, ButtonSl = Key.Unbound,
@ -1117,6 +1118,7 @@ namespace Ryujinx.UI.Common.Configuration
DpadLeft = Key.Left, DpadLeft = Key.Left,
DpadRight = Key.Right, DpadRight = Key.Right,
ButtonMinus = Key.Minus, ButtonMinus = Key.Minus,
ButtonScreenshot = Key.Unbound, // keyboard already has a default screenshot configured via ConfigurationState.Hid.Hotkeys.Screenshot, so no gamepad mapping needed
ButtonL = Key.E, ButtonL = Key.E,
ButtonZl = Key.Q, ButtonZl = Key.Q,
ButtonSl = Key.Unbound, ButtonSl = Key.Unbound,

View file

@ -82,6 +82,7 @@ namespace Ryujinx.Ava
private readonly MainWindowViewModel _viewModel; private readonly MainWindowViewModel _viewModel;
private readonly IKeyboard _keyboardInterface; private readonly IKeyboard _keyboardInterface;
private readonly List<IGamepad> _gamepadInterfaces;
private readonly TopLevel _topLevel; private readonly TopLevel _topLevel;
public RendererHost RendererHost; public RendererHost RendererHost;
@ -89,6 +90,8 @@ namespace Ryujinx.Ava
private float _newVolume; private float _newVolume;
private KeyboardHotkeyState _prevHotkeyState; private KeyboardHotkeyState _prevHotkeyState;
private int _gamepadsChanged; // use atomically via Interlocked
private long _lastCursorMoveTime; private long _lastCursorMoveTime;
private bool _isCursorInRenderer = true; private bool _isCursorInRenderer = true;
private bool _ignoreCursorState = false; private bool _ignoreCursorState = false;
@ -160,6 +163,13 @@ namespace Ryujinx.Ava
_keyboardInterface = (IKeyboard)_inputManager.KeyboardDriver.GetGamepad("0"); _keyboardInterface = (IKeyboard)_inputManager.KeyboardDriver.GetGamepad("0");
_gamepadInterfaces = new List<IGamepad>();
_inputManager.GamepadDriver.OnGamepadConnected += GamepadConnected;
_inputManager.GamepadDriver.OnGamepadDisconnected += GamepadDisconnected;
RefreshGamepads();
NpadManager = _inputManager.CreateNpadManager(); NpadManager = _inputManager.CreateNpadManager();
TouchScreenManager = _inputManager.CreateTouchScreenManager(); TouchScreenManager = _inputManager.CreateTouchScreenManager();
ApplicationPath = applicationPath; ApplicationPath = applicationPath;
@ -1103,6 +1113,11 @@ namespace Ryujinx.Ava
return false; return false;
} }
if (Interlocked.Exchange(ref _gamepadsChanged, 0) == 1)
{
RefreshGamepads();
}
NpadManager.Update(ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat()); NpadManager.Update(ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat());
if (_viewModel.IsActive) if (_viewModel.IsActive)
@ -1241,7 +1256,8 @@ namespace Ryujinx.Ava
{ {
state = KeyboardHotkeyState.ToggleVSync; state = KeyboardHotkeyState.ToggleVSync;
} }
else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot)) else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot) ||
ButtonPressedOnAnyGamepad(GamepadButtonInputId.Misc1))
{ {
state = KeyboardHotkeyState.Screenshot; state = KeyboardHotkeyState.Screenshot;
} }
@ -1276,5 +1292,37 @@ namespace Ryujinx.Ava
return state; return state;
} }
private void GamepadConnected(string id)
{
Interlocked.Exchange(ref _gamepadsChanged, 1);
}
private void GamepadDisconnected(string id)
{
Interlocked.Exchange(ref _gamepadsChanged, 1);
}
private void RefreshGamepads()
{
_gamepadInterfaces.Clear();
foreach (string id in _inputManager.GamepadDriver.GamepadsIds)
{
_gamepadInterfaces.Add(_inputManager.GamepadDriver.GetGamepad(id));
}
}
private bool ButtonPressedOnAnyGamepad(GamepadButtonInputId button)
{
foreach (IGamepad gamepad in _gamepadInterfaces)
{
if (gamepad.IsPressed(button))
{
return true;
}
}
return false;
}
} }
} }

View file

@ -233,6 +233,8 @@
"ControllerSettingsDPadDown": "Runter", "ControllerSettingsDPadDown": "Runter",
"ControllerSettingsDPadLeft": "Links", "ControllerSettingsDPadLeft": "Links",
"ControllerSettingsDPadRight": "Rechts", "ControllerSettingsDPadRight": "Rechts",
"ControllerSettingsRawScreenshot": "Unbearbeiteter Screenshot",
"ControllerSettingsRawScreenshotTooltip": "Macht ein Bildschirmfoto, ohne das Spiel zu informieren, sodass keine Wasserzeichen hinzugefügt werden.",
"ControllerSettingsStickButton": "Button", "ControllerSettingsStickButton": "Button",
"ControllerSettingsStickUp": "Hoch", "ControllerSettingsStickUp": "Hoch",
"ControllerSettingsStickDown": "Runter", "ControllerSettingsStickDown": "Runter",

View file

@ -234,6 +234,8 @@
"ControllerSettingsDPadDown": "Down", "ControllerSettingsDPadDown": "Down",
"ControllerSettingsDPadLeft": "Left", "ControllerSettingsDPadLeft": "Left",
"ControllerSettingsDPadRight": "Right", "ControllerSettingsDPadRight": "Right",
"ControllerSettingsRawScreenshot": "Raw Screenshot",
"ControllerSettingsRawScreenshotTooltip": "Takes a screenshot without informing the game, so no watermarks will be added.",
"ControllerSettingsStickButton": "Button", "ControllerSettingsStickButton": "Button",
"ControllerSettingsStickUp": "Up", "ControllerSettingsStickUp": "Up",
"ControllerSettingsStickDown": "Down", "ControllerSettingsStickDown": "Down",

View file

@ -128,6 +128,7 @@ namespace Ryujinx.Ava.Input
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadLeft, (Key)_configuration.LeftJoycon.DpadLeft)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadLeft, (Key)_configuration.LeftJoycon.DpadLeft));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadRight, (Key)_configuration.LeftJoycon.DpadRight)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadRight, (Key)_configuration.LeftJoycon.DpadRight));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Minus, (Key)_configuration.LeftJoycon.ButtonMinus)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Minus, (Key)_configuration.LeftJoycon.ButtonMinus));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Misc1, (Key)_configuration.LeftJoycon.ButtonScreenshot));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftShoulder, (Key)_configuration.LeftJoycon.ButtonL)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftShoulder, (Key)_configuration.LeftJoycon.ButtonL));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftTrigger, (Key)_configuration.LeftJoycon.ButtonZl)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftTrigger, (Key)_configuration.LeftJoycon.ButtonZl));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger0, (Key)_configuration.LeftJoycon.ButtonSr)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger0, (Key)_configuration.LeftJoycon.ButtonSr));

View file

@ -178,6 +178,17 @@ namespace Ryujinx.Ava.UI.Models.Input
} }
} }
private GamepadInputId _buttonScreenshot;
public GamepadInputId ButtonScreenshot
{
get => _buttonScreenshot;
set
{
_buttonScreenshot = value;
OnPropertyChanged();
}
}
private GamepadInputId _buttonL; private GamepadInputId _buttonL;
public GamepadInputId ButtonL public GamepadInputId ButtonL
{ {
@ -440,6 +451,7 @@ namespace Ryujinx.Ava.UI.Models.Input
DpadRight = controllerInput.LeftJoycon.DpadRight; DpadRight = controllerInput.LeftJoycon.DpadRight;
ButtonL = controllerInput.LeftJoycon.ButtonL; ButtonL = controllerInput.LeftJoycon.ButtonL;
ButtonMinus = controllerInput.LeftJoycon.ButtonMinus; ButtonMinus = controllerInput.LeftJoycon.ButtonMinus;
ButtonScreenshot = controllerInput.LeftJoycon.ButtonScreenshot;
LeftButtonSl = controllerInput.LeftJoycon.ButtonSl; LeftButtonSl = controllerInput.LeftJoycon.ButtonSl;
LeftButtonSr = controllerInput.LeftJoycon.ButtonSr; LeftButtonSr = controllerInput.LeftJoycon.ButtonSr;
ButtonZl = controllerInput.LeftJoycon.ButtonZl; ButtonZl = controllerInput.LeftJoycon.ButtonZl;
@ -502,6 +514,7 @@ namespace Ryujinx.Ava.UI.Models.Input
DpadRight = DpadRight, DpadRight = DpadRight,
ButtonL = ButtonL, ButtonL = ButtonL,
ButtonMinus = ButtonMinus, ButtonMinus = ButtonMinus,
ButtonScreenshot = ButtonScreenshot,
ButtonSl = LeftButtonSl, ButtonSl = LeftButtonSl,
ButtonSr = LeftButtonSr, ButtonSr = LeftButtonSr,
ButtonZl = ButtonZl, ButtonZl = ButtonZl,

View file

@ -381,6 +381,7 @@ namespace Ryujinx.Ava.UI.Models.Input
DpadRight = DpadRight, DpadRight = DpadRight,
ButtonL = ButtonL, ButtonL = ButtonL,
ButtonMinus = ButtonMinus, ButtonMinus = ButtonMinus,
ButtonScreenshot = Key.Unbound, // keyboard screenshot configured via ConfigurationState.Hid.Hotkeys.Screenshot, so no gamepad mapping needed
ButtonZl = ButtonZl, ButtonZl = ButtonZl,
ButtonSl = LeftButtonSl, ButtonSl = LeftButtonSl,
ButtonSr = LeftButtonSr, ButtonSr = LeftButtonSr,

View file

@ -545,6 +545,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
DpadLeft = Key.Left, DpadLeft = Key.Left,
DpadRight = Key.Right, DpadRight = Key.Right,
ButtonMinus = Key.Minus, ButtonMinus = Key.Minus,
ButtonScreenshot = Key.Unbound, // keyboard already has a default screenshot configured via ConfigurationState.Hid.Hotkeys.Screenshot, so no gamepad mapping needed
ButtonL = Key.E, ButtonL = Key.E,
ButtonZl = Key.Q, ButtonZl = Key.Q,
ButtonSl = Key.Unbound, ButtonSl = Key.Unbound,
@ -605,6 +606,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
DpadLeft = ConfigGamepadInputId.DpadLeft, DpadLeft = ConfigGamepadInputId.DpadLeft,
DpadRight = ConfigGamepadInputId.DpadRight, DpadRight = ConfigGamepadInputId.DpadRight,
ButtonMinus = ConfigGamepadInputId.Minus, ButtonMinus = ConfigGamepadInputId.Minus,
ButtonScreenshot = ConfigGamepadInputId.Misc1,
ButtonL = ConfigGamepadInputId.LeftShoulder, ButtonL = ConfigGamepadInputId.LeftShoulder,
ButtonZl = ConfigGamepadInputId.LeftTrigger, ButtonZl = ConfigGamepadInputId.LeftTrigger,
ButtonSl = ConfigGamepadInputId.Unbound, ButtonSl = ConfigGamepadInputId.Unbound,

View file

@ -311,6 +311,36 @@
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</Border> </Border>
<!-- Screenshot -->
<Border
BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1"
IsVisible="{Binding IsLeft}"
Margin="0,5,0,0"
CornerRadius="5">
<StackPanel
Margin="10"
Orientation="Vertical">
<!-- Raw Screenshot -->
<StackPanel
Margin="0,0,0,4"
Orientation="Horizontal">
<TextBlock
Margin="0,0,10,0"
Width="120"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsRawScreenshot}"
ToolTip.Tip="{locale:Locale ControllerSettingsRawScreenshotTooltip}"
TextAlignment="Center" />
<ToggleButton Name="ButtonScreenshot">
<TextBlock
Text="{Binding Config.ButtonScreenshot, Converter={StaticResource Key}}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
</StackPanel>
</Border>
</StackPanel> </StackPanel>
<!-- Triggers & Side Buttons --> <!-- Triggers & Side Buttons -->
<StackPanel <StackPanel

View file

@ -84,6 +84,9 @@ namespace Ryujinx.Ava.UI.Views.Input
case "ButtonMinus": case "ButtonMinus":
viewModel.Config.ButtonMinus = buttonValue.AsHidType<GamepadInputId>(); viewModel.Config.ButtonMinus = buttonValue.AsHidType<GamepadInputId>();
break; break;
case "ButtonScreenshot":
viewModel.Config.ButtonScreenshot = buttonValue.AsHidType<GamepadInputId>();
break;
case "LeftStickButton": case "LeftStickButton":
viewModel.Config.LeftStickButton = buttonValue.AsHidType<GamepadInputId>(); viewModel.Config.LeftStickButton = buttonValue.AsHidType<GamepadInputId>();
break; break;