Add missing hotkeys, allow modifiers and controller button mappings on Avalonia

This commit is contained in:
gdk 2023-03-02 23:00:19 -03:00
parent ecee34a50c
commit 17b0a77916
26 changed files with 901 additions and 148 deletions

View file

@ -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<IGamepad> _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<IGamepad>();
_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;
}
}
}

View file

@ -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:",

View file

@ -3,13 +3,16 @@
public enum KeyboardHotkeyState
{
None,
ToggleVSync,
Screenshot,
ShowUi,
Exit,
Pause,
ToggleMute,
ResScaleUp,
ResScaleDown,
Screenshot,
ShowUi,
ToggleDockedMode,
ToggleFullscreen,
ToggleMute,
ToggleVSync,
VolumeUp,
VolumeDown
}

View file

@ -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)

View file

@ -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<AvaKey> _pressedKeys;
private readonly Control _control;
private readonly Dictionary<AvaKey, KeyModifiers> _pressedKeys;
public event EventHandler<KeyEventArgs> KeyPressed;
public event EventHandler<KeyEventArgs> KeyRelease;
public event EventHandler<string> TextInput;
public event EventHandler<string> TextInput;
public string DriverName => "AvaloniaKeyboardDriver";
public string DriverName => "AvaloniaKeyboardDriver";
public ReadOnlySpan<string> GamepadsIds => _keyboardIdentifers;
public AvaloniaKeyboardDriver(Control control)
{
_control = control;
_pressedKeys = new HashSet<AvaKey>();
_control = control;
_pressedKeys = new Dictionary<AvaKey, KeyModifiers>();
_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<string> OnGamepadConnected
{
add { }
add { }
remove { }
}
public event Action<string> 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()

View file

@ -23,7 +23,7 @@ namespace Ryujinx.Ava.UI.Helpers
IsAssigned = isAssigned;
}
}
public ToggleButton ToggledButton { get; set; }
private bool _isWaitingForInput;

View file

@ -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<string, Hotkey> _updateHotkeyCallback;
private bool _isWaitingForInput;
private bool _shouldUnbind;
public event EventHandler<ButtonAssignedEventArgs> ButtonAssigned;
public HotkeyButtonKeyAssigner(ToggleButton toggleButton, Action<string, Hotkey> 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<PressedButton> 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<PressedButton> 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;
}
}
}

View file

@ -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);

View file

@ -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);
}

View file

@ -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<string> GameDirectories { get; set; }
public ObservableCollection<ComboBoxItem> 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;

View file

@ -1,4 +1,4 @@
<UserControl
<UserControl
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsHotkeysView"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
@ -17,7 +17,7 @@
<UserControl.Resources>
<helpers:KeyValueConverter x:Key="Key" />
</UserControl.Resources>
<ScrollViewer
<ScrollViewer
Name="HotkeysPage"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
@ -27,74 +27,98 @@
<StackPanel Margin="10" Orientation="Vertical" Spacing="10">
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabHotkeysHotkeys}" />
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysToggleVsyncHotkey}" Width="230" />
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysExitHotkey}" Width="250" />
<ToggleButton Tag="ExitHotkey" Width="350" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock
Text="{Binding KeyboardHotkeys.ToggleVsync, Mode=TwoWay, Converter={StaticResource Key}}"
Text="{Binding ExitHotkey}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysScreenshotHotkey}" Width="230" />
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysPauseHotkey}" Width="250" />
<ToggleButton Tag="PauseHotkey" Width="350" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock
Text="{Binding KeyboardHotkeys.Screenshot, Mode=TwoWay, Converter={StaticResource Key}}"
Text="{Binding PauseHotkey}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysShowUiHotkey}" Width="230" />
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysResScaleUpHotkey}" Width="250" />
<ToggleButton Tag="ResScaleUpHotkey" Width="350" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock
Text="{Binding KeyboardHotkeys.ShowUi, Mode=TwoWay, Converter={StaticResource Key}}"
Text="{Binding ResScaleUpHotkey}}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysPauseHotkey}" Width="230" />
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysResScaleDownHotkey}" Width="250" />
<ToggleButton Tag="ResScaleDownHotkey" Width="350" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock
Text="{Binding KeyboardHotkeys.Pause, Mode=TwoWay, Converter={StaticResource Key}}"
Text="{Binding ResScaleDownHotkey}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysToggleMuteHotkey}" Width="230" />
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysScreenshotHotkey}" Width="250" />
<ToggleButton Tag="ScreenshotHotkey" Width="350" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock
Text="{Binding KeyboardHotkeys.ToggleMute, Mode=TwoWay, Converter={StaticResource Key}}"
Text="{Binding ScreenshotHotkey}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysResScaleUpHotkey}" Width="230" />
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysShowUiHotkey}" Width="250" />
<ToggleButton Tag="ShowUiHotkey" Width="350" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock
Text="{Binding KeyboardHotkeys.ResScaleUp, Mode=TwoWay, Converter={StaticResource Key}}"
Text="{Binding ShowUiHotkey}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysResScaleDownHotkey}" Width="230" />
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysToggleDockedModeHotkey}" Width="250" />
<ToggleButton Tag="ToggleDockedModeHotkey" Width="350" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock
Text="{Binding KeyboardHotkeys.ResScaleDown, Mode=TwoWay, Converter={StaticResource Key}}"
Text="{Binding ToggleDockedModeHotkey}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysVolumeUpHotkey}" Width="230" />
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysToggleFullscreenHotkey}" Width="250" />
<ToggleButton Tag="ToggleFullscreenHotkey" Width="350" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock
Text="{Binding KeyboardHotkeys.VolumeUp, Mode=TwoWay, Converter={StaticResource Key}}"
Text="{Binding ToggleFullscreenHotkey}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysVolumeDownHotkey}" Width="230" />
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysToggleMuteHotkey}" Width="250" />
<ToggleButton Tag="ToggleMuteHotkey" Width="350" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock
Text="{Binding KeyboardHotkeys.VolumeDown, Mode=TwoWay, Converter={StaticResource Key}}"
Text="{Binding ToggleMuteHotkey}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysToggleVsyncHotkey}" Width="250" />
<ToggleButton Tag="ToggleVsyncHotkey" Width="350" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock
Text="{Binding ToggleVsyncHotkey}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysVolumeUpHotkey}" Width="250" />
<ToggleButton Tag="VolumeUpHotkey" Width="350" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock
Text="{Binding VolumeUpHotkey}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysVolumeDownHotkey}" Width="250" />
<ToggleButton Tag="VolumeDownHotkey" Width="350" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock
Text="{Binding VolumeDownHotkey}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>

View file

@ -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<IButtonAssigner> assigners = new List<IButtonAssigner>();
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();

View file

@ -66,6 +66,7 @@ namespace Ryujinx.Ava.UI.Windows
NavPanel.Content = InputPage;
break;
case "HotkeysPage":
HotkeysPage.ViewModel = ViewModel;
NavPanel.Content = HotkeysPage;
break;
case "SystemPage":

View file

@ -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;
}
}
}

View file

@ -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
}
}

View file

@ -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; }
}
}

View file

@ -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<PressedButton> GetPressedButtons()
{
IEnumerable<GamepadButtonInputId> pressedButtons = _detector.GetPressedButtons();
return _forStick
? pressedButtons.Select(x => new PressedButton((StickInputId)x))
: pressedButtons.Select(x => new PressedButton(x));
}
private void CollectButtonStats()
{
if (_forStick)

View file

@ -1,3 +1,5 @@
using System.Collections.Generic;
namespace Ryujinx.Input.Assigner
{
/// <summary>
@ -32,5 +34,11 @@ namespace Ryujinx.Input.Assigner
/// </summary>
/// <returns>The pressed button that was read</returns>
string GetPressedButton();
/// <summary>
/// Get the pressed button that was read in <see cref="ReadInput"/> by the button assigner.
/// </summary>
/// <returns>The pressed button that was read</returns>
IEnumerable<PressedButton> GetPressedButtons();
}
}

View file

@ -1,3 +1,6 @@
using System.Collections.Generic;
using System.Linq;
namespace Ryujinx.Input.Assigner
{
/// <summary>
@ -5,13 +8,15 @@ namespace Ryujinx.Input.Assigner
/// </summary>
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<PressedButton> GetPressedButtons()
{
if (ShouldCancel())
{
return Enumerable.Empty<PressedButton>();
}
List<PressedButton> pressedKeys = new List<PressedButton>();
for (Key key = Key.Unknown; key < Key.Count; key++)
{
if (_keyboardState.IsPressed(key))
{
pressedKeys.Add(new PressedButton(key));
}
}
return pressedKeys;
}
}
}

View file

@ -0,0 +1,72 @@
using System.Collections.Generic;
using System.Linq;
namespace Ryujinx.Input.Assigner
{
/// <summary>
/// Assigner for buttons from multiple input devices.
/// </summary>
public class MultiButtonAssigner : IButtonAssigner
{
private readonly IEnumerable<IButtonAssigner> _assigners;
public MultiButtonAssigner(IEnumerable<IButtonAssigner> 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<PressedButton> GetPressedButtons()
{
foreach (IButtonAssigner assigner in _assigners)
{
if (assigner.HasAnyButtonPressed())
{
return assigner.GetPressedButtons();
}
}
return Enumerable.Empty<PressedButton>();
}
}
}

View file

@ -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
};
}
}
}

View file

@ -0,0 +1,10 @@
namespace Ryujinx.Input.Assigner
{
public enum PressedButtonType : byte
{
None,
Key,
Button,
Stick
}
}

View file

@ -21,8 +21,8 @@ namespace Ryujinx.Input
/// <summary>
/// Motion
/// <remarks>Also named sixaxis</remarks>
/// </summary>
/// <remarks>Also named sixaxis</remarks>
Motion
}
}

View file

@ -3,7 +3,7 @@
/// <summary>
/// Represent a key from a keyboard.
/// </summary>
public enum Key
public enum Key : byte
{
Unknown,
ShiftLeft,

View file

@ -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<InputConfig>
{
@ -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)
};
}

View file

@ -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;
}