keyboard_remapper remaps keyboard keys on Windows using the API LowLevelKeyboardProc and LowLevelMouseProc, which are low-level keyboard and mouse hook procedures that intercept key events. It also uses the API SendInput to synthesize keystrokes, mouse motions, and button clicks.
keyboard_remapper does not store any settings permanently on Windows. The key remapping occurs in real time, so if the executable is terminated, the keyboard returns to its default behavior. If a key becomes stuck because keyboard_remapper was closed while the key was pressed, simply pressing and releasing the key will resolve the issue.
The idea for keyboard_remapper is inspired by the excellent work of ililim on his dual-key-remap. The main features of keyboard_remapper are:
- Dual-role keys with tap timeout and auto-repeat keypress.
- Hold delay for dual-role keys to prevent accidental activation of the hold functionality during typing.
- Remapping to multiple keys.
- Remapping for double-tap and tap&press actions.
- Remap locking.
- Layers, incorporating layer lock concepts from Pascal Getreuer.
- Mouse emulation, inspired by the Orbital Mouse ideas from Pascal Getreuer.
My use case for keyboard_remapper involves creating lower-level keyboard remappings, specifically to remap the spacebar to function as both the space key and the right Ctrl key. Additionally, I set up a layer for Vi-style hjkl movements. On top of keyboard_remapper, I use AutoHotkey for managing hotkeys and hotstrings, as well as for implementing Emacs key bindings universally with ewow.
The remappings for keyboard_remapper are configured through a config.txt
file, which is located in the same directory as the keyboard_remapper executable. This allows for easy customization of key behaviors and settings.
Configuration example:
remap_key=SPACE
when_alone=SPACE
with_other=RIGHT_CTRL
Explanation:
remap_key
: This is the key you want to remap, in this case, the SPACE key.when_alone
: The action triggered whenremap_key
is pressed by itself.with_other
: The action triggered if another key is pressed whileremap_key
is held down.
Dual-role key timings in keyboard_remapper are managed using two global settings expressed in milliseconds:
-
hold_delay
: This defines the duration that starts from when theremap_key
is pressed. If another key is pressed within this time frame, the action associated withwhen_alone
is triggered instead of the expected hold action. -
tap_timeout
: This setting typically defines the maximum time allowed for a tap action before it is considered a hold.
These settings provide fine control over how quickly the system distinguishes between a tap and a hold, allowing for a more customized user experience.
In keyboard_remapper, you can set up a tap&press remapping using the following configuration example:
remap_key=SPACE
when_alone=SPACE
with_other=RIGHT_CTRL
when_doublepress=LEFT_ALT
Explanation:
when_doublepress
: If theremap_key
is tapped and then held down within the duration specified by global settingdoublepress_timeout
(in milliseconds), the action associated with this setting (LEFT_ALT) will be triggered.
This setup allows for versatile key behavior based on how quickly the key is pressed.
If the when_doublepress
action is not defined in your keyboard_remapper configuration, the default behavior will be to treat a tap&press as a repetition of the action specified in when_alone
. This allows for normal key repetition as defined by the operating system (Windows), meaning that if the key is held down, it will continue to repeat the action until the key is released.
In keyboard_remapper, you can implement key locking behavior with the following configuration example:
remap_key=LEFT_SHIFT
when_alone=LEFT_SHIFT
when_tap_lock=LEFT_SHIFT
when_doublepress=LEFT_CTRL
when_double_tap_lock=LEFT_CTRL
Explanation:
when_tap_lock
: If theremap_key
is tapped, the assigned key (LEFT_SHIFT) is locked in that state until it is tapped again. This allows users to maintain the shift state without needing to hold the key.when_double_tap_lock
: If theremap_key
is double tapped, the assigned key (LEFT_CTRL) is locked in that state until a subsequent double tap of theremap_key
. This provides a way to maintain control functionality without holding the key.
This configuration enhances the flexibility of key usage, allowing for both temporary and persistent key states based on user input patterns.
In keyboard_remapper, layers allow for more complex key remappings, enabling users to switch between different sets of key configurations easily. This feature is particularly useful for different contexts, such as gaming, programming, or general typing.
Configuration example:
remap_key=KEY_H
layer=layer_vi
when_alone=LEFT
Explanation:
layer
: Theremap_key
remapping is enabled if the layer specified bylayer
is active. The name of specified layer has to start with "layer". In this example its name is "layer_vi".
Layers can be activated momentarily or permanently:
-
Momentary Activation
A layer can be activated temporarily by holding down one or more specific keys. While the key is pressed, the remappings associated with that layer will be in effect.
Configuration example:
remap_key=LEFT_CTRL when_press=layer_vi when_doublepress=layer_mouse
Explanation:
when_press
: While theremap_key
is held down, the layer specified bywhen_press
is active.when_doublepress
: While theremap_key
is tap&pressed, the layer specified bywhen_doublepress
is active.
It is possible to use a non-modifier key as a dual-role key to enable a layer. Here’s a configuration example:
remap_key=TAB when_alone=TAB with_other= when_press=layer_tab
Explanation:
with_other=
: This setting allows the TAB key to function as a dual-role key, where the tap action is registered as TAB and the hold action is set to nothing.when_press=layer_tab
: When the TAB key is pressed, it activates thelayer_tab
without registering the TAB key itself if it is held beyond thetap_timeout
.
To enable a layer with multiple key presses it is possibile to use
define_layer
as in the following configuration example:define_layer=layer_vi or_layer=layer_tab and_layer=layer_ctrl and_layer=layer_win and_not_layer=layer_shift
Explanation:
define_layer=layer_vi
: This line specifies that you are defining thelayer_vi
. This is the primary layer that will be activated when the conditions are met.or_layer=layer_tab
: This indicates that if thelayer_tab
is active, then thelayer_vi
is also active.and_layer=layer_ctrl
: This indicates that thelayer_ctrl
must also be active forlayer_vi
to be enabled iflayer_tab
is not active. This allows you to combine multiple layers for more complex key remapping.and_layer=layer_win
: Similarly, this specifies thatlayer_win
must also be active to enablelayer_vi
. This means that all specified layers byand_layer=
must be active simultaneously for the remappings oflayer_vi
to take effect.and_not_layer=layer_shift
: This setting specifies thatlayer_shift
must not be active to enablelayer_vi
.
The logic to enable a layer can be expressed as follows:
or_layer
ORor_layer
OR ... OR (and_layer
ANDand_layer
AND NOTand_not_layer
AND ...)This means that
layer_vi
will be active if eitherlayer_tab
is active, or bothlayer_ctrl
andlayer_win
are active whilelayer_shift
is not.If a layer is defined with
define_layer
and activated bywhen_press
orwhen_doublepress
, an implicitor_layer
is created. -
Permanent Activation (Lock System)
A layer can also be activated permanently using a lock mechanism. This means that once the layer is activated, all remappings associated with it remain in effect until the layer is explicitly deactivated.
Configuration example:
remap_key=RIGHT_WIN when_tap_lock=set_layer_vi remap_key=RIGHT_WIN layer=layer_vi when_tap_lock=reset_layer_vi when_tap_lock=set_layer_mouse remap_key=RIGHT_WIN layer=layer_mouse when_tap_lock=reset_layer_mouse
Explanation:
when_tap_lock=set_layer_vi
: This setting lock in active state thelayer_vi
layer.when_tap_lock=reset_layer_vi
: This setting release the lock oflayer_vi
layer.
Beside
set_
andreset_
is availabletoggle_
to toggle the lock state of specified layer.when_double_tap_lock
can be used to change lock state of specified layer withremap_key
double tap.In a key mapping, it is possible to specify multiple
when_tap_lock
andwhen_double_tap_lock
actions.Layers can also be locked using the concept from Pascal Getreuer. In this case, a key is used to lock and unlock the same layer.
Configuration example:
remap_key=SPACE layer=layer_vi when_tap_lock=toggle_layer_vi
Explanation:
layer=layer_vi
: The lock mechanism is enabled by thelayer_vi
.when_tap_lock=toggle_layer_vi
: When the SPACE key is tapped, it locks thelayer_vi
. A subsequent tap of the SPACE key unlocks thelayer_vi
.
In keyboard_remapper
keyboard keys can be remapped to mouse events.
Configuration example:
remap_key=KEY_I
layer=layer_mouse
when_alone=MOUSE_LBUTTON
Explanation:
when_alone=MOUSE_LBUTTON
: When thelayer_mouse
is active, the I key is mapped to the mouse left button.
Mappable mouse events:
- Up, Down, Left and Right movements:
MOUSE_UP
,MOUSE_DOWN
,MOUSE_LEFT
,MOUSE_RIGHT
. - Orbital mouse movements (from Pascal Getreuer):
MOUSE_FORWARD
,MOUSE_BACKWARD
,MOUSE_STEER_LEFT
,MOUSE_STEER_RIGHT
. - Mouse wheel rotations:
MOUSE_WHEEL_UP
,MOUSE_WHEEL_DOWN
,MOUSE_WHEEL_LEFT
,MOUSE_WHEEL_RIGHT
. - Mouse buttons:
MOUSE_LBUTTON
,MOUSE_RBUTTON
,MOUSE_MBUTTON
,MOUSE_XBUTTON1
,MOUSE_XBUTTON2
. - Selection of a mouse button:
MOUSE_LBUTTON_SEL
,MOUSE_RBUTTON_SEL
,MOUSE_MBUTTON_SEL
,MOUSE_XBUTTON1_SEL
,MOUSE_XBUTTON2_SEL
. - Selected mouse button events:
MOUSE_SBUTTON
,MOUSE_SHOLD
,MOUSE_SRELEASE
.
In keyboard_remapper
, a key can be remapped to multiple keys.
Configuration example:
remap_key=RIGHT_ALT
when_alone=CTRL
when_alone=ALT
when_alone=SHIFT
when_alone=LEFT_WIN
Explanation:
The Right ALT key is mapped to multiple keys: CTRL, ALT, SHIFT, and Left WIN.
The key press sequence follows the order of the when_alone
lines. Whereas the key release sequence occurs in reverse order.
It is possible to map multiple keys for when_alone
, with_other
, when_doublepress
, when_tap_lock
, and when_double_tap_lock
.
- CTRL LEFT_CTRL RIGHT_CTRL SHIFT LEFT_SHIFT RIGHT_SHIFT ALT LEFT_ALT RIGHT_ALT LEFT_WIN RIGHT_WIN
- BACKSPACE CAPSLOCK ENTER ESCAPE SPACE TAB PLUS COMMA MINUS PERIOD
- UP LEFT RIGHT DOWN
- F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24
- KEY_0 KEY_1 KEY_2 KEY_3 KEY_4 KEY_5 KEY_6 KEY_7 KEY_8 KEY_9
- KEY_A KEY_B KEY_C KEY_D KEY_E KEY_F KEY_G KEY_H KEY_I KEY_J KEY_K KEY_L KEY_M KEY_N KEY_O KEY_P KEY_Q KEY_R KEY_S KEY_T KEY_U KEY_V KEY_W KEY_X KEY_Y KEY_Z
- INSERT DELETE HOME END PAGE_UP PAGE_DOWN PRINT_SCREEN NUMLOCK SCROLLLOCK PAUSE US_SEMI US_SLASH US_TILDE
- MOUSE_UP MOUSE_DOWN MOUSE_LEFT MOUSE_RIGHT
- MOUSE_FORWARD MOUSE_BACKWARD MOUSE_STEER_LEFT MOUSE_STEER_RIGHT
- MOUSE_WHEEL_UP MOUSE_WHEEL_DOWN MOUSE_WHEEL_LEFT MOUSE_WHEEL_RIGHT
- MOUSE_LBUTTON MOUSE_RBUTTON MOUSE_MBUTTON MOUSE_XBUTTON1 MOUSE_XBUTTON2
- MOUSE_SBUTTON MOUSE_SHOLD MOUSE_SRELEASE MOUSE_LBUTTON_SEL MOUSE_RBUTTON_SEL MOUSE_MBUTTON_SEL MOUSE_XBUTTON1_SEL MOUSE_XBUTTON2_SEL