quimby/relay.py

225 lines
5.8 KiB
Python
Executable File

#!/usr/bin/env python3
import evdev
import argparse
import os
import sys
scan_to_hid = {
# Reserved: 0
# ErrorRollOver: 1
# POSTFail: 2
# ErrorUndefined: 3
evdev.ecodes.KEY_A: 0x04,
evdev.ecodes.KEY_B: 0x05,
evdev.ecodes.KEY_C: 0x06,
evdev.ecodes.KEY_D: 0x07,
evdev.ecodes.KEY_E: 0x08,
evdev.ecodes.KEY_F: 0x09,
evdev.ecodes.KEY_G: 0x0a,
evdev.ecodes.KEY_H: 0x0b,
evdev.ecodes.KEY_I: 0x0c,
evdev.ecodes.KEY_J: 0x0d,
evdev.ecodes.KEY_K: 0x0e,
evdev.ecodes.KEY_L: 0x0f,
evdev.ecodes.KEY_M: 0x10,
evdev.ecodes.KEY_N: 0x11,
evdev.ecodes.KEY_O: 0x12,
evdev.ecodes.KEY_P: 0x13,
evdev.ecodes.KEY_Q: 0x14,
evdev.ecodes.KEY_R: 0x15,
evdev.ecodes.KEY_S: 0x16,
evdev.ecodes.KEY_T: 0x17,
evdev.ecodes.KEY_U: 0x18,
evdev.ecodes.KEY_V: 0x19,
evdev.ecodes.KEY_W: 0x1a,
evdev.ecodes.KEY_X: 0x1b,
evdev.ecodes.KEY_Y: 0x1c,
evdev.ecodes.KEY_Z: 0x1d,
evdev.ecodes.KEY_1: 0x1e,
evdev.ecodes.KEY_2: 0x1f,
evdev.ecodes.KEY_3: 0x20,
evdev.ecodes.KEY_4: 0x21,
evdev.ecodes.KEY_5: 0x22,
evdev.ecodes.KEY_6: 0x23,
evdev.ecodes.KEY_7: 0x24,
evdev.ecodes.KEY_8: 0x25,
evdev.ecodes.KEY_9: 0x26,
evdev.ecodes.KEY_0: 0x27,
evdev.ecodes.KEY_ENTER: 0x28,
evdev.ecodes.KEY_ESC: 0x29,
evdev.ecodes.KEY_BACKSPACE: 0x2a,
evdev.ecodes.KEY_TAB: 0x2b,
evdev.ecodes.KEY_SPACE: 0x2c,
evdev.ecodes.KEY_MINUS: 0x2d,
evdev.ecodes.KEY_EQUAL: 0x2e,
evdev.ecodes.KEY_LEFTBRACE: 0x2f,
evdev.ecodes.KEY_RIGHTBRACE: 0x30,
evdev.ecodes.KEY_SLASH: 0x31,
# Non-US # and ~: 0x32,
evdev.ecodes.KEY_SEMICOLON: 0x33,
# Quote
evdev.ecodes.KEY_GRAVE: 0x35,
evdev.ecodes.KEY_COMMA: 0x36,
evdev.ecodes.KEY_STOP: 0x37,
evdev.ecodes.KEY_QUESTION: 0x38,
evdev.ecodes.KEY_CAPSLOCK: 0x39,
evdev.ecodes.KEY_F1: 0x3a,
evdev.ecodes.KEY_F2: 0x3b,
evdev.ecodes.KEY_F3: 0x3c,
evdev.ecodes.KEY_F4: 0x3d,
evdev.ecodes.KEY_F5: 0x3e,
evdev.ecodes.KEY_F6: 0x3f,
evdev.ecodes.KEY_F7: 0x40,
evdev.ecodes.KEY_F8: 0x41,
evdev.ecodes.KEY_F9: 0x42,
evdev.ecodes.KEY_F10: 0x43,
evdev.ecodes.KEY_F11: 0x44,
evdev.ecodes.KEY_F12: 0x45,
evdev.ecodes.KEY_PRINT: 0x46,
evdev.ecodes.KEY_SCROLLLOCK: 0x47,
evdev.ecodes.KEY_PAUSE: 0x48,
evdev.ecodes.KEY_INSERT: 0x49,
evdev.ecodes.KEY_HOME: 0x4a,
evdev.ecodes.KEY_PAGEUP: 0x4b,
# delete forward: 0x4c,
evdev.ecodes.KEY_END: 0x4d,
evdev.ecodes.KEY_PAGEDOWN: 0x4e,
evdev.ecodes.KEY_RIGHT: 0x4f,
evdev.ecodes.KEY_LEFT: 0x50,
evdev.ecodes.KEY_DOWN: 0x51,
evdev.ecodes.KEY_UP: 0x52,
evdev.ecodes.KEY_NUMLOCK: 0x53,
evdev.ecodes.KEY_KPSLASH: 0x54,
evdev.ecodes.KEY_KPASTERISK: 0x55,
evdev.ecodes.KEY_KPMINUS: 0x56,
evdev.ecodes.KEY_KPPLUS: 0x57,
evdev.ecodes.KEY_KPENTER: 0x58,
evdev.ecodes.KEY_KP1: 0x59,
evdev.ecodes.KEY_KP2: 0x5a,
evdev.ecodes.KEY_KP3: 0x5b,
evdev.ecodes.KEY_KP4: 0x5c,
evdev.ecodes.KEY_KP5: 0x5d,
evdev.ecodes.KEY_KP6: 0x5e,
evdev.ecodes.KEY_KP7: 0x5f,
evdev.ecodes.KEY_KP8: 0x60,
evdev.ecodes.KEY_KP9: 0x61,
evdev.ecodes.KEY_KP0: 0x62,
evdev.ecodes.KEY_KPDOT: 0x63,
# non-us / and |: 0x64,
evdev.ecodes.KEY_APPSELECT: 0x65,
evdev.ecodes.KEY_POWER: 0x66,
evdev.ecodes.KEY_KPEQUAL: 0x67,
evdev.ecodes.KEY_F13: 0x68,
evdev.ecodes.KEY_F14: 0x69,
evdev.ecodes.KEY_F15: 0x6a,
evdev.ecodes.KEY_F16: 0x6b,
evdev.ecodes.KEY_F17: 0x6c,
evdev.ecodes.KEY_F18: 0x6d,
evdev.ecodes.KEY_F19: 0x6e,
evdev.ecodes.KEY_F20: 0x6f,
evdev.ecodes.KEY_F21: 0x70,
evdev.ecodes.KEY_F22: 0x71,
evdev.ecodes.KEY_F23: 0x72,
evdev.ecodes.KEY_F24: 0x73,
# execute
evdev.ecodes.KEY_HELP: 0x75,
evdev.ecodes.KEY_MENU: 0x76,
evdev.ecodes.KEY_SELECT: 0x77,
evdev.ecodes.KEY_STOP: 0x78,
evdev.ecodes.KEY_AGAIN: 0x79,
evdev.ecodes.KEY_UNDO: 0x7a,
evdev.ecodes.KEY_CUT: 0x7b,
evdev.ecodes.KEY_COPY: 0x7c,
evdev.ecodes.KEY_PASTE: 0x7d,
evdev.ecodes.KEY_FIND: 0x7e,
evdev.ecodes.KEY_MUTE: 0x7f,
evdev.ecodes.KEY_VOLUMEUP: 0x80,
evdev.ecodes.KEY_VOLUMEDOWN: 0x81,
# locking caps
# locking num
# locking scroll
evdev.ecodes.KEY_KPCOMMA: 0x85,
evdev.ecodes.KEY_KPEQUAL: 0x86,
# ...
evdev.ecodes.KEY_SYSRQ: 0x9a,
# ...
}
LEFT_CTRL = 1 << 0
LEFT_SHIFT = 1 << 1
LEFT_ALT = 1 << 2
LEFT_GUI = 1 << 3
RIGHT_CTRL = 1 << 4
RIGHT_SHIFT = 1 << 5
RIGHT_ALT = 1 << 6
RIGHT_GUI = 1 << 7
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Forward input events to a USB descriptor')
parser.add_argument('input', type=str, help='The source evdev device')
parser.add_argument('output', type=str, help='The output HID device')
args = parser.parse_args()
dst = os.open(args.output, os.O_WRONLY)
src = evdev.InputDevice(args.input)
src.grab()
modifier_state = 0
keys_down = set()
for event in src.read_loop():
if event.type != evdev.ecodes.EV_KEY:
continue
data = evdev.categorize(event)
print("Got key", data.scancode)
code = scan_to_hid.get(data.scancode, None)
if code is None:
print("Ignoring unknown key")
continue
if data.keystate == data.key_down:
if len(keys_down) >= 6:
print("Ignoring key due to rollover")
continue
print("Adding key")
keys_down.add(code)
if data.keystate == data.key_up:
print("Removing key")
keys_down.remove(code)
# Build the packet
packet = [0] + [0] + [k for k in keys_down]
packet += [0] * (8 - len(packet))
assert(len(packet) == 8)
print("Sending", packet)
os.write(dst, bytes(packet))