Skip to content

Instantly share code, notes, and snippets.

@evertedsphere
Created May 21, 2020 15:52
Show Gist options
  • Save evertedsphere/4af5863210e904db6f89b6d990ec7a50 to your computer and use it in GitHub Desktop.
Save evertedsphere/4af5863210e904db6f89b6d990ec7a50 to your computer and use it in GitHub Desktop.
evdev-based remap
#!/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