lightweight variant (#1)

Reviewed-on: #1
Co-authored-by: Aleksandr Neychev <alexnejchev73@gmail.com>
This commit was merged in pull request #1.
This commit is contained in:
2026-08-12 13:37:30 +00:00
committed by alex
parent a0d3098fe4
commit 55ac8e6556
147 changed files with 4055 additions and 2349 deletions
@@ -0,0 +1,209 @@
using System.Reflection;
using System.Runtime.InteropServices;
using CursorLang.Core.Interop;
using CursorLang.Tests.Shared;
namespace CursorLang.Core.Tests.Interop;
/// <summary>
/// Making sense of the events of the system keyboard hook.
/// </summary>
/// <remarks>
/// The events are fed straight into the handler the way Windows sends them:
/// the tests have no right to press keys for real — the interception is shared
/// by the whole system, and a real press would land in someone else's window.
/// </remarks>
public sealed class LowLevelKeyboardHookTests
{
private const int HcAction = 0;
private const int WmKeyDown = 0x0100;
private const int WmKeyUp = 0x0101;
private const int WmSysKeyDown = 0x0104;
private const int WmSysKeyUp = 0x0105;
private const int WmMouseMove = 0x0200;
private const uint Injected = 0x10;
private const int CapsLock = 0x14;
[Theory]
[InlineData(WmKeyDown, true)]
[InlineData(WmSysKeyDown, true)]
[InlineData(WmKeyUp, false)]
[InlineData(WmSysKeyUp, false)]
public void Presses_and_releases_reach_the_handler(int message, bool expectedKeyDown)
{
List<(int Key, bool IsDown)> events = [];
var hook = new LowLevelKeyboardHook((key, isDown) =>
{
events.Add((key, isDown));
return false;
});
using (hook)
{
Send(hook, HcAction, message, CapsLock, flags: 0);
}
Assert.Equal([(CapsLock, expectedKeyDown)], events);
}
[Fact]
public void A_swallowed_event_goes_no_further()
{
using var hook = new LowLevelKeyboardHook(static (_, _) => true);
IntPtr result = Send(hook, HcAction, WmKeyDown, CapsLock, flags: 0);
// A non-zero answer breaks the chain: neither the application nor the
// case handler in Windows will see the event
Assert.Equal(new IntPtr(1), result);
}
// Synthetic input comes from on-screen keyboards and automation tools
[Fact]
public void Synthetic_input_is_not_intercepted()
{
List<int> keys = [];
using var hook = new LowLevelKeyboardHook((key, _) =>
{
keys.Add(key);
return true;
});
IntPtr result = Send(hook, HcAction, WmKeyDown, CapsLock, Injected);
Assert.Empty(keys);
Assert.NotEqual(new IntPtr(1), result);
}
// Windows asks for events below zero not to be inspected but simply passed on
[Fact]
public void Events_not_meant_for_inspection_are_passed_on()
{
List<int> keys = [];
using var hook = new LowLevelKeyboardHook((key, _) =>
{
keys.Add(key);
return true;
});
Send(hook, code: -1, WmKeyDown, CapsLock, flags: 0);
Assert.Empty(keys);
}
[Fact]
public void The_other_messages_are_not_shown_to_the_handler()
{
List<int> keys = [];
using var hook = new LowLevelKeyboardHook((key, _) =>
{
keys.Add(key);
return true;
});
Send(hook, HcAction, WmMouseMove, CapsLock, flags: 0);
Assert.Empty(keys);
}
[Fact]
public void The_handler_sees_the_code_of_the_pressed_key()
{
List<int> keys = [];
using var hook = new LowLevelKeyboardHook((key, _) =>
{
keys.Add(key);
return false;
});
Send(hook, HcAction, WmKeyDown, virtualKey: 0x41, flags: 0);
Send(hook, HcAction, WmKeyDown, virtualKey: 0x1B, flags: 0);
Assert.Equal([0x41, 0x1B], keys);
}
[Fact]
public void The_interception_is_installed_and_removed()
{
Pump.Run(() =>
{
using var hook = new LowLevelKeyboardHook(static (_, _) => false);
Assert.False(hook.IsInstalled);
Assert.True(hook.Install());
Assert.True(hook.IsInstalled);
hook.Uninstall();
Assert.False(hook.IsInstalled);
});
}
[Fact]
public void Installing_again_changes_nothing()
{
Pump.Run(() =>
{
using var hook = new LowLevelKeyboardHook(static (_, _) => false);
Assert.True(hook.Install());
Assert.True(hook.Install());
Assert.True(hook.IsInstalled);
hook.Uninstall();
});
}
[Fact]
public void Removing_without_installing_passes_silently()
{
var hook = new LowLevelKeyboardHook(static (_, _) => false);
hook.Uninstall();
hook.Uninstall();
Assert.False(hook.IsInstalled);
}
[Fact]
public void Closing_removes_the_interception()
{
Pump.Run(() =>
{
var hook = new LowLevelKeyboardHook(static (_, _) => false);
hook.Install();
hook.Dispose();
Assert.False(hook.IsInstalled);
});
}
// The event arrives from Windows as a structure in unmanaged memory
private static IntPtr Send(
LowLevelKeyboardHook hook, int code, int message, int virtualKey, uint flags)
{
// vkCode, scanCode, flags and time take four bytes each, then a pointer
const int Size = 24;
IntPtr data = Marshal.AllocHGlobal(Size);
try
{
Marshal.WriteInt32(data, 0, virtualKey);
Marshal.WriteInt32(data, 4, 0);
Marshal.WriteInt32(data, 8, (int)flags);
Marshal.WriteInt32(data, 12, 0);
Marshal.WriteIntPtr(data, 16, IntPtr.Zero);
MethodInfo handler = typeof(LowLevelKeyboardHook)
.GetMethod("OnHookEvent", BindingFlags.Instance | BindingFlags.NonPublic)!;
return (IntPtr)handler.Invoke(hook, [code, new IntPtr(message), data])!;
}
finally
{
Marshal.FreeHGlobal(data);
}
}
}