-
-
Notifications
You must be signed in to change notification settings - Fork 21
Input Handling
The Dapplo.Windows.Input package provides system-wide keyboard and mouse hooks built on Reactive Extensions (Rx.NET), plus utilities for programmatically generating input events.
Install-Package Dapplo.Windows.Inputusing Dapplo.Windows.Input.Keyboard;
using var keyboardHook = KeyboardHook.Create();
var subscription = keyboardHook.KeyboardEvents.Subscribe(e =>
{
Console.WriteLine($"Key: {e.Key} Down: {e.IsDown}");
});
Console.ReadLine(); // keep aliveusing System.Reactive.Linq;
// Key presses only
keyboardHook.KeyboardEvents
.Where(e => e.IsDown)
.Subscribe(e => Console.WriteLine($"Pressed: {e.Key}"));
// A specific key
keyboardHook.KeyboardEvents
.Where(e => e.Key == VirtualKeyCode.Escape && e.IsDown)
.Subscribe(_ => Console.WriteLine("Escape!"));Check modifier key state directly on the event:
// Ctrl+C
keyboardHook.KeyboardEvents
.Where(e => e.IsDown && e.Key == VirtualKeyCode.C && e.IsControlPressed)
.Subscribe(_ => Console.WriteLine("Ctrl+C"));
// Ctrl+Shift+A
keyboardHook.KeyboardEvents
.Where(e => e.IsDown && e.Key == VirtualKeyCode.A && e.IsControlPressed && e.IsShiftPressed)
.Subscribe(_ => Console.WriteLine("Ctrl+Shift+A"));KeyCombinationHandler makes complex hotkey detection clean and explicit:
var handler = new KeyCombinationHandler(
VirtualKeyCode.Control,
VirtualKeyCode.Menu, // Alt
VirtualKeyCode.T);
keyboardHook.KeyboardEvents
.Where(handler)
.Subscribe(e =>
{
Console.WriteLine("Ctrl+Alt+T");
e.Handled = true; // prevent other apps from seeing it
});When you want to inject keystrokes as a result of a hotkey, use TriggerOnKeyUp = true. This fires after the modifier keys are released so they do not contaminate the injected input.
var handler = new KeyCombinationHandler(
VirtualKeyCode.Control,
VirtualKeyCode.Menu,
VirtualKeyCode.LeftWin,
VirtualKeyCode.T)
{
TriggerOnKeyUp = true
};
keyboardHook.KeyboardEvents
.Where(handler)
.Subscribe(e =>
{
KeyboardInputGenerator.TypeText(DateTime.Now.ToString("yyyy-MM-dd--HH-mm-ss"));
e.Handled = true;
});This is useful for text-expansion / AutoHotKey-style scripts.
using System.Collections.Generic;
var sequence = new List<VirtualKeyCode>();
keyboardHook.KeyboardEvents
.Where(e => e.IsDown)
.Subscribe(e =>
{
sequence.Add(e.Key);
if (sequence.Count > 3) sequence.RemoveAt(0);
if (sequence is [VirtualKeyCode.G, VirtualKeyCode.G, VirtualKeyCode.O])
{
Console.WriteLine("GGO sequence detected!");
sequence.Clear();
}
});KeyboardInputGenerator injects input events into the system's input stream.
using Dapplo.Windows.Input.Keyboard;
KeyboardInputGenerator.TypeText("Hello, World!");// Copy (Ctrl+C)
KeyboardInputGenerator.KeyCombinationPress(VirtualKeyCode.Control, VirtualKeyCode.C);
// Paste (Ctrl+V)
KeyboardInputGenerator.KeyCombinationPress(VirtualKeyCode.Control, VirtualKeyCode.V);KeyboardInputGenerator.KeyDown(VirtualKeyCode.Shift);
KeyboardInputGenerator.KeyPress(VirtualKeyCode.A); // types "A"
KeyboardInputGenerator.KeyUp(VirtualKeyCode.Shift);using Dapplo.Windows.Input.Mouse;
using var mouseHook = MouseHook.Create();
var subscription = mouseHook.MouseEvents.Subscribe(e =>
{
Console.WriteLine($"Button: {e.Button} Down: {e.IsButtonDown} At: {e.Point}");
});using System.Reactive.Linq;
// Left-button clicks
mouseHook.MouseEvents
.Where(e => e.Button == MouseButtons.Left && e.IsButtonDown)
.Subscribe(e => Console.WriteLine($"Left click at {e.Point}"));
// Mouse movement
mouseHook.MouseEvents
.Where(e => e.IsMouseMoveEvent)
.Subscribe(e => Console.WriteLine($"Mouse at {e.Point}"));
// Scroll wheel
mouseHook.MouseEvents
.Where(e => e.IsScrollEvent)
.Subscribe(e => Console.WriteLine($"Scroll delta: {e.ScrollDelta}"));using Dapplo.Windows.Input.Mouse;
using Dapplo.Windows.Common.Structs;
// Move to absolute screen coordinate
MouseInputGenerator.MoveTo(new NativePoint(500, 300));
// Click left button
MouseInputGenerator.LeftButtonClick();
// Double-click
MouseInputGenerator.LeftButtonDoubleClick();
// Right-click
MouseInputGenerator.RightButtonClick();
// Scroll down
MouseInputGenerator.MoveMouseWheel(-120);Set e.Handled = true inside a hook subscription to prevent the key or mouse event from being passed to other applications:
keyboardHook.KeyboardEvents
.Where(e => e.IsDown && e.Key == VirtualKeyCode.F12)
.Subscribe(e =>
{
Console.WriteLine("F12 intercepted");
e.Handled = true; // other apps will not see F12
});Note: Suppression works only while the hook is active. Dispose the hook to restore normal behavior.
- Dispose hooks and subscriptions when they are no longer needed to free the system hook slot.
- Keep hook handlers fast — low-level input hooks are synchronous and will delay system input if they block.
-
Use
TriggerOnKeyUpwhen injecting input as part of a hotkey response. - Test on all target DPI settings — mouse coordinates are in physical pixels.