#!/usr/bin/env bash # Wrapper that activates local venv then executes embedded Python DIR=$(dirname "$(readlink -f "$0")") VENV=/opt/navka-relay/venv PY="$VENV/bin/python" if [[ ! -x $PY ]]; then echo "❌ venv missing: run install-navka-relay.sh first." >&2 exit 1 fi # shellcheck disable=SC2091 exec $PY - <<'PYCODE' import os, socket, selectors, signal, sys from evdev import InputDevice, ecodes # --------- config via env ---------- DEV = os.getenv("NAVKA_KBD", "/dev/input/event4") IP = os.getenv("NAVKA_IP", "192.168.42.253") PORT = int(os.getenv("NAVKA_PORT", "4000")) # ----------------------------------- # full HID tables (letters, numbers, F1-F12, arrows, etc.) HID = {**{getattr(ecodes,f"KEY_F{i}"):i+53 for i in range(1,13)}} for ev,h in { **{k:4+i for i,k in enumerate(range(ecodes.KEY_A,ecodes.KEY_Z+1))}, **{k:30+i for i,k in enumerate(range(ecodes.KEY_1,ecodes.KEY_0+1))}, ecodes.KEY_ENTER:40, ecodes.KEY_ESC:41, ecodes.KEY_BACKSPACE:42, ecodes.KEY_TAB:43, ecodes.KEY_SPACE:44, ecodes.KEY_MINUS:45, ecodes.KEY_EQUAL:46, ecodes.KEY_LEFTBRACE:47, ecodes.KEY_RIGHTBRACE:48, ecodes.KEY_BACKSLASH:49, ecodes.KEY_SEMICOLON:51, ecodes.KEY_APOSTROPHE:52, ecodes.KEY_GRAVE:53, ecodes.KEY_COMMA:54, ecodes.KEY_DOT:55, ecodes.KEY_SLASH:56, ecodes.KEY_RIGHT:79, ecodes.KEY_LEFT:80, ecodes.KEY_DOWN:81, ecodes.KEY_UP:82 }.items(): HID.setdefault(ev, h) MOD = { ecodes.KEY_LEFTCTRL:1, ecodes.KEY_LEFTSHIFT:2, ecodes.KEY_LEFTALT:4, ecodes.KEY_LEFTMETA:8, ecodes.KEY_RIGHTCTRL:16, ecodes.KEY_RIGHTSHIFT:32, ecodes.KEY_RIGHTALT:64, ecodes.KEY_RIGHTMETA:128 } try: dev = InputDevice(DEV) except FileNotFoundError: sys.exit(f"Input device {DEV} not found") dev.grab() sock = socket.create_connection((IP,PORT), timeout=5) sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) mods, keys = 0, [] def send(): sock.sendall(bytes([mods,0]+keys[:6]+[0]*(6-len(keys)))) def quit_handler(sig, frame): # graceful Ctrl-C dev.ungrab(); sock.close(); sys.exit(0) signal.signal(signal.SIGINT, quit_handler) for ev in dev.read_loop(): if ev.type != ecodes.EV_KEY: continue pressed = ev.value == 1 if ev.code in MOD: mods = mods | MOD[ev.code] if pressed else mods & ~MOD[ev.code] elif ev.code in HID: h = HID[ev.code] if pressed and h not in keys: keys.insert(0,h) elif not pressed and h in keys: keys.remove(h) else: continue send() PYCODE