Note
Documentation is in the process of being written.
The DeftSharp library provides flexible and powerful functionality for keyboard and mouse control in the Windows OS.
It is built using P/Invoke methods, with the help of libraries such as User32 and Kernel32.
The principle of the library is based on a chain of interceptors that are formed into a pipeline. Before an incoming event can be successfully processed by the system, it must pass through all registered interceptors. If the pipeline is empty, the library will not use system resources and will not affect their operation.
New interceptors are registered by your interaction with classes such as KeyboardListener. This allows you to observe special events and have control over them. If the provided classes do not fit your needs, you can create your own custom interceptor.
The functionality of the library is divided into two types of functionality, temporary and permanent changes. Temporary changes are active only while the program is running, such as subscribing to input events. Permanent changes do not depend on your application, such as changes in mouse speed. Permanent changes are marked with the SystemChanges
attribute.
You should make sure that your application fits the requirements of this library.
- Version .NET 8 or higher (Install)
- Any Windows UI framework (WPF, WinUI, Avalonia, and MAUI)
Next, you need to use the Nuget package manager and install this library.
Or you can use the command in the console:
dotnet add package DeftSharp.Windows.Input
Now, using a basic WPF application as an example, we'll subscribe to our first event, the Escape button press. When this button is pressed, our application should close, regardless of whether our window is currently active or not.
To do this, we'll go into MainWindow.xaml.cs
and add a little logic. We need to create an object of the KeyboardListener class, with which we can subscribe to global keyboard input events.
The DeftSharp library has many different classes for handle user input. Below, you will be able to familiarize yourself with all of them.
In this section, you can familiarize yourself with all the existing classes.
These classes provide global control and observation of the keyboard.
The KeyboardListener class provides the ability to subscribe to global keyboard input events. This can give you information about the user's presses, sequences, and key combinations.
You can subscribe to different events from the KeyboardListener class to enable these features and customize them.
This class stores active subscriptions in the Keys
, Sequences
, and Combinations
properties.
Each object of the KeyboardListener class stores its own subscriptions. Keep this in mind when you use the Unsubscribe
methods.
Note
💡 Best Practice: Before closing the application, unsubscribe from all events. This correctly releases the system resources used by the application.
Call one of the following methods to subscribe to a keyboard event.
var keyboardListener = new KeyboardListener();
// Subscription for each click
keyboardListener.Subscribe(Key.Space, () => Trace.WriteLine($"The Space was pressed"));
// One-time subscription
keyboardListener.SubscribeOnce(Key.Space, key => Trace.WriteLine($"The {key} was pressed"));
// Subscription with interval and event type
keyboardListener.Subscribe(Key.Space, (key, eventType) =>
{
Trace.WriteLine($"The {key} was pressed")
},
TimeSpan.FromSeconds(1), // Interval of callback triggering
KeyboardEvent.Up); // Subscribe to up events
Each subscription method returns an object with event details, such as a unique identifier and event type.
You can unsubscribe from an event using several options. Unsubscribe by GUID, by key, or unsubscribe from all events at once.
// Subscribe to the event
var subscription = keyboardListener.Subscribe(Key.A, key => { });
// 3 different unsubscribe options
keyboardListener.Unsubscribe();
keyboardListener.Unsubscribe(Key.A);
keyboardListener.Unsubscribe(subscription.Id);
You can get information about the current state of the keys. To do this you can use the created properties or call the IsKeyPressed()
method.
var isNumLockActive = keyboardListener.IsNumLockActive;
var isCapsLockActive = keyboardListener.IsCapsLockActive;
var isSpacePressed = keyboardListener.IsKeyPressed(Key.Space);
The KeyboardManipulator class provides the ability to control the keyboard.
Note
This class works with a single context. Therefore, all your objects of this class have the same state.
Features
- Simulate keyboard input
- Prevent input events
- Set the press interval
You can simulate pressing keyboard buttons. To do this, use the Press()
method, which accepts a collection of keys. The simulated keys are fully compatible with your keyboard, so different active modifiers will be applied to them, such as the Shift key.
var keyboard = new KeyboardManipulator();
// Single button press
keyboard.Press(Key.Space);
// Combination press
keyboard.Press(Key.LeftCtrl, Key.V);
If you need to trigger a specific event, you can use Simulate()
methods that directly simulate keyboard input events.
// Hold the button
keyboard.Simulate(Key.LeftShift, KeyboardSimulateOption.KeyDown);
// Release the button
keyboard.Simulate(Key.LeftShift, KeyboardSimulateOption.KeyUp);
Use Prevent()
to prevent input events. You can prevent input events either by default or through a condition. All locked keys are stored in the LockedKeys
collection. The keys will be locked until you call the Release()
method or
complete the application.
var keyboard = new KeyboardManipulator();
// Each press of this button will be ignored
keyboard.Prevent(Key.Delete);
// Prevent with condition
keyboard.Prevent(Key.Escape, () =>
{
var currentTime = DateTime.Now;
return currentTime.Minute > 30;
});
// Release locked keys
keyboard.Release();
To check the current state of a button, use IsKeyLocked()
.
// Preventing input of the space button
keyboard.Prevent(Key.Space);
// Checks if the code prevents the space button input
keyboard.IsKeyLocked(Key.Space);
// Returns true
You can also use the KeyPrevented
event to display a message about the prevented input.
// Subscription to input preventing event
keyboard.KeyPrevented += args => Trace.WriteLine($"Pressing the {args.KeyPressed} button has been prevented");
Use SetInterval()
to control the frequency of presses. You can use this method to set a global interval for pressing a keyboard key. As with locked buttons, the interval will remain until you remove it or the application completes.
var keyboard = new KeyboardManipulator();
// Space will now trigger no more than once per second
keyboard.SetInterval(Key.Space, TimeSpan.FromSeconds(1));
// Remove interval
keyboard.ResetInterval(Key.Space);
// Remove interval alternative
keyboard.SetInterval(Key.Space, TimeSpan.Zero);
The KeyboardBinder class provides the ability to modify the bindings of specific keys. All bindings are stored in the BoundedKeys
property.
Note
This class works with a single context. Therefore, all your objects of this class have the same state.
Use Bind()
to change the binding of a key. This enables a key to behave like another key. If used on a already binded key, it will update the bind to the key you choose.
var keyboardBinder = new KeyboardBinder();
// Sets the 'Q' key to behave like the 'W' key. Any time the 'Q' button is triggered, it will behave like the 'W' button
keyboardBinder.Bind(Key.Q, Key.W);
Use Swap()
to swap key bindings. You can also swap keys by using the Bind()
method.
var keyboardBinder = new KeyboardBinder();
// Swaps the 'Q' key with the 'W' key
keyboardBinder.Swap(Key.Q, Key.W);
// Alternate way to swap the 'Q' key with the 'W' key
keyboardBinder.Bind(Key.Q, Key.W);
keyboardBinder.Bind(Key.W, Key.Q);
Use IsKeyBounded()
to get the current state of the bindings. This method returns true or false depending on the existence of the binding.
To return the binding of a specific key, use GetBoundKey()
.
var keyboardBinder = new KeyboardBinder();
// Binds 'Q' to behave like 'W'
keyboardBinder.Bind(Key.Q, Key.W);
// Returns true
keyboardBinder.IsKeyBounded(Key.Q);
//Returns 'W'
keyboardBinder.GetBoundKey(Key.Q);
//Returns 'W'
keyboardBinder.GetBoundKey(Key.W);
Use Unbind()
to unbind keys. You can use this method for specific binded keys, or all binded keys.
var keyboardBinder = new KeyboardBinder();
// Binds 'Q' to behave like 'W'
keyboardBinder.Bind(Key.Q, Key.W);
// Unbinds 'Q'
keyboardBinder.Unbind(Key.Q);
// Unbinds all binded keys
keyboardBinder.Unbind();
This class provides various information about the keyboard, both physical and software.
This method helps to find out the active layout of the user.
var keyboardInfo = new KeyboardInfo();
// Getting the layout
var layout = keyboardInfo.GetLayout();
Trace.WriteLine(layout.Id); // 1033
Trace.WriteLine(layout.LocaleId); // 1033
Trace.WriteLine(layout.Name); // en-US
Trace.WriteLine(layout.DisplayName); // English (United States)
Up to 7 basic keyboard types are supported.
var keyboardInfo = new KeyboardInfo();
// Getting the type
var type = keyboardInfo.GetKeyboardType();
Trace.WriteLine(type.Name); // IBM enhanced (101- or 102-key) keyboard
Trace.WriteLine(type.Value); // 4
These classes provide global control and observation of the mouse.
The MouseListener class provides the ability to use global mouse input events. This gives you information about mouse clicks, scrolling, mouse movement, and more.
You can subscribe to different events from the MouseListener class to enable these features and customize them.
This class stores active subscriptions in the Subscriptions
property.
Each object of the MouseListener class stores its own subscriptions. Keep this in mind when you use the Unsubscribe
methods.
Note
💡 Best Practice: Before closing the application, unsubscribe from all events. This correctly releases the system resources used by the application.
Call one of the following methods to subscribe to a mouse event.
var mouseListener = new MouseListener();
// Subscription for left button down event
mouseListener.Subscribe(MouseEvent.LeftButtonDown,
() => Trace.WriteLine($"The left mouse button was pressed"));
// One-time subscription
mouseListener.SubscribeOnce(MouseEvent.RightButtonDown,
() => Trace.WriteLine($"The right mouse button was pressed"));
// Subscription to generic mouse down event that will trigger on any mouse button
mouseListener.Subscribe(MouseEvent.ButtonDown, mouseEvent
=> Trace.WriteLine($"The {mouseEvent} was pressed"));
Each subscription method returns an object with event details, such as a unique identifier and event type.
You can unsubscribe from an event using several options. Unsubscribe by GUID, by key, or unsubscribe from all events at once.
// Subscribe to the event
var subscription = mouseListener.Subscribe(MouseEvent.MiddleButtonDown, () => { });
// 3 different unsubscribe options
mouseListener.Unsubscribe();
mouseListener.Unsubscribe(subscription.Id);
mouseListener.Unsubscribe(MouseEvent.MiddleButtonDown);
You can get information about the current state of the keys. To do this you can call the IsKeyPressed()
method.
var isLeftButtonPressed = mouseListener.IsKeyPressed(MouseButton.Left);
You can get the position of the mouse by using the Position
property. It will return the Point class with an X and Y value.
var mouseListener = new MouseListener();
var position = mouseListener.Position;
Trace.WriteLine($"X: {position.X} Y: {position.Y}"); // X: 943 Y: 378
The MouseManipulator class provides the ability to control the mouse. This class contains both temporal and permanent methods that change mouse behavior. Temporary methods will cease once the application terminates. Permanent methods will stay active after the application terminates.
Note
📌 This class works with a single context. Therefore, all your objects of this class have the same state.
Temporary changes
- Simulating mouse input
- Preventing input events
Permanent changes
- Setting the mouse speed
You can simulate mouse click events.
var mouse = new MouseManipulator();
// Simulates one click
mouse.Click();
// Simulates two clicks
mouse.DoubleClick();
You can also set the coordinates of moves and clicks.
// Sets the mouse cursor to X:840 Y:420 coordinates
mouse.SetPosition(840, 420);
// Right click by coordinates
mouse.Click(500, 500, MouseButton.Right);
You can scroll the mouse wheel in the direction you want.
// Scroll up
mouse.Scroll(450);
// Scroll down
mouse.Scroll(-150);
Use Prevent()
to prevent input events. You can prevent input events either by default or through a condition. All locked keys are stored in the LockedKeys
collection. The keys will be locked until you call the Release()
method or you complete the application.
var mouse = new MouseManipulator();
// Each scroll event will be ignored
mouse.Prevent(MousePreventOption.Scroll);
// Prevent with condition
mouse.Prevent(MousePreventOption.RightButton, () =>
{
var currentTime = DateTime.Now;
return currentTime.Minute > 30;
});
// Release locked keys
mouse.Release();
To check the current state of a button, use IsKeyLocked()
.
// Preventing the input of mouse movement
mouse.Prevent(MousePreventOption.Move);
// Checks if the code prevents the mouse movement input
mouse.IsKeyLocked(MousePreventOption.Move);
// Returns true
You can also use the InputPrevented
event to display a message about the prevented input.
// Subscription to mouse input preventing event
mouse.InputPrevented += mEvent => Trace.WriteLine($"The {mEvent} event was prevented");
Use SetMouseSpeed()
to change the mouse speed on your system. This method will persist regardless of your system changes.
var mouse = new MouseManipulator();
// changes the mouse speed on your system
mouse.SetMouseSpeed(5);
This class provides various information about the mouse, both physical and software.
var mouseInfo = new MouseInfo();
// Getting the speed
var speed = mouseInfo.GetSpeed();
Trace.WriteLine(speed); // 10
You can change the speed of the mouse using the MouseManipulator class.
Custom interceptors provide the ability for users to manage incoming events themselves. This is a bit more complex than using already created interceptors such as KeyboardListener. But in some cases you can't do without it.
The work of interceptors can be visualized as a pipeline. Before an incoming event is processed by the system, it passes through all registered interceptors and if at least one interceptor blocks an incoming event, it will not be processed. Each interceptor has its own name, which corresponds to the name of the class. And also the type of interceptor.
Interceptors are registered using the Hook
method. A call to this method adds an interceptor to the pipeline, and a call to the Unhook
method removes it.
To create an interceptor you need to inherit from MouseInterceptor
or KeyboardInterceptor
and implement IsInputAllowed
method.
IsInputAllowed
method is called when any input event occurs. It decides whether we need to block this event or not.
Also, interceptors have two optional methods, such as OnInputSuccess
and OnInputFailure
. The OnInputSuccess
method is called when the input event is successfully processed by all interceptors. In this method we can get the details of the event and execute the code we need. The OnInputFailure
method is called if the event was blocked, and besides the event details we can also get the list of interceptors that did not approve this input.
Note
💡 Best Practice: Design interceptors so that each interceptor solves only one specific problem.
As an example we will implement 2 interceptors. One of them will block mouse scroll event. The second one will output mouse input events.
Interceptor for blocking mouse scroll events:
public class ScrollDisabler : MouseInterceptor
{
protected override bool IsInputAllowed(MouseInputArgs args)
{
if (args.Event is MouseInputEvent.Scroll)
return false; // disallow mouse scroll input
return true; // all other input events can be processed
}
}
Interceptor for logging mouse events:
public class MouseLogger : MouseInterceptor
{
// Always allow input because it's a logger
protected override bool IsInputAllowed(MouseInputArgs args) => true;
// If the input event was successfully processed
protected override void OnInputSuccess(MouseInputArgs args)
{
if (args.Event is MouseInputEvent.Move) // Don't log a move event
return;
Trace.WriteLine($"Processed {args.Event}");
}
// If the input event has been blocked
protected override void OnInputFailure(MouseInputArgs args, IEnumerable<InterceptorInfo> failedInterceptors)
{
var failureReason = failedInterceptors.ToNames();
Trace.WriteLine($"Failed {args.Event} by {failureReason}");
}
}
In order to use them, we need to call the Hook
method.
var scrollDisabler = new ScrollDisabler();
var mouseLogger = new MouseLogger();
scrollDisabler.Hook();
mouseLogger.Hook();
Now let's run our project in Debug mode and test their work:
In the console, we can see that the mouse button events have fired. And mouse wheel scrolling was blocked by ScrollDisabler
class. If we need to disable this interceptor, it is enough to call the Unhook
method.
It was a simple implementation of a custom interceptor. In your scenarios they can be much larger and with stronger logic.
Identical functionality using existing interceptors:
var mouseListener = new MouseListener();
var mouseManipulator = new MouseManipulator();
mouseListener.SubscribeAll(mouseEvent =>
{
if (mouseEvent is MouseInputEvent.Move)
return;
Trace.WriteLine($"Processed {mouseEvent}");
});
mouseManipulator.Prevent(MousePreventOption.Scroll);
mouseManipulator.InputPrevented += mouseEvent =>
Trace.WriteLine($"Failed {mouseEvent} by MouseManipulator");
Additional functionality of the library.
The NumpadListener class allows you to easily subscribe to numpad buttons. It is a decorator over the KeyboardListener class. To create an object of this class, it needs to be passed an existing KeyboardListener through which it will create subscriptions.
Using the Subscribe()
method, it subscribes to each numeric Numpad key. The method has a required parameter Action<Key>
which is triggered when one of the buttons is pressed. To unsubscribe from all created subscriptions, you need to call the Unsubscribe()
method.
var keyboardListener = new KeyboardListener();
var numpadListener = new NumpadListener(keyboardListener);
// 0-9 numpad buttons
numpadListener.Subscribe(number =>
{
Trace.WriteLine($"The {number} was pressed");
});
// ...
numpadListener.Unsubscribe();
This extension method is applied to Key enum. It returns the interpretation of the key as a unicode string depending on your current keyboard layout. If the key cannot be represented in text format, String.Empty
will be returned.
var key = Key.Z;
Trace.WriteLine(key.ToUnicode());
// English layout: z
// German layout: y
// Russian layout: я