关于 Xorg 下 Wayland 快捷操作的简单平替

1.问题提出

在学习和使用 Linux 操作系统的过程中,我一直频繁遇到与显示驱动相关的问题。从最开始安装双系统、驱动副屏,到使用 NVIDIA GPU 加速 YOLO 推理,再到研究桌面环境客制化时遇见 X11、Xorg、Wayland、Hyprland 等像蟑螂一样多的专有名词,这些概念就像一堵堵绕不开的墙。

庞大的技术栈一度让我望而却步。不过在日积月累的折腾中,我确实逐渐梳理清了从应用计算、图形 API、窗口系统、桌面合成到显示器扫描输出的大致链路,也终于摸到了一点 Linux 桌面环境的脉络。

这里先校正几个很容易混在一起的概念:

  • X11Wayland 是两套窗口系统协议,规定客户端、显示服务器或合成器之间如何交换窗口、输入与显示相关的信息;
  • Xorg 是 X11 协议最常见的一套显示服务器实现;
  • Mutter、KWin、Hyprland 等才是常见的 Wayland 合成器,其中 Mutter 同时也是 GNOME 在 Xorg 会话下使用的窗口管理器和合成器;
  • Wayland 不是一个名为“Wayland 合成器”的统一程序,而是一套把更多显示控制权交给合成器的协议和架构。

传统的 X11/Xorg 技术经过几十年发展,生态成熟、兼容性广,在某些硬件和旧应用场景中仍然非常可靠。但它的架构也背负着大量历史包袱,在混合 DPI、多显示器独立缩放、输入隔离和现代桌面动画等方面不如 Wayland 自然。Wayland 让合成器统一掌握窗口合成、输入路由和最终显示,在高 DPI、触摸板连续手势和安全隔离方面有明显优势。Ubuntu 24.04 的 GNOME 桌面也默认使用 Wayland 会话。

但在我的实际设备上,Wayland 与 NVIDIA 混合显卡的配合没有想象中理想。我的电脑是 AMD 核显加 NVIDIA 独显的笔记本,并连接了一台 2560×1440、200 Hz 的外接显示器。在 Wayland 会话中,显示设置可以选择 200 Hz,但 UFO Test 实际经常只能运行到 140 FPS 左右甚至更低;切换到 Xorg 后,同一外屏则能更稳定地跑满 200 Hz,游戏帧率也有明显提升。

这里必须强调:显示设置显示 200 Hz,只代表输出模式配置为 200 Hz,不等于桌面合成器真的每秒提交了 200 个不同的新帧。 在混合显卡笔记本上,还可能存在“AMD 核显负责合成、NVIDIA 接口负责外屏扫描输出”的跨 GPU 传输链路。这个拓扑是潜在瓶颈,但仅凭模式标签不能确认根因。本文记录的是我这台具体设备上的 A/B 使用结果,不代表所有 NVIDIA + Wayland 设备都会出现同样现象。

最开始使用 Linux 时,我并没有注意到系统实际上主要使用 AMD 核显完成运算和渲染,因此把许多软件卡顿误以为是 Xorg 本身的问题。为了追求更顺滑的桌面体验,我迁移到了 Wayland。后来我才发现外屏跑不满的问题,但当时 Wayland 对我的帮助更大:它既能让外屏正常工作,也能给笔记本内屏设置独立缩放;而在我的 Xorg 双屏配置中,两块屏幕难以获得同样理想的独立缩放体验,小尺寸高分辨率内屏上的文字非常难读。权衡之下,我在 Wayland 环境里使用了很长时间。

后来,为了解决初始登录界面没有显示在 2K 大屏上的问题,我重新切回 Xorg,意外发现外屏刷新表现好了很多,能够稳定跑满 200 Hz。这时我才意识到,早期卡顿不应全部归咎于 Xorg,GPU 驱动、PRIME 渲染路径和实际输出拓扑同样关键。在陆续修复登录界面布局、Fcitx5 输入法等问题之后,我最终把主要开发环境迁回 Xorg,也获得了更理想的游戏帧率。

然而新的问题随之出现:Wayland 下习惯使用的桌面操作不见了。例如,按住 Super 后滚动鼠标滚轮可以切换工作区,触摸板三指滑动还可以让工作区动画连续跟手;回到 Xorg 后,这些操作无法在普通应用窗口上全局使用。

三指连续手势依赖合成器直接掌握 libinput 手势进度,Xorg 下可以通过 Touchégg、Fusuma 或 X11 Gestures 扩展做近似替代,但实现复杂度较高。相比之下,我最常用的 Super + 滚轮 是离散操作,只要在 X11 层捕获组合输入并通知 GNOME 切换工作区,就有机会做到足够自然。

接下来记录的,就是这个功能从简单绑定到低延迟常驻程序的完整解决过程。

2.目标与验收条件

最终方案需要满足:

  1. GNOME Xorg 会话的任意窗口上均可使用 Super + 滚轮
  2. 不按 Super 时,普通滚轮不受影响;
  3. 第一个和最后一个工作区必须停在边界,不能首尾循环;
  4. 松开 Super 时不能误触 Overview;
  5. 单独按一下 Super 打开 Overview 的功能仍然保留;
  6. 输入到切换之间不能有明显延迟;
  7. 登录后自动启动,并且能够完整回滚。

开始前确认当前会话:

1
echo "$XDG_SESSION_TYPE"

输出应为 x11

3.第一版:xbindkeys + xdotool

最直接的思路是使用 xbindkeys 全局捕获鼠标组合,再让 xdotool 切换工作区:

1
2
3
4
5
"xdotool set_desktop --relative -- -1"
Mod4 + b:4

"xdotool set_desktop --relative 1"
Mod4 + b:5

其中 Mod4SuperButton4/5 是滚轮向上/向下。这版很快暴露出三个问题:

  • xdotool --relative 在边界会首尾回环;
  • GNOME 不知道滚轮已经用过 Super,松开时仍可能打开 Overview;
  • 加上边界查询和模拟组合键后,每格滚轮都要启动 shell 和多个 xdotool 进程。

最初尝试注入一个没有实际绑定的 F24,让 GNOME 把本次 Super 判断为组合键,但实际仍会偶发误触。边界脚本还需要依次查询当前工作区、工作区总数再发送请求,实测额外耗时约 30~60 ms,在 200 Hz 桌面上已经能感觉到黏滞。

因此最终没有继续给脚本打补丁,而是改成常驻的原生 X11 程序。

4.最终方案:常驻 X11 处理器

最终程序使用 C 编写,启动后保持一条 X11 连接:

1
2
3
4
5
6
7
8
9
10
11
12
13
Super + 滚轮

XGrabButton 全局捕获 Button4 / Button5

读取 _NET_CURRENT_DESKTOP 与 _NET_NUMBER_OF_DESKTOPS
├─ 已到首尾:停止,不回环
└─ 未到边界:发送 _NET_CURRENT_DESKTOP ClientMessage

临时清空 org.gnome.mutter overlay-key

XInput2 捕获下一次 Super RawKeyRelease

等待 75 ms 后恢复 overlay-key=Super_L

4.1 低延迟切换

旧方案每滚动一格都要创建若干进程并反复连接 X Server。新程序始终保持同一条连接,收到滚轮事件后直接读取根窗口属性并发送 EWMH ClientMessage,因此切换更加及时。

4.2 首尾硬边界

程序读取 _NET_CURRENT_DESKTOP_NET_NUMBER_OF_DESKTOPS 后判断:

1
2
3
4
if ((step < 0 && current == 0) ||
(step > 0 && current + 1 >= count)) {
return;
}

所以第一个工作区继续向前滚、最后一个工作区继续向后滚,都不会跳到另一端。

4.3 只屏蔽下一次 Super 抬起

永久清空 overlay-key 虽然能消除误触,却会让单独按 Super 打开 Overview 的功能彻底消失。最终程序采用临时门控:

  1. 检测到 Super + 滚轮
  2. 保存并临时清空 org.gnome.mutter overlay-key
  3. 使用 XInput2 监听全局 XI_RawKeyRelease
  4. 检测到对应的 Super_L 抬起后等待 75 ms;
  5. 恢复原来的 overlay-key

实际测试状态为:

1
2
3
滚轮前:   desktop=0  overlay='Super_L'
按住滚动: desktop=1 overlay=''
抬起之后: desktop=1 overlay='Super_L'

滚轮后的这一次释放不会打开 Overview,而下一次单独按 Super 时,Overview 仍然可用。

5.编译与安装

Ubuntu 24.04 所需开发依赖:

1
sudo apt install build-essential libx11-dev libxi-dev libglib2.0-dev

源码与二进制位置:

1
2
~/.local/src/super-scroll-workspace.c
~/.local/bin/super-scroll-workspace

推荐使用 pkg-config 编译:

1
2
3
4
gcc -O2 -Wall -Wextra -Wpedantic \
~/.local/src/super-scroll-workspace.c \
-o ~/.local/bin/super-scroll-workspace \
$(pkg-config --cflags --libs x11 xi gio-2.0)

然后启动:

1
~/.local/bin/super-scroll-workspace

若组合键已经被其他程序占用,程序会退出并报告:

1
super-scroll-workspace: Super+wheel is already grabbed

这样可以避免多个处理器同时运行、一次滚轮跳过多个工作区。

6.登录自启动与旧方案停用

新建 ~/.config/autostart/super-scroll-workspace.desktop

1
2
3
4
5
6
7
8
[Desktop Entry]
Type=Application
Name=Super Scroll Workspace
Comment=Switch GNOME Xorg workspaces with Super and the mouse wheel
Exec=/home/harekasa/.local/bin/super-scroll-workspace
OnlyShowIn=GNOME;
X-GNOME-Autostart-enabled=true
NoDisplay=true

系统安装 xbindkeys 后提供 /etc/xdg/autostart/xbindkeys.desktop。最终方案已不再需要它,因此添加用户级同名覆盖文件 ~/.config/autostart/xbindkeys.desktop

1
2
[Desktop Entry]
Hidden=true

这样不会卸载软件或修改系统文件,只会在当前用户登录时屏蔽旧自启动。原来的 ~/.xbindkeysrc 也已停用,仅保留说明。

7.最终修改清单

路径 作用
~/.local/src/super-scroll-workspace.c 常驻 X11 处理器源码
~/.local/bin/super-scroll-workspace 编译后的可执行文件
~/.config/autostart/super-scroll-workspace.desktop GNOME 登录自启动
~/.config/autostart/xbindkeys.desktop 覆盖并停用系统级 xbindkeys 自启动
~/.xbindkeysrc 旧方案已停用,仅保留说明
~/display-backups/20260806-xbindkeys-super-scroll/ 各阶段配置、源码备份和回滚说明

最终效果:

  • Super + 滚轮向上/向下 切换相邻工作区;
  • 首尾停止,不循环;
  • 普通滚轮不受影响;
  • 切换低延迟;
  • 滚轮后的 Super Release 不触发 Overview;
  • 单独按 Super 仍可打开 Overview;
  • 注销并重新登录后自动生效。

8.风险、恢复与回滚

程序会在 Super + 滚轮Super 抬起之间短暂修改 GNOME 的 overlay-key。如果程序恰好在这个时间窗口被 SIGKILL 强制终止,设置可能来不及恢复。此时执行:

1
gsettings set org.gnome.mutter overlay-key 'Super_L'

即可恢复单独按 Super 打开 Overview。

停止常驻程序:

1
pkill -f '^/home/harekasa/.local/bin/super-scroll-workspace$'

随后删除或重命名 ~/.config/autostart/super-scroll-workspace.desktop。如果还要恢复旧 xbindkeys 自启动,则移除用户级覆盖文件 ~/.config/autostart/xbindkeys.desktop

整个方案没有修改 /etc/X11/、GDM 显示布局或 NVIDIA 驱动配置,也没有改变显示器模式,只在当前用户的 Xorg 会话中增加了一层输入处理。

9.总结

从 Wayland 回到 Xorg 并不是简单的“技术倒退”,而是在具体硬件、驱动和使用需求之间做取舍。Wayland 的多屏缩放、输入模型和连续手势更现代;Xorg 在我的混合显卡和 200 Hz 外屏组合上,却提供了更符合当前需求的刷新率与游戏表现。

需要避免的是把某个协议当成所有问题的唯一答案。显示模式、实际合成帧率、游戏 FPS、撕裂、输入延迟和缩放体验是不同问题,也可能分别由驱动、GPU 拓扑、合成器与应用渲染路径决定。只有拆开测试,才能知道自己究竟在优化什么。

这次最终没有在 Xorg 上完整复制 Wayland 的全部手势系统,而是只补回最常用的 Super + 滚轮。一个范围明确、能够备份和回滚的小程序,让我保留了 Xorg 下稳定的 200 Hz 使用体验,也找回了已经形成肌肉记忆的工作区切换方式。