Skip to content

About

Create a virtual monitor on Linux (Wayland-friendly, no dummy plug) so you can stream your desktop to a tablet or phone with Sunshine + Moonlight while your physical monitors stay off

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

virtual-monitor

Create a virtual monitor on Linux (Wayland-friendly, no dummy plug) so you can stream your desktop to a tablet or phone with Sunshine + Moonlight while your physical monitors stay off.

Español

It forces an unused graphics connector to appear "connected" with a fake EDID that you define (resolution, refresh rate, name), then switches your desktop to use only that monitor while you stream and restores your normal layout afterwards.

Status: developed and tested on one machine (Ubuntu, GNOME 50 on Wayland, NVIDIA RTX 3060 + AMD Vega iGPU, Sunshine → Samsung Galaxy Tab S6 Lite). Other setups are untested; see Compatibility. Reports and PRs welcome (CONTRIBUTING).

How it works

  1. Phantom connector. Kernel parameters video=<CONNECTOR>:e drm.edid_firmware=<CONNECTOR>:edid/virtual-monitor.bin make the GPU driver treat a free connector as plugged in, with an EDID generated by lib/gen-edid.py (CVT reduced-blanking timings, no dependencies). The desktop sees an ordinary monitor.
  2. Solo mode. on disables the physical monitors and keeps only the virtual one; off restores the layout saved by on. Implemented for GNOME/mutter through gdctl.
  3. Sunshine. An app entry runs on when a stream starts and off when it ends. Sunshine captures via KMS.

Why the connector must be on a non-NVIDIA GPU: the proprietary NVIDIA driver ignores drm.edid_firmware. Use a free connector on an AMD/Intel GPU (an iGPU is fine); Sunshine then encodes with VAAPI on that GPU. If your only GPU is NVIDIA, use an HDMI/DP dummy plug instead (see Alternatives).

Requirements

  • A free (unplugged) connector on an amdgpu/i915/xe/nouveau GPU. Check with ./virtual-monitor.sh connectors.
  • python3 (3.8+), bash, sudo, a GRUB or manual bootloader entry, and an initrd generator (initramfs-tools, dracut or mkinitcpio).
  • For on/off/login: GNOME with gdctl (shipped with recent mutter; GNOME 48+ to my knowledge).
  • Sunshine with KMS capture working (its package normally grants the needed capability) and VAAPI encoding (mesa-va-drivers/libva for your GPU).

Quick start

git clone <this repo> && cd virtual-monitor
./virtual-monitor.sh connectors          # which connector will be used?
./virtual-monitor.sh profile tab-s6-lite # or: fullhd-60, ipad-pro-11, phone-landscape, or edit the config
sudo ./virtual-monitor.sh install        # EDID + kernel params + initrd config
sudo reboot
./virtual-monitor.sh status              # should list the virtual monitor and its mode
./virtual-monitor.sh sunshine            # adds the "Virtual Monitor" app + KMS/VAAPI settings (backs up originals)
# restart Sunshine (the command prints the exact line), then launch "Virtual Monitor" from Moonlight
./virtual-monitor.sh login               # GNOME/GDM only; see "Login screen"

install prints and runs everything it changes; sudo ./virtual-monitor.sh uninstall removes it all.

Configuration

config/virtual-monitor.conf is your active config (git-ignored). It is created from the documented template config/virtual-monitor.conf.example on first run, or replaced by a preset with ./virtual-monitor.sh profile <name>. Copy a file from profiles/ to add a device.

Variable Meaning
CONNECTOR Connector to fake, e.g. DP-4, or auto (first unplugged DP, then HDMI, then others, never NVIDIA/eDP)
WIDTH HEIGHT REFRESH Native resolution and refresh rate of the client device
EXTRA_MODE Optional second mode, 1920x1080@60; empty to disable
DIAGONAL_INCH Physical size hint for desktop scaling
NAME EDID product name (≤13 chars, A-Za-z0-9_-); used to find the monitor again
VENDOR 3-letter EDID maker id. GNOME shows "maker + inches" (e.g. VMN 11"): a registered code in /usr/share/hwdata/pnp.ids shows its company name (LNX = The Linux Foundation), an unregistered one shows the raw letters. No free-text label exists
SCALE Desktop scale applied to the virtual monitor in solo mode (1, 1.25, 1.5, 2)

Change a value, then sudo ./virtual-monitor.sh install and reboot — or sudo ./virtual-monitor.sh reload to re-apply the EDID live through debugfs (best effort, untested on most drivers). In Moonlight, request the same resolution so nothing is rescaled. If you change VENDOR, re-run login: GNOME keys saved layouts by maker.

Commands

Command Root Does
connectors no List DRM connectors, state, driver, and the resolved CONNECTOR
profile NAME no Activate a profile from profiles/
gen no Build the EDID into build/ and show how GNOME will label it
kargs no Print the kernel parameters install would add (for manual bootloaders)
install / uninstall yes Add / remove EDID, kernel parameters and initrd config
reload yes Apply a changed EDID without rebooting (best effort)
on / off no Solo mode / restore the saved layout (needs a graphical GNOME session, not a tty)
login sudo Persist a layout without the virtual monitor and copy it to GDM
status no Show configuration, kernel parameters and what the desktop sees
sunshine / sunshine-restore no Add the app + KMS/VAAPI settings / restore the originals

What install changes

Only new files where possible (all removed by uninstall):

  • /usr/lib/firmware/edid/virtual-monitor.bin
  • Debian/Ubuntu: /etc/default/grub.d/99-virtual-monitor.cfg. Other GRUB distros: a marked block appended to /etc/default/grub (backup at /etc/default/grub.vm-backup). No GRUB: it prints the parameters for you to add.
  • Initrd, because GPU drivers load before the root filesystem: /etc/initramfs-tools/hooks/virtual-monitor-edid, /etc/dracut.conf.d/99-virtual-monitor.conf and/or /etc/mkinitcpio.conf.d/virtual-monitor.conf, depending on what is installed, then the matching regeneration command. On Ubuntu with dracut, update-initramfs may call dracut: check with sudo lsinitrd /boot/initrd.img-$(uname -r) | grep edid.

Boot entries for recovery don't carry the parameters, so they are a clean fallback. If a boot goes wrong, edit the GRUB entry (e), delete video=...:e drm.edid_firmware=..., boot with F10, then run uninstall.

Login screen (GNOME/GDM)

With three monitors connected, GDM has no saved layout and may put the login prompt on the virtual one (physical monitors look grey), and GNOME may extend your desktop onto it. Run ./virtual-monitor.sh login once, with your physical monitors active (not while streaming). It persists a layout with the virtual monitor disabled in ~/.config/monitors.xml and copies it to GDM's monitors.xml. Undo: delete ~gdm/.config/monitors.xml (/var/lib/gdm3/.config/monitors.xml on Debian/Ubuntu). Other display managers are not handled; if you get a blank login, type your credentials blind (Enter, password, Enter).

Sunshine notes

  • sunshine sets capture = kms, encoder = vaapi and adapter_name to the render node of the GPU that owns the connector, and adds an app whose prep-cmd runs virtual-monitor.sh on / off. Originals are kept once as *.orig next to the config; sunshine-restore puts them back. Flatpak Sunshine config is detected too.
  • If Sunshine captures the wrong display, set output_name in sunshine.conf (check the log for detected displays).
  • VAAPI AV1 is not available on many iGPUs; the av1_vaapi error in the log is harmless, H.264/HEVC are used.
  • With no physical monitor active, the desktop may lock or suspend when idle: disable automatic suspend while streaming.

Compatibility

Component Tested Expected / notes
Distro Ubuntu (dracut + GRUB, kernel 7.0) Fedora/Arch paths are implemented but untested
Desktop GNOME 50, Wayland on/off/login are GNOME-only (see below)
GPU for the connector AMD (amdgpu) Intel (i915/xe) should work; NVIDIA proprietary cannot
Streaming Sunshine + Moonlight (Android tablet)
Bootloader GRUB systemd-boot/others: use kargs and add manually

Other desktops

The phantom connector works with any compositor that reads DRM connectors. Only the solo-mode switching is GNOME-specific (lib/gnome-layout.py). For KDE (kscreen-doctor), Sway/Hyprland (wlr-randr/hyprctl) or X11 (xrandr) you can still run ./virtual-monitor.sh install, then disable your physical outputs with your desktop's tool, or contribute a backend (see CONTRIBUTING).

Alternatives

  • Dummy plug (HDMI/DP, a few euros) in a free port: no kernel changes, works on NVIDIA (so you can use NVENC), but fixed resolutions.
  • NVIDIA on X11: CustomEDID in xorg.conf with a connected-monitor override, not covered here.
  • Software virtual displays (e.g. evdi, or Sunshine's Windows virtual display driver) are separate projects.

Tests

python3 -m unittest discover -s tests -v

License

MIT, see LICENSE.

About

Create a virtual monitor on Linux (Wayland-friendly, no dummy plug) so you can stream your desktop to a tablet or phone with Sunshine + Moonlight while your physical monitors stay off

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages