diff --git a/UI/Windows/GetKeyWindow.axaml.cs b/UI/Windows/GetKeyWindow.axaml.cs index 0d4bf26fd..842bf9a34 100644 --- a/UI/Windows/GetKeyWindow.axaml.cs +++ b/UI/Windows/GetKeyWindow.axaml.cs @@ -13,9 +13,8 @@ using Mesen.Config; using Mesen.Utilities; using Mesen.Localization; -using Avalonia.Remote.Protocol.Input; -using DynamicData; using Avalonia.Layout; +using System.Diagnostics; namespace Mesen.Windows { @@ -27,6 +26,9 @@ public class GetKeyWindow : MesenWindow private TextBlock lblCurrentKey; private bool _allowKeyboardOnly; + private Stopwatch _stopWatch = Stopwatch.StartNew(); + private Dictionary _keyPressedStamp = new(); + public string HintLabel { get; } public bool SingleKeyMode { get; set; } = false; @@ -71,16 +73,31 @@ private void OnPreviewKeyDown(object? sender, KeyEventArgs e) { InputApi.SetKeyState((UInt16)e.Key, true); DbgShortcutKey = new DbgShortKeys(e.KeyModifiers, e.Key); + _keyPressedStamp[e.Key] = _stopWatch.ElapsedTicks; e.Handled = true; } private void OnPreviewKeyUp(object? sender, KeyEventArgs e) { + e.Handled = true; + + if(!_allowKeyboardOnly && (!_keyPressedStamp.TryGetValue(e.Key, out long stamp) || ((_stopWatch.ElapsedTicks - stamp) * 1000 / Stopwatch.Frequency) < 10)) { + //Key up received without key down, or key pressed for less than 10 ms, pretend the key was pressed for 50ms + //Some special keys can behave this way (e.g printscreen) + DbgShortcutKey = new DbgShortKeys(e.KeyModifiers, e.Key); + InputApi.SetKeyState((UInt16)e.Key, true); + UpdateKeyDisplay(); + DispatcherTimer.RunOnce(() => InputApi.SetKeyState((UInt16)e.Key, false), TimeSpan.FromMilliseconds(50), DispatcherPriority.MaxValue); + _keyPressedStamp.Remove(e.Key); + return; + } + + _keyPressedStamp.Remove(e.Key); + InputApi.SetKeyState((UInt16)e.Key, false); if(_allowKeyboardOnly) { this.Close(); } - e.Handled = true; } protected override void OnOpened(EventArgs e) diff --git a/UI/Windows/MainWindow.axaml.cs b/UI/Windows/MainWindow.axaml.cs index bdad35720..011c7c418 100644 --- a/UI/Windows/MainWindow.axaml.cs +++ b/UI/Windows/MainWindow.axaml.cs @@ -22,6 +22,7 @@ using System.Collections.Generic; using Mesen.Controls; using Mesen.Localization; +using System.Diagnostics; namespace Mesen.Windows { @@ -58,6 +59,9 @@ public class MainWindow : MesenWindow private Dictionary _pendingKeyUpEvents = new(); private bool _isLinux = false; + private Stopwatch _stopWatch = Stopwatch.StartNew(); + private Dictionary _keyPressedStamp = new(); + static MainWindow() { WindowStateProperty.Changed.AddClassHandler((x, e) => x.OnWindowStateChanged()); @@ -558,11 +562,6 @@ private bool ProcessTestModeShortcuts(Key key) return false; } - private static bool IsModifierKey(Key key) - { - return key == Key.LeftShift || key == Key.LeftCtrl || key == Key.LeftAlt || key == Key.RightShift || key == Key.RightCtrl || key == Key.RightAlt; - } - private void OnPreviewKeyDown(object? sender, KeyEventArgs e) { if(_testModeEnabled && e.KeyModifiers == KeyModifiers.Alt && ProcessTestModeShortcuts(e.Key)) { @@ -575,6 +574,8 @@ private void OnPreviewKeyDown(object? sender, KeyEventArgs e) } if(e.Key != Key.None) { + _keyPressedStamp[e.Key] = _stopWatch.ElapsedTicks; + if(_isLinux && _pendingKeyUpEvents.TryGetValue(e.Key, out IDisposable? cancelTimer)) { //Cancel any pending key up event cancelTimer.Dispose(); @@ -598,6 +599,17 @@ private void OnPreviewKeyUp(object? sender, KeyEventArgs e) } if(e.Key != Key.None) { + if(!_keyPressedStamp.TryGetValue(e.Key, out long stamp) || ((_stopWatch.ElapsedTicks - stamp) * 1000 / Stopwatch.Frequency) < 10) { + //Key up received without key down, or key pressed for less than 10 ms, pretend the key was pressed for 50ms + //Some special keys can behave this way (e.g printscreen) + InputApi.SetKeyState((UInt16)e.Key, true); + DispatcherTimer.RunOnce(() => InputApi.SetKeyState((UInt16)e.Key, false), TimeSpan.FromMilliseconds(50), DispatcherPriority.MaxValue); + _keyPressedStamp.Remove(e.Key); + return; + } + + _keyPressedStamp.Remove(e.Key); + if(_isLinux) { //Process keyup events after 1ms on Linux to prevent key repeat from triggering key up/down repeatedly IDisposable cancelTimer = DispatcherTimer.RunOnce(() => InputApi.SetKeyState((UInt16)e.Key, false), TimeSpan.FromMilliseconds(1), DispatcherPriority.MaxValue);