mirror of
https://github.com/microsoft/PowerToys
synced 2025-09-07 18:05:12 +00:00
[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:
@@ -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);
|
||||
|
Reference in New Issue
Block a user