Linux一个屏幕分割为多个虚拟显示器
客户有一个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不能直接使用,需要额外修改代码,如有需要可私信。
