An alternative approach to a software KVM switch for GPU passthrough that aims to appear much like LookingGlass but without relaying frames.
screenstub
uses DDC/CI
to switch monitor inputs when its window is visible and/or in focus. It is
intended to be used in fullscreen with virtual workspaces, and switches to the
VM input when its workspace is visible. Many options are available for working
with input forwarding to a VM, and if used without DDC/CI it becomes similar to
something like Synergy.
- Install screenstub via cargo.
- Run
screenstub detect
to check that DDC/CI is working and your monitor is detected. You may need to enable DDC/CI in your monitor's settings, and/or load the i2c drivers. - Configure screenstub by modifying the example as necessary, and setting up the QEMU sockets.
Additional configuration may be required for advanced usage:
- Install and set up qemu-ga to run on Windows startup.
- Install a command-line DDC/CI program in Windows.
- Configure permissions, and check your input devices in order to use the advanced routing modes.
Requires a modern stable Rust toolchain, and can be installed and run like so:
cargo install --force --git https://github.com/arcnmx/screenstub
screenstub -c config.yml x
- udev (Debian: libudev-dev)
- libxcb (Debian: libxcb1-dev libxcb-dpms0-dev libxcb-xtest0-dev libxcb-xkb-dev)
- python (build-time only to generate xcb bindings)
- Nix{,OS}:
nix run -f https://github.com/nix-community/NUR/archive/master.tar.gz repos.arc.packages.screenstub -c screenstub
An example configuration is available to use as a starting
point. There are a few specific items that need to be set up for everything to
work. The screenstub detect
command can be used to find information about
DDC/CI capable monitors and their inputs.
screenstub
requires both QMP and guest agent sockets available to properly
control the VM and QEMU itself. This requires something similar to the following
command-line flags to be passed to QEMU (note libvirt may already expose some of
these for you):
qemu-system-x86_64 \
-chardev socket,path=/tmp/vfio-qga,server,nowait,id=qga0 \
-device virtserialport,chardev=qga0,name=org.qemu.guest_agent.0 \
-chardev socket,path=/tmp/vfio-qmp,server,nowait,id=qmp0 \
-mon chardev=qmp0,id=qmp,mode=control
screenstub
emulates three different input devices: a keyboard, a tablet for
absolute coordinate mouse mapping, and a mouse for relative motion events. Each
of these devices may be configured as USB devices, PS/2, or virtio. It is
recommended that you remove all existing input devices from your QEMU command
line configuration (-usbdevice kbd
, -usbdevice mouse
, -usbdevice tablet
, -device usb-kbd
, -device usb-mouse
, -device usb-tablet
).
The default configuration sets them up for optimal compatibility, but virtio input drivers (vioinput) are recommended instead for performance reasons. These require drivers to be installed in the guest. You can download them for Windows here.
The routing mode describes how input events are translated from the host mouse
and keyboard to the guest devices. The default qmp
routing mode sends all input
commands over the QEMU control socket. This requires no additional configuration
on the host, but may not be optimal for performance. The other routing modes use
uinput
instead to transport events, which requires additional configuration.
To use the virtio-host
or input-linux
routing modes, screenstub
needs
access to /dev/uinput
and the virtual /dev/input/event*
devices.
udev rules can be used to set up device
permissions. Additional rules may be included for any external devices you want
to "grab" and forward to the guest.
Xorg also needs to be configured to ignore the virtual devices. Copy
the xorg config into your xorg.conf
or
/etc/X11/xorg.conf.d/
directory to prevent Xorg from trying to use the virtual
input devices for the host.
These are pretty straightforward to use when they work, however it is recommended
to use the built-in DDC/CI support instead. You will probably need to load the i2c-dev
kernel module for it to work, by placing i2c.conf
in /etc/modules-load.d/
.
Some NVIDIA Linux drivers have had broken DDC/CI support. There are workarounds but there may be issues when using DDC/CI over DisplayPort from the host.
Many monitors require a DDC/CI command to be issued by the GPU on the currently
active input. In this case screenstub
must issue a command to the guest OS
to instruct it to relinquish control over the screen back to the host.
QEMU Guest Agent and SSH are two common methods of executing commands inside
of a guest.
- ddcset setvcp 60 3
- ScreenBright -set 0x60 3
- ClickMonitorDDC s DVI1
Note that Windows applications interfacing with the screen must run as a logged
in graphical user. Services like QEMU Guest Agent or SSHd often run as a system
service and may have trouble running these commands without adjustments. NVAPI
for example (via ddcset -b nvapi
) may be used as an alternative on NVIDIA cards.
A recent version of qemu-ga is required to be able to execute processes in the VM. It can be built from from source, or you may download a compiled installer here.
It is recommended that you disable the default qemu-ga system service, and instead schedule it to run on user login. I run the following in a powershell script on startup:
Start-Process "C:\Program Files\Qemu-ga\qemu-ga.exe" -WindowStyle Hidden
This needs to run as admin, so you can use task scheduler for that pointing to a script containing the above:
schtasks /create /sc onlogon /tn qemu-ga /rl highest /tr "powershell C:\path\to\qemu-ga.ps1"