多用户 Wayland 现状

Lobsters Hottest 新闻

摘要

对 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 合成器

Hacker News Top

一个在《我的世界》中实现完整 Wayland 合成器的模组,允许用户在游戏内启动和管理 Firefox、GIMP、VLC 等窗口。仅限 Linux。

我的无障碍技术栈与 Wayland 上的未来

Lobsters Hottest

一篇个人记述,讲述 Linux 桌面即将全面转向 Wayland 的未来将如何破坏依赖 Talon Voice 等输入工具的无障碍用户体验,并指出输入无障碍相较于输出无障碍受到的关注严重不足。

Linux延迟测量与合成器调优

Lobsters Hottest

一项详细调查,使用基于Teensy的LDAT工具测量游戏中的Linux延迟,在KDE Wayland下的Nvidia GPU上使用各种设置测量点击到光子延迟,并与Windows进行比较。