Introduction
To run fauxput, you’ll need:
- Linux kernel ≥ 7.0 with the vkms driver enabled.
- Patched
vkmsproviding the EDID-via-configfs interface. Until those patches land in mainline, install thevkms-edid-dkmsout-of-tree module. - A Wayland compositor such as kwin. Support is planned for mutter and the
wlrootsfamily of compositors (Sway, Hyprland, river, Wayfire, COSMIC, etc).
The kernel dependency guide covers the OOT module build and install. For the API reference, see the generated rustdoc.
Project links
License
MIT.
Kernel dependency: vkms-edid-dkms
fauxput writes per-display EDID into /sys/kernel/config/vkms/<inst>/connectors/0/edid. That attribute doesn’t exist in mainline Linux 7.0.x; it’s part of Louis Chauvet’s VKMS configfs attributes patch series. Until that lands in upstream, fauxput needs a patched vkms.ko. fauxput will detect the edid attribute and warns when it’s missing; with the patched module loaded, requested resolutions take effect.
Building the patched module
Nearly all of the patches modify only files under drivers/gpu/drm/vkms/. The other patches add helper functions in DRM core, but those helpers are only consumed by vkms_config_show() for debugfs output. An OOT shim header (vkms_oot_shim.h) replaces them with static stubs, so the patched module compiles against installed kernel headers, avoiding the need for a full kernel rebuild.
Prerequisites: b4, git, kernel headers for the running kernel (linux-headers-$(uname -r) on Debian/Ubuntu, linux-headers on Arch), make, a C compiler.
# Fetch a clean kernel
git clone --depth 1 --branch v7.0 \
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git \
linux-vkms
cd linux-vkms
# Pull the Chauvet's "v3" patch series from lore.
b4 am 20251222-vkms-all-config-v3-0-ba42dc3fb9ff@bootlin.com -o ../patches.mbx
# Apply. Patch 19 conflicts on v7.0; skip it (it doesn't affect EDID).
git am ../patches.mbx # stops on patch 19
git am --skip
git am --continue
# Stage the patched vkms sources into an out-of-tree package directory.
mkdir -p ../vkms-edid-dkms
cp drivers/gpu/drm/vkms/*.{c,h} ../vkms-edid-dkms/
cd ../vkms-edid-dkms
# Copy the OOT companion files
cp /path/to/fauxput/docs/src/snippets/vkms_oot_shim.h .
cp /path/to/fauxput/docs/src/snippets/Makefile .
cp /path/to/fauxput/docs/src/snippets/dkms.conf .
# Wire the shim into the patched vkms source that uses the stubbed helpers.
sed -i '1i #include "vkms_oot_shim.h"' vkms_config.c
# Build against the running kernel.
make KDIR=/lib/modules/$(uname -r)/build
# produces vkms.ko. Verified against 7.0.3-arch1-2.
The companion files:
vkms_oot_shim.h — stubs the DRM-core helpers that aren’t linkable OOT:
#ifndef _VKMS_OOT_SHIM_H_
#define _VKMS_OOT_SHIM_H_
/* Stubs for DRM-core helpers not linkable from an OOT module on
* Linux 7.0.x. These are debugfs-only paths consumed by
* vkms_config_show(); functional vkms code is unaffected. */
static inline const char *drm_get_color_encoding_name(int v) { return "?"; }
static inline const char *drm_get_color_range_name(int v) { return "?"; }
static inline const char *drm_get_colorspace_name(int v) { return "?"; }
static inline const char *drm_get_plane_type_name(int v) { return "?"; }
static inline const char *drm_get_rotation_name(unsigned v) { return "?"; }
#endif
Makefile — standard kernel out-of-tree boilerplate. The vkms-y .o list mirrors drivers/gpu/drm/vkms/Makefile from the patched tree.
# Out-of-tree build of patched vkms (with v3 EDID-via-configfs support).
# Targets the running kernel by default; override KDIR for cross-builds.
KDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
obj-m := vkms.o
# Mirror drivers/gpu/drm/vkms/Makefile object list. Tests are skipped.
vkms-y := \
vkms_drv.o \
vkms_plane.o \
vkms_output.o \
vkms_crtc.o \
vkms_composer.o \
vkms_writeback.o \
vkms_formats.o \
vkms_luts.o \
vkms_colorop.o \
vkms_connector.o \
vkms_config.o \
vkms_configfs.o
# vkms_config_show / vkms_configfs touch sysfs/configfs heavily; ensure
# CONFIG_DRM_VKMS_CONFIGFS is enabled in the running kernel's .config.
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
install:
$(MAKE) -C $(KDIR) M=$(PWD) modules_install
.PHONY: all clean install
dkms.conf — DKMS manifest, only needed for Approach B (DKMS-managed install) below. DEST_MODULE_LOCATION="/updates" puts the built module ahead of the in-tree vkms.ko in modprobe’s search order, so loading “vkms” picks up our patched build automatically.
# DKMS config for the patched vkms module (v3 configfs+EDID series).
# Builds a vkms.ko that overrides the in-tree one for every installed kernel.
PACKAGE_NAME="vkms-edid"
PACKAGE_VERSION="0.1"
MAKE[0]="make KDIR=/lib/modules/${kernelver}/build"
CLEAN="make KDIR=/lib/modules/${kernelver}/build clean"
BUILT_MODULE_NAME[0]="vkms"
DEST_MODULE_LOCATION[0]="/updates"
AUTOINSTALL="yes"
# Override the in-tree vkms.ko on module load.
REMAKE_INITRD="no"
All three companion files should be reusable across kernel versions, to an extent. You only re-run the b4/git-am dance when a new patch revision lands.
Loading the module
Approach A — insmod (temporary)
Load the freshly-built vkms.ko directly.
just vkms-insmod
# or, equivalently:
make -C path/to/vkms-edid-dkms -s
sudo modprobe -r vkms
sudo insmod path/to/vkms-edid-dkms/vkms.ko create_default_dev=0
If modprobe -r vkms fails (“module is in use”), the host compositor is holding it. Reboot or logout/login first.
Approach B — DKMS (persistent)
Stage under /usr/src/, register with DKMS, and DKMS should rebuild the module on every kernel upgrade.
just vkms-dkms
# or, equivalently:
sudo cp -rT path/to/vkms-edid-dkms /usr/src/vkms-edid-0.1
sudo dkms add -m vkms-edid -v 0.1
sudo dkms install -m vkms-edid -v 0.1
sudo modprobe -r vkms && sudo modprobe vkms
To uninstall:
sudo dkms remove vkms-edid/0.1 --all
sudo rm -rf /usr/src/vkms-edid-0.1
sudo modprobe -r vkms && sudo modprobe vkms # back to in-tree
Verifying the patched module is active
sudo mkdir -p /sys/kernel/config/vkms/probe/connectors/0
ls /sys/kernel/config/vkms/probe/connectors/0/
sudo rmdir /sys/kernel/config/vkms/probe/connectors/0 /sys/kernel/config/vkms/probe
Listing should include both edid and edid_enabled (along with possible_encoders, status, type). With in-tree vkms only the latter three appear. fauxput up will also stop printing the “EDID write skipped” warning.
About the patch series
Louis Chauvet’s VKMS configfs attributes patch series (33 patches, posted to dri-devel December 2025) adds a configfs interface to the kernel’s virtual KMS driver, letting userspace configure simulated displays at runtime with writes under /sys/kernel/config/vkms/. fauxput only needs the per-connector edid and edid_enabled attributes from that series (patches 27 and 28), but they build on shared infrastructure earlier in the series, so I suggest applying the full patchset.
When upstream lands
Watch dri-devel for the v3/v4 series merging into mainline:
- https://lore.kernel.org/dri-devel/?q=s%3A%22vkms%3A+Introduce+multiple+configFS%22
git log --grep configfs drivers/gpu/drm/vkms/in mainline/linux-next
Once the in-tree vkms exposes edid and edid_enabled:
- Uninstall the DKMS package (Approach B uninstall above).
- Reboot or
modprobe -r vkms; modprobe vkms.
Streaming setup
Wire fauxput into Sunshine + Moonlight so a Moonlight client connecting at, say, 2560×1440@120 gets a virtual display at exactly that resolution, configured automatically, and torn down on disconnect.
Install fauxput per the README’s Installation section. The PKGBUILD drops the Sunshine wrappers under /usr/share/fauxput/; for source builds, copy them from contrib/.
Sunshine capture backends
Pick one in ~/.config/sunshine/sunshine.conf:
- Portal (
capture = portal) — recommended. DE-agnostic; goes through xdg-desktop-portal and PipeWire, so hardware acceleration (NVENC, VAAPI) works against the virtual display. First connect prompts a source-picker dialog from the active portal backend (e.g. xdg-desktop-portal-kde); select fauxput’s output. Subsequent connects auto-restore. Available in Sunshine $\geq$ 2026.4. - KMS (
capture = kms) — legacy. Reads framebuffers directly through DRM; the vkms framebuffer lives in CPU memory, so encoders fall back to a GPU→RAM→GPU readback path with no hardware-accelerated capture.
Configure Sunshine to use fauxputs
Add an app in Sunshine’s web UI (the Apps tab) with the fauxput wrappers as do / undo prep-cmds: sunshine-fauxput-{up,down}.sh. See Sunshine’s App Examples for details on how to configure. An example config is at /usr/share/fauxput/sunshine-apps.json.example.
The wrappers read SUNSHINE_CLIENT_WIDTH/HEIGHT/FPS and call fauxput up --primary --disable-real-outputs / fauxput down. Set FAUXPUT_KEEP_REAL=1 in the app’s env block to leave host outputs enabled while streaming.
Verify
Connect from Moonlight at a non-host resolution. The stream should render at exactly that resolution; fauxput status shows the virtual display while connected and it disappears on disconnect.
Issues
See troubleshooting.
Troubleshooting
Gotchyas that will break fauxput in non-obvious ways.
SDDM with an X11 greeter results in logout on fauxput down
If you’re running SDDM with an X11 greeter, fauxput down (or anything else that destroys a vkms instance) will kick you back to the login screen.
The greeter crashes on the DRM hot-unplug event, taking your session down as collateral.
To check:
loginctl show-session $(loginctl list-sessions --no-legend | awk '/greeter/{print $1}') -p Type
# Type=x11 > affected
# Type=wayland > safe
The fix is to use SDDM in Wayland mode, or PLM.
Sunshine portal-capture token doesn’t survive an unclean shutdown
When using the Sunshine’s portal backend, after a reboot, Moonlight reconnect re-prompts the portal chooser instead of silently restoring the previous fauxput selection.
If Sunshine shuts down uncleanly, the compositor will ask for a new token on the next connect.
Pick the chooser display before --disable-real-outputs
The xdg-desktop-portal permission dialog appears on the host desktop. If --disable-real-outputs is enabled, there’s nowhere to interact with the modal. On first connect, run fauxput up with just --primary, accept the permissions modal when it appears, and pick fauxput’s virtual output. Subsequent connects auto-restore the choice without the dialog.
API documentation
The crate-level rustdoc lives at: