#!/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, for the key combinations below (default for keyboards) 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), gaze_precision = while held, the pointer stops where you look and the mouse steers it, and the release clicks there, gaze_drag = the same, but pressed at once, so it drags ("precision|gazedrag mouse|keyboard 1|0" to the helper), gaze_left and gaze_right = keyboard clicks at the gaze: a tap clicks where you look; held, the pointer stops there and your head steers it (it stays put in your view), and the release clicks; held still for half a second, it's a real press that your head drags ("gazekey left|right 1|0" to the helper; by default Meta+J and Meta+K, DEFAULT_KEY_BINDINGS), gaze_quickcal = the gaze service's one-dot check ("quickcal" to @ft_gazed), sens_up, sens_down, layout_reset = put the desktop screens back in their saved layout, screens_toggle = hide or show the desktop screens, keyboard_toggle = open or close Frametop's keyboard, float_toggle = float the desktop window under the pointer (else the active one) in VR, or put it back if it floats, dock_all = put every floating window back (both to ft-floatd, @frametop_float), profile:NAME = switch to that profile (ft-layout use NAME: its screens and apps; docs/profiles.md), steam_menu = open the SteamVR dashboard on Steam's menu, or close the dashboard (steam/ft-steam menu, through Steam's UI), command:CMD = run CMD with sh -c (on the host, as this service: its environment, output to its log, and COMMAND_PATH, so ft-layout, ft-float and ft-steam need no path), 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 and the gaze ones, GAZE_ACTIONS: gaze mode is a mouse feature, docs/gaze-controllers.md). So can key combinations on any keyboard ("key_bindings": {"29+56+34": action}, evdev codes joined by "+", modifiers first and left-hand codes for either side, here Ctrl+Alt+G): the combination does the action, and its last key isn't typed. A modifier on its own ("125": Meta) is a tap: pressed and released with no other key, mouse button, or scroll in between; the desktop gets an F24 press before its release, so Plasma's launcher doesn't open on a Meta tap that's bound. A rules file without "key_bindings" gets DEFAULT_KEY_BINDINGS (Meta tap: steam_menu, Meta+J: gaze_left, Meta+K: gaze_right, Meta+Shift+F: float_toggle); one with its own, even an empty one, doesn't. The float, profile, Steam menu and command actions work without pointer mode too. A combination with Meta also sends the desktop an F24 press and Meta's release right away: so letting go of Meta doesn't open Plasma's launcher, and a gaze click isn't Meta+click (KWin's window move and resize). Another key while Meta is still held gives the desktop Meta back. While typing goes to Steam, keyboards aren't grabbed, so Steam or the game sees a combination's keys too. The controllers aren't input devices here, only SteamVR sees them, so the pointer helper reads them with SteamVR input and sends "vrbtn