Blog

Virtual displays and headless game streaming on Linux — without the EDID hacks

Why Sunshine has no first-class virtual display on Linux, the EDID/modeline hacks people resort to, and how punktfunk spins one up on demand — sized to your client.

You want to stream a Linux gaming PC to another device — but the PC is headless, or the client's resolution doesn't match the monitor that's plugged in. On Sunshine and Moonlight that quickly turns into a fight with dummy plugs, EDID overrides, and hand-written modelines. Here's why it happens, and how punktfunk removes that whole layer.

Why Sunshine has no first-class virtual display on Linux

On Windows, Sunshine can pair with an indirect display driver (the well-known IddSample) to conjure a virtual screen on demand. On Linux there's no built-in equivalent — Sunshine captures whatever display the compositor already exposes. No physical monitor, or a monitor stuck at the wrong resolution, means there's no good surface to capture in the first place.

The hacks people resort to today

To get a streamable display on a headless or mismatched Linux host, the community has assembled a toolbox:

  • Physical dummy HDMI/DisplayPort plugs that fake a connected monitor and its EDID.
  • EDID overrides (custom EDID blobs, drm.edid_firmware) to force a resolution the GPU will light up.
  • The `evdi` kernel module (DisplayLink's virtual display driver) to create a fake output.
  • Hand-rolled `cvt`/`gtf` + `xrandr` modelines for non-standard resolutions and refresh rates.
  • Third-party daemons like `sunshine_virt_display` that automate parts of this.
  • Connect/disconnect scripts that toggle physical outputs so only the virtual one gets captured.

Where it breaks

None of these are first-class, so they fail in familiar ways:

  • Black screen after reboot, when the dummy plug or EDID override isn't re-applied or the login seat changes.
  • CRTC reassignment on NVIDIA and Wayland compositors like Hyprland, which drops or moves your virtual output mid-session.
  • Per-compositor differences — what works on bare KMS doesn't on wlroots or Hyprland, and X11 vs Wayland each need their own incantations.
  • Resolution and refresh that almost match the client, leaving black bars or forcing ugly scaling.

How Windows and the Apollo fork sidestep it

It's not that virtual displays are impossible on Linux — it's that Sunshine doesn't ship one. Windows has the IddSample indirect display driver, and the Apollo/Artemis fork of Sunshine added a built-in virtual display. Both prove the model: the host should create the screen, sized to the client, on demand — not borrow a physical one.

How punktfunk does it — no dummy plug, no modeline math

punktfunk treats the virtual display as a first-class part of every session. When a client connects, the host spins up a virtual output at the client's exact resolution and refresh rate, then tears it down when the session ends — RAII cleanup, so nothing lingers after a crash or reboot.

Because Linux has no single cross-compositor API for this, punktfunk ships a backend per compositor and picks the right one automatically:

  • KWinzkde_screencast virtual outputs, with custom modes for refresh rates above 60 Hz.
  • gamescope — spawned headless at the exact WxH@Hz, its PipeWire node captured directly.
  • Mutter / GNOME — the D-Bus RecordVirtual virtual monitor (validated headless, zero-copy).
  • Sway / wlrootsswaymsg create_output with a custom mode and portal capture.

The result is a genuinely headless host: a mini-PC or spare rig with no monitor attached, streaming at whatever your phone, tablet, TV, or laptop asks for. No dummy plug, no EDID blob, no xrandr modeline arithmetic, and no black screen after the next reboot.

Try it

If you've been juggling EDID hacks to stream a headless Linux box, punktfunk is built to make that entire layer disappear. Grab the native Linux client, pair it with the host, and let it create the display for you.