xremap
is a key remapper for Linux. Unlike xmodmap
, it supports app-specific remapping and Wayland.
-
Fast - Xremap is written in Rust, which is faster than JIT-less interpreters like Python.
-
Cross-platform - Xremap uses
evdev
anduinput
, which works whether you use X11 or Wayland. -
Language-agnostic - The config is JSON-compatible. Generate it from any language, e.g. Ruby, Python.
- Remap any keys, e.g. Ctrl or CapsLock.
- Remap any key combination to another, even to a key sequence.
- Remap a key sequence as well. You could do something like Emacs's
C-x C-c
. - Remap a key to two different keys depending on whether it's pressed alone or held.
- Application-specific remapping. Even if it's not supported by your application, xremap can.
- Device-specific remapping.
- Automatically remap newly connected devices by starting xremap with
--watch
. - Support Emacs-like key remapping, including the mark mode.
- Trigger commands on key press/release events.
- Use a non-modifier key as a virtual modifier key.
Download a binary from Releases.
If it doesn't work, please install Rust and run one of the following commands:
cargo install xremap --features x11 # X11
cargo install xremap --features gnome # GNOME Wayland
cargo install xremap --features kde # KDE-Plasma Wayland
cargo install xremap --features wlroots # Sway, Hyprland, etc.
cargo install xremap # Others
You may also need to install libx11-dev
to run the xremap
binary for X11.
If you are on Arch Linux and X11, you can install xremap-x11-bin from AUR.
If you are using NixOS, xremap can be installed and configured through a flake.
Write a config file directly, or generate it with xremap-ruby or xremap-python.
Then start the xremap
daemon by running:
sudo xremap config.yml
(You will need to leave it running for your mappings to take effect.)
If you want to run xremap without sudo, click here.
To do so, your normal user should be able to use evdev
and uinput
without sudo.
In Ubuntu, this can be configured by running the following commands and rebooting your machine.
sudo gpasswd -a YOUR_USER input
echo 'KERNEL=="uinput", GROUP="input", TAG+="uaccess"' | sudo tee /etc/udev/rules.d/input.rules
The following can be used on Arch.
lsmod | grep uinput
If this module is not loaded, add to /etc/modules-load.d/uinput.conf
:
uinput
Then add udev rule.
echo 'KERNEL=="uinput", GROUP="input", TAG+="uaccess"' | sudo tee /etc/udev/rules.d/99-input.rules
Then reboot the machine.
Make sure uinput
is loaded same as in Arch:
lsmod | grep uinput
If it shows up empty:
echo uinput | sudo tee /etc/modules-load.d/uinput.conf
Add your user to the input
group and add the same udev rule as in Ubuntu:
sudo gpasswd -a YOUR_USER input
echo 'KERNEL=="uinput", GROUP="input", TAG+="uaccess"' | sudo tee /etc/udev/rules.d/input.rules
Reboot the machine afterwards or try:
sudo modprobe uinput
sudo udevadm control --reload-rules && sudo udevadm trigger
In other platforms, you might need to create an input
group first
and run echo 'KERNEL=="event*", NAME="input/%k", MODE="660", GROUP="input"' | sudo tee /etc/udev/rules.d/input.rules
as well.
If you do this, in some environments, --watch
may fail to recognize new devices due to temporary permission issues.
Using sudo
might be more useful in such cases.
See the following instructions for your environment to make application
-specific remapping work.
If you use sudo
to run xremap
, you may need to run xhost +SI:localuser:root
if you see No protocol specified
.
Install xremap's GNOME Shell extension from this link, switching OFF to ON.
If you use sudo
to run xremap
, also click here.
Update /usr/share/dbus-1/session.conf
as follows, and reboot your machine.
<policy context="default">
+ <allow user="root"/>
<!-- Allow everything to be sent -->
<allow send_destination="*" eavesdrop="true"/>
<!-- Allow everything to be received -->
Xremap cannot be run as root. Follow the instructions above to run xremap without sudo.
Your config.yml
should look like this:
modmap:
- name: Except Chrome
application:
not: Google-chrome
remap:
CapsLock: Esc
keymap:
- name: Emacs binding
application:
only: Slack
remap:
C-b: left
C-f: right
C-p: up
C-n: down
See also: example/config.yml and example/emacs.yml
modmap
is for key-to-key remapping like xmodmap.
Note that remapping a key to a modifier key, e.g. CapsLock to Control_L,
is supported only in modmap
since keymap
handles modifier keys differently.
modmap:
- name: Name # Optional
exact_match: false # Optional, defaults to false
remap: # Required
# Replace a key with another
KEY_XXX: KEY_YYY # Required
# Dispatch different keys depending on whether you hold it or press it alone
KEY_XXX:
held: [KEY_YYY] # Required
alone: [KEY_ZZZ] # Required
alone_timeout_millis: 1000 # Optional
# Hook `keymap` action on key press/release events.
KEY_XXX:
skip_key_event: false # Optional, skip original key event ,defaults to false
press: { launch: ["xdotool", "mousemove", "0", "7200"] } # Required
release: { launch: ["xdotool", "mousemove", "0", "0"] } # Required
application: # Optional
not: [Application, ...]
# or
only: [Application, ...]
device: # Optional
not: [Device, ...]
# or
only: [Device, ...]
For KEY_XXX
and KEY_YYY
, use these names.
You can skip KEY_
and the name is case-insensitive. So KEY_CAPSLOCK
, CAPSLOCK
, and CapsLock
are the same thing.
Some custom aliases like SHIFT_R
, CONTROL_L
, etc. are provided.
If you specify a map containing held
and alone
, you can use the key for two purposes.
The key is considered alone
if it's pressed and released within alone_timeout_millis
(default: 1000)
before any other key is pressed. Otherwise it's considered held
.
keymap
is for remapping a sequence of key combinations to another sequence of key combinations or other actions.
keymap:
- name: Name # Optional
remap: # Required
# Key press -> Key press
MOD1-KEY_XXX: MOD2-KEY_YYY
# Sequence (MOD1-KEY_XXX, MOD2-KEY_YYY) -> Key press (MOD3-KEY_ZZZ)
MOD1-KEY_XXX:
remap:
MOD2-KEY_YYY: MOD3-KEY_ZZZ
timeout_millis: 200 # Optional. No timeout by default.
# Key press (MOD1-KEY_XXX) -> Sequence (MOD2-KEY_YYY, MOD3-KEY_ZZZ)
MOD1-KEY_XXX: [MOD2-KEY_YYY, MOD3-KEY_ZZZ]
# Execute a command
MOD1-KEY_XXX:
launch: ["bash", "-c", "echo hello > /tmp/test"]
# Let `with_mark` also press a Shift key (useful for Emacs emulation)
MOD1-KEY_XXX: { set_mark: true } # use { set_mark: false } to disable it
# Also press Shift only when { set_mark: true } is used before
MOD1-KEY_XXX: { with_mark: MOD2-KEY_YYY }
# The next key press will ignore keymap
MOD1-KEY_XXX: { escape_next_key: true }
# Set mode to configure Vim-like modal remapping
MOD1-KEY_XXX: { set_mode: default }
application: # Optional
not: [Application, ...]
# or
only: [Application, ...]
device: # Optional
not: [Device, ...]
# or
only: [Device, ...]
mode: default # Optional
default_mode: default # Optional
For KEY_XXX
, use these names.
You can skip KEY_
and the name is case-insensitive. So KEY_CAPSLOCK
, CAPSLOCK
, and CapsLock
are the same thing.
For the MOD1-
part, the following prefixes can be used (also case-insensitive):
- Shift:
SHIFT-
- Control:
C-
,CTRL-
,CONTROL-
- Alt:
M-
,ALT-
- Windows:
SUPER-
,WIN-
,WINDOWS-
You can use multiple prefixes like C-M-Shift-a
.
You may also suffix them with _L
or _R
(case-insensitive) so that
remapping is triggered only on a left or right modifier, e.g. Ctrl_L-a
.
If you use virtual_modifiers
explained below, you can use it in the MOD1-
part too.
exact_match
defines whether to use exact match when matching key presses. For
example, given a mapping of C-n: down
and exact_match: false
(default), and
you pressed C-Shift-n, it will automatically be remapped to
Shift-down, without you having to define a mapping for
C-Shift-n, which you would have to do if you use exact_match: true
.
application
can be used for both modmap
and keymap
, which allows you to specify application-specific remapping.
application:
not: Application
# or
not: [Application, ...]
# or
only: Application
# or
only: [Application, ...]
The application name can be specified as a normal string to exactly match the name,
or a regex surrounded by /
s like /application/
.
To check the application names, you can use the following commands:
$ wmctrl -x -l
0x02800003 0 slack.Slack ubuntu-jammy Slack | general | ruby-jp
0x05400003 0 code.Code ubuntu-jammy application.rs - xremap - Visual Studio Code
You may use the entire string of the third column (slack.Slack
, code.Code
),
or just the last segment after .
(Slack
, Code
).
Use the following command or check windows' WMClass by pressing Alt+F2 and running lg
command in LookingGlass:
busctl --user call org.gnome.Shell /com/k0kubun/Xremap com.k0kubun.Xremap WMClasses
Xremap prints the active window to the console.
However, it will only start printing, once a mapping has been triggered that uses an application filter.
So you have to create a mapping with a filter using a dummy application name and trigger it.
Then each time you switch to a new window xremap will print its caption, class, and name in the following style:
active window: caption: '<caption>', class: '<class>', name: '<name>'
You want to use the class for the filter.
If you use a systemd-daemon to manage xremap, the prints will be visible in the system-logs (Can be opened with journalctl -f
)
swaymsg -t get_tree
Locate app_id
in the output.
Sometimes you want to define a generic key map that is available in all applications, but give specific keys in that map their own definition in specific applications. You can do this by putting the generic map at the bottom of the config, after any specific overrides, as follows.
# Emacs-style word-forward and word-back
keymap:
- name: override to make libreoffice-writer go to end of word but before final space like emacs
application:
only: libreoffice-writter
remap:
Alt-f: [right, C-right, left]
- name: generic for all apps
remap:
Alt-f: C-right
Alt-b: C-left
Note how Alt-f and Alt-b work in all apps, but the definition of Alt-f is slightly different in LibreOffice Writer. When that app is active, the first definition overrides the second definition; but for any other app, only the second definition is found. This is because xremap uses the first matching definition that it finds.
Much like application
, you may specify {keymap,modmap}.device.{not,only}
in your configuration for device-specific remapping. Consistent with the global --device
flag, device-matching strings may be any of:
- the full path of the device
- the filename of the device
- the device name
- a substring of the device name
To determine the names and paths of your devices, examine xremap
's log output at startup.
device:
not: '/dev/input/event0'
# or
not: ['event0', ...]
# or
only: 'Some Cool Device Name'
# or
only: ['Cool Device', ...]
# etc...
Unlike for application
, regexs are not supported for device
.
You can declare keys that should act like a modifier.
virtual_modifiers:
- CapsLock
keymap:
- remap:
CapsLock-i: Up
CapsLock-j: Left
CapsLock-k: Down
CapsLock-l: Right
Some applications have trouble understanding synthesized key events, especially on
Wayland. keypress_delay_ms
can be used to workaround the issue.
See #179 for the detail.
You can declare data that does not directly go into the config under the shared
field.
This can be usefull when using Anchors and Aliases.
For more information about the use of Yaml anchors see the Yaml specification.
shared:
terminals: &terminals # The & Symbol marks this entry as a Anchor
- Gnome-terminal
- Kitty
some_remaps: &some_remaps
Ctrl-f: C-right
Alt-b: C-up
keymap:
- application:
only: *terminals # we can reuse the list here
remap: *some_remaps # and we can reuse a map here.
xremap
is available as open source under the terms of the MIT License.