当前位置:首页 > 文章中心

Linux一个屏幕分割为多个虚拟显示器

VirtualDisplay1年前 (2025-04-04)文章中心9

客户有一个4K的屏幕,放在两个显示器上面显示,现在需要将其对半分割为两个正常的显示器。这么说不好理解,转换需求可以这样理解,一个4K的显示分辨率的显示器,需要系统将其认为两个2K的显示器显示。通过前期调研,发现了两种方法实现。分别如下

一:多个Monitor

此方案是为X增加了两个monitor,然后根据窗管的识别,将其认定为两个单独的屏幕,此方法需要修改系统代码。主要如下

1.1 修改xorg

--- xorg-server-21.1.4/randr/rrmonitor.c.orig 2022-09-3000:09:40.458561832 +0200
+++ xorg-server-21.1.4/randr/rrmonitor.c 2022-09-3000:09:46.298529786 +0200
@@ -528,27 +528,6 @@
continue;
 }

/* For each output in 'info.outputs', each one is removed from all
- * pre-existing Monitors. If removing the output causes the list
- * of outputs for that Monitor to become empty, then that
- * Monitor will be deleted as if RRDeleteMonitor were called.
- */

-
for (eo = 0; eo < existing->numOutputs; eo++) {
for (o = 0; o < monitor->numOutputs; o++) {
if (monitor->outputs[o] == existing->outputs[eo]) {
- memmove(existing->outputs + eo, existing->outputs + eo + 1,
- (existing->numOutputs - (eo + 1)) * sizeof (RROutput));
- --existing->numOutputs;
- --eo;
break;
- }
- }
if (existing->numOutputs == 0) {
- (void) RRMonitorDelete(client, screen, existing->name);
break;
- }
- }
if (monitor->primary)
 existing->primary = FALSE;
 }

这里屏蔽了Xorg本身将存在的output的monitor删除的代码

1.2 修改gtk3

--- src/gtk/gdk/x11/gdkscreen-x11.c.orig 2022-09-30 16:24:00.181850959 +0200
+++ src/gtk/gdk/x11/gdkscreen-x11.c 2022-09-30 21:32:47.288912422 +0200
@@ -644,21 +644,14 @@
 #undef EDID_LENGTH
 }

- monitor = find_monitor_by_output (x11_display, output);
- if (monitor)
- monitor->remove = FALSE;
- else
- {
- monitor = g_object_new (GDK_TYPE_X11_MONITOR,
- "display", display,
- NULL);
- monitor->output = output;
- monitor->add = TRUE;
- g_ptr_array_add (x11_display->monitors, monitor);
- }
+ monitor = g_object_new (GDK_TYPE_X11_MONITOR,
+ "display", display,
+ NULL);
+ monitor->add = TRUE;
+ g_ptr_array_add (x11_display->monitors, monitor);

 gdk_monitor_get_geometry (GDK_MONITOR (monitor), &geometry);
- name = g_strndup (output_info->name, output_info->nameLen);
+ name = gdk_x11_get_xatom_name_for_display(display, rr_monitors[i].name);

 newgeo.x = rr_monitors[i].x / x11_screen->window_scale;
 newgeo.y = rr_monitors[i].y / x11_screen->window_scale;
@@ -687,7 +680,6 @@
 gdk_monitor_set_connector (GDK_MONITOR (monitor), name);
 gdk_monitor_set_manufacturer (GDK_MONITOR (monitor), manufacturer);
 g_free (manufacturer);
- g_free (name);

 if (rr_monitors[i].primary)
 primary_output = monitor->output;

这里找到monitor不是通过output寻找,而是直接增加monitor,这就避免了同一个显示器上不能输出两块屏幕的问题

1.3 使用xfce4

这边尝试了metacity和mutter,均没办法正常识别,但是基于gtk3的xfce是能够正常识别到两个屏幕的。

apt install -y xfce4

1.4 其他窗管

根据如上理解,我们后续要做的工作是,将例如kwin这类的窗管,在创建屏幕的过程中,不应该绑定一个output就一个monitor的方式。而是根据所有的monitor来都创建显示display

1.5 使用方法

xrandr --delmonitor VIRTUAL-LEFT
xrandr --delmonitor VIRTUAL-RIGHT
xrandr --setmonitor VIRTUAL-LEFT 960/0x1200/1+0+0 DSI-1
xrandr --setmonitor VIRTUAL-RIGHT 960/1x1200/1+960+0 none
xrandr --listmonitors
xrandr --fb 1921x1200
xrandr --fb 1920x1200

1.6 相关补丁

https://github.com/micw/xorg-split-screen-archlinux/blob/packages/gtk3/trunk/multiple_monitors_per_output.patch
https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/981/diffs?commit_id=21e569ef1e87e4dfcedbdac99216e7147e2b05ff

1.7 相关博客

https://gitlab.gnome.org/GNOME/gtk/-/issues/2013#note_1280968

二:虚假的Xrandr

通过修改xrandr的api,可以实现将x划分为多个显示接口,也就是xrandr显示的是自己设置的虚假的显示器

参考仓库地址

https://github.com/phillipberndt/fakexrandr

参考测试示例

https://gist.github.com/phillipberndt/7688785#file-fake-xinerama-L50

相应论坛信息

https://unix.stackexchange.com/questions/605755/use-xrandr-to-split-display-in-two-virtual-screens

DWM窗管支持全屏补丁

https://dwm.suckless.org/patches/fakefullscreen/dwm-fakefullscreen-20210714-138b405.diff

fakeXinerama仓库

https://github.com/Xpra-org/libfakeXinerama

xrdp补丁

https://github.com/asafge/xrdp_dualmon/blob/master/fakexinerama/Xinerama.c

参考论坛

https://gitlab.gnome.org/GNOME/gtk/-/issues/2013#note_1280968

测试例子

fakexrandr-manage.py gui

三:实验结果

通过上述两种办法试验,均可以在X86上正常切换屏幕。当前在麒麟系统上的kwin不能直接使用,需要额外修改代码,如有需要可私信。