#!/usr/bin/env python3 """Input relay for the Steam Frame: stable virtual devices, the 3D pointer, device rules. SteamVR opens /dev/input/event* only when it starts and never hotplugs, so a Bluetooth mouse that sleeps and reconnects (new event nodes) stops working until SteamVR restarts. This relay creates a virtual mouse and a virtual keyboard through /dev/uinput once, before SteamVR starts, and feeds them from the physical devices as they come and go. Every USB or Bluetooth mouse and keyboard is a candidate. A physical device is identified by its Bluetooth address (EVIOCGUNIQ) or USB bus:vendor:product:name, so all its event nodes share one role (the Swiftpoint Z3 has a mouse node and a keyboard node for its extra buttons). Roles, from ~/.config/frametop-input.json (written by the Frametop Input Settings app): pointer grabbed; drives the universal 3D mouse (default for devices with a mouse node) passthrough keys go to the desktop; grabbed only while typing goes there, otherwise only observed, e.g. for the Meta dashboard shortcut (default for keyboards; the shortcut is off unless META_DASHBOARD=1 is in ~/.config/frametop.conf) ignore not grabbed, only observed for identification in the settings app Buttons and keys of pointer devices go through a per-device map to actions (left, right, middle, back, scroll_up, scroll_down, dashboard, recenter, pointer_toggle, follow_toggle = head follow on or off, gaze_toggle = gaze mode on or off (the pointer goes where you look; see pointer/helper/ft-pointer.cpp), sens_up, sens_down, layout_reset = put the desktop screens back in their saved layout, screens_toggle = hide or show the desktop screens, key = pass through as a key, none). Frame controller buttons can be mapped too ("controller_buttons": {"right/a": action} in the rules file; any action but key). The controllers aren't input devices here, only SteamVR sees them, so the pointer helper reads them with SteamVR input and sends "vrbtn