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 in any way.
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 base classes are not enough for your needs, you can use custom interceptors.
The functionality of the library can be divided into temporary and permanent changes. Temporary ones are those that will be active only while the program is running, such as subscribing to input events. Permanent changes are those that do not depend on your application, such as changes in mouse speed.
You should make sure that your application fits the requirements of this library.
- Version .NET 7 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 allows you, to get the information you need about the user's presses, sequences and key combinations. The whole operation of this class is based on subscriptions, you can subscribe to different events, customizing the configuration to suit your needs.
This class stores active subscriptions in properties: Keys, Sequences and Combinations.
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 will allow the application to correctly release all system resources it uses.
In order to subscribe to press events, you need to call one of the subscribe method.
var keyboardListener = new KeyboardListener();
// Subscription for each click
keyboardListener.Subscribe(Key.Space, key => Trace.WriteLine($"The {key} 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 eventsEach subscription method returns a subscription object with all the details. Including the unique identifier and event type.
You can unsubscribe from an event using several options. Unsubscribe by GUID, by key, and unsubscribe from all events at once.
// Subscribe to the event
var subscription = keyboardListener.Subscribe(Key.A, key => { });
// 3 different unsubscribe options
keyboardListener.Unsubscribe(subscription.Id);
keyboardListener.Unsubscribe(Key.A);
keyboardListener.UnsubscribeAll(); You can get information about the current state of the keys. To do this, you can use the already created properties, or you can call the IsKeyPressed() method.
var isNumLockActive = keyboardListener.IsNumLockActive;
var isCapsLockActive = keyboardListener.IsCapsLockActive;
var isWinPressed = keyboardListener.IsWinPressed;
var isAltPressed = keyboardListener.IsAltPressed;
var isCtrlPressed = keyboardListener.IsCtrlPressed;
var isShiftPressed = keyboardListener.IsShiftPressed;
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.
- Simulate keyboard input
- Prevent input events
- Set the press interval
You can simulate button presses from the keyboard with this class. The simulated keys are fully compatible with other pressed keys. If you press the Shift button and simulate the call of some key, the Shift modifier will be applied to this input.
var keyboard = new KeyboardManipulator();
// Single button press
keyboard.Press(Key.Space);
// Combination press
keyboard.Press(Key.LeftCtrl, Key.V);You can prevent global input events by default or with some condition. All locked keys are stored in the LockedKeys collection. The keys will be locked until you call the Release() method or the application is completed.
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.ReleaseAll();To check the current state of a button you can use the IsKeyLocked() method.
keyboard.Prevent(Key.Space);
keyboard.IsKeyLocked(Key.Space); // trueAlso, the class has a KeyPrevented event that fires when a press has been prevented by this class.
keyboard.KeyPrevented += args => Trace.WriteLine($"Pressing the {args.KeyPressed} button has been prevented");The interval setting allows you to control the frequency of presses. With SetInterval() method, you will set a global interval for pressing a key on the keyboard. As with locked buttons, the interval will remain until you remove it or the application is completed.
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 the specified 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.
To change the button binding, you must call the Bind() method. This method always works, even if a bind already exists, it will just change it to a new one.
var keyboardBinder = new KeyboardBinder();
keyboardBinder.Bind(Key.Q, Key.W);
// Now any time the 'Q' button is triggered, it will behave like the 'W' buttonIn order to swap the button bindings, you can use the Swap() method.
var keyboardBinder = new KeyboardBinder();
keyboardBinder.Swap(Key.Q, Key.W);
// Alternative option
keyboardBinder.Bind(Key.Q, Key.W);
keyboardBinder.Bind(Key.W, Key.Q);Get the current state of the bindings using the IsKeyBounded() method, which returns true/false depending on the existence of the binding. And GetBoundKey() method which returns the current key to which the specified key belongs.
var keyboardBinder = new KeyboardBinder();
keyboardBinder.Bind(Key.Q, Key.W);
keyboardBinder.IsKeyBounded(Key.Q); // true
keyboardBinder.GetBoundKey(Key.Q); // W
keyboardBinder.GetBoundKey(Key.W); // WTo unbind buttons, you need to call one of the Unbind() method overloads.
var keyboardBinder = new KeyboardBinder();
keyboardBinder.Bind(Key.Q, Key.W);
keyboardBinder.Unbind(Key.Q);
// Alternative option
keyboardBinder.UnbindAll();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); // 4These 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.
In order to subscribe to mouse events, you need to call one of the subscribe method.
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 a subscription object with all the details. Including the unique identifier and event type.
You can unsubscribe from an event using several options. Unsubscribe by GUID, by key, and unsubscribe from all events at once.
// Subscribe to the event
var subscription = mouseListener.Subscribe(MouseEvent.MiddleButtonDown, () => { });
// 3 different unsubscribe options
mouseListener.Unsubscribe(subscription.Id);
mouseListener.Unsubscribe(MouseEvent.MiddleButtonDown);
mouseListener.UnsubscribeAll(); You can get the position of the mouse using the Position property. It will return you the Point class with two values as X and Y.
var mouseListener = new MouseListener();
var position = mouseListener.Position;
Trace.WriteLine($"X: {position.X} Y: {position.Y}"); // X: 943 Y: 378The MouseManipulator class provides the ability to control the mouse.
Note
📌 This class works with a single context. Therefore, all your objects of this class have the same state.
- Simulate mouse input
- Prevent input events
- Global mouse configuration
You can simulate mouse clicks events.
var mouse = new MouseManipulator();
mouse.Click();
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);You can prevent global input events by default or with some condition. All locked keys are stored in the LockedKeys collection. The keys will be locked until you call the Release() method or the application is completed.
var mouse = new MouseManipulator();
// Each scroll event will be ignored
mouse.Prevent(PreventMouseEvent.Scroll);
// Prevent with condition
mouse.Prevent(PreventMouseEvent.RightButton, () =>
{
var currentTime = DateTime.Now;
return currentTime.Minute > 30;
});
// Release locked keys
mouse.ReleaseAll();To check the current state of a key you can use the IsKeyLocked() method.
mouse.Prevent(PreventMouseEvent.Move);
mouse.IsKeyLocked(PreventMouseEvent.Move); // trueAlso, the class has a InputPrevented event that fires when a input has been prevented by this class.
mouse.InputPrevented += mEvent => Trace.WriteLine($"The {mEvent} event was prevented");You can make permanent changes with this class. They will be active even after your application is terminated.
var mouse = new MouseManipulator();
mouse.SetMouseSpeed(5); // changes the mouse speed on your systemThis 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); // 10You 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(PreventMouseEvent.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 single 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: я

