多用户 Wayland 现状
摘要
对 Linux Wayland 合成器和库中多座位(多个鼠标/键盘)支持的深入调查,并提供了已发布的工具和补丁以改善多用户计算。
<p><a href="https://lobste.rs/s/anvtjk/state_multi_player_wayland">评论</a></p>
查看缓存全文
缓存时间: 2026/07/28 22:30
# 多人 Wayland 的状态
来源:https://blinry.org/multi-seat-wayland/
我一直对将多个鼠标连接到一台电脑、然后在同一个桌面环境中拥有多个鼠标光标的想法着迷!我花了三周时间调查这在 Linux 和 Wayland 上目前的支持情况。让我告诉你我的发现吧!结果出乎意料地酷。在这篇博文中,我将发布一些小程序和打过补丁的库,用于添加或改进“多席位”支持。但这个谜题中的许多部分仍然不完整或缺失。我在博文中用 📝 表情标记了一些“开放的项目想法”。通过发布这篇文章,我希望找到有兴趣一起探索和拓展这个领域的人。如果你就是那个人,请联系我!
(https://blinry.org/about/)
(是的,我的默认鼠标光标是一只乌龟。(https://blinry.org/cursor-workshop/))
## 为什么你*会*想要这个?
我承认这是一种有些小众的电脑使用方式。但请听我说完:
- 结对编程很方便:一个人可以修复一个窗口中的 bug,另一个人同时浏览文档!
- 玩游戏很有趣!使用多个手柄是常态,为什么不能有多个鼠标呢?
- 这种多人计算风格能非常自然地促进应用内的协作,无需复杂的同步算法。
## 总结
以下是在各种软件中“多席位”目前支持情况的概览:
| 软件 | 支持程度 | 备注 |
|------|----------|------|
| 核心 `wayland` 协议 | 5 ★★★★★ | 深度集成! |
| Weston | 3 ★★★☆☆ | 没有简单的方法动态重新配置席位。 |
| sway | 4 ★★★★☆ | 无法从席位分离设备(#3491)。(https://github.com/swaywm/sway/issues/3491) |
| niri | 1 ★☆☆☆☆ | 尚无支持(#3159)。(https://github.com/niri-wm/niri/discussions/3159) |
| River | 4 ★★★★☆ | 不支持 `ext-transient-seat-v1`(#1497)。(https://codeberg.org/river/river/issues/1497) |
| GTK | 4 ★★★★☆ | 新席位在应用运行时不会注册。 |
| SDL | 3 ★★★☆☆ | 绝对鼠标模式中没有席位信息(#16027)(https://github.com/libsdl-org/SDL/issues/16027),席位信息只能通过设备名称获取。 |
| wayvnc | 5 ★★★★★ | 支持 `ext-transient-seat-v1`(https://isaacfreund.com/docs/wayland/ext-transient-seat-v1/)。 |
## 目录
- 术语(https://blinry.org/multi-seat-wayland/#terminology)
- 逻辑席位(https://blinry.org/multi-seat-wayland/#logical-seats)
- 物理席位(https://blinry.org/multi-seat-wayland/#physical-seat)
- `wayland` 协议(https://blinry.org/multi-seat-wayland/#the-wayland-protocol)
- Wayland 合成器(https://blinry.org/multi-seat-wayland/#wayland-compositors)
- Weston(https://blinry.org/multi-seat-wayland/#weston)
- sway(https://blinry.org/multi-seat-wayland/#sway)
- River(https://blinry.org/multi-seat-wayland/#river)
- niri(https://blinry.org/multi-seat-wayland/#niri)
- 图形库(https://blinry.org/multi-seat-wayland/#graphics-libraries)
- GTK(https://blinry.org/multi-seat-wayland/#gtk)
- SDL(https://blinry.org/multi-seat-wayland/#sdl)
- LÖVE(https://blinry.org/multi-seat-wayland/#lve)
- 应用(https://blinry.org/multi-seat-wayland/#applications)
- 多席位 GTK 文本控件(https://blinry.org/multi-seat-wayland/#multi-seat-gtk-text-widget)
- LÖVE 物理游乐场(https://blinry.org/multi-seat-wayland/#lve-physics-playground)
- 终端模拟器(https://blinry.org/multi-seat-wayland/#terminal-emulators)
- 浏览器(https://blinry.org/multi-seat-wayland/#browsers)
- VNC 设置(https://blinry.org/multi-seat-wayland/#vnc-setup)
## 术语
我将在本文中大量使用“席位”一词。它描述的是分组在一起的输入设备,供同一个人使用。例如,你可以让两个人坐在一台电脑前,每人拥有自己的鼠标和键盘。在这种设置下,会有两个“席位”,每个席位有一个鼠标和一个键盘:
- seat0
- 笔记本电脑触摸板
- 笔记本电脑键盘
- seat1
- 外接鼠标
- 外接键盘
不过要注意——人们对这个词有两种不同的用法:
### 逻辑席位
我感兴趣的那种席位有时被称为(https://wayland.freedesktop.org/libinput/doc/latest/seats.html#seats)**“逻辑席位”**。这是指多个人坐在一台电脑前,每人有自己的鼠标/键盘,但控制的是*同一个桌面环境*。也就是说,桌面上会显示多个鼠标光标!这些人可以互相“交接”窗口,一起在白板应用中绘图,或者直接在同一个软件中协作。(当然,目前这些事还不可能全部实现,但概念上可行!)我想可能有一个更好的术语来描述这个概念——在这篇文章的标题中,我选择了“多人”,以强调这主要是关于多个*人*在协作。我也见过“多光标”或“多指针”(它们缺少了键盘组件)。
### 物理席位
“席位”一词也可以指另一种意思,即**“物理席位”**:这指的是多个用户各自拥有独立的一套设备:一个屏幕、一个键盘、一个鼠标。他们之间没有太多交互,但输入输出设备都连接在同一台电脑上。这就是维基百科上“多席位配置”(https://en.wikipedia.org/wiki/Multiseat_configuration)文章所指的内容。但*这*不是本文要讨论的。如果你对 Linux 下这方面支持的情况了解更多,我鼓励你自己写一篇博文!
## 概览
我只调查了 Linux 上的情况,具体来说是 Wayland(一个现代图形显示系统)。X11(X Window 系统)有自己的多席位扩展,叫做“多指针 X”(Multi-Pointer X,简称 MPX)(https://en.wikipedia.org/wiki/Multi-Pointer_X),几年前我也用它玩得(https://github.com/blinry/mousetoy)很开心。但现在我在日常使用电脑时已经转移到了 Wayland 合成器。结果发现 Wayland 对多席位有着*极好*的内置支持!我将讨论如何搭建多用户系统,涉及哪些 Wayland 协议,以及最终当前的应用支持情况如何。
棘手的是,多席位支持必须在每一层都存在:
1. Wayland 合成器(通常包含窗口管理器)。
2. `wayland` 协议。
3. 图形应用所使用的 GUI 库。
4. 图形程序本身。
另外,如果你希望允许他人远程协作,那么用于让他们连接的程序(比如 VNC 服务器)也需要多席位支持。我稍后将展示如何设置!
因此,我们将逐一审视所有这些层,从 `wayland` 协议本身开始。
## `wayland` 协议
这次深入调查是我第一次真正深入了解 Wayland 到底是什么!它的核心是一组协议。我非常高兴地发现,核心的 `wayland`(https://isaacfreund.com/docs/wayland/wayland/)协议(图形程序和 Wayland 合成器之间通信所用)已经深度集成了多席位支持:每个输入事件都连接到一个 `wl_pointer` 或 `wl_keyboard`,而这些又连接到一个 `wl_seat`(https://isaacfreund.com/docs/wayland/wayland/#wl_seat)。然后你可以利用这些席位信息来“分组”输入事件!还有当席位出现/消失时触发的事件。
## Wayland 合成器
但是,合成器(管理窗口并绘制其内容的程序)在多大程度上利用这些概念与客户端应用通信呢?我尝试了几个:
### Weston
Weston(https://gitlab.freedesktop.org/wayland/weston)曾经是 Wayland 合成器的参考实现,由 freedesktop.org 的 Wayland 团队维护。它是我第一个意识到具有真正多席位支持的合成器!你可以拥有多个鼠标光标,每个光标都可以独立移动窗口,互不干扰!Weston 还实现了按席位的窗口焦点,这意味着如果一个席位有鼠标和键盘,鼠标可以聚焦某个窗口,然后键盘就会向该窗口发送按键——不同席位可以同时聚焦不同的窗口。
我在试验中发现了一些有趣的边界情况:
- 一旦一个光标开始调整窗口大小或移动窗口,另一个光标就无法执行这些操作。
- 一个光标可以打开菜单,另一个光标可以使用它——这个功能相当好用!
- 当一个光标关闭窗口,而另一个光标正在拖动它时,第二个光标会*消失*!:D
这些都是非常棘手(也很有趣)的 UI 问题!很多时候,我并不清楚正确的行为应该是什么!
#### 如何在 Weston 上设置多个席位
在 Weston 上设置席位比较繁琐。后来的合成器会让这变得容易得多!要将输入设备分离到多个席位,你需要使用 udev 规则来设置 `ENV{WL_SEAT}` 属性(https://wayland.freedesktop.org/libinput/doc/latest/device-configuration-via-udev.html)。给鼠标和键盘分配相同的席位名称,它们就能一起工作!默认席位是“default”。详细步骤:
1. 使用 `sudo libinput list-devices` 查找输入设备的设备文件(如 “`/dev/input/event12`”)。
2. 使用 `udevadm info -a /dev/input/event12` 查找带有易于识别的 `ATTRS{name}` 的父设备。
3. 创建文件 `/run/udev/rules.d/00-multiseat.rules`,内容如下:
``
ATTRS{name}=="你的鼠标名称" ENV{WL_SEAT}="second"
``
(注意,测试时用了两个 `=` 字符,但赋值时只需一个 `=`。)
4. 运行 `sudo udevadm trigger` 来应用新规则。
现在再次检查 `sudo libinput list-devices`。设备的“Seat”应该显示为 “seat0, second”!
### sway
sway(https://github.com/swaywm/sway)是 i3 的 Wayland 重新实现。对多个席位的支持在 `wlroots` 0.1 版本(2017 年发布)中就已添加(https://github.com/swaywm/wlroots/pull/352),随后在 sway 1.0 中添加(https://github.com/swaywm/sway/pull/1505)。sway 也拥有出色的多席位支持,包括按席位的窗口焦点!
#### 如何在 sway 上设置多个席位
你可以使用 `swaymsg -t get_inputs` 列出所有输入,使用 `swaymsg -t get_seats` 列出所有席位。然后,使用 `swaymsg seat assign <seat> <input>` 将输入设备分配给特定席位。如果该席位不存在,它会自动创建。目前,这种席位管理有一个有趣的特点:如果你将一个设备分配给一个席位,然后又分配给另一个席位,它不会替换原来的分配,而是*添加*。这意味着你用一个鼠标会控制两个鼠标指针!
📝 有一个开放的 issue(https://github.com/swaywm/sway/issues/3491)在讨论解决方案,我自己打了个补丁来解决这个问题(https://github.com/blinry/sway/tree/one-seat-per-device)。如果你运行了这个补丁,可以使用我的 `multi-seat-configurator`(https://codeberg.org/blinry/multi-seat-configurator)工具,通过 TUI 快速将输入设备分配给席位!
我还没有尝试过其他的 wlroots 合成器,如果你有使用经验,请告诉我!
### River
River 是一个非整体式的 Wayland 合成器,它提供合成器部分,并允许你编写自己的窗口管理器!我认为这是一个很棒的想法。它的多席位能力相当不错,几乎内建于所有功能中。River 还有自己的 `river-input-management-v1`(https://isaacfreund.com/docs/wayland/river-input-management-v1/)协议,可以在新输入设备可用时通知你,创建/销毁席位,以及将设备分配给席位。我写了一个小的 Rust 工具(`river-multi-seat-configurator`(https://codeberg.org/blinry/river-multi-seat-configurator)),使用这个协议按需将设备分配给席位。我只尝试了 River 的示例窗口管理器 tinyrwm(https://codeberg.org/river/tinyrwm),它甚至在一定程度上支持按席位的窗口焦点!
📝 为 River 编写一个具有一流多席位支持的窗口管理器会是一个有趣的项目!窗口焦点可以用多种颜色高亮显示;与窗口交互的边界情况可以以合理的方式处理;还可以有按席位的剪贴板?
### niri
niri 是我目前日常使用的 Wayland 合成器。<3 去年,有人请求了多席位支持(https://github.com/niri-wm/niri/discussions/3159),但应用支持不足被引为不添加的理由。我认为加上会很好。几个月前,我 hack 了一些多席位支持,详见我的 fork 的 `multi-seat` 分支(https://github.com/blinry/niri/tree/multi-seat)。
📝 目前,我的“hack 版本”只支持两个席位,并且代码中有一些硬编码的设备名称。下一步是添加一个合适的“席位管理器”。
## 图形库
### GTK
令我惊讶的是,GUI 工具包 GTK(https://gtk.org/)在概念上似乎对多席位有很好的支持!事件有一个 `get_seat` 方法(https://docs.gtk.org/gdk4/method.Event.get_seat.html),可以直接告诉你事件属于哪个 `Seat`(https://docs.gtk.org/gdk4/class.Seat.html)。似乎没有办法获取席位的名称,但你可以比较它们来判断是否不同,这应该足够了。我只在 GTK4 上测试过,但这些类型和方法在 GTK3 中也存在。大多数 GTK 控件并不真正尊重多席位输入,它们通常表现得就像所有输入都来自同一个设备一样。但我写了一个实验性的 Rust MultiSeatText 控件,详见下文(https://blinry.org/multi-seat-wayland/#multi-seat-gtk-widget)!
📝 我注意到一个 bug:虽然 GTK 会在席位被移除时通知你,但它似乎没有注意到席位被添加时的变化。因此,要让某个席位中的设备发挥作用,它们必须在程序启动之前就连接好。这个 bug 应该被复现、报告并修复!
### SDL
Simple DirectMedia Layer 从 v3.3.4(2025 年发布)开始支持*一些*多席位特性(https://github.com/libsdl-org/SDL/pull/12626)。尝试时,请务必**将 `SDL_VIDEODRIVER` 环境变量设置为 `wayland`**。在我的测试中,如果不设置,SDL 似乎会回退到 XWayland。诸如 `SDL_MouseButtonEvent`(https://wiki.libsdl.org/SDL3/SDL_MouseButtonEvent)或 `SDL_KeyboardEvent`(https://wiki.libsdl.org/SDL3/SDL_KeyboardEvent)等事件都有一个 `which` 字段,如果事件来自不同席位,该字段会不同。你也可以通过 `SDL_MouseDeviceEvent`(https://wiki.libsdl.org/SDL3/SDL_MouseDeviceEvent)和 `SDL_KeyboardDeviceEvent`(https://wiki.libsdl.org/SDL3/SDL_KeyboardDeviceEvent)来获知设备何时插拔,这些事件同样带有 `which` 字段。设备 ID 是任意唯一的 ID,拔出再插入时不会重复使用(https://wiki.libsdl.org/SDL3/SDL_MouseID)。要了解设备属于哪个席位,可以使用 `SDL_GetMouseNameForID`(https://wiki.libsdl.org/SDL3/SDL_GetMouseNameForID)和 `SDL_GetKeyboardNameForID`(https://wiki.libsdl.org/SDL3/SDL_GetKeyboardNameForID)函数。它们会报告类似 “Virtual core pointer (default)” 或 “Virtual core keyboard (second)” 这样的名称,其中括号内的部分就是席位名称。
📝 有一个注意事项:当鼠标处于(默认的)绝对模式时,鼠标事件中的 `which` 字段会被强制设为 0。你必须激活相对模式(https://wiki.libsdl.org/SDL3/SDL_SetWindowRelativeMouseMode),才能获得包含实际设备 ID 的 `which` 字段。我开了这个 issue(https://github.com/libsdl-org/SDL/issues/16027)来了解原因,答案是“为了保持一致性,以匹配其他平台的行为”。
相似文章
《我的世界》中的 Wayland 合成器
一个在《我的世界》中实现完整 Wayland 合成器的模组,允许用户在游戏内启动和管理 Firefox、GIMP、VLC 等窗口。仅限 Linux。
我终于可以在2026年开始使用Wayland了吗?
作者重新评估了2026年Wayland在Linux上的可用性,指出虽然有改进,但NVIDIA驱动和8K显示器支持仍存在问题,使其仍未准备好用于其设置。
在Minecraft中作为模组实现的Wayland合成器
一个在游戏内实现了功能完整的Wayland合成器的Minecraft模组,允许用户在Minecraft世界中运行和管理应用程序。仅支持Linux。
我的无障碍技术栈与 Wayland 上的未来
一篇个人记述,讲述 Linux 桌面即将全面转向 Wayland 的未来将如何破坏依赖 Talon Voice 等输入工具的无障碍用户体验,并指出输入无障碍相较于输出无障碍受到的关注严重不足。
Linux延迟测量与合成器调优
一项详细调查,使用基于Teensy的LDAT工具测量游戏中的Linux延迟,在KDE Wayland下的Nvidia GPU上使用各种设置测量点击到光子延迟,并与Windows进行比较。