Created
May 21, 2020 15:52
-
-
Save evertedsphere/4af5863210e904db6f89b6d990ec7a50 to your computer and use it in GitHub Desktop.
evdev-based remap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python3 | |
# Original: | |
# https://gist.github.com/t184256/f4994037a2a204774ef3b9a2b38736dc | |
# I used this as a starting point. | |
# 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 atexit | |
import evdev | |
from evdev import ecodes | |
# Define an example dictionary describing the remaps. | |
REMAP_TABLE = { | |
ecodes.KEY_ENTER: ecodes.KEY_SEMICOLON, | |
ecodes.KEY_SEMICOLON: ecodes.KEY_ENTER, | |
ecodes.KEY_CAPSLOCK: ecodes.KEY_LEFTCTRL, | |
ecodes.KEY_TAB: ecodes.KEY_LEFTMETA, | |
} | |
TAP_TABLE = { | |
ecodes.KEY_CAPSLOCK: ecodes.KEY_ESC, | |
ecodes.KEY_TAB: ecodes.KEY_TAB, | |
} | |
# 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. | |
prev_key = None | |
shift_on = False | |
ctrl_on = False | |
meta_on = False | |
alt_on = False | |
def kbd_write(kbd, ty, key, val): | |
print(" {} {}".format(ecodes.KEY[key], val)) | |
kbd.write(ty, key, val) | |
# TODO: sometimes if i press, say, I-caps very fast you get Ipress-capspress-Irelease-capsrelease | |
# some way to detect this? | |
# TODO: assume tap after a short timeout so that we don't have to wait for release to send, say, ctrl-tab | |
# 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 == ecodes.EV_KEY: # Process key events. | |
if ev.code == ecodes.KEY_GRAVE and ev.value == 1: | |
# Exit on pressing ```. | |
break | |
print(ecodes.KEY[ev.code], ev.value) | |
emitted = False | |
final_code = None | |
if ev.code in REMAP_TABLE: | |
remapped_code = REMAP_TABLE[ev.code] | |
kbd_write(ui, ecodes.EV_KEY, remapped_code, ev.value) | |
emitted = True | |
final_code = remapped_code | |
if ev.code in TAP_TABLE: | |
if ev.value == 0: | |
if prev_key == (ev.code, 1): | |
remapped_code = TAP_TABLE[ev.code] | |
kbd_write(ui, ecodes.EV_KEY, remapped_code, 1) | |
kbd_write(ui, ecodes.EV_KEY, remapped_code, 0) | |
emitted = True | |
if final_code is None: | |
final_code = ev.code | |
if final_code in [ecodes.KEY_LEFTSHIFT, ecodes.KEY_RIGHTSHIFT]: | |
shift_on = ev.value != 0 | |
if final_code in [ecodes.KEY_LEFTMETA, ecodes.KEY_RIGHTMETA]: | |
meta_on = ev.value != 0 | |
if final_code in [ecodes.KEY_LEFTCTRL, ecodes.KEY_RIGHTCTRL]: | |
ctrl_on = ev.value != 0 | |
if final_code in [ecodes.KEY_LEFTALT, ecodes.KEY_RIGHTALT]: | |
alt_on = ev.value != 0 | |
if ev.code in [ecodes.KEY_1, ecodes.KEY_2, ecodes.KEY_3, ecodes.KEY_4, | |
ecodes.KEY_5, ecodes.KEY_6, ecodes.KEY_7, | |
ecodes.KEY_8, ecodes.KEY_9, ecodes.KEY_0]: | |
# Shift-reverse if no modifiers are being held. | |
if not (ctrl_on or meta_on or alt_on): | |
pre = None | |
post = None | |
if shift_on: | |
pre = 0 | |
post = 1 | |
else: | |
pre = 1 | |
post = 0 | |
if ev.value == 0: | |
kbd_write(ui, ecodes.EV_KEY, ev.code, 0) | |
else: | |
kbd_write(ui, ecodes.EV_KEY, ecodes.KEY_LEFTSHIFT, pre) | |
kbd_write(ui, ecodes.EV_KEY, ev.code, ev.value) | |
kbd_write(ui, ecodes.EV_KEY, ecodes.KEY_LEFTSHIFT, post) | |
emitted = True | |
if not emitted: | |
# Passthrough other key events unmodified. | |
kbd_write(ui, ecodes.EV_KEY, ev.code, ev.value) | |
prev_key = (ev.code, ev.value) | |
else: | |
# Passthrough other events unmodified (e.g. SYNs). | |
ui.write(ev.type, ev.code, ev.value) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment