[Settings/Run] LowLevel Keyboard hooking for Hotkeys (#3825)

* [Launcher/Settings] Low Level Keyboard Hooks

* [Run] LowLevel Keyboard Hook for Hotkeys

* Prevent shortcuts from auto repeating when keeping the keys pressed down
This commit is contained in:
Tomas Agustin Raies
2020-06-11 12:59:36 -07:00
committed by GitHub
parent fa7e4cc817
commit 670033c4da
28 changed files with 584 additions and 546 deletions

View File

@@ -2,9 +2,9 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.PowerToys.Settings.UI.Lib.Utilities;
using System.Text;
using System.Text.Json.Serialization;
using Microsoft.PowerToys.Settings.UI.Lib.Utilities;
namespace Microsoft.PowerToys.Settings.UI.Lib
{
@@ -30,6 +30,11 @@ namespace Microsoft.PowerToys.Settings.UI.Lib
Code = code;
}
public HotkeySettings Clone()
{
return new HotkeySettings(Win, Ctrl, Alt, Shift, Key, Code);
}
[JsonPropertyName("win")]
public bool Win { get; set; }
@@ -72,8 +77,16 @@ namespace Microsoft.PowerToys.Settings.UI.Lib
output.Append("Shift + ");
}
var localKey = Helper.GetKeyName((uint) Code);
output.Append(localKey);
if (Code > 0)
{
var localKey = Helper.GetKeyName((uint)Code);
output.Append(localKey);
}
else if (output.Length >= 2)
{
output.Remove(output.Length - 2, 2);
}
return output.ToString();
}

View File

@@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Text;
using interop;
namespace Microsoft.PowerToys.Settings.UI.Lib
{
public delegate void KeyEvent(int key);
public delegate bool IsActive();
public class HotkeySettingsControlHook
{
const int WM_KEYDOWN = 0x100;
const int WM_KEYUP = 0x101;
const int WM_SYSKEYDOWN = 0x0104;
const int WM_SYSKEYUP = 0x0105;
private KeyboardHook hook;
private KeyEvent keyDown;
private KeyEvent keyUp;
private IsActive isActive;
public HotkeySettingsControlHook(KeyEvent keyDown, KeyEvent keyUp, IsActive isActive)
{
this.keyDown = keyDown;
this.keyUp = keyUp;
this.isActive = isActive;
hook = new KeyboardHook(HotkeySettingsHookCallback, IsActive, null);
hook.Start();
}
private bool IsActive()
{
return isActive();
}
private void HotkeySettingsHookCallback(KeyboardEvent ev)
{
switch (ev.message)
{
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
keyDown(ev.key);
break;
case WM_KEYUP:
case WM_SYSKEYUP:
keyUp(ev.key);
break;
}
}
}
}

View File

@@ -7,6 +7,7 @@ using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using System;
// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236
namespace Microsoft.PowerToys.Settings.UI.Controls
@@ -23,7 +24,10 @@ namespace Microsoft.PowerToys.Settings.UI.Controls
null);
private HotkeySettings hotkeySettings;
private HotkeySettings internalSettings = new HotkeySettings();
private HotkeySettings internalSettings;
private HotkeySettings lastValidSettings;
private HotkeySettingsControlHook hook;
private bool _isActive;
public HotkeySettings HotkeySettings
{
@@ -48,74 +52,85 @@ namespace Microsoft.PowerToys.Settings.UI.Controls
InitializeComponent();
internalSettings = new HotkeySettings();
HotkeyTextBox.PreviewKeyDown += HotkeyTextBox_KeyDown;
HotkeyTextBox.LostFocus += HotkeyTextBox_LosingFocus;
HotkeyTextBox.GettingFocus += HotkeyTextBox_GettingFocus;
HotkeyTextBox.LosingFocus += HotkeyTextBox_LosingFocus;
hook = new HotkeySettingsControlHook(Hotkey_KeyDown, Hotkey_KeyUp, Hotkey_IsActive);
}
private static bool IsDown(Windows.System.VirtualKey key)
private void KeyEventHandler(int key, bool matchValue, int matchValueCode, string matchValueText)
{
return Window.Current.CoreWindow.GetKeyState(key).HasFlag(CoreVirtualKeyStates.Down);
switch ((Windows.System.VirtualKey)key)
{
case Windows.System.VirtualKey.LeftWindows:
case Windows.System.VirtualKey.RightWindows:
internalSettings.Win = matchValue;
break;
case Windows.System.VirtualKey.Control:
case Windows.System.VirtualKey.LeftControl:
case Windows.System.VirtualKey.RightControl:
internalSettings.Ctrl = matchValue;
break;
case Windows.System.VirtualKey.Menu:
case Windows.System.VirtualKey.LeftMenu:
case Windows.System.VirtualKey.RightMenu:
internalSettings.Alt = matchValue;
break;
case Windows.System.VirtualKey.Shift:
case Windows.System.VirtualKey.LeftShift:
case Windows.System.VirtualKey.RightShift:
internalSettings.Shift = matchValue;
break;
case Windows.System.VirtualKey.Escape:
internalSettings = new HotkeySettings();
HotkeySettings = new HotkeySettings();
return;
default:
internalSettings.Code = matchValueCode;
internalSettings.Key = matchValueText;
break;
}
}
private void HotkeyTextBox_KeyDown(object sender, KeyRoutedEventArgs e)
private async void Hotkey_KeyDown(int key)
{
e.Handled = true;
if (
e.Key == Windows.System.VirtualKey.LeftWindows ||
e.Key == Windows.System.VirtualKey.RightWindows ||
e.Key == Windows.System.VirtualKey.Control ||
e.Key == Windows.System.VirtualKey.Menu ||
e.Key == Windows.System.VirtualKey.Shift)
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
return;
}
KeyEventHandler(key, true, key, Lib.Utilities.Helper.GetKeyName((uint)key));
if (internalSettings.Code > 0)
{
lastValidSettings = internalSettings.Clone();
HotkeyTextBox.Text = lastValidSettings.ToString();
}
});
}
if (e.Key == Windows.System.VirtualKey.Escape)
private async void Hotkey_KeyUp(int key)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
internalSettings = new HotkeySettings();
HotkeySettings = new HotkeySettings();
return;
}
KeyEventHandler(key, false, 0, string.Empty);
});
}
var settings = new HotkeySettings();
private bool Hotkey_IsActive()
{
return _isActive;
}
// Display HotKey value
if (IsDown(Windows.System.VirtualKey.LeftWindows) ||
IsDown(Windows.System.VirtualKey.RightWindows))
{
settings.Win = true;
}
if (IsDown(Windows.System.VirtualKey.Control))
{
settings.Ctrl = true;
}
if (IsDown(Windows.System.VirtualKey.Menu))
{
settings.Alt = true;
}
if (IsDown(Windows.System.VirtualKey.Shift))
{
settings.Shift = true;
}
settings.Key = Lib.Utilities.Helper.GetKeyName((uint)e.Key);
settings.Code = (int)e.OriginalKey;
internalSettings = settings;
HotkeyTextBox.Text = internalSettings.ToString();
private void HotkeyTextBox_GettingFocus(object sender, RoutedEventArgs e)
{
_isActive = true;
}
private void HotkeyTextBox_LosingFocus(object sender, RoutedEventArgs e)
{
if (internalSettings.IsValid() || internalSettings.IsEmpty())
if (lastValidSettings != null && (lastValidSettings.IsValid() || lastValidSettings.IsEmpty()))
{
HotkeySettings = internalSettings;
HotkeySettings = lastValidSettings.Clone();
}
HotkeyTextBox.Text = hotkeySettings.ToString();
_isActive = false;
}
}
}