编辑
2025-03-03
工作知识
0
请注意,本文编写于 95 天前,最后修改于 95 天前,其中某些信息可能已经过时。

目录

一:多个Monitor
1.1 修改xorg
1.2 修改gtk3
1.3 使用xfce4
1.4 其他窗管
1.5 使用方法
1.6 相关补丁
1.7 相关博客
二:虚假的Xrandr
三:实验结果

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

一:多个Monitor

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

1.1 修改xorg

--- xorg-server-21.1.4/randr/rrmonitor.c.orig 2022-09-30 00:09:40.458561832 +0200 +++ xorg-server-21.1.4/randr/rrmonitor.c 2022-09-30 00: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不能直接使用,需要修改代码