Cross-platform library for simulating keyboard input events.
KEYBOARD-AUTO-TYPE
This library allows you to send keystrokes to different applications as if they were typed by the user. It provides both simple and low-level API consistent across platforms. It can be useful in password managers, automation, testing software, and everywhere else where you need to simulate user input.
A minimalistic example:
#include "keyboard-auto-type.h"
// create an instance of AutoType
keyboard_auto_type::AutoType typer;
// get active / frontmost window information
auto win = typer.active_window({ .get_window_title = true });
do_something_with(win.pid, win.app_name, win.title);
// type something
typer.text(U"Hello, world!");
// press Enter
typer.key_press(keyboard_auto_type::KeyCode::Enter);
// select all
typer.shortcut(keyboard_auto_type::KeyCode::A);
See more in Usage section.
Status
Features
Installation
Usage
Low-level API
Modifiers
Key codes
Shortcuts
Errors
Window management
Strings
Thread safety
C++ standard
Development
Tests
Bindings
License
The library is ready and tests are passing, so basic stuff should work. It's been recently added to KeeWeb as a new auto-type engine, I'm carefully monitoring and fixing all issues reported there, the expectation is that it will become mature soon.
macOS | Windows | Linux: Xorg | Linux: Wayland | |
---|---|---|---|---|
Sending key codes | ✅ | ✅ | ✅ | ❌ |
Typing text | ✅ | ✅ | ✅ | ❌ |
Layout-aware text entry | ✅ | ✅ | ✅ | ❌ |
Emoji and CJK characters | ✅ | ✅ | ✅ | ❌ |
Getting window information | ✅ | ✅ | ✅ | ❌ |
Getting browser URL | ✴️ | ✴️ | ✴️ | ❌ |
Switching to a window | ✴️ | ✅ | ✅ | ❌ |
Virtual desktops (spaces) | ✴️ | ✅ | ✅ | ❌ |
Before sending keys keyboard-auto-type
makes sure other modifiers are not pressed to prevent accidental surprisees, see more in Modifiers.
Text entry is performed according to keyboard rules, for example, to enter "C++", you need to press these keys: ShiftC==Shift↑. If you pass string "C++" to the library, it will generate the same sequence of events.
Here's what you will see on the W3C Keyboard Event Viewer if you call
typer.text(U"C++");
Note the Shift key pressed only once and correct key codes.
keyboard-auto-type
checks the locale before every high-level operation (text
method). After this it does its best to find matching keys on the keyboard. If it fails to do so, it just sends text without a key code, which also works in most of cases. The library will never switch system layouts.
You can pass all range of Unicode characters to text
method, it accepts both std::u32string
and std::wstring
, whichever your prefer. To type characters with high code points, such as emoji, it's recommended to use cross-platform 32-bit characters (std::u32string
). See more in Strings.
Using active_window
method you can get active app pid, name, and window title, more in Window management.
The methos above also returns the URL if the active window is a browser. Currently supported:
- Windows: Chrome, Firefox, Edge, Internet Explorer, other Chromium-based browsers
- macOS: Chrome, Safari
- Linux: Chrome, Firefox, some of other Chromium-based browsers
There's a method show_window
that activates the window using the window information obtained using active_window
, you will find more in Window management. On Windows this will bring up the specific window, while on macOS it will activate the app and show its topmost window.
Suppose a user has an app with windows on several virtual desktops (spaces), for example a browser with different tabs. You obtained window information using active_window
, after which the active space was switched to another one. If you call show_window
on Windows, it will correctly activate the window, however on macOS it will bring up the browser, but not its specific window. It's because there seems to be no way of doing it with public API and we would like to refrein from using private frameworks.
In the following snippets 0.0.0
stands for the version you need, or a commit hash (where applicable). Replace it with the latest version that can be found in Releases.
include(FetchContent)
FetchContent_Declare(
keyboard-auto-type
GIT_REPOSITORY https://github.com/antelle/keyboard-auto-type.git
GIT_TAG 0.0.0
)
FetchContent_MakeAvailable(keyboard-auto-type)
target_link_libraries(your-target PRIVATE keyboard-auto-type)
Include the library header and create an AutoType
object:
#include "keyboard-auto-type.h"
// for convenience, to type less
namespace kbd = keyboard_auto_type;
kbd::AutoType typer;
Perform some auto-typing using the high-level API:
typer.text(U"Hello, world!");
Alternatively, you can simulate one key stroke:
typer.key_press(kbd::KeyCode::A);
You can also use modifiers to perform different actions, for example, this will send ⌘A to select all text:
typer.key_press(kbd::KeyCode::A, typer.shortcut_modifier());
There's also a method to run combinations like ⌘A / ⌃A:
typer.shortcut(kbd::KeyCode::C); // copy
typer.shortcut(kbd::KeyCode::V); // paste
Another example of word deletion using ⌥⌫, in this case you pass code
, but not character
(also see more about shortcuts):
typer.key_press(kbd::KeyCode::BackwardDelete, kbd::Modifier::Option);
If you need access to a low-level API, there's a method key_move
that can trigger specific individual key events, for example, using this your can trigger only keyUp
or simulate a keypress with an unmapped key code.
Other methods (key_press
, text
) will also press the modifier key for you, while key_move
won't do it. However it accepts modifier
parameter because you may need to pass it to the key event. For example, an event emitted when A is moved down in ⌘A combination, contains a flag that allows to understand that Command is now pressed.
First of all, there are three things that you can do using a keyboard:
- enter text, which sends unicode values of typed characters, this usually depends on keyboard layout
- press a key and send a key code, for example ⌘A works in the same way no matter which keyboard layout is selected
- manipulate the modifier key itself (Shift, Ctrl, ...)
Therefore, there are three methods:
// press the A key as text
typer.key_move(kbd::Direction::Down,
U'a',
kbd::Modifier::Alt);
// send the key code A
typer.key_move(kbd::Direction::Down,
kbd::KeyCode::A,
kbd::Modifier::Alt);
// use a modifier: Shift, Ctrl, ...
typer.key_move(kbd::Direction::Down,
kbd::Modifier::Alt);
Additionally, this method checks if any of modifiers is currently pressed. It makes sense to call it before starting text input. It will throw an exception if the user keeps holding a key unnaturally long (more than 10s, to be precise):
typer.ensure_modifier_not_pressed();
It's called by default in text
method, you can use set_auto_unpress_modifiers
and set_unpress_modifiers_total_wait_time
to disable or customize this behavior.
And finally, if you want to mess with underlying OS key codes, there's a way to do so:
// get a system key code, type length varies per OS
// keep in mind that zero is a valid key code, hence std::optional
// not all keys exist on all operating systems
auto key_code = typer.os_key_code(kbd::KeyCode::A)
// using the current locale, find its key by character
auto key = typer.os_key_code_for_char(character)
// the result is not guaranteed to exist, so it's std::optional
// e.g. there's no key combination for "★"
key->code // key code that represents the character
key->modifier // modifier required to get the desired result
// same as above, but optimized for long strings
typer.os_key_codes_for_chars(long_string)
// and here we go, pass what you get to:
typer.key_move(kbd::Direction::Down, U'★', key.code, key.modifier)
// but be careful, it's a low-level API, all checks are on you
The library supports keyboard modifiers in most of methods. The modifiers are:
kbd::Modifiers::Ctrl
kbd::Modifiers::Alt
kbd::Modifiers::Shift
kbd::Modifiers::Meta
If you like these names more, there are aliases:
kbd::Modifiers::Control // alias for Ctrl
kbd::Modifiers::Option // alias for Alt
kbd::Modifiers::Command // alias for Meta
kbd::Modifiers::Win // alias for Meta
Since the "command" shortcut is a very common one, the library provides a convenience method that returns ⌘ on macOS and Ctrl on other OS:
kbd::AutoType::shortcut_modifier()
which is also exposed as shortcut
method:
typer.shortcut(kbd::KeyCode::A); // select all
For each modifier there are three versions:
- neutral:
kbd::Modifiers::Ctrl
- left:
kbd::Modifiers::LeftCtrl
- right:
kbd::Modifiers::RightCtrl
To figure out if it's Ctrl no matter left or right, you do:
if ((modifier & kbd::Modifier::Ctrl) == kbd::Modifier::Ctrl) {
// that's Ctrl
}
Other modifiers follow the same pattern.
Where applicable, you can pass different key codes, for example:
kbd::KeyCode::A
Key codes are mapped depending on operating system. If the key doesn't exist, an error is returned.
There are a lot of keyboard shortcuts available in operating systems and applications. While you can trigger them using this library, please keep in mind that the library doesn't provide a standard way of triggering them. For example, ⌘A will select text on macOS, but not on Windows. It's not a goal of this library to standartize this.
By default, if exception support is enabled, auto-type methods can throw an exception. If exception support is disabled, they will just return an error code described below. You can also disable exceptions using KEYBOARD_AUTO_TYPE_NO_EXCEPTIONS
flag, this way the rest of your code is compiled with C++ exceptions, but keyboard-auto-type
is instructed not to throw them.
All functions return AutoTypeResult
. If it's not AutoTypeResult::Ok
, there are following errors possible:
AutoTypeResult::BadArg
: bad argument, for example, this key code is not supportedAutoTypeResult::ModifierNotReleased
: the user is holding a modifier key, simulating keystrokes in this state can have unexpected consequencesAutoTypeResult::KeyPressFailed
: we have sent a keypress event, however it didn't have any effectAutoTypeResult::NotSupported
: auto-typing is not supported on this operating systemAutoTypeResult::OsError
: opereating system reported an error during simulating keyboard input
The library also contains window management functions that you may need for simulating keyboard input.
Get information about the active / frontmost window:
kbd::AutoType::active_window()
There's an option to get window title and url from browsers, this will display prompts about managing other apps. You can pass these options to get window title and url respectively:
window_info = kbd::AutoType::active_window({
.get_window_title = true,
.get_browser_url = true,
})
This function returns just a pid of the active / frontmost process:
kbd::AutoType::active_pid()
To activate a window found using active_window
:
kbd::AutoType::show_window(window_info)
The library accepts 32-bit platform-independent wide characters in form of std::u32string
or char32_t
, the conversion is up to you. If you prefer, you can also pass std::wstring
. In some places, such as window information, it will return std::string
, these strings are in UTF-8.
The library is not thread safe. Moreover, it's not a good idea to manipulate the keyboard from different threads at the same time, don't do it.
The library requires C++17 or above. Tests and examples are using C++20 features.
You will need:
- standard development toolchain
- CMake
- clang-tidy and clang-format
- cppcheck
- node.js
On linux, install these packages:
apt-get install xorg-dev libxtst-dev libatspi2.0-dev
Makefile
is provided as a convenience measure to launch cmake
commands. There's nothing important there, however using it is easier than typing commands. If you're familiar with CMake, you can build without make
if you prefer.
Build the library:
make
On Windows you can use nmake
instead:
nmake
IDE projects can be generated on macOS using
make xcode-project
and on Windows with
nmake vs-project
Tests currently run only in English language (for Copy-Paste menu) and QWERTY keyboard layout. To run tests:
make tests
or, on Windows:
nmake tests
- Node.js: node-keyboard-auto-type