ref:main

Phase 2: gamepads for Linux guests #7

open Opened by cole.christensen@gmail.com

Phase 2: gamepads for Linux guests

Parent: #1. Depends on #5 (input routing). Windows guests are tracked separately in #11.

Requirements

  • REQ-INP-004: gamepad input for Linux guests

Background

  • QEMU’s D-Bus display has no gamepad interface (verified in ui/dbus-display1.xml, QEMU 11.1).
  • Sunshine on Linux already creates host uinput gamepads through libvirtualhid (platf::alloc_gamepad, gamepad_update in src/platform/virtualhid_input.cpp), including Xbox/DS4/DS5-style devices with rumble feedback.
  • QEMU can pass a host evdev device to the guest: -device virtio-input-host-pci,evdev=/dev/input/eventN. Linux guests have virtio-input drivers. Unverified: whether force-feedback/rumble goes through virtio-input-host, and whether guests see a usable joystick (buttons/axes) rather than just raw events.
  • Gamepads appear dynamically when a client connects a controller, so the evdev device doesn’t exist when QEMU starts. It has to be hot-plugged over QMP (device_add driver=virtio-input-host-pci,evdev=...,id=...) and removed on disconnect (device_del).

Tasks

  • Spike first: create a uinput gamepad by hand (or with Sunshine’s existing path), hot-plug it into a running Linux guest with QMP, and check in the guest with evtest and jstest (buttons, axes, triggers, D-pad, rumble). Record the results in a comment on this issue. If virtio-input-host doesn’t give a working gamepad, stop and escalate with the alternatives: usb-host passthrough of the uinput device isn’t possible because uinput isn’t USB; that leaves a QEMU USB gamepad device (#11) for Linux guests too.
  • QMP client in qemu/qmp.{h,cpp}: config key qemu_qmp_socket; minimal JSON QMP with capabilities negotiation, device_add, device_del, and waiting for the DEVICE_DELETED event. Keep it small; nlohmann/json is already a Sunshine dependency, so check before adding anything.
  • Wire-up: when capture == qemu and qemu_qmp_socket is set, the gamepad functions still create the libvirtualhid uinput device. After creation, find its /dev/input/eventN node through the existing libvirtualhid API or sysfs, then device_add. On gamepad removal or client disconnect, device_del, then destroy the uinput device.
  • The uinput devices must not also drive the host desktop. Document that the host shouldn’t run a session that grabs them, or EVIOCGRAB them for exclusive use; test whether QEMU’s grab_all/exclusive grab behavior is enough.
  • Rumble feedback: guest FF events → host uinput → Moonlight feedback queue, only if the spike shows FF works through virtio-input-host.
  • Permissions: document udev rules so the Sunshine user can create uinput devices and QEMU can open the event nodes.

Tests

  • Unit: QMP client against a fake QMP socket (negotiation, device_add/del, errors, DEVICE_DELETED wait).
  • E2E: the test client sends gamepad state (button A, left stick full right); the guest evdev logger shows BTN_SOUTH and ABS_X max; disconnect → device_del observed.

Done when

REQ-INP-004 is linked to passing tests, a manual check with a real controller through Moonlight into a Linux guest is noted in the PR, and anvil requirement status passes. PR Closes #7.