Virtual Output Mapping to Ignore Hotplug Events

Hello,

I have a multi-monitor setup: four monitors in total, but one of them is in mirror mode. I would like to always create a fixed 1920×3×1080 surface, effectively ignoring hotplug events for certain connectors.

The motivation is that some applications (e.g., ags or hyprpanel) panic whenever the full surface disappears — for example, if all displays are cycled off, or if a single monitor is turned off and back on.

I was thinking whether it would be possible to map real monitors to virtual outputs, e.g.:

  • Create virtual outputs wl-1, wl-2, wl-3

  • Map my real displays (including the mirrored one) to these virtual outputs

  • Hotplug events could still occur, but the overall layout wouldn’t change, so applications relying on a stable surface wouldn’t crash

I’ve also tried kernel-level EDID overrides to “freeze” monitor connections, which works in the sense that the kernel always sees monitors as connected. However, udev still emits change events when monitors cycle on/off, and I couldn’t find a way to reliably disable those:

ACTION=="change", SUBSYSTEM=="drm", ENV{CONNECTOR}=="100", ENV{HOTPLUG}=="1", ENV{IGNORE_HOTPLUG}="1"

I might be missing a simpler solution, but I haven’t found one. I suspect this is somewhat of a unique case, and I’m wondering:

  • Is there a supported way to maintain a fixed virtual surface while still mapping real monitors?

  • Or could I somehow ignore hotplug for specific monitors?

Thanks in advance for any guidance or suggestions!

Hello, first of all, for what you’re saying to be possible, the DRM needs to be configured and matched according to the virtual outputs. A real monitor system that bypasses hotplug with modes like “freeze” can sometimes cause screen overlaps or screen output issues. However, the only way to do this is to configure the real screen output to the virtual screen path in non-freezing mode. There are some tools for this, but most may not accurately reflect the virtual screen based on the real screen output. If you want to try it:

kanshi – Profile manager for wlroots

Available in the Arch Linux repository:

sudo pacman -S kanshi

→ Initially, you create a

~/.config/kanshi/config

file and write the profiles.

wlr-randr – The wlroots equivalent of xrandr.

Official repository:

sudo pacman -S wlr-randr

→ You configure the settings with commands like wlr-randr --output eDP-1 --pos 0,0 --mode 1920x1080.
weston – Reference Wayland Compositor (for testing)

sudo pacman -S weston

→ Not normally for everyday use, but you can run

weston --backend=headless-backend.so

to test virtual output.
vkms – Virtual KMS driver (kernel module)

Not a package, but a kernel module. It’s included in the kernel.
To install:

sudo modprobe vkms

→ If it’s working, you’ll see it in the

sudo dmesg | grep vkms output.

If you want it to be permanent, write vkms to

/etc/modules-load.d/vkms.conf.

You can create a secure virtual screen output with these, but conflicts and problems may occur in some areas.

First of all, thank you so much for the incredibly detailed answer — and sorry for the late reply! I’ve been really busy lately.
I’ve read through everything carefully, but I still have a few questions about this setup.

From what I understand, you’re saying that if I were to enable the vkms kernel module (which creates virtual surfaces), I could then map those using kanshi or wlr-randr, correct?

I tried enabling vkms, but it seems to only create a single surface:

sudo dmesg | grep -i vkms
[    4.828653] [drm] Initialized vkms 1.0.0 for vkms on minor 0
[    4.829543] platform vkms: [drm] fb1: vkmsdrmfb frame buffer device
ls -l /sys/class/drm/ | grep vkms
lrwxrwxrwx    - root  4 Oct 12:30 card0 -> ../../devices/platform/vkms/drm/card0
lrwxrwxrwx    - root  4 Oct 12:30 card0-Virtual-1 -> ../../devices/platform/vkms/drm/card0/card0-Virtual-1
lrwxrwxrwx    - root  4 Oct 12:30 card0-Writeback-1 -> ../../devices/platform/vkms/drm/card0/

So these are my new questions:

  • Should I be attempting to create three virtual surfaces to match my needs?
  • Shouldn’t I configure the virtual surfaces directly in Hyprland’s configuration, instead of relying on, let’s say, kanshi or wlr-roots?
    (That’s what I was originally trying to do with hyprctl output.)

Here’s what my current display configuration in hypr looks like:

monitor=HDMI-A-2,preferred,0x0,1.0
monitor=DVI-D-1,highrr,1920x0,1.0
monitor=DP-2,preferred,3840x0,1.0
monitor=HDMI-A-1,preferred,3840x0,1.0,mirror,DP-2

No problem, and yes, even though a virtual interface only creates a single surface, it’s possible to duplicate it, i.e., create multiple instances.

sudo modprobe -r vkms
sudo modprobe vkms enable_cursor=1 num_outputs=3

or

in the /etc/modprobe.d/vkms.conf

file:

options vkms num_outputs=3

This creates three virtual outputs: Virtual-1, Virtual-2, Virtual-3.

Then check again:

ls /sys/class/drm | grep Virtual

You should see these three connections.
This method provides three separate virtual monitors at the kernel level. However, it causes some problems. The display surfaces you’re trying to create will meet your needs in certain respects. You can also use the kanshi or wlroot-based wlr-randr API, but it’s more used for dynamic profile management. Yes, I may need to configure the display output for Hyprland, so adjusting these settings below will suffice:

The lines you’re currently using are:

monitor=HDMI-A-2,preferred,0x0,1.0
monitor=DVI-D-1,highrr,1920x0,1.0
monitor=DP-2,preferred,3840x0,1.0

You can expand these to virtual displays as follows:

monitor=Virtual-1,1920x1080@60,0x0,1.0
monitor=Virtual-2,1920x1080@60,1920x0,1.0
monitor=Virtual-3,1920x1080@60,3840x0,1.0

This allows Hyprland to manage windows/spaces across three virtual displays.

Mh, seems like I am out of luck:

sudo dmesg | grep -i vkms
[    4.777483] vkms: unknown parameter 'num_outputs' ignored
[    4.777766] [drm] Initialized vkms 1.0.0 for vkms on minor 0
[    4.778669] platform vkms: [drm] fb1: vkmsdrmfb frame buffer device

Seems like num_outputs is not recognised as a parameter for the vkms kernel module, I am running on the stable linux kernel afterall, maybe this is a newer feature?

modinfo vkms
filename:       /lib/modules/6.16.10-arch1-1/kernel/drivers/gpu/drm/vkms/vkms.ko.zst
license:        GPL
description:    Virtual Kernel Mode Setting
author:         Rodrigo Siqueira <[email protected]>
author:         Haneen Mohammed <[email protected]>
srcversion:     FC492964326927AA8192BBB
depends:
intree:         Y
name:           vkms
retpoline:      Y
vermagic:       6.16.10-arch1-1 SMP preempt mod_unload
sig_id:         PKCS#7
signer:         Build time autogenerated kernel key
sig_key:        5D:1B:93:5A:A1:C3:E9:FB:50:67:C5:D4:E3:10:22:4D:A9:5C:C6:0E
sig_hashalgo:   sha512
signature:      30:65:02:31:00:FF:BC:E5:2A:D5:28:EF:C8:E9:AC:8A:AA:14:A1:2D:
                52:B9:99:25:7F:4C:21:EA:66:31:7B:7A:28:06:C6:3E:AA:A5:86:9E:
                01:68:E4:3A:8F:90:18:F8:B5:1F:34:9B:1A:02:30:18:9C:CF:0E:8D:
                DB:43:25:34:99:00:26:CA:58:CE:F6:B6:C5:73:C7:F4:2D:0A:2F:A0:
                61:9E:2D:7E:86:8D:6E:57:C2:94:46:76:A5:FD:46:0B:25:A1:09:66:
                A0:D5:E7
parm:           enable_cursor:Enable/Disable cursor support (bool)
parm:           enable_writeback:Enable/Disable writeback connector support (bool)
parm:           enable_overlay:Enable/Disable overlay support (bool)

I’ve tried looking up whether or not that specific parameter was added in a later pre-release kernel, I couldn’t to be honest

I then saw and read this article which talks about vkms and ConfigFS, but I couldn’t see the /sys/kernel/config/vkms folder, again I suspect the stable linux kernel doesn’t support this functionality yet?

zgrep CONFIG_DRM_VKMS /proc/config.gz 
CONFIG_DRM_VKMS=m 

zgrep CONFIG_CONFIGFS /proc/config.gz 
CONFIG_CONFIGFS_FS=y

mount | grep configfs 
configfs on /sys/kernel/config type configfs (rw,nosuid,nodev,noexec,relatime) 

ls /sys/kernel/config/
 crash_dm_crypt_keys  rdma_cm

Yes, it is possible because this support is still not provided for Mainline Kernel. You can create multiple windows by activating Configfs using more innovative kernels or non -mainline kernels than it. But the kernel should be at least 6.10+, and the kernel modules or num_outputs I have written here have now been removed. Configfs came instead. But if you still want to use it, there are official documents that correct it:

Official vkms module documentary

Configfs Mount

sudo mkdir -p /sys/kernel/config
sudo mount -t configfs none /sys/kernel/config

Create a new device (example “dev_1”) under VKMS SubSystem

cd /sys/kernel/config/vkms
sudo mkdir DEV_1
cd DEV_1

Create sub -objects (Connectors, CRTCS, Encoders, Planes)
Thanks to the patchs, there are sub -indexes such as Connectors/, CRTCS/, Encoders/, Planes/. For example:

sudo mkdir connectors/CON_1
sudo mkdir crtcs/CRTC_1
sudo mkdir encoders/ENC_1
sudo mkdir planes/PLA_1

Symbolic Links (Link)
Connector and Encoder, Encoder and CRTC, Plane and CRTC should be made with a symbolic link:

for example

ln -s ../crtcs/CRTC_1 encoders/ENC_1/possible_crtcs/
ln -s ../encoders/ENC_1 connectors/CON_1/possible_encoders/
ln -s ../crtcs/CRTC_1 planes/PLA_1/possible_crtcs/

Activate Connector / Enable Meat

echo 1 | sudo tee connectors/CON_1/enabled

Enable Device
Finally:

echo 1 | sudo tee enabled

With this step, the configuration is loaded on the kernel and you can see with tools like DRM_info

modetest -c -M vkms.

Yes, so you need to change your kernel version before using it. Feel free to write if there is any problem, but you are responsible for the things done here.