Skip to content

Use Global Hook for HotkeyControl #933

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions Flow.Launcher.Infrastructure/Hotkey/GlobalHotkey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ namespace Flow.Launcher.Infrastructure.Hotkey
public unsafe class GlobalHotkey : IDisposable
{
private static readonly IntPtr hookId;



public delegate bool KeyboardCallback(KeyEvent keyEvent, int vkCode, SpecialKeyState state);
internal static Func<KeyEvent, int, SpecialKeyState, bool> hookedKeyboardCallback;

Expand All @@ -27,12 +27,15 @@ public unsafe class GlobalHotkey : IDisposable
static GlobalHotkey()
{
// Set the hook
hookId = InterceptKeys.SetHook(& LowLevelKeyboardProc);
hookId = InterceptKeys.SetHook(&LowLevelKeyboardProc);
}

public static SpecialKeyState CheckModifiers()
{
SpecialKeyState state = new SpecialKeyState();



if ((InterceptKeys.GetKeyState(VK_SHIFT) & 0x8000) != 0)
{
//SHIFT is pressed
Expand Down
6 changes: 3 additions & 3 deletions Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@ public ModifierKeys ModifierKeys
}
if (Shift)
{
modifierKeys = modifierKeys | ModifierKeys.Shift;
modifierKeys |= ModifierKeys.Shift;
}
if (Win)
{
modifierKeys = modifierKeys | ModifierKeys.Windows;
modifierKeys |= ModifierKeys.Windows;
}
if (Ctrl)
{
modifierKeys = modifierKeys | ModifierKeys.Control;
modifierKeys |= ModifierKeys.Control;
}
return modifierKeys;
}
Expand Down
3 changes: 3 additions & 0 deletions Flow.Launcher.Infrastructure/Hotkey/InterceptKeys.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,8 @@ public static IntPtr SetHook(delegate* unmanaged<int, UIntPtr, IntPtr, IntPtr> p

[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
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);
}
}
1 change: 0 additions & 1 deletion Flow.Launcher/HotkeyControl.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@
Margin="0,0,18,0"
VerticalContentAlignment="Center"
input:InputMethod.IsInputMethodEnabled="False"
PreviewKeyDown="TbHotkey_OnPreviewKeyDown"
TabIndex="100"
LostFocus="tbHotkey_LostFocus"/>
</Grid>
Expand Down
82 changes: 71 additions & 11 deletions Flow.Launcher/HotkeyControl.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
using Flow.Launcher.Infrastructure.Hotkey;
using Flow.Launcher.Plugin;
using System.Threading;
using Flow.Launcher.Core.Plugin;
using Flow.Launcher.Infrastructure.Logger;

namespace Flow.Launcher
{
Expand All @@ -25,48 +27,106 @@ public partial class HotkeyControl : UserControl

protected virtual void OnHotkeyChanged() => HotkeyChanged?.Invoke(this, EventArgs.Empty);

private Func<int, int, SpecialKeyState, bool> callback { get; set; }

public HotkeyControl()
{
InitializeComponent();
tbMsgTextOriginal = tbMsg.Text;
tbMsgForegroundColorOriginal = tbMsg.Foreground;

callback = TbHotkey_OnPreviewKeyDown;

GotFocus += (_, _) =>
{
PluginManager.API.RegisterGlobalKeyboardCallback(callback);
};
LostFocus += (_, _) =>
{
PluginManager.API.RemoveGlobalKeyboardCallback(callback);
state.AltPressed = false;
state.CtrlPressed = false;
state.ShiftPressed = false;
state.WinPressed = false;
};
}

private CancellationTokenSource hotkeyUpdateSource;

private void TbHotkey_OnPreviewKeyDown(object sender, KeyEventArgs e)
private SpecialKeyState state = new();

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?.Dispose();
hotkeyUpdateSource = new();
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(
specialKeyState.AltPressed,
specialKeyState.ShiftPressed,
specialKeyState.WinPressed,
specialKeyState.CtrlPressed,
state.AltPressed,
state.ShiftPressed,
state.WinPressed,
state.CtrlPressed,
key);

var hotkeyString = hotkeyModel.ToString();

if (hotkeyString == tbHotkey.Text)
{
return;
return false;
}
Log.Debug("test hotkey" + hotkeyString);

_ = Dispatcher.InvokeAsync(async () =>
{
await Task.Delay(500, token);
if (!token.IsCancellationRequested)
await SetHotkey(hotkeyModel);
});

return false;
}

public async Task SetHotkey(HotkeyModel keyModel, bool triggerValidate = true)
Expand Down
2 changes: 2 additions & 0 deletions Flow.Launcher/Languages/en.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@
<system:String x:Key="hotkey">Hotkey</system:String>
<system:String x:Key="flowlauncherHotkey">Flow Launcher Hotkey</system:String>
<system:String x:Key="flowlauncherHotkeyToolTip">Enter shortcut to show/hide Flow Launcher.</system:String>
<system:String x:Key="flowlauncherSpecificHotkey">Recommended Hotkeys</system:String>
<system:String x:Key="flowlauncherSpecificHotkeyToolTip">List of recommended hotkeys.</system:String>
<system:String x:Key="openResultModifiers">Open Result Modifier Key</system:String>
<system:String x:Key="openResultModifiersToolTip">Select a modifier key to open selected result via keyboard.</system:String>
<system:String x:Key="showOpenResultHotkey">Show Hotkey</system:String>
Expand Down
Loading