mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Use Global Hook for Hotkey Control
This commit is contained in:
parent
add175730b
commit
05dbe94f53
6 changed files with 81 additions and 20 deletions
|
|
@ -12,9 +12,9 @@ namespace Flow.Launcher.Infrastructure.Hotkey
|
||||||
public unsafe class GlobalHotkey : IDisposable
|
public unsafe class GlobalHotkey : IDisposable
|
||||||
{
|
{
|
||||||
private static readonly IntPtr hookId;
|
private static readonly IntPtr hookId;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public delegate bool KeyboardCallback(KeyEvent keyEvent, int vkCode, SpecialKeyState state);
|
public delegate bool KeyboardCallback(KeyEvent keyEvent, int vkCode, SpecialKeyState state);
|
||||||
internal static Func<KeyEvent, int, SpecialKeyState, bool> hookedKeyboardCallback;
|
internal static Func<KeyEvent, int, SpecialKeyState, bool> hookedKeyboardCallback;
|
||||||
|
|
||||||
|
|
@ -27,12 +27,15 @@ namespace Flow.Launcher.Infrastructure.Hotkey
|
||||||
static GlobalHotkey()
|
static GlobalHotkey()
|
||||||
{
|
{
|
||||||
// Set the hook
|
// Set the hook
|
||||||
hookId = InterceptKeys.SetHook(& LowLevelKeyboardProc);
|
hookId = InterceptKeys.SetHook(&LowLevelKeyboardProc);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SpecialKeyState CheckModifiers()
|
public static SpecialKeyState CheckModifiers()
|
||||||
{
|
{
|
||||||
SpecialKeyState state = new SpecialKeyState();
|
SpecialKeyState state = new SpecialKeyState();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if ((InterceptKeys.GetKeyState(VK_SHIFT) & 0x8000) != 0)
|
if ((InterceptKeys.GetKeyState(VK_SHIFT) & 0x8000) != 0)
|
||||||
{
|
{
|
||||||
//SHIFT is pressed
|
//SHIFT is pressed
|
||||||
|
|
|
||||||
|
|
@ -31,15 +31,15 @@ namespace Flow.Launcher.Infrastructure.Hotkey
|
||||||
}
|
}
|
||||||
if (Shift)
|
if (Shift)
|
||||||
{
|
{
|
||||||
modifierKeys = modifierKeys | ModifierKeys.Shift;
|
modifierKeys |= ModifierKeys.Shift;
|
||||||
}
|
}
|
||||||
if (Win)
|
if (Win)
|
||||||
{
|
{
|
||||||
modifierKeys = modifierKeys | ModifierKeys.Windows;
|
modifierKeys |= ModifierKeys.Windows;
|
||||||
}
|
}
|
||||||
if (Ctrl)
|
if (Ctrl)
|
||||||
{
|
{
|
||||||
modifierKeys = modifierKeys | ModifierKeys.Control;
|
modifierKeys |= ModifierKeys.Control;
|
||||||
}
|
}
|
||||||
return modifierKeys;
|
return modifierKeys;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,5 +34,8 @@ namespace Flow.Launcher.Infrastructure.Hotkey
|
||||||
|
|
||||||
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
|
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
|
||||||
public static extern short GetKeyState(int keyCode);
|
public static extern short GetKeyState(int keyCode);
|
||||||
|
|
||||||
|
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
|
||||||
|
public static extern short GetAsyncKeyState(int keyCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -49,7 +49,6 @@
|
||||||
Margin="0,0,18,0"
|
Margin="0,0,18,0"
|
||||||
VerticalContentAlignment="Center"
|
VerticalContentAlignment="Center"
|
||||||
input:InputMethod.IsInputMethodEnabled="False"
|
input:InputMethod.IsInputMethodEnabled="False"
|
||||||
PreviewKeyDown="TbHotkey_OnPreviewKeyDown"
|
|
||||||
TabIndex="100"
|
TabIndex="100"
|
||||||
LostFocus="tbHotkey_LostFocus"/>
|
LostFocus="tbHotkey_LostFocus"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ using Flow.Launcher.Helper;
|
||||||
using Flow.Launcher.Infrastructure.Hotkey;
|
using Flow.Launcher.Infrastructure.Hotkey;
|
||||||
using Flow.Launcher.Plugin;
|
using Flow.Launcher.Plugin;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using Flow.Launcher.Core.Plugin;
|
||||||
|
using Flow.Launcher.Infrastructure.Logger;
|
||||||
|
|
||||||
namespace Flow.Launcher
|
namespace Flow.Launcher
|
||||||
{
|
{
|
||||||
|
|
@ -25,41 +27,93 @@ namespace Flow.Launcher
|
||||||
|
|
||||||
protected virtual void OnHotkeyChanged() => HotkeyChanged?.Invoke(this, EventArgs.Empty);
|
protected virtual void OnHotkeyChanged() => HotkeyChanged?.Invoke(this, EventArgs.Empty);
|
||||||
|
|
||||||
|
private Func<int, int, SpecialKeyState, bool> callback { get; set; }
|
||||||
|
|
||||||
public HotkeyControl()
|
public HotkeyControl()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
tbMsgTextOriginal = tbMsg.Text;
|
tbMsgTextOriginal = tbMsg.Text;
|
||||||
tbMsgForegroundColorOriginal = tbMsg.Foreground;
|
tbMsgForegroundColorOriginal = tbMsg.Foreground;
|
||||||
|
|
||||||
|
callback = TbHotkey_OnPreviewKeyDown;
|
||||||
|
|
||||||
|
GotFocus += (_, _) =>
|
||||||
|
{
|
||||||
|
PluginManager.API.RegisterGlobalKeyboardCallback(callback);
|
||||||
|
};
|
||||||
|
LostFocus += (_, _) =>
|
||||||
|
{
|
||||||
|
PluginManager.API.RemoveGlobalKeyboardCallback(callback);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private CancellationTokenSource hotkeyUpdateSource;
|
private CancellationTokenSource hotkeyUpdateSource;
|
||||||
|
|
||||||
private void TbHotkey_OnPreviewKeyDown(object sender, KeyEventArgs e)
|
private SpecialKeyState state = new SpecialKeyState();
|
||||||
|
|
||||||
|
private bool TbHotkey_OnPreviewKeyDown(int keyevent, int vkcode, SpecialKeyState dummy)
|
||||||
{
|
{
|
||||||
|
var key = KeyInterop.KeyFromVirtualKey(vkcode);
|
||||||
|
|
||||||
|
if ((KeyEvent)keyevent is not (KeyEvent.WM_KEYDOWN or KeyEvent.WM_SYSKEYDOWN))
|
||||||
|
{
|
||||||
|
switch (key)
|
||||||
|
{
|
||||||
|
case Key.LeftAlt or Key.RightAlt:
|
||||||
|
state.AltPressed = false;
|
||||||
|
break;
|
||||||
|
case Key.LeftCtrl or Key.RightCtrl:
|
||||||
|
state.CtrlPressed = false;
|
||||||
|
break;
|
||||||
|
case Key.LeftShift or Key.RightShift:
|
||||||
|
state.ShiftPressed = false;
|
||||||
|
break;
|
||||||
|
case Key.LWin or Key.LWin:
|
||||||
|
state.WinPressed = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (key)
|
||||||
|
{
|
||||||
|
case Key.LeftAlt or Key.RightAlt:
|
||||||
|
state.AltPressed = true;
|
||||||
|
break;
|
||||||
|
case Key.LeftCtrl or Key.RightCtrl:
|
||||||
|
state.CtrlPressed = true;
|
||||||
|
break;
|
||||||
|
case Key.LeftShift or Key.RightShift:
|
||||||
|
state.ShiftPressed = true;
|
||||||
|
break;
|
||||||
|
case Key.LWin or Key.LWin:
|
||||||
|
state.WinPressed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
hotkeyUpdateSource?.Cancel();
|
hotkeyUpdateSource?.Cancel();
|
||||||
hotkeyUpdateSource?.Dispose();
|
hotkeyUpdateSource?.Dispose();
|
||||||
hotkeyUpdateSource = new();
|
hotkeyUpdateSource = new();
|
||||||
var token = hotkeyUpdateSource.Token;
|
var token = hotkeyUpdateSource.Token;
|
||||||
e.Handled = true;
|
|
||||||
|
|
||||||
//when alt is pressed, the real key should be e.SystemKey
|
|
||||||
Key key = e.Key == Key.System ? e.SystemKey : e.Key;
|
|
||||||
|
|
||||||
SpecialKeyState specialKeyState = GlobalHotkey.CheckModifiers();
|
|
||||||
|
|
||||||
var hotkeyModel = new HotkeyModel(
|
var hotkeyModel = new HotkeyModel(
|
||||||
specialKeyState.AltPressed,
|
state.AltPressed,
|
||||||
specialKeyState.ShiftPressed,
|
state.ShiftPressed,
|
||||||
specialKeyState.WinPressed,
|
state.WinPressed,
|
||||||
specialKeyState.CtrlPressed,
|
state.CtrlPressed,
|
||||||
key);
|
key);
|
||||||
|
|
||||||
var hotkeyString = hotkeyModel.ToString();
|
var hotkeyString = hotkeyModel.ToString();
|
||||||
|
|
||||||
if (hotkeyString == tbHotkey.Text)
|
if (hotkeyString == tbHotkey.Text)
|
||||||
{
|
{
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
Log.Debug("test hotkey" + hotkeyString);
|
||||||
|
|
||||||
_ = Dispatcher.InvokeAsync(async () =>
|
_ = Dispatcher.InvokeAsync(async () =>
|
||||||
{
|
{
|
||||||
|
|
@ -67,6 +121,8 @@ namespace Flow.Launcher
|
||||||
if (!token.IsCancellationRequested)
|
if (!token.IsCancellationRequested)
|
||||||
await SetHotkey(hotkeyModel);
|
await SetHotkey(hotkeyModel);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SetHotkey(HotkeyModel keyModel, bool triggerValidate = true)
|
public async Task SetHotkey(HotkeyModel keyModel, bool triggerValidate = true)
|
||||||
|
|
|
||||||
|
|
@ -137,7 +137,7 @@ namespace Flow.Launcher
|
||||||
|
|
||||||
private void OnHotkeyControlFocused(object sender, RoutedEventArgs e)
|
private void OnHotkeyControlFocused(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
HotKeyMapper.RemoveHotkey(settings.Hotkey);
|
//HotKeyMapper.RemoveHotkey(settings.Hotkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnHotkeyControlFocusLost(object sender, RoutedEventArgs e)
|
private void OnHotkeyControlFocusLost(object sender, RoutedEventArgs e)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue