From 17b0a77916204830c96a96baf79923176ed945ac Mon Sep 17 00:00:00 2001 From: gdk Date: Thu, 2 Mar 2023 23:00:19 -0300 Subject: [PATCH] Add missing hotkeys, allow modifiers and controller button mappings on Avalonia --- Ryujinx.Ava/AppHost.cs | 164 ++++++++++++++---- Ryujinx.Ava/Assets/Locales/en_US.json | 3 + Ryujinx.Ava/Common/KeyboardHotkeyState.cs | 11 +- Ryujinx.Ava/Input/AvaloniaKeyboard.cs | 12 ++ Ryujinx.Ava/Input/AvaloniaKeyboardDriver.cs | 63 +++++-- Ryujinx.Ava/UI/Helpers/ButtonKeyAssigner.cs | 2 +- .../UI/Helpers/HotkeyButtonKeyAssigner.cs | 150 ++++++++++++++++ .../ViewModels/ControllerSettingsViewModel.cs | 7 +- .../UI/ViewModels/MainWindowViewModel.cs | 6 +- .../UI/ViewModels/SettingsViewModel.cs | 144 +++++++++++++++ .../Views/Settings/SettingsHotkeysView.axaml | 82 +++++---- .../Settings/SettingsHotkeysView.axaml.cs | 56 ++++-- .../UI/Windows/SettingsWindow.axaml.cs | 1 + Ryujinx.Common/Configuration/Hid/Hotkey.cs | 30 ++++ .../Configuration/Hid/KeyModifier.cs | 14 ++ .../Configuration/Hid/KeyboardHotkeys.cs | 21 ++- .../Assigner/GamepadButtonAssigner.cs | 12 +- Ryujinx.Input/Assigner/IButtonAssigner.cs | 8 + Ryujinx.Input/Assigner/KeyboardKeyAssigner.cs | 62 ++++++- Ryujinx.Input/Assigner/MultiButtonAssigner.cs | 72 ++++++++ Ryujinx.Input/Assigner/PressedButton.cs | 55 ++++++ Ryujinx.Input/Assigner/PressedButtonType.cs | 10 ++ Ryujinx.Input/GamepadFeaturesFlag.cs | 2 +- Ryujinx.Input/Key.cs | 2 +- .../Configuration/ConfigurationState.cs | 42 ++--- Ryujinx/Ui/RendererWidgetBase.cs | 18 +- 26 files changed, 901 insertions(+), 148 deletions(-) create mode 100644 Ryujinx.Ava/UI/Helpers/HotkeyButtonKeyAssigner.cs create mode 100644 Ryujinx.Common/Configuration/Hid/Hotkey.cs create mode 100644 Ryujinx.Common/Configuration/Hid/KeyModifier.cs create mode 100644 Ryujinx.Input/Assigner/MultiButtonAssigner.cs create mode 100644 Ryujinx.Input/Assigner/PressedButton.cs create mode 100644 Ryujinx.Input/Assigner/PressedButtonType.cs diff --git a/Ryujinx.Ava/AppHost.cs b/Ryujinx.Ava/AppHost.cs index eb22b39e9..2d354ec7f 100644 --- a/Ryujinx.Ava/AppHost.cs +++ b/Ryujinx.Ava/AppHost.cs @@ -19,6 +19,7 @@ using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.Windows; using Ryujinx.Common; using Ryujinx.Common.Configuration; +using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Logging; using Ryujinx.Common.SystemInterop; using Ryujinx.Graphics.GAL; @@ -44,6 +45,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Numerics; using System.Threading; using System.Threading.Tasks; using static Ryujinx.Ava.UI.Helpers.Win32NativeInterop; @@ -77,6 +79,7 @@ namespace Ryujinx.Ava private readonly MainWindowViewModel _viewModel; private readonly IKeyboard _keyboardInterface; + private readonly List _gamepadInterfaces; private readonly TopLevel _topLevel; public RendererHost _rendererHost; @@ -84,6 +87,8 @@ namespace Ryujinx.Ava private float _newVolume; private KeyboardHotkeyState _prevHotkeyState; + private int _gamepadsChanged; + private long _lastCursorMoveTime; private bool _isCursorInRenderer; @@ -139,6 +144,13 @@ namespace Ryujinx.Ava _keyboardInterface = (IKeyboard)_inputManager.KeyboardDriver.GetGamepad("0"); + _gamepadInterfaces = new List(); + + _inputManager.GamepadDriver.OnGamepadConnected += GamepadConnected; + _inputManager.GamepadDriver.OnGamepadDisconnected += GamepadDisconnected; + + RefreshGamepads(); + NpadManager = _inputManager.CreateNpadManager(); TouchScreenManager = _inputManager.CreateTouchScreenManager(); ApplicationPath = applicationPath; @@ -916,6 +928,11 @@ namespace Ryujinx.Ava return false; } + if (Interlocked.Exchange(ref _gamepadsChanged, 0) == 1) + { + RefreshGamepads(); + } + NpadManager.Update(ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat()); if (_viewModel.IsActive) @@ -960,15 +977,11 @@ namespace Ryujinx.Ava { switch (currentHotkeyState) { - case KeyboardHotkeyState.ToggleVSync: - Device.EnableDeviceVsync = !Device.EnableDeviceVsync; - + case KeyboardHotkeyState.None: + (_keyboardInterface as AvaloniaKeyboard).Clear(); break; - case KeyboardHotkeyState.Screenshot: - ScreenshotRequested = true; - break; - case KeyboardHotkeyState.ShowUi: - _viewModel.ShowMenuAndStatusBar = true; + case KeyboardHotkeyState.Exit: + _viewModel.ExitCurrentState(); break; case KeyboardHotkeyState.Pause: if (_viewModel.IsPaused) @@ -980,6 +993,24 @@ namespace Ryujinx.Ava Pause(); } break; + case KeyboardHotkeyState.ResScaleUp: + GraphicsConfig.ResScale = GraphicsConfig.ResScale % MaxResolutionScale + 1; + break; + case KeyboardHotkeyState.ResScaleDown: + GraphicsConfig.ResScale = (MaxResolutionScale + GraphicsConfig.ResScale - 2) % MaxResolutionScale + 1; + break; + case KeyboardHotkeyState.Screenshot: + ScreenshotRequested = true; + break; + case KeyboardHotkeyState.ShowUi: + _viewModel.ShowMenuAndStatusBar = true; + break; + case KeyboardHotkeyState.ToggleDockedMode: + _viewModel.ToggleDockMode(); + break; + case KeyboardHotkeyState.ToggleFullscreen: + _viewModel.ToggleFullscreen(); + break; case KeyboardHotkeyState.ToggleMute: if (Device.IsAudioMuted()) { @@ -992,12 +1023,8 @@ namespace Ryujinx.Ava _viewModel.Volume = Device.GetVolume(); break; - case KeyboardHotkeyState.ResScaleUp: - GraphicsConfig.ResScale = GraphicsConfig.ResScale % MaxResolutionScale + 1; - break; - case KeyboardHotkeyState.ResScaleDown: - GraphicsConfig.ResScale = - (MaxResolutionScale + GraphicsConfig.ResScale - 2) % MaxResolutionScale + 1; + case KeyboardHotkeyState.ToggleVSync: + Device.EnableDeviceVsync = !Device.EnableDeviceVsync; break; case KeyboardHotkeyState.VolumeUp: _newVolume = MathF.Round((Device.GetVolume() + VolumeDelta), 2); @@ -1011,9 +1038,6 @@ namespace Ryujinx.Ava _viewModel.Volume = Device.GetVolume(); break; - case KeyboardHotkeyState.None: - (_keyboardInterface as AvaloniaKeyboard).Clear(); - break; } } @@ -1048,44 +1072,112 @@ namespace Ryujinx.Ava { KeyboardHotkeyState state = KeyboardHotkeyState.None; - if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ToggleVsync)) + if (IsHotkeyPressed(ConfigurationState.Instance.Hid.Hotkeys.Value.Exit)) { - state = KeyboardHotkeyState.ToggleVSync; + state = KeyboardHotkeyState.Exit; } - else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot)) - { - state = KeyboardHotkeyState.Screenshot; - } - else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUi)) - { - state = KeyboardHotkeyState.ShowUi; - } - else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Pause)) + else if (IsHotkeyPressed(ConfigurationState.Instance.Hid.Hotkeys.Value.Pause)) { state = KeyboardHotkeyState.Pause; } - else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ToggleMute)) - { - state = KeyboardHotkeyState.ToggleMute; - } - else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ResScaleUp)) + else if (IsHotkeyPressed(ConfigurationState.Instance.Hid.Hotkeys.Value.ResScaleUp)) { state = KeyboardHotkeyState.ResScaleUp; } - else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ResScaleDown)) + else if (IsHotkeyPressed(ConfigurationState.Instance.Hid.Hotkeys.Value.ResScaleDown)) { state = KeyboardHotkeyState.ResScaleDown; } - else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.VolumeUp)) + else if (IsHotkeyPressed(ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot)) + { + state = KeyboardHotkeyState.Screenshot; + } + else if (IsHotkeyPressed(ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUi)) + { + state = KeyboardHotkeyState.ShowUi; + } + else if (IsHotkeyPressed(ConfigurationState.Instance.Hid.Hotkeys.Value.ToggleDockedMode)) + { + state = KeyboardHotkeyState.ToggleDockedMode; + } + else if (IsHotkeyPressed(ConfigurationState.Instance.Hid.Hotkeys.Value.ToggleFullscreen)) + { + state = KeyboardHotkeyState.ToggleFullscreen; + } + else if (IsHotkeyPressed(ConfigurationState.Instance.Hid.Hotkeys.Value.ToggleMute)) + { + state = KeyboardHotkeyState.ToggleMute; + } + else if (IsHotkeyPressed(ConfigurationState.Instance.Hid.Hotkeys.Value.ToggleVsync)) + { + state = KeyboardHotkeyState.ToggleVSync; + } + else if (IsHotkeyPressed(ConfigurationState.Instance.Hid.Hotkeys.Value.VolumeUp)) { state = KeyboardHotkeyState.VolumeUp; } - else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.VolumeDown)) + else if (IsHotkeyPressed(ConfigurationState.Instance.Hid.Hotkeys.Value.VolumeDown)) { state = KeyboardHotkeyState.VolumeDown; } 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 IsHotkeyPressed(Hotkey hotkey) + { + if (hotkey.HasKeyboard() && + (_keyboardInterface as AvaloniaKeyboard).IsPressed((Ryujinx.Input.Key)hotkey.Key, hotkey.Modifier)) + { + return true; + } + + if (hotkey.HasGamepad()) + { + foreach (IGamepad gamepad in _gamepadInterfaces) + { + ulong mask = hotkey.GamepadInputMask; + + while (mask != 0UL) + { + int bit = BitOperations.TrailingZeroCount(mask); + + if (!gamepad.IsPressed((GamepadButtonInputId)bit)) + { + break; + } + + mask &= ~(1UL << bit); + } + + if (mask == 0UL) + { + return true; + } + } + } + + return false; + } } } \ No newline at end of file diff --git a/Ryujinx.Ava/Assets/Locales/en_US.json b/Ryujinx.Ava/Assets/Locales/en_US.json index db8d24241..844484644 100644 --- a/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/Ryujinx.Ava/Assets/Locales/en_US.json @@ -565,6 +565,9 @@ "RyujinxUpdater": "Ryujinx Updater", "SettingsTabHotkeys": "Keyboard Hotkeys", "SettingsTabHotkeysHotkeys": "Keyboard Hotkeys", + "SettingsTabHotkeysExitHotkey": "Exit:", + "SettingsTabHotkeysToggleDockedModeHotkey": "Toggle Docked Mode:", + "SettingsTabHotkeysToggleFullscreenHotkey": "Toggle Fullscreen:", "SettingsTabHotkeysToggleVsyncHotkey": "Toggle VSync:", "SettingsTabHotkeysScreenshotHotkey": "Screenshot:", "SettingsTabHotkeysShowUiHotkey": "Show UI:", diff --git a/Ryujinx.Ava/Common/KeyboardHotkeyState.cs b/Ryujinx.Ava/Common/KeyboardHotkeyState.cs index e85bdf341..92774f86e 100644 --- a/Ryujinx.Ava/Common/KeyboardHotkeyState.cs +++ b/Ryujinx.Ava/Common/KeyboardHotkeyState.cs @@ -3,13 +3,16 @@ public enum KeyboardHotkeyState { None, - ToggleVSync, - Screenshot, - ShowUi, + Exit, Pause, - ToggleMute, ResScaleUp, ResScaleDown, + Screenshot, + ShowUi, + ToggleDockedMode, + ToggleFullscreen, + ToggleMute, + ToggleVSync, VolumeUp, VolumeDown } diff --git a/Ryujinx.Ava/Input/AvaloniaKeyboard.cs b/Ryujinx.Ava/Input/AvaloniaKeyboard.cs index d40ebbd2b..0e13bc6d7 100644 --- a/Ryujinx.Ava/Input/AvaloniaKeyboard.cs +++ b/Ryujinx.Ava/Input/AvaloniaKeyboard.cs @@ -112,6 +112,18 @@ namespace Ryujinx.Ava.Input } } + public bool IsPressed(Key key, KeyModifier modifier) + { + try + { + return _driver.IsPressed(key, modifier); + } + catch + { + return false; + } + } + public void SetConfiguration(InputConfig configuration) { lock (_userMappingLock) diff --git a/Ryujinx.Ava/Input/AvaloniaKeyboardDriver.cs b/Ryujinx.Ava/Input/AvaloniaKeyboardDriver.cs index edcdb52fd..c086e51cf 100644 --- a/Ryujinx.Ava/Input/AvaloniaKeyboardDriver.cs +++ b/Ryujinx.Ava/Input/AvaloniaKeyboardDriver.cs @@ -2,6 +2,8 @@ using Avalonia.Input; using Avalonia.Interactivity; using Ryujinx.Ava.Common.Locale; +using Ryujinx.Common.Configuration.Hid; +using Ryujinx.Common.Configuration.Hid.Keyboard; using Ryujinx.Input; using System; using System.Collections.Generic; @@ -13,23 +15,23 @@ namespace Ryujinx.Ava.Input internal class AvaloniaKeyboardDriver : IGamepadDriver { private static readonly string[] _keyboardIdentifers = new string[1] { "0" }; - private readonly Control _control; - private readonly HashSet _pressedKeys; + private readonly Control _control; + private readonly Dictionary _pressedKeys; public event EventHandler KeyPressed; public event EventHandler KeyRelease; - public event EventHandler TextInput; + public event EventHandler TextInput; - public string DriverName => "AvaloniaKeyboardDriver"; + public string DriverName => "AvaloniaKeyboardDriver"; public ReadOnlySpan GamepadsIds => _keyboardIdentifers; public AvaloniaKeyboardDriver(Control control) { - _control = control; - _pressedKeys = new HashSet(); + _control = control; + _pressedKeys = new Dictionary(); - _control.KeyDown += OnKeyPress; - _control.KeyUp += OnKeyRelease; + _control.KeyDown += OnKeyPress; + _control.KeyUp += OnKeyRelease; _control.TextInput += Control_TextInput; _control.AddHandler(InputElement.TextInputEvent, Control_LastChanceTextInput, RoutingStrategies.Bubble); } @@ -47,13 +49,13 @@ namespace Ryujinx.Ava.Input public event Action OnGamepadConnected { - add { } + add { } remove { } } public event Action OnGamepadDisconnected { - add { } + add { } remove { } } @@ -71,14 +73,14 @@ namespace Ryujinx.Ava.Input { if (disposing) { - _control.KeyUp -= OnKeyPress; + _control.KeyUp -= OnKeyPress; _control.KeyDown -= OnKeyRelease; } } protected void OnKeyPress(object sender, KeyEventArgs args) { - _pressedKeys.Add(args.Key); + _pressedKeys[args.Key] = args.KeyModifiers; KeyPressed?.Invoke(this, args); } @@ -99,7 +101,42 @@ namespace Ryujinx.Ava.Input AvaloniaKeyboardMappingHelper.TryGetAvaKey(key, out var nativeKey); - return _pressedKeys.Contains(nativeKey); + return _pressedKeys.TryGetValue(nativeKey, out _); + } + + internal bool IsPressed(Key key, KeyModifier modifier) + { + if (key == Key.Unbound || key == Key.Unknown) + { + return false; + } + + AvaloniaKeyboardMappingHelper.TryGetAvaKey(key, out var nativeKey); + bool keyPressed = _pressedKeys.TryGetValue(nativeKey, out var modifiers); + + KeyModifiers avaModifiers = KeyModifiers.None; + + if (modifier.HasFlag(KeyModifier.Alt)) + { + avaModifiers |= KeyModifiers.Alt; + } + + if (modifier.HasFlag(KeyModifier.Control)) + { + avaModifiers |= KeyModifiers.Control; + } + + if (modifier.HasFlag(KeyModifier.Shift)) + { + avaModifiers |= KeyModifiers.Shift; + } + + if (modifier.HasFlag(KeyModifier.Meta)) + { + avaModifiers |= KeyModifiers.Meta; + } + + return keyPressed && (modifiers & avaModifiers) == avaModifiers; } public void ResetKeys() diff --git a/Ryujinx.Ava/UI/Helpers/ButtonKeyAssigner.cs b/Ryujinx.Ava/UI/Helpers/ButtonKeyAssigner.cs index 6730b5711..7e8ba7342 100644 --- a/Ryujinx.Ava/UI/Helpers/ButtonKeyAssigner.cs +++ b/Ryujinx.Ava/UI/Helpers/ButtonKeyAssigner.cs @@ -23,7 +23,7 @@ namespace Ryujinx.Ava.UI.Helpers IsAssigned = isAssigned; } } - + public ToggleButton ToggledButton { get; set; } private bool _isWaitingForInput; diff --git a/Ryujinx.Ava/UI/Helpers/HotkeyButtonKeyAssigner.cs b/Ryujinx.Ava/UI/Helpers/HotkeyButtonKeyAssigner.cs new file mode 100644 index 000000000..d6095932b --- /dev/null +++ b/Ryujinx.Ava/UI/Helpers/HotkeyButtonKeyAssigner.cs @@ -0,0 +1,150 @@ +using Avalonia.Controls.Primitives; +using Avalonia.Threading; +using Ryujinx.Common.Configuration.Hid; +using Ryujinx.Input.Assigner; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using HidKey = Ryujinx.Common.Configuration.Hid.Key; +using Key = Ryujinx.Input.Key; + +namespace Ryujinx.Ava.UI.Helpers +{ + internal class HotkeyButtonKeyAssigner + { + internal class ButtonAssignedEventArgs : EventArgs + { + public ToggleButton Button { get; } + public bool IsAssigned { get; } + + public ButtonAssignedEventArgs(ToggleButton button, bool isAssigned) + { + Button = button; + IsAssigned = isAssigned; + } + } + + public ToggleButton ToggledButton { get; set; } + + private readonly Action _updateHotkeyCallback; + private bool _isWaitingForInput; + private bool _shouldUnbind; + public event EventHandler ButtonAssigned; + + public HotkeyButtonKeyAssigner(ToggleButton toggleButton, Action updateHotkeyCallback) + { + ToggledButton = toggleButton; + _updateHotkeyCallback = updateHotkeyCallback; + } + + public async void GetInputAndAssign(IButtonAssigner assigner) + { + Dispatcher.UIThread.Post(() => + { + ToggledButton.IsChecked = true; + }); + + if (_isWaitingForInput) + { + Dispatcher.UIThread.Post(() => + { + Cancel(); + }); + + return; + } + + _isWaitingForInput = true; + + assigner.Initialize(); + + await Task.Run(async () => + { + while (true) + { + if (!_isWaitingForInput) + { + return; + } + + await Task.Delay(10); + + assigner.ReadInput(); + + if (assigner.HasAnyButtonPressed() || assigner.ShouldCancel()) + { + break; + } + } + }); + + await Dispatcher.UIThread.InvokeAsync(() => + { + IEnumerable pressedButtons = assigner.GetPressedButtons(); + bool isAssigned = pressedButtons.Any(); + + if (_shouldUnbind) + { + _updateHotkeyCallback((string)ToggledButton.Tag, new Hotkey(HidKey.Unbound)); + } + else if (isAssigned) + { + _updateHotkeyCallback((string)ToggledButton.Tag, CreateHotkey(pressedButtons)); + } + + _shouldUnbind = false; + _isWaitingForInput = false; + + ToggledButton.IsChecked = false; + + ButtonAssigned?.Invoke(this, new ButtonAssignedEventArgs(ToggledButton, isAssigned)); + }); + } + + private Hotkey CreateHotkey(IEnumerable buttons) + { + Key pressedKey = Key.Unknown; + KeyModifier modifier = KeyModifier.None; + ulong gamepadInputMask = 0UL; + + foreach (PressedButton button in buttons) + { + if (button.Type == PressedButtonType.Key) + { + Key key = button.AsKey(); + + if (key == Key.ControlLeft || key == Key.ControlRight) + { + modifier |= KeyModifier.Control; + } + else if (key == Key.AltLeft || key == Key.AltRight) + { + modifier |= KeyModifier.Alt; + } + else if (key == Key.ShiftLeft || key == Key.ShiftRight) + { + modifier |= KeyModifier.Shift; + } + else + { + pressedKey = key; + } + } + else if (button.Type == PressedButtonType.Button) + { + gamepadInputMask |= 1UL << (int)button.AsGamepadButtonInputId(); + } + } + + return new Hotkey((HidKey)pressedKey, modifier, gamepadInputMask); + } + + public void Cancel(bool shouldUnbind = false) + { + _isWaitingForInput = false; + ToggledButton.IsChecked = false; + _shouldUnbind = shouldUnbind; + } + } +} diff --git a/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs b/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs index f63fc3491..0cff77a1d 100644 --- a/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs @@ -3,11 +3,8 @@ using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Svg.Skia; using Avalonia.Threading; -using LibHac.Bcat; -using LibHac.Tools.Fs; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Input; -using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.Windows; @@ -238,9 +235,7 @@ namespace Ryujinx.Ava.UI.ViewModels if (Program.PreviewerDetached) { - _mainWindow = - (MainWindow)((IClassicDesktopStyleApplicationLifetime)Avalonia.Application.Current - .ApplicationLifetime).MainWindow; + _mainWindow = (MainWindow)((IClassicDesktopStyleApplicationLifetime)Avalonia.Application.Current.ApplicationLifetime).MainWindow; AvaloniaKeyboardDriver = new AvaloniaKeyboardDriver(owner); diff --git a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs index 489dfe621..fc3bf9481 100644 --- a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs @@ -1220,17 +1220,17 @@ namespace Ryujinx.Ava.UI.ViewModels public void LoadConfigurableHotKeys() { - if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUi, out var showUiKey)) + if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUi.Key, out var showUiKey)) { ShowUiKey = new KeyGesture(showUiKey); } - if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot, out var screenshotKey)) + if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot.Key, out var screenshotKey)) { ScreenshotKey = new KeyGesture(screenshotKey); } - if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Pause, out var pauseKey)) + if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Pause.Key, out var pauseKey)) { PauseKey = new KeyGesture(pauseKey); } diff --git a/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs b/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs index 7045c9ed3..4010b9fc9 100644 --- a/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs @@ -11,6 +11,7 @@ using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Windows; using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration.Hid; +using Ryujinx.Common.Configuration.Hid.Controller; using Ryujinx.Common.GraphicsDriver; using Ryujinx.Common.Logging; using Ryujinx.Graphics.Vulkan; @@ -22,6 +23,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; +using System.Numerics; using System.Runtime.InteropServices; using TimeZone = Ryujinx.Ava.UI.Models.TimeZone; @@ -240,6 +242,19 @@ namespace Ryujinx.Ava.UI.ViewModels public AvaloniaList GameDirectories { get; set; } public ObservableCollection AvailableGpus { get; set; } + public string ExitHotkey => GetButtonName(_keyboardHotkeys.Exit); + public string PauseHotkey => GetButtonName(_keyboardHotkeys.Pause); + public string ResScaleUpHotkey => GetButtonName(_keyboardHotkeys.ResScaleUp); + public string ResScaleDownHotkey => GetButtonName(_keyboardHotkeys.ResScaleDown); + public string ScreenshotHotkey => GetButtonName(_keyboardHotkeys.Screenshot); + public string ShowUiHotkey => GetButtonName(_keyboardHotkeys.ShowUi); + public string ToggleDockedModeHotkey => GetButtonName(_keyboardHotkeys.ToggleDockedMode); + public string ToggleFullscreenHotkey => GetButtonName(_keyboardHotkeys.ToggleFullscreen); + public string ToggleMuteHotkey => GetButtonName(_keyboardHotkeys.ToggleMute); + public string ToggleVsyncHotkey => GetButtonName(_keyboardHotkeys.ToggleVsync); + public string VolumeUpHotkey => GetButtonName(_keyboardHotkeys.VolumeUp); + public string VolumeDownHotkey => GetButtonName(_keyboardHotkeys.VolumeDown); + public KeyboardHotkeys KeyboardHotkeys { get => _keyboardHotkeys; @@ -277,6 +292,135 @@ namespace Ryujinx.Ava.UI.ViewModels } } + public void UpdateHotkey(string name, Hotkey hotkey) + { + switch (name) + { + case "ExitHotkey": + _keyboardHotkeys.Exit = UpdateHotkey(_keyboardHotkeys.Exit, hotkey); + break; + case "PauseHotkey": + _keyboardHotkeys.Pause = UpdateHotkey(_keyboardHotkeys.Pause, hotkey); + break; + case "ResScaleUpHotkey": + _keyboardHotkeys.ResScaleUp = UpdateHotkey(_keyboardHotkeys.ResScaleUp, hotkey); + break; + case "ResScaleDownHotkey": + _keyboardHotkeys.ResScaleDown = UpdateHotkey(_keyboardHotkeys.ResScaleDown, hotkey); + break; + case "ScreenshotHotkey": + _keyboardHotkeys.Screenshot = UpdateHotkey(_keyboardHotkeys.Screenshot, hotkey); + break; + case "ShowUiHotkey": + _keyboardHotkeys.ShowUi = UpdateHotkey(_keyboardHotkeys.ShowUi, hotkey); + break; + case "ToggleDockedModeHotkey": + _keyboardHotkeys.ToggleDockedMode = UpdateHotkey(_keyboardHotkeys.ToggleDockedMode, hotkey); + break; + case "ToggleFullscreenHotkey": + _keyboardHotkeys.ToggleFullscreen = UpdateHotkey(_keyboardHotkeys.ToggleFullscreen, hotkey); + break; + case "ToggleMuteHotkey": + _keyboardHotkeys.ToggleMute = UpdateHotkey(_keyboardHotkeys.ToggleMute, hotkey); + break; + case "ToggleVsyncHotkey": + _keyboardHotkeys.ToggleVsync = UpdateHotkey(_keyboardHotkeys.ToggleVsync, hotkey); + break; + case "VolumeUpHotkey": + _keyboardHotkeys.VolumeUp = UpdateHotkey(_keyboardHotkeys.VolumeUp, hotkey); + break; + case "VolumeDownHotkey": + _keyboardHotkeys.VolumeDown = UpdateHotkey(_keyboardHotkeys.VolumeDown, hotkey); + break; + } + + OnPropertyChanged(name); + } + + private Hotkey UpdateHotkey(Hotkey hotkey, Hotkey newHotkey) + { + if (newHotkey.HasKeyboard()) + { + hotkey.Key = newHotkey.Key; + hotkey.Modifier = newHotkey.Modifier; + } + else if (newHotkey.HasGamepad()) + { + hotkey.GamepadInputMask = newHotkey.GamepadInputMask; + } + else + { + hotkey = new Hotkey(Key.Unbound); + } + + return hotkey; + } + + private static string GetButtonName(Hotkey hotkey) + { + string keyboardBinding = null; + string gamepadBinding = null; + + if (hotkey.HasKeyboard()) + { + int keyCount = 1 + BitOperations.PopCount((uint)hotkey.Modifier); + int index = 0; + + string[] keys = new string[keyCount]; + + if (hotkey.Modifier.HasFlag(KeyModifier.Control)) + { + keys[index++] = KeyModifier.Control.ToString(); + } + + if (hotkey.Modifier.HasFlag(KeyModifier.Meta)) + { + keys[index++] = KeyModifier.Meta.ToString(); + } + + if (hotkey.Modifier.HasFlag(KeyModifier.Alt)) + { + keys[index++] = KeyModifier.Alt.ToString(); + } + + if (hotkey.Modifier.HasFlag(KeyModifier.Shift)) + { + keys[index++] = KeyModifier.Shift.ToString(); + } + + keys[index] = hotkey.Key.ToString(); + + keyboardBinding = string.Join(" + ", keys); + } + + if (hotkey.HasGamepad()) + { + int buttonCount = BitOperations.PopCount(hotkey.GamepadInputMask); + int index = 0; + + string[] buttons = new string[buttonCount]; + ulong mask = hotkey.GamepadInputMask; + + while (mask != 0UL) + { + int bit = BitOperations.TrailingZeroCount(mask); + + buttons[index++] = ((GamepadInputId)bit).ToString(); + + mask &= ~(1UL << bit); + } + + gamepadBinding = string.Join(" + ", buttons); + } + + if (keyboardBinding != null && gamepadBinding != null) + { + return $"{keyboardBinding} or {gamepadBinding}"; + } + + return keyboardBinding ?? gamepadBinding ?? "Unbound"; + } + public void CheckSoundBackends() { IsOpenAlEnabled = OpenALHardwareDeviceDriver.IsSupported; diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml b/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml index 361125bfe..7ea726395 100644 --- a/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml +++ b/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml @@ -1,4 +1,4 @@ - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml.cs b/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml.cs index 702f73e87..f44988b51 100644 --- a/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml.cs +++ b/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml.cs @@ -1,23 +1,32 @@ using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.Primitives; using Avalonia.Input; using Avalonia.Interactivity; using Ryujinx.Ava.Input; using Ryujinx.Ava.UI.Helpers; +using Ryujinx.Ava.UI.ViewModels; +using Ryujinx.Ava.UI.Windows; using Ryujinx.Input; using Ryujinx.Input.Assigner; +using System.Collections.Generic; namespace Ryujinx.Ava.UI.Views.Settings { public partial class SettingsHotkeysView : UserControl { - private ButtonKeyAssigner _currentAssigner; - private IGamepadDriver AvaloniaKeyboardDriver; - + public SettingsViewModel ViewModel; + + private HotkeyButtonKeyAssigner _currentAssigner; + private readonly MainWindow _mainWindow; + private readonly IGamepadDriver _avaloniaKeyboardDriver; + public SettingsHotkeysView() { InitializeComponent(); - AvaloniaKeyboardDriver = new AvaloniaKeyboardDriver(this); + + _mainWindow = (MainWindow)((IClassicDesktopStyleApplicationLifetime)Avalonia.Application.Current.ApplicationLifetime).MainWindow; + _avaloniaKeyboardDriver = new AvaloniaKeyboardDriver(this); } private void MouseClick(object sender, PointerPressedEventArgs e) @@ -28,7 +37,7 @@ namespace Ryujinx.Ava.UI.Views.Settings PointerPressed -= MouseClick; } - + private void Button_Checked(object sender, RoutedEventArgs e) { if (sender is ToggleButton button) @@ -40,16 +49,13 @@ namespace Ryujinx.Ava.UI.Views.Settings if (_currentAssigner == null && button.IsChecked != null && (bool)button.IsChecked) { - _currentAssigner = new ButtonKeyAssigner(button); + _currentAssigner = new HotkeyButtonKeyAssigner(button, ViewModel.UpdateHotkey); FocusManager.Instance?.Focus(this, NavigationMethod.Pointer); PointerPressed += MouseClick; - var keyboard = (IKeyboard)AvaloniaKeyboardDriver.GetGamepad(AvaloniaKeyboardDriver.GamepadsIds[0]); - IButtonAssigner assigner = new KeyboardKeyAssigner(keyboard); - - _currentAssigner.GetInputAndAssign(assigner); + _currentAssigner.GetInputAndAssign(CreateButtonAssigner()); } else { @@ -66,6 +72,36 @@ namespace Ryujinx.Ava.UI.Views.Settings } } + private IButtonAssigner CreateButtonAssigner() + { + List assigners = new List(); + + IGamepadDriver keyboardDriver; + IGamepadDriver gamepadDriver = _mainWindow.InputManager.GamepadDriver; + + if (_mainWindow.InputManager.KeyboardDriver is AvaloniaKeyboardDriver) + { + // NOTE: To get input in this window, we need to bind a custom keyboard driver instead of using the InputManager one as the main window isn't focused... + keyboardDriver = _avaloniaKeyboardDriver; + } + else + { + keyboardDriver = _mainWindow.InputManager.KeyboardDriver; + } + + foreach (string id in keyboardDriver.GamepadsIds) + { + assigners.Add(new KeyboardKeyAssigner((IKeyboard)keyboardDriver.GetGamepad(id), allowModifiers: true)); + } + + foreach (string id in gamepadDriver.GamepadsIds) + { + assigners.Add(new GamepadButtonAssigner(gamepadDriver.GetGamepad(id), 0.2f, forStick: false)); + } + + return new MultiButtonAssigner(assigners); + } + private void Button_Unchecked(object sender, RoutedEventArgs e) { _currentAssigner?.Cancel(); diff --git a/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml.cs b/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml.cs index bdf7e94d8..93f9af35a 100644 --- a/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml.cs +++ b/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml.cs @@ -66,6 +66,7 @@ namespace Ryujinx.Ava.UI.Windows NavPanel.Content = InputPage; break; case "HotkeysPage": + HotkeysPage.ViewModel = ViewModel; NavPanel.Content = HotkeysPage; break; case "SystemPage": diff --git a/Ryujinx.Common/Configuration/Hid/Hotkey.cs b/Ryujinx.Common/Configuration/Hid/Hotkey.cs new file mode 100644 index 000000000..e895029df --- /dev/null +++ b/Ryujinx.Common/Configuration/Hid/Hotkey.cs @@ -0,0 +1,30 @@ +namespace Ryujinx.Common.Configuration.Hid +{ + public struct Hotkey + { + public Key Key { get; set; } + public KeyModifier Modifier { get; set; } + public ulong GamepadInputMask { get; set; } + + public Hotkey(Key key, KeyModifier modifier, ulong gamepadInputMask) + { + Key = key; + Modifier = modifier; + GamepadInputMask = gamepadInputMask; + } + + public Hotkey(Key key) : this(key, KeyModifier.None, 0UL) + { + } + + public bool HasKeyboard() + { + return Key != Key.Unknown && Key != Key.Unbound; + } + + public bool HasGamepad() + { + return GamepadInputMask != 0UL; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Common/Configuration/Hid/KeyModifier.cs b/Ryujinx.Common/Configuration/Hid/KeyModifier.cs new file mode 100644 index 000000000..8e4125546 --- /dev/null +++ b/Ryujinx.Common/Configuration/Hid/KeyModifier.cs @@ -0,0 +1,14 @@ +using System; + +namespace Ryujinx.Common.Configuration.Hid +{ + [Flags] + public enum KeyModifier + { + None = 0, + Alt = 1 << 0, + Control = 1 << 1, + Shift = 1 << 2, + Meta = 1 << 3 + } +} \ No newline at end of file diff --git a/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs b/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs index 45217e02f..815e57a0d 100644 --- a/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs +++ b/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs @@ -2,14 +2,17 @@ { public class KeyboardHotkeys { - public Key ToggleVsync { get; set; } - public Key Screenshot { get; set; } - public Key ShowUi { get; set; } - public Key Pause { get; set; } - public Key ToggleMute { get; set; } - public Key ResScaleUp { get; set; } - public Key ResScaleDown { get; set; } - public Key VolumeUp { get; set; } - public Key VolumeDown { get; set; } + public Hotkey Exit { get; set; } + public Hotkey Pause { get; set; } + public Hotkey ResScaleUp { get; set; } + public Hotkey ResScaleDown { get; set; } + public Hotkey Screenshot { get; set; } + public Hotkey ShowUi { get; set; } + public Hotkey ToggleDockedMode { get; set; } + public Hotkey ToggleFullscreen { get; set; } + public Hotkey ToggleMute { get; set; } + public Hotkey ToggleVsync { get; set; } + public Hotkey VolumeUp { get; set; } + public Hotkey VolumeDown { get; set; } } } diff --git a/Ryujinx.Input/Assigner/GamepadButtonAssigner.cs b/Ryujinx.Input/Assigner/GamepadButtonAssigner.cs index e3aaf8b1b..066ba8374 100644 --- a/Ryujinx.Input/Assigner/GamepadButtonAssigner.cs +++ b/Ryujinx.Input/Assigner/GamepadButtonAssigner.cs @@ -13,7 +13,6 @@ namespace Ryujinx.Input.Assigner private IGamepad _gamepad; private GamepadStateSnapshot _currState; - private GamepadStateSnapshot _prevState; private JoystickButtonDetector _detector; @@ -35,7 +34,7 @@ namespace Ryujinx.Input.Assigner { _currState = _gamepad.GetStateSnapshot(); _prevState = _currState; - } + } } public void ReadInput() @@ -71,6 +70,15 @@ namespace Ryujinx.Input.Assigner return ""; } + public IEnumerable GetPressedButtons() + { + IEnumerable pressedButtons = _detector.GetPressedButtons(); + + return _forStick + ? pressedButtons.Select(x => new PressedButton((StickInputId)x)) + : pressedButtons.Select(x => new PressedButton(x)); + } + private void CollectButtonStats() { if (_forStick) diff --git a/Ryujinx.Input/Assigner/IButtonAssigner.cs b/Ryujinx.Input/Assigner/IButtonAssigner.cs index 021736df4..f7335b63b 100644 --- a/Ryujinx.Input/Assigner/IButtonAssigner.cs +++ b/Ryujinx.Input/Assigner/IButtonAssigner.cs @@ -1,3 +1,5 @@ +using System.Collections.Generic; + namespace Ryujinx.Input.Assigner { /// @@ -32,5 +34,11 @@ namespace Ryujinx.Input.Assigner /// /// The pressed button that was read string GetPressedButton(); + + /// + /// Get the pressed button that was read in by the button assigner. + /// + /// The pressed button that was read + IEnumerable GetPressedButtons(); } } \ No newline at end of file diff --git a/Ryujinx.Input/Assigner/KeyboardKeyAssigner.cs b/Ryujinx.Input/Assigner/KeyboardKeyAssigner.cs index 23ae3655d..8074936ad 100644 --- a/Ryujinx.Input/Assigner/KeyboardKeyAssigner.cs +++ b/Ryujinx.Input/Assigner/KeyboardKeyAssigner.cs @@ -1,3 +1,6 @@ +using System.Collections.Generic; +using System.Linq; + namespace Ryujinx.Input.Assigner { /// @@ -5,13 +8,15 @@ namespace Ryujinx.Input.Assigner /// public class KeyboardKeyAssigner : IButtonAssigner { - private IKeyboard _keyboard; + private readonly IKeyboard _keyboard; + private readonly bool _allowModifiers; private KeyboardStateSnapshot _keyboardState; - public KeyboardKeyAssigner(IKeyboard keyboard) + public KeyboardKeyAssigner(IKeyboard keyboard, bool allowModifiers = false) { _keyboard = keyboard; + _allowModifiers = allowModifiers; } public void Initialize() { } @@ -23,7 +28,38 @@ namespace Ryujinx.Input.Assigner public bool HasAnyButtonPressed() { - return GetPressedButton().Length != 0; + for (Key key = Key.Unknown; key < Key.Count; key++) + { + // If modifiers are allowed, we should wait until a non-modifier key + // is pressed and return the full combo. + if (_allowModifiers && IsModifierKey(key)) + { + continue; + } + + if (_keyboardState.IsPressed(key)) + { + return true; + } + } + + return false; + } + + private static bool IsModifierKey(Key key) + { + switch (key) + { + case Key.AltLeft: + case Key.AltRight: + case Key.ControlLeft: + case Key.ControlRight: + case Key.ShiftLeft: + case Key.ShiftRight: + return true; + } + + return false; } public bool ShouldCancel() @@ -46,5 +82,25 @@ namespace Ryujinx.Input.Assigner return !ShouldCancel() ? keyPressed : ""; } + + public IEnumerable GetPressedButtons() + { + if (ShouldCancel()) + { + return Enumerable.Empty(); + } + + List pressedKeys = new List(); + + for (Key key = Key.Unknown; key < Key.Count; key++) + { + if (_keyboardState.IsPressed(key)) + { + pressedKeys.Add(new PressedButton(key)); + } + } + + return pressedKeys; + } } } \ No newline at end of file diff --git a/Ryujinx.Input/Assigner/MultiButtonAssigner.cs b/Ryujinx.Input/Assigner/MultiButtonAssigner.cs new file mode 100644 index 000000000..5b494345f --- /dev/null +++ b/Ryujinx.Input/Assigner/MultiButtonAssigner.cs @@ -0,0 +1,72 @@ +using System.Collections.Generic; +using System.Linq; + +namespace Ryujinx.Input.Assigner +{ + /// + /// Assigner for buttons from multiple input devices. + /// + public class MultiButtonAssigner : IButtonAssigner + { + private readonly IEnumerable _assigners; + + public MultiButtonAssigner(IEnumerable assigners) + { + _assigners = assigners; + } + + public void Initialize() + { + foreach (IButtonAssigner assigner in _assigners) + { + assigner.Initialize(); + } + } + + public void ReadInput() + { + foreach (IButtonAssigner assigner in _assigners) + { + assigner.ReadInput(); + } + } + + public bool HasAnyButtonPressed() + { + return _assigners.Any(x => x.HasAnyButtonPressed()); + } + + public bool ShouldCancel() + { + return _assigners.All(x => x.ShouldCancel()); + } + + public string GetPressedButton() + { + foreach (IButtonAssigner assigner in _assigners) + { + string pressedButton = assigner.GetPressedButton(); + + if (!string.IsNullOrEmpty(pressedButton)) + { + return pressedButton; + } + } + + return ""; + } + + public IEnumerable GetPressedButtons() + { + foreach (IButtonAssigner assigner in _assigners) + { + if (assigner.HasAnyButtonPressed()) + { + return assigner.GetPressedButtons(); + } + } + + return Enumerable.Empty(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Input/Assigner/PressedButton.cs b/Ryujinx.Input/Assigner/PressedButton.cs new file mode 100644 index 000000000..0d2368ab6 --- /dev/null +++ b/Ryujinx.Input/Assigner/PressedButton.cs @@ -0,0 +1,55 @@ +namespace Ryujinx.Input.Assigner +{ + public struct PressedButton + { + public PressedButtonType Type { get; } + + private readonly byte _value; + + internal PressedButton(PressedButtonType type) + { + Type = type; + } + + internal PressedButton(Key key) : this(PressedButtonType.Key) + { + _value = (byte)key; + } + + internal PressedButton(GamepadButtonInputId button) : this(PressedButtonType.Button) + { + _value = (byte)button; + } + + internal PressedButton(StickInputId stick) : this(PressedButtonType.Stick) + { + _value = (byte)stick; + } + + public Key AsKey() + { + return (Key)_value; + } + + public GamepadButtonInputId AsGamepadButtonInputId() + { + return (GamepadButtonInputId)_value; + } + + public StickInputId AsStickInputId() + { + return (StickInputId)_value; + } + + public override string ToString() + { + return Type switch + { + PressedButtonType.Key => AsKey().ToString(), + PressedButtonType.Button => AsGamepadButtonInputId().ToString(), + PressedButtonType.Stick => AsStickInputId().ToString(), + _ => string.Empty + }; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Input/Assigner/PressedButtonType.cs b/Ryujinx.Input/Assigner/PressedButtonType.cs new file mode 100644 index 000000000..4a8b43532 --- /dev/null +++ b/Ryujinx.Input/Assigner/PressedButtonType.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Input.Assigner +{ + public enum PressedButtonType : byte + { + None, + Key, + Button, + Stick + } +} \ No newline at end of file diff --git a/Ryujinx.Input/GamepadFeaturesFlag.cs b/Ryujinx.Input/GamepadFeaturesFlag.cs index 87310a322..f0f6a4c2d 100644 --- a/Ryujinx.Input/GamepadFeaturesFlag.cs +++ b/Ryujinx.Input/GamepadFeaturesFlag.cs @@ -21,8 +21,8 @@ namespace Ryujinx.Input /// /// Motion - /// Also named sixaxis /// + /// Also named sixaxis Motion } } diff --git a/Ryujinx.Input/Key.cs b/Ryujinx.Input/Key.cs index 5fa044847..c3797f13d 100644 --- a/Ryujinx.Input/Key.cs +++ b/Ryujinx.Input/Key.cs @@ -3,7 +3,7 @@ /// /// Represent a key from a keyboard. /// - public enum Key + public enum Key : byte { Unknown, ShiftLeft, diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs b/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs index bcdd2e70a..8d7638bb8 100644 --- a/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs +++ b/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs @@ -716,15 +716,15 @@ namespace Ryujinx.Ui.Common.Configuration Hid.EnableMouse.Value = false; Hid.Hotkeys.Value = new KeyboardHotkeys { - ToggleVsync = Key.F1, - ToggleMute = Key.F2, - Screenshot = Key.F8, - ShowUi = Key.F4, - Pause = Key.F5, - ResScaleUp = Key.Unbound, - ResScaleDown = Key.Unbound, - VolumeUp = Key.Unbound, - VolumeDown = Key.Unbound + ToggleVsync = new Hotkey(Key.F1), + ToggleMute = new Hotkey(Key.F2), + Screenshot = new Hotkey(Key.F8), + ShowUi = new Hotkey(Key.F4), + Pause = new Hotkey(Key.F5), + ResScaleUp = new Hotkey(Key.Unbound), + ResScaleDown = new Hotkey(Key.Unbound), + VolumeUp = new Hotkey(Key.Unbound), + VolumeDown = new Hotkey(Key.Unbound) }; Hid.InputConfig.Value = new List { @@ -854,7 +854,7 @@ namespace Ryujinx.Ui.Common.Configuration configurationFileFormat.Hotkeys = new KeyboardHotkeys { - ToggleVsync = Key.F1 + ToggleVsync = new Hotkey(Key.F1) }; configurationFileUpdated = true; @@ -1035,8 +1035,8 @@ namespace Ryujinx.Ui.Common.Configuration configurationFileFormat.Hotkeys = new KeyboardHotkeys { - ToggleVsync = Key.F1, - Screenshot = Key.F8 + ToggleVsync = new Hotkey(Key.F1), + Screenshot = new Hotkey(Key.F8) }; configurationFileUpdated = true; @@ -1048,9 +1048,9 @@ namespace Ryujinx.Ui.Common.Configuration configurationFileFormat.Hotkeys = new KeyboardHotkeys { - ToggleVsync = Key.F1, - Screenshot = Key.F8, - ShowUi = Key.F4 + ToggleVsync = new Hotkey(Key.F1), + Screenshot = new Hotkey(Key.F8), + ShowUi = new Hotkey(Key.F4) }; configurationFileUpdated = true; @@ -1094,7 +1094,7 @@ namespace Ryujinx.Ui.Common.Configuration ToggleVsync = configurationFileFormat.Hotkeys.ToggleVsync, Screenshot = configurationFileFormat.Hotkeys.Screenshot, ShowUi = configurationFileFormat.Hotkeys.ShowUi, - Pause = Key.F5 + Pause = new Hotkey(Key.F5) }; configurationFileUpdated = true; @@ -1110,7 +1110,7 @@ namespace Ryujinx.Ui.Common.Configuration Screenshot = configurationFileFormat.Hotkeys.Screenshot, ShowUi = configurationFileFormat.Hotkeys.ShowUi, Pause = configurationFileFormat.Hotkeys.Pause, - ToggleMute = Key.F2 + ToggleMute = new Hotkey(Key.F2) }; configurationFileFormat.AudioVolume = 1; @@ -1185,8 +1185,8 @@ namespace Ryujinx.Ui.Common.Configuration ShowUi = configurationFileFormat.Hotkeys.ShowUi, Pause = configurationFileFormat.Hotkeys.Pause, ToggleMute = configurationFileFormat.Hotkeys.ToggleMute, - ResScaleUp = Key.Unbound, - ResScaleDown = Key.Unbound + ResScaleUp = new Hotkey(Key.Unbound), + ResScaleDown = new Hotkey(Key.Unbound) }; configurationFileUpdated = true; @@ -1216,8 +1216,8 @@ namespace Ryujinx.Ui.Common.Configuration ToggleMute = configurationFileFormat.Hotkeys.ToggleMute, ResScaleUp = configurationFileFormat.Hotkeys.ResScaleUp, ResScaleDown = configurationFileFormat.Hotkeys.ResScaleDown, - VolumeUp = Key.Unbound, - VolumeDown = Key.Unbound + VolumeUp = new Hotkey(Key.Unbound), + VolumeDown = new Hotkey(Key.Unbound) }; } diff --git a/Ryujinx/Ui/RendererWidgetBase.cs b/Ryujinx/Ui/RendererWidgetBase.cs index 957bbcd55..5010ac7f3 100644 --- a/Ryujinx/Ui/RendererWidgetBase.cs +++ b/Ryujinx/Ui/RendererWidgetBase.cs @@ -731,47 +731,47 @@ namespace Ryujinx.Ui { KeyboardHotkeyState state = KeyboardHotkeyState.None; - if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ToggleVsync)) + if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ToggleVsync.Key)) { state |= KeyboardHotkeyState.ToggleVSync; } - if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot)) + if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot.Key)) { state |= KeyboardHotkeyState.Screenshot; } - if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUi)) + if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUi.Key)) { state |= KeyboardHotkeyState.ShowUi; } - if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Pause)) + if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Pause.Key)) { state |= KeyboardHotkeyState.Pause; } - if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ToggleMute)) + if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ToggleMute.Key)) { state |= KeyboardHotkeyState.ToggleMute; } - if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ResScaleUp)) + if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ResScaleUp.Key)) { state |= KeyboardHotkeyState.ResScaleUp; } - if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ResScaleDown)) + if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ResScaleDown.Key)) { state |= KeyboardHotkeyState.ResScaleDown; } - if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.VolumeUp)) + if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.VolumeUp.Key)) { state |= KeyboardHotkeyState.VolumeUp; } - if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.VolumeDown)) + if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.VolumeDown.Key)) { state |= KeyboardHotkeyState.VolumeDown; }