mirror of
https://git.naxdy.org/Mirror/Ryujinx.git
synced 2025-01-30 14:20:32 +00:00
Add missing hotkeys, allow modifiers and controller button mappings on Avalonia
This commit is contained in:
parent
ecee34a50c
commit
17b0a77916
26 changed files with 901 additions and 148 deletions
|
@ -19,6 +19,7 @@ using Ryujinx.Ava.UI.ViewModels;
|
||||||
using Ryujinx.Ava.UI.Windows;
|
using Ryujinx.Ava.UI.Windows;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
|
using Ryujinx.Common.Configuration.Hid;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Common.SystemInterop;
|
using Ryujinx.Common.SystemInterop;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
|
@ -44,6 +45,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Numerics;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using static Ryujinx.Ava.UI.Helpers.Win32NativeInterop;
|
using static Ryujinx.Ava.UI.Helpers.Win32NativeInterop;
|
||||||
|
@ -77,6 +79,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;
|
||||||
|
|
||||||
|
@ -84,6 +87,8 @@ namespace Ryujinx.Ava
|
||||||
private float _newVolume;
|
private float _newVolume;
|
||||||
private KeyboardHotkeyState _prevHotkeyState;
|
private KeyboardHotkeyState _prevHotkeyState;
|
||||||
|
|
||||||
|
private int _gamepadsChanged;
|
||||||
|
|
||||||
private long _lastCursorMoveTime;
|
private long _lastCursorMoveTime;
|
||||||
private bool _isCursorInRenderer;
|
private bool _isCursorInRenderer;
|
||||||
|
|
||||||
|
@ -139,6 +144,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;
|
||||||
|
@ -916,6 +928,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)
|
||||||
|
@ -960,15 +977,11 @@ namespace Ryujinx.Ava
|
||||||
{
|
{
|
||||||
switch (currentHotkeyState)
|
switch (currentHotkeyState)
|
||||||
{
|
{
|
||||||
case KeyboardHotkeyState.ToggleVSync:
|
case KeyboardHotkeyState.None:
|
||||||
Device.EnableDeviceVsync = !Device.EnableDeviceVsync;
|
(_keyboardInterface as AvaloniaKeyboard).Clear();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case KeyboardHotkeyState.Screenshot:
|
case KeyboardHotkeyState.Exit:
|
||||||
ScreenshotRequested = true;
|
_viewModel.ExitCurrentState();
|
||||||
break;
|
|
||||||
case KeyboardHotkeyState.ShowUi:
|
|
||||||
_viewModel.ShowMenuAndStatusBar = true;
|
|
||||||
break;
|
break;
|
||||||
case KeyboardHotkeyState.Pause:
|
case KeyboardHotkeyState.Pause:
|
||||||
if (_viewModel.IsPaused)
|
if (_viewModel.IsPaused)
|
||||||
|
@ -980,6 +993,24 @@ namespace Ryujinx.Ava
|
||||||
Pause();
|
Pause();
|
||||||
}
|
}
|
||||||
break;
|
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:
|
case KeyboardHotkeyState.ToggleMute:
|
||||||
if (Device.IsAudioMuted())
|
if (Device.IsAudioMuted())
|
||||||
{
|
{
|
||||||
|
@ -992,12 +1023,8 @@ namespace Ryujinx.Ava
|
||||||
|
|
||||||
_viewModel.Volume = Device.GetVolume();
|
_viewModel.Volume = Device.GetVolume();
|
||||||
break;
|
break;
|
||||||
case KeyboardHotkeyState.ResScaleUp:
|
case KeyboardHotkeyState.ToggleVSync:
|
||||||
GraphicsConfig.ResScale = GraphicsConfig.ResScale % MaxResolutionScale + 1;
|
Device.EnableDeviceVsync = !Device.EnableDeviceVsync;
|
||||||
break;
|
|
||||||
case KeyboardHotkeyState.ResScaleDown:
|
|
||||||
GraphicsConfig.ResScale =
|
|
||||||
(MaxResolutionScale + GraphicsConfig.ResScale - 2) % MaxResolutionScale + 1;
|
|
||||||
break;
|
break;
|
||||||
case KeyboardHotkeyState.VolumeUp:
|
case KeyboardHotkeyState.VolumeUp:
|
||||||
_newVolume = MathF.Round((Device.GetVolume() + VolumeDelta), 2);
|
_newVolume = MathF.Round((Device.GetVolume() + VolumeDelta), 2);
|
||||||
|
@ -1011,9 +1038,6 @@ namespace Ryujinx.Ava
|
||||||
|
|
||||||
_viewModel.Volume = Device.GetVolume();
|
_viewModel.Volume = Device.GetVolume();
|
||||||
break;
|
break;
|
||||||
case KeyboardHotkeyState.None:
|
|
||||||
(_keyboardInterface as AvaloniaKeyboard).Clear();
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1048,44 +1072,112 @@ namespace Ryujinx.Ava
|
||||||
{
|
{
|
||||||
KeyboardHotkeyState state = KeyboardHotkeyState.None;
|
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))
|
else if (IsHotkeyPressed(ConfigurationState.Instance.Hid.Hotkeys.Value.Pause))
|
||||||
{
|
|
||||||
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))
|
|
||||||
{
|
{
|
||||||
state = KeyboardHotkeyState.Pause;
|
state = KeyboardHotkeyState.Pause;
|
||||||
}
|
}
|
||||||
else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ToggleMute))
|
else if (IsHotkeyPressed(ConfigurationState.Instance.Hid.Hotkeys.Value.ResScaleUp))
|
||||||
{
|
|
||||||
state = KeyboardHotkeyState.ToggleMute;
|
|
||||||
}
|
|
||||||
else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ResScaleUp))
|
|
||||||
{
|
{
|
||||||
state = KeyboardHotkeyState.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;
|
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;
|
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;
|
state = KeyboardHotkeyState.VolumeDown;
|
||||||
}
|
}
|
||||||
|
|
||||||
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 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -565,6 +565,9 @@
|
||||||
"RyujinxUpdater": "Ryujinx Updater",
|
"RyujinxUpdater": "Ryujinx Updater",
|
||||||
"SettingsTabHotkeys": "Keyboard Hotkeys",
|
"SettingsTabHotkeys": "Keyboard Hotkeys",
|
||||||
"SettingsTabHotkeysHotkeys": "Keyboard Hotkeys",
|
"SettingsTabHotkeysHotkeys": "Keyboard Hotkeys",
|
||||||
|
"SettingsTabHotkeysExitHotkey": "Exit:",
|
||||||
|
"SettingsTabHotkeysToggleDockedModeHotkey": "Toggle Docked Mode:",
|
||||||
|
"SettingsTabHotkeysToggleFullscreenHotkey": "Toggle Fullscreen:",
|
||||||
"SettingsTabHotkeysToggleVsyncHotkey": "Toggle VSync:",
|
"SettingsTabHotkeysToggleVsyncHotkey": "Toggle VSync:",
|
||||||
"SettingsTabHotkeysScreenshotHotkey": "Screenshot:",
|
"SettingsTabHotkeysScreenshotHotkey": "Screenshot:",
|
||||||
"SettingsTabHotkeysShowUiHotkey": "Show UI:",
|
"SettingsTabHotkeysShowUiHotkey": "Show UI:",
|
||||||
|
|
|
@ -3,13 +3,16 @@
|
||||||
public enum KeyboardHotkeyState
|
public enum KeyboardHotkeyState
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
ToggleVSync,
|
Exit,
|
||||||
Screenshot,
|
|
||||||
ShowUi,
|
|
||||||
Pause,
|
Pause,
|
||||||
ToggleMute,
|
|
||||||
ResScaleUp,
|
ResScaleUp,
|
||||||
ResScaleDown,
|
ResScaleDown,
|
||||||
|
Screenshot,
|
||||||
|
ShowUi,
|
||||||
|
ToggleDockedMode,
|
||||||
|
ToggleFullscreen,
|
||||||
|
ToggleMute,
|
||||||
|
ToggleVSync,
|
||||||
VolumeUp,
|
VolumeUp,
|
||||||
VolumeDown
|
VolumeDown
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
public void SetConfiguration(InputConfig configuration)
|
||||||
{
|
{
|
||||||
lock (_userMappingLock)
|
lock (_userMappingLock)
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
|
using Ryujinx.Common.Configuration.Hid;
|
||||||
|
using Ryujinx.Common.Configuration.Hid.Keyboard;
|
||||||
using Ryujinx.Input;
|
using Ryujinx.Input;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -14,7 +16,7 @@ namespace Ryujinx.Ava.Input
|
||||||
{
|
{
|
||||||
private static readonly string[] _keyboardIdentifers = new string[1] { "0" };
|
private static readonly string[] _keyboardIdentifers = new string[1] { "0" };
|
||||||
private readonly Control _control;
|
private readonly Control _control;
|
||||||
private readonly HashSet<AvaKey> _pressedKeys;
|
private readonly Dictionary<AvaKey, KeyModifiers> _pressedKeys;
|
||||||
|
|
||||||
public event EventHandler<KeyEventArgs> KeyPressed;
|
public event EventHandler<KeyEventArgs> KeyPressed;
|
||||||
public event EventHandler<KeyEventArgs> KeyRelease;
|
public event EventHandler<KeyEventArgs> KeyRelease;
|
||||||
|
@ -26,7 +28,7 @@ namespace Ryujinx.Ava.Input
|
||||||
public AvaloniaKeyboardDriver(Control control)
|
public AvaloniaKeyboardDriver(Control control)
|
||||||
{
|
{
|
||||||
_control = control;
|
_control = control;
|
||||||
_pressedKeys = new HashSet<AvaKey>();
|
_pressedKeys = new Dictionary<AvaKey, KeyModifiers>();
|
||||||
|
|
||||||
_control.KeyDown += OnKeyPress;
|
_control.KeyDown += OnKeyPress;
|
||||||
_control.KeyUp += OnKeyRelease;
|
_control.KeyUp += OnKeyRelease;
|
||||||
|
@ -78,7 +80,7 @@ namespace Ryujinx.Ava.Input
|
||||||
|
|
||||||
protected void OnKeyPress(object sender, KeyEventArgs args)
|
protected void OnKeyPress(object sender, KeyEventArgs args)
|
||||||
{
|
{
|
||||||
_pressedKeys.Add(args.Key);
|
_pressedKeys[args.Key] = args.KeyModifiers;
|
||||||
|
|
||||||
KeyPressed?.Invoke(this, args);
|
KeyPressed?.Invoke(this, args);
|
||||||
}
|
}
|
||||||
|
@ -99,7 +101,42 @@ namespace Ryujinx.Ava.Input
|
||||||
|
|
||||||
AvaloniaKeyboardMappingHelper.TryGetAvaKey(key, out var nativeKey);
|
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()
|
public void ResetKeys()
|
||||||
|
|
150
Ryujinx.Ava/UI/Helpers/HotkeyButtonKeyAssigner.cs
Normal file
150
Ryujinx.Ava/UI/Helpers/HotkeyButtonKeyAssigner.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,11 +3,8 @@ using Avalonia.Controls;
|
||||||
using Avalonia.Controls.ApplicationLifetimes;
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
using Avalonia.Svg.Skia;
|
using Avalonia.Svg.Skia;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using LibHac.Bcat;
|
|
||||||
using LibHac.Tools.Fs;
|
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
using Ryujinx.Ava.Input;
|
using Ryujinx.Ava.Input;
|
||||||
using Ryujinx.Ava.UI.Controls;
|
|
||||||
using Ryujinx.Ava.UI.Helpers;
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
using Ryujinx.Ava.UI.Models;
|
using Ryujinx.Ava.UI.Models;
|
||||||
using Ryujinx.Ava.UI.Windows;
|
using Ryujinx.Ava.UI.Windows;
|
||||||
|
@ -238,9 +235,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
|
|
||||||
if (Program.PreviewerDetached)
|
if (Program.PreviewerDetached)
|
||||||
{
|
{
|
||||||
_mainWindow =
|
_mainWindow = (MainWindow)((IClassicDesktopStyleApplicationLifetime)Avalonia.Application.Current.ApplicationLifetime).MainWindow;
|
||||||
(MainWindow)((IClassicDesktopStyleApplicationLifetime)Avalonia.Application.Current
|
|
||||||
.ApplicationLifetime).MainWindow;
|
|
||||||
|
|
||||||
AvaloniaKeyboardDriver = new AvaloniaKeyboardDriver(owner);
|
AvaloniaKeyboardDriver = new AvaloniaKeyboardDriver(owner);
|
||||||
|
|
||||||
|
|
|
@ -1220,17 +1220,17 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
|
|
||||||
public void LoadConfigurableHotKeys()
|
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);
|
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);
|
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);
|
PauseKey = new KeyGesture(pauseKey);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ using Ryujinx.Ava.UI.Helpers;
|
||||||
using Ryujinx.Ava.UI.Windows;
|
using Ryujinx.Ava.UI.Windows;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Common.Configuration.Hid;
|
using Ryujinx.Common.Configuration.Hid;
|
||||||
|
using Ryujinx.Common.Configuration.Hid.Controller;
|
||||||
using Ryujinx.Common.GraphicsDriver;
|
using Ryujinx.Common.GraphicsDriver;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Graphics.Vulkan;
|
using Ryujinx.Graphics.Vulkan;
|
||||||
|
@ -22,6 +23,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using TimeZone = Ryujinx.Ava.UI.Models.TimeZone;
|
using TimeZone = Ryujinx.Ava.UI.Models.TimeZone;
|
||||||
|
|
||||||
|
@ -240,6 +242,19 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
public AvaloniaList<string> GameDirectories { get; set; }
|
public AvaloniaList<string> GameDirectories { get; set; }
|
||||||
public ObservableCollection<ComboBoxItem> AvailableGpus { 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
|
public KeyboardHotkeys KeyboardHotkeys
|
||||||
{
|
{
|
||||||
get => _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()
|
public void CheckSoundBackends()
|
||||||
{
|
{
|
||||||
IsOpenAlEnabled = OpenALHardwareDeviceDriver.IsSupported;
|
IsOpenAlEnabled = OpenALHardwareDeviceDriver.IsSupported;
|
||||||
|
|
|
@ -27,74 +27,98 @@
|
||||||
<StackPanel Margin="10" Orientation="Vertical" Spacing="10">
|
<StackPanel Margin="10" Orientation="Vertical" Spacing="10">
|
||||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabHotkeysHotkeys}" />
|
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabHotkeysHotkeys}" />
|
||||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||||
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysToggleVsyncHotkey}" Width="230" />
|
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysExitHotkey}" Width="250" />
|
||||||
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
|
<ToggleButton Tag="ExitHotkey" Width="350" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Text="{Binding KeyboardHotkeys.ToggleVsync, Mode=TwoWay, Converter={StaticResource Key}}"
|
Text="{Binding ExitHotkey}"
|
||||||
TextAlignment="Center" />
|
TextAlignment="Center" />
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||||
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysScreenshotHotkey}" Width="230" />
|
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysPauseHotkey}" Width="250" />
|
||||||
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
|
<ToggleButton Tag="PauseHotkey" Width="350" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Text="{Binding KeyboardHotkeys.Screenshot, Mode=TwoWay, Converter={StaticResource Key}}"
|
Text="{Binding PauseHotkey}"
|
||||||
TextAlignment="Center" />
|
TextAlignment="Center" />
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||||
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysShowUiHotkey}" Width="230" />
|
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysResScaleUpHotkey}" Width="250" />
|
||||||
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
|
<ToggleButton Tag="ResScaleUpHotkey" Width="350" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Text="{Binding KeyboardHotkeys.ShowUi, Mode=TwoWay, Converter={StaticResource Key}}"
|
Text="{Binding ResScaleUpHotkey}}"
|
||||||
TextAlignment="Center" />
|
TextAlignment="Center" />
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||||
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysPauseHotkey}" Width="230" />
|
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysResScaleDownHotkey}" Width="250" />
|
||||||
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
|
<ToggleButton Tag="ResScaleDownHotkey" Width="350" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Text="{Binding KeyboardHotkeys.Pause, Mode=TwoWay, Converter={StaticResource Key}}"
|
Text="{Binding ResScaleDownHotkey}"
|
||||||
TextAlignment="Center" />
|
TextAlignment="Center" />
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||||
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysToggleMuteHotkey}" Width="230" />
|
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysScreenshotHotkey}" Width="250" />
|
||||||
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
|
<ToggleButton Tag="ScreenshotHotkey" Width="350" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Text="{Binding KeyboardHotkeys.ToggleMute, Mode=TwoWay, Converter={StaticResource Key}}"
|
Text="{Binding ScreenshotHotkey}"
|
||||||
TextAlignment="Center" />
|
TextAlignment="Center" />
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||||
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysResScaleUpHotkey}" Width="230" />
|
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysShowUiHotkey}" Width="250" />
|
||||||
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
|
<ToggleButton Tag="ShowUiHotkey" Width="350" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Text="{Binding KeyboardHotkeys.ResScaleUp, Mode=TwoWay, Converter={StaticResource Key}}"
|
Text="{Binding ShowUiHotkey}"
|
||||||
TextAlignment="Center" />
|
TextAlignment="Center" />
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||||
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysResScaleDownHotkey}" Width="230" />
|
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysToggleDockedModeHotkey}" Width="250" />
|
||||||
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
|
<ToggleButton Tag="ToggleDockedModeHotkey" Width="350" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Text="{Binding KeyboardHotkeys.ResScaleDown, Mode=TwoWay, Converter={StaticResource Key}}"
|
Text="{Binding ToggleDockedModeHotkey}"
|
||||||
TextAlignment="Center" />
|
TextAlignment="Center" />
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||||
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysVolumeUpHotkey}" Width="230" />
|
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysToggleFullscreenHotkey}" Width="250" />
|
||||||
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
|
<ToggleButton Tag="ToggleFullscreenHotkey" Width="350" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Text="{Binding KeyboardHotkeys.VolumeUp, Mode=TwoWay, Converter={StaticResource Key}}"
|
Text="{Binding ToggleFullscreenHotkey}"
|
||||||
TextAlignment="Center" />
|
TextAlignment="Center" />
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||||
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysVolumeDownHotkey}" Width="230" />
|
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysToggleMuteHotkey}" Width="250" />
|
||||||
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
|
<ToggleButton Tag="ToggleMuteHotkey" Width="350" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
|
||||||
<TextBlock
|
<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" />
|
TextAlignment="Center" />
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
|
@ -1,23 +1,32 @@
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
using Avalonia.Controls.Primitives;
|
using Avalonia.Controls.Primitives;
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Ryujinx.Ava.Input;
|
using Ryujinx.Ava.Input;
|
||||||
using Ryujinx.Ava.UI.Helpers;
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
|
using Ryujinx.Ava.UI.ViewModels;
|
||||||
|
using Ryujinx.Ava.UI.Windows;
|
||||||
using Ryujinx.Input;
|
using Ryujinx.Input;
|
||||||
using Ryujinx.Input.Assigner;
|
using Ryujinx.Input.Assigner;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Views.Settings
|
namespace Ryujinx.Ava.UI.Views.Settings
|
||||||
{
|
{
|
||||||
public partial class SettingsHotkeysView : UserControl
|
public partial class SettingsHotkeysView : UserControl
|
||||||
{
|
{
|
||||||
private ButtonKeyAssigner _currentAssigner;
|
public SettingsViewModel ViewModel;
|
||||||
private IGamepadDriver AvaloniaKeyboardDriver;
|
|
||||||
|
private HotkeyButtonKeyAssigner _currentAssigner;
|
||||||
|
private readonly MainWindow _mainWindow;
|
||||||
|
private readonly IGamepadDriver _avaloniaKeyboardDriver;
|
||||||
|
|
||||||
public SettingsHotkeysView()
|
public SettingsHotkeysView()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
AvaloniaKeyboardDriver = new AvaloniaKeyboardDriver(this);
|
|
||||||
|
_mainWindow = (MainWindow)((IClassicDesktopStyleApplicationLifetime)Avalonia.Application.Current.ApplicationLifetime).MainWindow;
|
||||||
|
_avaloniaKeyboardDriver = new AvaloniaKeyboardDriver(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void MouseClick(object sender, PointerPressedEventArgs e)
|
private void MouseClick(object sender, PointerPressedEventArgs e)
|
||||||
|
@ -40,16 +49,13 @@ namespace Ryujinx.Ava.UI.Views.Settings
|
||||||
|
|
||||||
if (_currentAssigner == null && button.IsChecked != null && (bool)button.IsChecked)
|
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);
|
FocusManager.Instance?.Focus(this, NavigationMethod.Pointer);
|
||||||
|
|
||||||
PointerPressed += MouseClick;
|
PointerPressed += MouseClick;
|
||||||
|
|
||||||
var keyboard = (IKeyboard)AvaloniaKeyboardDriver.GetGamepad(AvaloniaKeyboardDriver.GamepadsIds[0]);
|
_currentAssigner.GetInputAndAssign(CreateButtonAssigner());
|
||||||
IButtonAssigner assigner = new KeyboardKeyAssigner(keyboard);
|
|
||||||
|
|
||||||
_currentAssigner.GetInputAndAssign(assigner);
|
|
||||||
}
|
}
|
||||||
else
|
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)
|
private void Button_Unchecked(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
_currentAssigner?.Cancel();
|
_currentAssigner?.Cancel();
|
||||||
|
|
|
@ -66,6 +66,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||||
NavPanel.Content = InputPage;
|
NavPanel.Content = InputPage;
|
||||||
break;
|
break;
|
||||||
case "HotkeysPage":
|
case "HotkeysPage":
|
||||||
|
HotkeysPage.ViewModel = ViewModel;
|
||||||
NavPanel.Content = HotkeysPage;
|
NavPanel.Content = HotkeysPage;
|
||||||
break;
|
break;
|
||||||
case "SystemPage":
|
case "SystemPage":
|
||||||
|
|
30
Ryujinx.Common/Configuration/Hid/Hotkey.cs
Normal file
30
Ryujinx.Common/Configuration/Hid/Hotkey.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
Ryujinx.Common/Configuration/Hid/KeyModifier.cs
Normal file
14
Ryujinx.Common/Configuration/Hid/KeyModifier.cs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,14 +2,17 @@
|
||||||
{
|
{
|
||||||
public class KeyboardHotkeys
|
public class KeyboardHotkeys
|
||||||
{
|
{
|
||||||
public Key ToggleVsync { get; set; }
|
public Hotkey Exit { get; set; }
|
||||||
public Key Screenshot { get; set; }
|
public Hotkey Pause { get; set; }
|
||||||
public Key ShowUi { get; set; }
|
public Hotkey ResScaleUp { get; set; }
|
||||||
public Key Pause { get; set; }
|
public Hotkey ResScaleDown { get; set; }
|
||||||
public Key ToggleMute { get; set; }
|
public Hotkey Screenshot { get; set; }
|
||||||
public Key ResScaleUp { get; set; }
|
public Hotkey ShowUi { get; set; }
|
||||||
public Key ResScaleDown { get; set; }
|
public Hotkey ToggleDockedMode { get; set; }
|
||||||
public Key VolumeUp { get; set; }
|
public Hotkey ToggleFullscreen { get; set; }
|
||||||
public Key VolumeDown { get; set; }
|
public Hotkey ToggleMute { get; set; }
|
||||||
|
public Hotkey ToggleVsync { get; set; }
|
||||||
|
public Hotkey VolumeUp { get; set; }
|
||||||
|
public Hotkey VolumeDown { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@ namespace Ryujinx.Input.Assigner
|
||||||
private IGamepad _gamepad;
|
private IGamepad _gamepad;
|
||||||
|
|
||||||
private GamepadStateSnapshot _currState;
|
private GamepadStateSnapshot _currState;
|
||||||
|
|
||||||
private GamepadStateSnapshot _prevState;
|
private GamepadStateSnapshot _prevState;
|
||||||
|
|
||||||
private JoystickButtonDetector _detector;
|
private JoystickButtonDetector _detector;
|
||||||
|
@ -71,6 +70,15 @@ namespace Ryujinx.Input.Assigner
|
||||||
return "";
|
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()
|
private void CollectButtonStats()
|
||||||
{
|
{
|
||||||
if (_forStick)
|
if (_forStick)
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.Input.Assigner
|
namespace Ryujinx.Input.Assigner
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -32,5 +34,11 @@ namespace Ryujinx.Input.Assigner
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The pressed button that was read</returns>
|
/// <returns>The pressed button that was read</returns>
|
||||||
string GetPressedButton();
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,3 +1,6 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Ryujinx.Input.Assigner
|
namespace Ryujinx.Input.Assigner
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -5,13 +8,15 @@ namespace Ryujinx.Input.Assigner
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class KeyboardKeyAssigner : IButtonAssigner
|
public class KeyboardKeyAssigner : IButtonAssigner
|
||||||
{
|
{
|
||||||
private IKeyboard _keyboard;
|
private readonly IKeyboard _keyboard;
|
||||||
|
private readonly bool _allowModifiers;
|
||||||
|
|
||||||
private KeyboardStateSnapshot _keyboardState;
|
private KeyboardStateSnapshot _keyboardState;
|
||||||
|
|
||||||
public KeyboardKeyAssigner(IKeyboard keyboard)
|
public KeyboardKeyAssigner(IKeyboard keyboard, bool allowModifiers = false)
|
||||||
{
|
{
|
||||||
_keyboard = keyboard;
|
_keyboard = keyboard;
|
||||||
|
_allowModifiers = allowModifiers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Initialize() { }
|
public void Initialize() { }
|
||||||
|
@ -23,7 +28,38 @@ namespace Ryujinx.Input.Assigner
|
||||||
|
|
||||||
public bool HasAnyButtonPressed()
|
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()
|
public bool ShouldCancel()
|
||||||
|
@ -46,5 +82,25 @@ namespace Ryujinx.Input.Assigner
|
||||||
|
|
||||||
return !ShouldCancel() ? keyPressed : "";
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
72
Ryujinx.Input/Assigner/MultiButtonAssigner.cs
Normal file
72
Ryujinx.Input/Assigner/MultiButtonAssigner.cs
Normal 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>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
55
Ryujinx.Input/Assigner/PressedButton.cs
Normal file
55
Ryujinx.Input/Assigner/PressedButton.cs
Normal 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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
Ryujinx.Input/Assigner/PressedButtonType.cs
Normal file
10
Ryujinx.Input/Assigner/PressedButtonType.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
namespace Ryujinx.Input.Assigner
|
||||||
|
{
|
||||||
|
public enum PressedButtonType : byte
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Key,
|
||||||
|
Button,
|
||||||
|
Stick
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,8 +21,8 @@ namespace Ryujinx.Input
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Motion
|
/// Motion
|
||||||
/// <remarks>Also named sixaxis</remarks>
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>Also named sixaxis</remarks>
|
||||||
Motion
|
Motion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represent a key from a keyboard.
|
/// Represent a key from a keyboard.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum Key
|
public enum Key : byte
|
||||||
{
|
{
|
||||||
Unknown,
|
Unknown,
|
||||||
ShiftLeft,
|
ShiftLeft,
|
||||||
|
|
|
@ -716,15 +716,15 @@ namespace Ryujinx.Ui.Common.Configuration
|
||||||
Hid.EnableMouse.Value = false;
|
Hid.EnableMouse.Value = false;
|
||||||
Hid.Hotkeys.Value = new KeyboardHotkeys
|
Hid.Hotkeys.Value = new KeyboardHotkeys
|
||||||
{
|
{
|
||||||
ToggleVsync = Key.F1,
|
ToggleVsync = new Hotkey(Key.F1),
|
||||||
ToggleMute = Key.F2,
|
ToggleMute = new Hotkey(Key.F2),
|
||||||
Screenshot = Key.F8,
|
Screenshot = new Hotkey(Key.F8),
|
||||||
ShowUi = Key.F4,
|
ShowUi = new Hotkey(Key.F4),
|
||||||
Pause = Key.F5,
|
Pause = new Hotkey(Key.F5),
|
||||||
ResScaleUp = Key.Unbound,
|
ResScaleUp = new Hotkey(Key.Unbound),
|
||||||
ResScaleDown = Key.Unbound,
|
ResScaleDown = new Hotkey(Key.Unbound),
|
||||||
VolumeUp = Key.Unbound,
|
VolumeUp = new Hotkey(Key.Unbound),
|
||||||
VolumeDown = Key.Unbound
|
VolumeDown = new Hotkey(Key.Unbound)
|
||||||
};
|
};
|
||||||
Hid.InputConfig.Value = new List<InputConfig>
|
Hid.InputConfig.Value = new List<InputConfig>
|
||||||
{
|
{
|
||||||
|
@ -854,7 +854,7 @@ namespace Ryujinx.Ui.Common.Configuration
|
||||||
|
|
||||||
configurationFileFormat.Hotkeys = new KeyboardHotkeys
|
configurationFileFormat.Hotkeys = new KeyboardHotkeys
|
||||||
{
|
{
|
||||||
ToggleVsync = Key.F1
|
ToggleVsync = new Hotkey(Key.F1)
|
||||||
};
|
};
|
||||||
|
|
||||||
configurationFileUpdated = true;
|
configurationFileUpdated = true;
|
||||||
|
@ -1035,8 +1035,8 @@ namespace Ryujinx.Ui.Common.Configuration
|
||||||
|
|
||||||
configurationFileFormat.Hotkeys = new KeyboardHotkeys
|
configurationFileFormat.Hotkeys = new KeyboardHotkeys
|
||||||
{
|
{
|
||||||
ToggleVsync = Key.F1,
|
ToggleVsync = new Hotkey(Key.F1),
|
||||||
Screenshot = Key.F8
|
Screenshot = new Hotkey(Key.F8)
|
||||||
};
|
};
|
||||||
|
|
||||||
configurationFileUpdated = true;
|
configurationFileUpdated = true;
|
||||||
|
@ -1048,9 +1048,9 @@ namespace Ryujinx.Ui.Common.Configuration
|
||||||
|
|
||||||
configurationFileFormat.Hotkeys = new KeyboardHotkeys
|
configurationFileFormat.Hotkeys = new KeyboardHotkeys
|
||||||
{
|
{
|
||||||
ToggleVsync = Key.F1,
|
ToggleVsync = new Hotkey(Key.F1),
|
||||||
Screenshot = Key.F8,
|
Screenshot = new Hotkey(Key.F8),
|
||||||
ShowUi = Key.F4
|
ShowUi = new Hotkey(Key.F4)
|
||||||
};
|
};
|
||||||
|
|
||||||
configurationFileUpdated = true;
|
configurationFileUpdated = true;
|
||||||
|
@ -1094,7 +1094,7 @@ namespace Ryujinx.Ui.Common.Configuration
|
||||||
ToggleVsync = configurationFileFormat.Hotkeys.ToggleVsync,
|
ToggleVsync = configurationFileFormat.Hotkeys.ToggleVsync,
|
||||||
Screenshot = configurationFileFormat.Hotkeys.Screenshot,
|
Screenshot = configurationFileFormat.Hotkeys.Screenshot,
|
||||||
ShowUi = configurationFileFormat.Hotkeys.ShowUi,
|
ShowUi = configurationFileFormat.Hotkeys.ShowUi,
|
||||||
Pause = Key.F5
|
Pause = new Hotkey(Key.F5)
|
||||||
};
|
};
|
||||||
|
|
||||||
configurationFileUpdated = true;
|
configurationFileUpdated = true;
|
||||||
|
@ -1110,7 +1110,7 @@ namespace Ryujinx.Ui.Common.Configuration
|
||||||
Screenshot = configurationFileFormat.Hotkeys.Screenshot,
|
Screenshot = configurationFileFormat.Hotkeys.Screenshot,
|
||||||
ShowUi = configurationFileFormat.Hotkeys.ShowUi,
|
ShowUi = configurationFileFormat.Hotkeys.ShowUi,
|
||||||
Pause = configurationFileFormat.Hotkeys.Pause,
|
Pause = configurationFileFormat.Hotkeys.Pause,
|
||||||
ToggleMute = Key.F2
|
ToggleMute = new Hotkey(Key.F2)
|
||||||
};
|
};
|
||||||
|
|
||||||
configurationFileFormat.AudioVolume = 1;
|
configurationFileFormat.AudioVolume = 1;
|
||||||
|
@ -1185,8 +1185,8 @@ namespace Ryujinx.Ui.Common.Configuration
|
||||||
ShowUi = configurationFileFormat.Hotkeys.ShowUi,
|
ShowUi = configurationFileFormat.Hotkeys.ShowUi,
|
||||||
Pause = configurationFileFormat.Hotkeys.Pause,
|
Pause = configurationFileFormat.Hotkeys.Pause,
|
||||||
ToggleMute = configurationFileFormat.Hotkeys.ToggleMute,
|
ToggleMute = configurationFileFormat.Hotkeys.ToggleMute,
|
||||||
ResScaleUp = Key.Unbound,
|
ResScaleUp = new Hotkey(Key.Unbound),
|
||||||
ResScaleDown = Key.Unbound
|
ResScaleDown = new Hotkey(Key.Unbound)
|
||||||
};
|
};
|
||||||
|
|
||||||
configurationFileUpdated = true;
|
configurationFileUpdated = true;
|
||||||
|
@ -1216,8 +1216,8 @@ namespace Ryujinx.Ui.Common.Configuration
|
||||||
ToggleMute = configurationFileFormat.Hotkeys.ToggleMute,
|
ToggleMute = configurationFileFormat.Hotkeys.ToggleMute,
|
||||||
ResScaleUp = configurationFileFormat.Hotkeys.ResScaleUp,
|
ResScaleUp = configurationFileFormat.Hotkeys.ResScaleUp,
|
||||||
ResScaleDown = configurationFileFormat.Hotkeys.ResScaleDown,
|
ResScaleDown = configurationFileFormat.Hotkeys.ResScaleDown,
|
||||||
VolumeUp = Key.Unbound,
|
VolumeUp = new Hotkey(Key.Unbound),
|
||||||
VolumeDown = Key.Unbound
|
VolumeDown = new Hotkey(Key.Unbound)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -731,47 +731,47 @@ namespace Ryujinx.Ui
|
||||||
{
|
{
|
||||||
KeyboardHotkeyState state = KeyboardHotkeyState.None;
|
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;
|
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;
|
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;
|
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;
|
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;
|
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;
|
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;
|
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;
|
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;
|
state |= KeyboardHotkeyState.VolumeDown;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue