-
-
Save t184256/f4994037a2a204774ef3b9a2b38736dc to your computer and use it in GitHub Desktop.
#!/usr/bin/python3 | |
# CC0, originally written by t184256. | |
# This is an example Python program for Linux that remaps a keyboard. | |
# The events (key presses releases and repeats), are captured with evdev, | |
# and then injected back with uinput. | |
# This approach should work in X, Wayland, anywhere! | |
# Also it is not limited to keyboards, may be adapted to any input devices. | |
# The program should be easily portable to other languages or extendable to | |
# run really any code in 'macros', e.g., fetching and typing current weather. | |
# The ones eager to do it in C can take a look at (overengineered) caps2esc: | |
# https://github.com/oblitum/caps2esc | |
# Import necessary libraries. | |
import atexit | |
# You need to install evdev with a package manager or pip3. | |
import evdev # (sudo pip3 install evdev) | |
# Define an example dictionary describing the remaps. | |
REMAP_TABLE = { | |
# Let's swap A and B... | |
evdev.ecodes.KEY_A: evdev.ecodes.KEY_B, | |
evdev.ecodes.KEY_B: evdev.ecodes.KEY_A, | |
# ... and make the right Shift into a second Space. | |
evdev.ecodes.KEY_RIGHTSHIFT: evdev.ecodes.KEY_SPACE, | |
# We'll also remap CapsLock to Control when held ... | |
evdev.ecodes.KEY_CAPSLOCK: evdev.ecodes.KEY_LEFTCTRL, | |
# ... but to Esc when pressed solo, xcape style! See below. | |
} | |
# The names can be found with evtest or in evdev docs. | |
# The keyboard name we will intercept the events for. Obtainable with evtest. | |
MATCH = 'AT Translated Set 2 keyboard' | |
# Find all input devices. | |
devices = [evdev.InputDevice(fn) for fn in evdev.list_devices()] | |
# Limit the list to those containing MATCH and pick the first one. | |
kbd = [d for d in devices if MATCH in d.name][0] | |
atexit.register(kbd.ungrab) # Don't forget to ungrab the keyboard on exit! | |
kbd.grab() # Grab, i.e. prevent the keyboard from emitting original events. | |
soloing_caps = False # A flag needed for CapsLock example later. | |
# Create a new keyboard mimicking the original one. | |
with evdev.UInput.from_device(kbd, name='kbdremap') as ui: | |
for ev in kbd.read_loop(): # Read events from original keyboard. | |
if ev.type == evdev.ecodes.EV_KEY: # Process key events. | |
if ev.code == evdev.ecodes.KEY_PAUSE and ev.value == 1: | |
# Exit on pressing PAUSE. | |
# Useful if that is your only keyboard. =) | |
# Also if you bind that script to PAUSE, it'll be a toggle. | |
break | |
elif ev.code in REMAP_TABLE: | |
# Lookup the key we want to press/release instead... | |
remapped_code = REMAP_TABLE[ev.code] | |
# And do it. | |
ui.write(evdev.ecodes.EV_KEY, remapped_code, ev.value) | |
# Also, remap a 'solo CapsLock' into an Escape as promised. | |
if ev.code == evdev.ecodes.KEY_CAPSLOCK and ev.value == 0: | |
if soloing_caps: | |
# Single-press Escape. | |
ui.write(evdev.ecodes.EV_KEY, evdev.ecodes.KEY_ESC, 1) | |
ui.write(evdev.ecodes.EV_KEY, evdev.ecodes.KEY_ESC, 0) | |
else: | |
# Passthrough other key events unmodified. | |
ui.write(evdev.ecodes.EV_KEY, ev.code, ev.value) | |
# If we just pressed (or held) CapsLock, remember it. | |
# Other keys will reset this flag. | |
soloing_caps = (ev.code == evdev.ecodes.KEY_CAPSLOCK and ev.value) | |
else: | |
# Passthrough other events unmodified (e.g. SYNs). | |
ui.write(ev.type, ev.code, ev.value) |
Also with your script I believe it's reading at the evdev rate which is great, but python-evdev creating the uinput node is just passing back into libinput which negates everything as I mentioned. Yeah the keyboard itself has it's own rate in hardware which is certainly odd. I don't think many if any newer hardware does this. I'm using an AT keyboard passively converted to PS/2.
The keyboard can switch on the fly what rate sends, but the software end (X11/libinput) is basically acting as a bottleneck at that point. This is why I'm attempting to use this to bypass that completely. python-xlib is newer and has a package and python-xtest is older and does not but seems more likely to work. I'll have to mess around and see what I can come up with.
Example of the raw events https://cdn.discordapp.com/attachments/318262911846055936/647244328904949760/simplescreenrecorder-2019-11-21_20.17.13.mp4
Is it possible to remap a key to a Unicode code point or a sequence of (UTF-8-encoded) bytes?
Is it possible to remap a key to a Unicode code point or a sequence of (UTF-8-encoded) bytes?
You can mapping input events to executing arbitrary python code. So, if you figure out how to input these using python, then sure.
Hey, this works like a charm, thanks! Is it possible to map a key to a combination of keys (like A to Shift+A)? If yes, how?
Hey, this works like a charm, thanks! Is it possible to map a key to a combination of keys (like A to Shift+A)? If yes, how?
The way you press them on your keyboard. So, something like
ui.write(evdev.ecodes.EV_KEY, evdev.ecodes.KEY_RIGHTSHIFT, 1)
ui.write(evdev.ecodes.EV_KEY, evdev.ecodes.KEY_A, 1)
ui.write(evdev.ecodes.EV_SYN, evdev.ecodes.SYN_REPORT, 0) # not sure if needed, may work without it
ui.write(evdev.ecodes.EV_KEY, evdev.ecodes.KEY_A, 0)
ui.write(evdev.ecodes.EV_KEY, evdev.ecodes.KEY_RIGHTSHIFT, 0)
The xset rate sets a hard rate and works but still doesn't respect the raw evdev rate. It's always constant.
X11/libinput itself is capping it at whatever is set.
And yeah I know it's less agnostic at that point. I've been poking at xtest/xlib without any luck. I'm sure this can be done but still trying to find out how.