Selkies
Developer Reference

input_handler

Server-side input, clipboard, cursor, and gamepad injection for Selkies.

Both transports (WebSockets and WebRTC) feed client messages into one WebRTCInput handler, so input authority and fallback behavior stay identical across modes. Injection follows fallback ladders that resolve the newest mechanism first and degrade rung by rung, with cooldowns that re-probe the top rung rather than latching into a fallback:

  • Wayland keyboard: the compositor seat keymap (_WaylandKeymapOwner), then the in-process zwp_virtual_keyboard client, then a data-control clipboard paste. The Wayland path is subprocess-free by design; never reintroduce wtype, wl-copy or similar forks where the in-process pixelflux harness exists.
  • X11 keyboard: in-process XTEST (_XTestKeyboard, with a dynamic spare-keycode overlay for unmapped keysyms and an XKB group lock for keysyms a later layout group carries), then an xdotool fork.
  • Clipboard: native event-driven monitors (XFixes on X11, compositor callbacks / data-control on Wayland), with xclip polling as the X11 fallback.

Under a nested app compositor the seat rung only carries keysyms the base layout resolves, so the keyboard worker routes character-bearing keysyms the base lacks onto the virtual-keyboard text batch by classification, not by failure. That batch and the selection are aimed at the auto-detected app compositor: the live wayland-\<N> socket beside the capture compositor's, never a differently named relay; aiming at the capture compositor instead silently kills every non-ASCII key.

Input authority is enforced here, shared by both transports, so a modified client cannot inject input a controller did not grant: a read-only viewer peer (shared/#player co-op) may send only VIEWER_ALLOWED_PREFIXES; a viewer holding the active mk token while enable_collab is on (a read-write collaborator) may additionally send VIEWER_COLLAB_EXTRA_PREFIXES — the keyboard, mouse and clipboard set, including co, because IME commits and atomic typing arrive that way. cmd and every settings-mutating message stay controller-only. Blur/visibility lifecycle noise (VIEWER_SILENT_DROP_PREFIXES) from a read-only viewer is normal operation and is dropped without a warning.

Blocking X and compositor work runs on worker threads or dedicated event threads; the asyncio loop only queues, dispatches, and awaits, so input never stalls behind a slow display server.

attributelibxkb
= ctypes.CDLL('libxkbcommon.so.0')
attributeX11_LIBS_AVAILABLE
= True
attributeVIEWER_ALLOWED_PREFIXES
= ('SETTINGS,', 'START_VIDEO', 'STOP_VIDEO', 'REQUEST_KEYFRAME', 'js,')
attributeVIEWER_COLLAB_EXTRA_PREFIXES
= ('kd', 'ku', 'kh', 'kr', 'm', 'm2', 'co,', 'cws', 'cbs', 'cwd', 'cbd', 'cwe', 'cbe', 'cw', 'cb', 'cr', 'REQUEST_CLIPBOARD')
attributeVIEWER_SILENT_DROP_PREFIXES
= ('kr', 'cr')
attributelogger_webrtc_input
= logging.getLogger('webrtc_input')
attributelogger_selkies_gamepad
= logging.getLogger('selkies_gamepad')
attributeMULTIPART_CLIPBOARD_MAX_SIZE
= 64 * 1024 * 1024
attributeINPUT_X_REPLY_TIMEOUT_S
= 20.0
attributeINPUT_X_EVENT_POLL_S
= 0.02
attributeEV_SYN
= 0
attributeEV_KEY
= 1
attributeEV_REL
= 2
attributeEV_ABS
= 3
attributeEV_MSC
= 4
attributeSYN_REPORT
= 0
attributeBTN_MOUSE
= 272
attributeBTN_LEFT
= 272
attributeBTN_RIGHT
= 273
attributeBTN_MIDDLE
= 274
attributeBTN_SIDE
= 275
attributeBTN_EXTRA
= 276
attributeBTN_A
= 304
attributeBTN_B
= 305
attributeBTN_C
= 306
attributeBTN_X
= 307
attributeBTN_Y
= 308
attributeBTN_Z
= 309
attributeBTN_TL
= 310
attributeBTN_TR
= 311
attributeBTN_SELECT
= 314
attributeBTN_START
= 315
attributeBTN_MODE
= 316
attributeBTN_THUMBL
= 317
attributeBTN_THUMBR
= 318
attributeABS_X
= 0
attributeABS_Y
= 1
attributeABS_Z
= 2
attributeABS_RX
= 3
attributeABS_RY
= 4
attributeABS_RZ
= 5
attributeABS_HAT0X
= 16
attributeABS_HAT0Y
= 17
attributeJS_EVENT_BUTTON
= 1
attributeJS_EVENT_AXIS
= 2
attributeJS_EVENT_INIT
= 128
attributeINTERPOSER_MAX_BTNS
= 512
attributeINTERPOSER_MAX_AXES
= 64
attributeCONTROLLER_NAME_MAX_LEN
= 255
attributeC_INTERPOSER_STRUCT_SIZE
= 1360
attributeBULK_DRAIN_TIMEOUT_S
= 15.0
attributeCLIPBOARD_CHUNK_SIZE
= min((WS_MAX_MESSAGE_BYTES - 4096) * 3 // 4, (1024 * 1024 - 512) * 3 // 4)
attributeKEYSYM_ALT_L
= 65513
attributeKEYSYM_LEFT_ARROW
= 65361
attributeKEYSYM_RIGHT_ARROW
= 65363
attributeCYRILLIC_TO_QWERTY_KEYSYM
= {1738: 113, 1731: 119, 1749: 101, 1739: 114, 1733: 116, 1742: 121, 1735: 117, 1755: 105, 1757: 111, 1754: 112, 1734: 97, 1753: 115, 1751: 100, 1729: 102, 1744: 103, 1746: 104, 1743: 106, 1740: 107, 1732: 108, 1745: 122, 1758: 120, 1747: 99, 1741: 118, 1737: 98, 1748: 110, 1752: 109}
attributeEXPECTED_C_STRUCT_SIZEint
= ctypes.sizeof(JsConfigCtypes)
attributeABS_MIN_VAL
= -32767
attributeABS_MAX_VAL
= 32767
attributeABS_TRIGGER_MIN_VAL
= 0
attributeABS_TRIGGER_MAX_VAL
= 255
attributeABS_HAT_MIN_VAL
= -1
attributeABS_HAT_MAX_VAL
= 1
attributeSTANDARD_XPAD_CONFIG
= {'name': 'Microsoft X-Box 360 pad', 'vendor_id': 1118, 'product_id': 654, 'version': 276, 'btn_map': [BTN_A, BTN_B, BTN_X, BTN_Y, BTN_TL, BTN_TR, BTN_SELECT, BTN_START, BTN_MODE, BTN_THUMBL, BTN_THUMBR], 'axes_map': [ABS_X, ABS_Y, ABS_Z, ABS_RX, ABS_RY, ABS_RZ, ABS_HAT0X, ABS_HAT0Y], 'mapping': {'btns': {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 8: 6, 9: 7, 10: 9, 11: 10, 16: 8}, 'axes': {0: 0, 1: 1, 2: 3, 3: 4}, 'client_btns_to_internal_axes': {6: 2, 7: 5}, 'dpad_to_hat': {12: (7, -1), 13: (7, 1), 14: (6, -1), 15: (6, 1)}, 'trigger_internal_abstract_axis_indices': [2, 5], 'hat_internal_abstract_axis_indices': [6, 7]}}
attributeUINPUT_PATH
= '/dev/uinput'
attributeUINPUT_SYSFS_BASE
= '/sys/devices/virtual/input'
attributeUINPUT_MAX_NAME_SIZE
= 80
attributeBUS_USB
= 3
attributeUINPUT_SETUP_FMT
= '=HHHH80sI'
attributeUINPUT_ABS_SETUP_FMT
= '=H2x6i'
attributeUINPUT_SYSNAME_LEN
= 64
attributeUI_DEV_CREATE
= _uinput_ioc(0, 1, 0)
attributeUI_DEV_DESTROY
= _uinput_ioc(0, 2, 0)
attributeUI_DEV_SETUP
= _uinput_ioc(_IOC_WRITE, 3, struct.calcsize(UINPUT_SETUP_FMT))
attributeUI_ABS_SETUP
= _uinput_ioc(_IOC_WRITE, 4, struct.calcsize(UINPUT_ABS_SETUP_FMT))
attributeUI_SET_EVBIT
= _uinput_ioc(_IOC_WRITE, 100, 4)
attributeUI_SET_KEYBIT
= _uinput_ioc(_IOC_WRITE, 101, 4)
attributeUI_SET_ABSBIT
= _uinput_ioc(_IOC_WRITE, 103, 4)
attributeUI_GET_SYSNAME
= _uinput_ioc(_IOC_READ, 44, UINPUT_SYSNAME_LEN)
attributeUINPUT_ABS_INFO_DEFAULT
= (ABS_MIN_VAL, ABS_MAX_VAL, 16, 128, 1)
attributeUINPUT_ABS_INFO
= {ABS_HAT0X: (ABS_HAT_MIN_VAL, ABS_HAT_MAX_VAL, 0, 0, 0), ABS_HAT0Y: (ABS_HAT_MIN_VAL, ABS_HAT_MAX_VAL, 0, 0, 0)}
attributeLOCAL_ARCH_BITS
= 64 if struct.calcsize('P') == 8 else 32
attributeX11_APP_TERMINALS
= ('st', 'xterm -e')
attributeWAYLAND_APP_TERMINALS
= ('foot', 'st', 'xterm -e')
attributeAPP_RUNNER
= 'selkies-proot'
attributeAPP_RUNNER_CHECK_TIMEOUT_S
= 10.0
attributeSESSION_ENV_ADOPTED
= ('DBUS_SESSION_BUS_ADDRESS', 'XAUTHORITY', 'XDG_CURRENT_DESKTOP', 'DESKTOP_SESSION', 'XDG_SESSION_DESKTOP', 'XDG_MENU_PREFIX', 'XDG_SESSION_CLASS', 'XDG_SESSION_ID')
attributeCOMMAND_TAIL_LINES
= 20
attributeCOMMAND_LINE_CHARS
= 4096
attributeCOMMAND_REASON_CHARS
= 200
attributeMOUSE_POSITION
= 10
attributeMOUSE_MOVE
= 11
attributeMOUSE_SCROLL_UP
= 20
attributeMOUSE_SCROLL_DOWN
= 21
attributeMOUSE_SCROLL_LEFT
= 22
attributeMOUSE_SCROLL_RIGHT
= 23
attributeMOUSE_BUTTON_PRESS
= 30
attributeMOUSE_BUTTON_RELEASE
= 31
attributeMOUSE_BUTTON
= 40
attributeMOUSE_BUTTON_LEFT_ID
= 41
attributeMOUSE_BUTTON_MIDDLE_ID
= 42
attributeMOUSE_BUTTON_RIGHT_ID
= 43
attributeMOUSE_MASK_BIT_PRIMARY
= 1 << 0
attributeMOUSE_MASK_BIT_ERASER
= 1 << 5
attributeUINPUT_BTN_LEFT
= (EV_KEY, BTN_LEFT)
attributeUINPUT_BTN_MIDDLE
= (EV_KEY, BTN_MIDDLE)
attributeUINPUT_BTN_RIGHT
= (EV_KEY, BTN_RIGHT)
attributeUINPUT_REL_X
= (EV_REL, 0)
attributeUINPUT_REL_Y
= (EV_REL, 1)
attributeUINPUT_REL_HWHEEL
= (EV_REL, 6)
attributeUINPUT_REL_WHEEL
= (EV_REL, 8)
attributeXBUTTON_LEFT
= 1
attributeXBUTTON_MIDDLE
= 2
attributeXBUTTON_RIGHT
= 3
attributeMOUSE_BUTTON_MAP
= {MOUSE_BUTTON_LEFT_ID: {'uinput': UINPUT_BTN_LEFT, 'x11': XBUTTON_LEFT}, MOUSE_BUTTON_MIDDLE_ID: {'uinput': UINPUT_BTN_MIDDLE, 'x11': XBUTTON_MIDDLE}, MOUSE_BUTTON_RIGHT_ID: {'uinput': UINPUT_BTN_RIGHT, 'x11': XBUTTON_RIGHT}}

On this page

No Headings
Edit on GitHub