[PowerToys Run] Use global HotKey instead of low level keyboard hook (#13114)

* [PowerToys Run] Register global HotKey

Using low level keyboard hooks caused focus issues when invoking
PowerToys Run. Using a global HotKey solves this issue.

* Properly unregister hotkey on dispose

* fix spellchecker errors
This commit is contained in:
Jaime Bernardo
2021-09-08 18:39:51 +01:00
committed by GitHub
parent 2c58bdbfb2
commit 5963294b04
8 changed files with 189 additions and 50 deletions

View File

@@ -50,6 +50,12 @@ namespace PowerLauncher.ViewModel
private bool _saved;
private ushort _hotkeyHandle;
private const int _globalHotKeyId = 0x0001;
private IntPtr _globalHotKeyHwnd;
private uint _globalHotKeyVK;
private uint _globalHotKeyFSModifiers;
private bool _usingGlobalHotKey;
internal HotkeyManager HotkeyManager { get; private set; }
public MainViewModel(PowerToysRunSettings settings)
@@ -75,39 +81,44 @@ namespace PowerLauncher.ViewModel
RegisterResultsUpdatedEvent();
}
public void RegisterHotkey()
public void RegisterHotkey(IntPtr hwnd)
{
Log.Info("RegisterHotkey()", GetType());
if (_settings != null && _settings.UsePowerToysRunnerKeyboardHook)
_settings.PropertyChanged += (s, e) =>
{
NativeEventWaiter.WaitForEventLoop(Constants.PowerLauncherSharedEvent(), OnHotkey);
_hotkeyHandle = 0;
}
else
{
HotkeyManager = new HotkeyManager();
_settings.PropertyChanged += (s, e) =>
if (e.PropertyName == nameof(PowerToysRunSettings.Hotkey))
{
if (e.PropertyName == nameof(PowerToysRunSettings.Hotkey))
Application.Current.Dispatcher.Invoke(() =>
{
Application.Current.Dispatcher.Invoke(() =>
if (!string.IsNullOrEmpty(_settings.PreviousHotkey))
{
if (!string.IsNullOrEmpty(_settings.PreviousHotkey))
if (_usingGlobalHotKey)
{
HotkeyManager.UnregisterHotkey(_hotkeyHandle);
NativeMethods.UnregisterHotKey(_globalHotKeyHwnd, _globalHotKeyId);
_usingGlobalHotKey = false;
Log.Info("Unregistering previous global hotkey", GetType());
}
if (!string.IsNullOrEmpty(_settings.Hotkey))
if (_hotkeyHandle != 0)
{
SetHotkey(_settings.Hotkey, OnHotkey);
HotkeyManager?.UnregisterHotkey(_hotkeyHandle);
_hotkeyHandle = 0;
Log.Info("Unregistering previous low level key handler", GetType());
}
});
}
};
}
SetHotkey(_settings.Hotkey, OnHotkey);
SetCustomPluginHotkey();
}
if (!string.IsNullOrEmpty(_settings.Hotkey))
{
SetHotkey(hwnd, _settings.Hotkey, OnHotkey);
}
});
}
};
SetHotkey(hwnd, _settings.Hotkey, OnHotkey);
// TODO: Custom plugin hotkeys.
// SetCustomPluginHotkey();
}
private void RegisterResultsUpdatedEvent()
@@ -672,15 +683,35 @@ namespace PowerLauncher.ViewModel
return selected;
}
private void SetHotkey(string hotkeyStr, HotkeyCallback action)
#pragma warning disable CA1801 // Review unused parameters
internal bool ProcessHotKeyMessages(IntPtr wparam, IntPtr lparam)
#pragma warning restore CA1801 // Review unused parameters
{
var hotkey = new HotkeyModel(hotkeyStr);
SetHotkey(hotkey, action);
if (wparam.ToInt32() == _globalHotKeyId)
{
OnHotkey();
return true;
}
return false;
}
private void SetHotkey(HotkeyModel hotkeyModel, HotkeyCallback action)
private static uint VKModifiersFromHotKey(Hotkey hotkey)
{
return (uint)(HOTKEY_MODIFIERS.NOREPEAT | (hotkey.Alt ? HOTKEY_MODIFIERS.ALT : 0) | (hotkey.Ctrl ? HOTKEY_MODIFIERS.CONTROL : 0) | (hotkey.Shift ? HOTKEY_MODIFIERS.SHIFT : 0) | (hotkey.Win ? HOTKEY_MODIFIERS.WIN : 0));
}
private void SetHotkey(IntPtr hwnd, string hotkeyStr, HotkeyCallback action)
{
var hotkey = new HotkeyModel(hotkeyStr);
SetHotkey(hwnd, hotkey, action);
}
private void SetHotkey(IntPtr hwnd, HotkeyModel hotkeyModel, HotkeyCallback action)
{
Log.Info("Set HotKey()", GetType());
string hotkeyStr = hotkeyModel.ToString();
try
{
Hotkey hotkey = new Hotkey
@@ -692,6 +723,39 @@ namespace PowerLauncher.ViewModel
Key = (byte)KeyInterop.VirtualKeyFromKey(hotkeyModel.CharKey),
};
if (_usingGlobalHotKey)
{
NativeMethods.UnregisterHotKey(_globalHotKeyHwnd, _globalHotKeyId);
_usingGlobalHotKey = false;
Log.Info("Unregistering previous global hotkey", GetType());
}
if (_hotkeyHandle != 0)
{
HotkeyManager?.UnregisterHotkey(_hotkeyHandle);
_hotkeyHandle = 0;
Log.Info("Unregistering previous low level key handler", GetType());
}
_globalHotKeyVK = hotkey.Key;
_globalHotKeyFSModifiers = VKModifiersFromHotKey(hotkey);
if (NativeMethods.RegisterHotKey(hwnd, _globalHotKeyId, _globalHotKeyFSModifiers, _globalHotKeyVK))
{
// Using global hotkey registered through the native RegisterHotKey method.
_globalHotKeyHwnd = hwnd;
_usingGlobalHotKey = true;
Log.Info("Registered global hotkey", GetType());
return;
}
Log.Warn("Registering global shortcut failed. Will use low-level keyboard hook instead.", GetType());
// Using fallback low-level keyboard hook through HotkeyManager.
if (HotkeyManager == null)
{
HotkeyManager = new HotkeyManager();
}
_hotkeyHandle = HotkeyManager.RegisterHotkey(hotkey, action);
}
#pragma warning disable CA1031 // Do not catch general exception types
@@ -721,6 +785,11 @@ namespace PowerLauncher.ViewModel
return false;
}
/* TODO: Custom Hotkeys for Plugins. Commented since this is an incomplete feature.
* This needs:
* - Support for use with global shortcut.
* - Support for use with the fallback Shortcut Manager.
* - Support for use through the runner centralized keyboard hooks.
private void SetCustomPluginHotkey()
{
if (_settings.CustomPluginHotkeys == null)
@@ -742,6 +811,7 @@ namespace PowerLauncher.ViewModel
});
}
}
*/
private void OnHotkey()
{
@@ -966,6 +1036,12 @@ namespace PowerLauncher.ViewModel
{
if (disposing)
{
if (_usingGlobalHotKey)
{
NativeMethods.UnregisterHotKey(_globalHotKeyHwnd, _globalHotKeyId);
_usingGlobalHotKey = false;
}
if (_hotkeyHandle != 0)
{
HotkeyManager?.UnregisterHotkey(_hotkeyHandle);