前言

同一台 Arch Linux 机器上,向日葵发起远程连接时,GNOME 的屏幕共享授权框会停住,等用户点一下“共享”。换成 ToDesk,授权框只闪一下,大约两三百毫秒就没了,手还没来得及移到按钮上;有时远端黑屏,有时连接刚建立又退回去。

下面这条记录来自一次完整排查。ToDesk 4.9.8.1 本身有 Wayland/PipeWire 抓屏能力;授权框闪一下,来自 AT-SPI 自动点击。另有一条会话创建失败链路,需要单独看。

环境:Arch Linux / GNOME 51.1 Wayland / mutter 51.1 / todesk-bin 4.9.8.1-1

摘要:本机日志表明,ToDesk 4.9.8.1 已具备 Wayland/PipeWire 抓屏能力。授权框一闪而过的直接原因,是 ToDesk 通过 AT-SPI 无障碍接口自动点击了“共享”按钮。为 xdg-desktop-portal-gnome 单独设置 GTK_A11Y=none,可以阻止这一步自动操作,让授权框保留到手动确认。此方案针对授权框被自动确认的问题;日志中的会话创建失败仍需单独排查。

图一

图一:调整后的 GNOME 屏幕共享授权框,等待用户选择显示器并点击“共享”。自动确认的日志证据见第 3.1 节。

问题现象

在同一台机器上:

  • Sunlogin(向日葵):发起远程连接时,GNOME 的屏幕共享授权框正常弹出并停留,手动点击“共享”后可以正常连接。
  • ToDesk:授权框只出现约 200–400 ms 就消失,来不及手动确认;有时远端黑屏,或连接刚建立便中断并重试。
  • AUR 的 todesk-bin 包在安装时还会打印一句:

    1
    Todesk Remote Control doesn't support Wayland, Xorg ONLY!

这些现象很容易让人得出“ToDesk 不支持 Wayland,只能切换到 X11”的结论。不过,本机对 4.9.8.1 的排查表明:它已经具备 Wayland 抓屏能力,问题出在授权交互及后续会话建立环节。

确认前提:4.9.8.1 已具备 Wayland 抓屏能力

二进制包含 Wayland/PipeWire 抓屏相关符号

1
strings -a /opt/todesk/bin/ToDesk | grep -iE 'wayland|pipewire|portal'

能看到:

1
2
3
4
CScreenCapturerWayland
WaylandPortalHelper::RequestScreenCapture
WaylandSecureUiController
SDL Wayland video driver

并且它确实链接了 PipeWire:

1
2
ldd /opt/todesk/bin/ToDesk | grep -i pipewire
# libpipewire-0.3.so.0 => /usr/lib/libpipewire-0.3.so.0

上游还自带了兜底库:

1
/opt/todesk/lib/libpipewire-0.3.so.0

AUR 的 PKGBUILD 和 todesk.install 注释也提到:“4.9.8.1 起上游新增 /opt/todesk/lib,内置 xcb / pipewire 兜底库”,并有对应的打包与链接代码,详见第 3.3 节。这些符号和依赖说明程序包含相关实现,但仅凭它们还不能判断实际运行情况;更直接的证据来自运行时日志。

运行时日志显示抓屏与推流已成功

ToDesk 的用户日志在 ~/.local/share/todesk/Logs/:

1
grep -iE 'wayland|pipewire' ~/.local/share/todesk/Logs/session_*.log | tail

可以看到:

1
2
3
[screencap][wayland] selecting PipeWire capturer
PipeWire stream connected successfully
[screencap][wayland][frame-diag] screen=530 ... callbacks=... consumed=...

同一排查过程中,还能看到推流成功的记录:

1
[zrtc] PublishCallback onSuccess stream label:desktop-stream

这些记录表明,本机上至少已有成功建立 PipeWire 流、处理画面帧并发布桌面流的运行实例。因此,不能仅凭安装提示就断定该版本完全不支持 Wayland。

原因分析:自动确认与会话创建失败

授权框一闪而过:ToDesk 通过 AT-SPI 自动确认

GNOME 的屏幕共享对话框由 xdg-desktop-portal-gnome 提供,其控件可通过 AT-SPI 无障碍总线访问。结合相关符号与运行日志,ToDesk 的 WaylandSecureUiController 会执行以下自动化操作:

  1. 连接 AT-SPI 无障碍总线;
  2. 在控件树里找到 portal 对话框;
  3. 检查或选中显示器,并自动点击“共享”按钮。

日志原文:

1
2
3
4
5
6
[screencap][wayland] starting AT-SPI portal automation ...
[screencap][wayland] AT-SPI portal application found window_count=1 ...
[screencap][wayland] portal screen already selected ...
[screencap][wayland] portal confirmation attempt=1, accepted=true,
role=button, action=click
[screencap][wayland] portal dialog closed after confirmation attempt

本机日志原文(截图里的 sed 去掉了 PID/TID 等无关字段,时间和事件保持原样):

图二

图二:2026-11-03 12:56:53 的真实日志。12:56:53.109 对 role=button 执行 action=click,12:56:53.364 对话框已关闭,间隔 255 ms;从启动自动化到对话框关闭共 360 ms。

这组日志直接记录了自动确认与对话框关闭的过程,与观察到的约 200–400 ms 闪现现象吻合。阻止自动点击后,授权框便可保留到手动确认。

作为对照,本机使用 Sunlogin 时,授权框会保持显示,等待用户手动确认。

另一类故障:Session creation inhibited

自动确认可以解释授权框为何迅速消失,却不能解释所有连接失败。排查中还遇到了连接反复重试、授权框反复弹出、远端始终没有画面的情况。

xdg-desktop-portal-gnome 的 journal 里能看到:

1
2
3
Failed to start session: GDBus.Error:org.freedesktop.DBus.Error.Failed: Session creation inhibited
Failed to close GNOME screen cast session: GDBus.Error:org.freedesktop.DBus.Error.UnknownMethod:
对象在路径“/org/gnome/Mutter/ScreenCast/Session/u11”处不存在

本机 journal 原文:

图三

图三:同一天的 portal 日志,同时出现会话创建被阻止和关闭旧会话失败的记录。

这些记录说明,会话启动及清理环节也发生了异常。结合 ToDesk 的重试行为,可以观察到以下过程:

  1. 对话框弹出;
  2. ToDesk 自动点“共享”;
  3. portal 启动 ScreenCast 会话失败(Session creation inhibited);
  4. 对话框关闭,客户端连接失败;
  5. ToDesk 重试,回到第 1 步。

一种可能的解释是,重连时旧会话尚未完成清理,新会话便开始创建。不过,仅凭上述日志,还不足以确定是 ToDesk 的会话管理、Mutter 的状态限制,还是两者交互导致。可以确定的是:这属于会话建立阶段的故障,不能据此判断程序没有 Wayland 抓屏能力。

为什么 AUR 还写着 “Xorg ONLY”

翻阅 AUR 的 Git 历史后,可以确认这是一条沿用多年的安装提示,并非针对 4.9.8.1 新增的判断。

2021 年 2 月 9 日,更新到 1.2.4d 的提交首次加入了这句话:

1
2
b095cb57d080b175e7558b593c2c871c4c49c98e
[updpkg] todesk-bin 1.2.4d

该提交在 todesk.install 的 post_install() 中增加了一条输出语句:

1
$ECHO "\033[36m*  Todesk Remote Control doesn't support Wayland, Xorg ONLY!  *\033[0m"

它是固定文本,不会检测当前桌面、portal 后端或 ToDesk 的运行能力。到了 2026 年 11 月 1 日的 4.9.8.1 更新,这条提示仍然保留,但同一次提交已经加入了 PipeWire 兜底库的处理:

1
2
4d59901f7b559b0b363819ba9a9150878a4b1935
upgpkg: todesk-bin 4.9.8.1-1

在这个版本的 PKGBUILD 中,第 65–67 行将上游新增的库目录一并装入包中:

1
2
3
# 4.9.8.1 起上游新增 opt/todesk/lib:系统缺失时提供 xcb / pipewire 兜底库,
# 由 todesk.install 在 post_install 里按需软链到 bin/(RPATH=$ORIGIN)
cp -a opt/${_pkgname}/lib ${pkgdir}/opt/${_pkgname}/

todesk.install 的 _todesk_bundled_libs 数组则明确包含:

1
2
3
4
5
libxcb-keysyms.so.1
libxcb-icccm.so.4
libxcb-xkb.so.1
libxcb-util.so.1
libpipewire-0.3.so.0

因此,历史记录能证明这条提示早于当前版本多年,也能证明 4.9.8.1 的打包逻辑已经处理 PipeWire 库;但无法据此确定维护者是否仍有意保留该提示来表示支持范围。结合第 2.2 节的成功运行日志,更准确的结论是:这条旧提示不足以概括 4.9.8.1 在本机的实际能力。

AUR 代码能证明什么,又能帮助排查什么

本次核对以提交 4d59901f7b559b0b363819ba9a9150878a4b1935 为准。AUR 仓库提供的是打包和安装脚本,并不包含 ToDesk 的抓屏或 AT-SPI 自动化实现源码,需要区分证据的作用范围:

AUR 代码或记录 可以确认的事实 对排查的帮助与限制
PKGBUILD 第 29 行及 build()、package() x86_64 包取自官方 todesk-v4.9.8.1-amd64.deb,解包后安装其中的程序、库与服务文件 当前脚本中没有加入 AT-SPI 自动点击补丁;自动点击的直接证据仍是 ToDesk 运行日志
PKGBUILD 第 65–67 行、todesk.install 第 1–25 行 打包 /opt/todesk/lib,并为系统缺少的指定库按需建立链接,其中包括 PipeWire 可以解释系统未安装同名库时的兜底方式;仅有库文件不代表抓屏一定成功
todesk.install 第 17–23 行 若 ldconfig -p 已列出 /opt/todesk/ 以外的同名库,就跳过创建链接;否则在目标不存在时创建 bin/ 到 ../lib/ 的链接 没有 bin/libpipewire-0.3.so.0 软链接不一定是安装异常,应结合 ldd 检查实际解析结果
todesk.install 第 40–43、64–65 行 安装或升级时,仅在 todeskd.service 已运行的情况下重启它 安装成功不代表服务已启动,也不代表已设置开机自启;应另行检查服务状态
PKGBUILD 第 30 行、.SRCINFO 第 23 行 AArch64 实际下载的仍是 4.8.5.1-arm64.deb 不能仅凭 AUR 页面显示的 4.9.8.1-1,将本文针对 4.9.8.1 的结论直接推广到 ARM64
depends 列表 显式运行依赖为 gtk3、libappindicator-gtk3、noto-fonts-cjk 包中未显式声明 portal 或 PipeWire 服务依赖,需结合桌面环境检查;内置客户端库不能替代这些运行服务

可在本机用以下只读命令补充检查:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 检查架构、包版本与服务状态
uname -m
pacman -Q todesk-bin
systemctl status todeskd.service --no-pager

# 检查 PipeWire 库的解析结果与可能存在的兜底链接
ldd /opt/todesk/bin/ToDesk | grep -E 'pipewire|not found'
ls -l /opt/todesk/lib/libpipewire-0.3.so.0
ls -l /opt/todesk/bin/libpipewire-0.3.so.0

# 检查当前用户会话中的 portal 与 PipeWire 服务
systemctl --user status xdg-desktop-portal.service \
xdg-desktop-portal-gnome.service pipewire.service --no-pager

其中,最后一个 ls 若提示文件不存在,应结合前面的 ldd 结果判断,不必立即手动建立链接。上述命令用于收集状态,不会启动或重启服务。

核对记录与原始来源(查阅日期:2026-11-03;代码链接固定到对应提交):

如需自行复核历史,可克隆打包仓库后查看,无需执行构建或安装脚本:

1
2
3
4
git clone https://aur.archlinux.org/todesk-bin.git
cd todesk-bin
git show 4d59901f7b559b0b363819ba9a9150878a4b1935 -- PKGBUILD todesk.install .SRCINFO
git log -p -S 'Wayland' -- todesk.install

修复方案:关闭 portal 进程的无障碍桥接

原理

在这条自动化链路中,ToDesk 是 AT-SPI 的客户端,portal 对话框则由 GTK4 应用提供。让 xdg-desktop-portal-gnome 不再通过无障碍总线暴露控件,就能阻止 ToDesk 沿这条路径找到并点击“共享”按钮。

GTK4 用环境变量 GTK_A11Y 控制无障碍桥接:

  • GTK_A11Y=atspi:使用 AT-SPI 无障碍后端。
  • GTK_A11Y=none:关闭无障碍后端。

为 xdg-desktop-portal-gnome.service 添加 systemd 用户服务覆盖配置(drop-in),将该变量限定在 portal 进程内。

操作步骤

在本机终端执行以下命令。重启 portal 后端可能中断正在进行的屏幕共享,请先结束相关会话。

1
2
3
4
5
6
7
8
9
mkdir -p ~/.config/systemd/user/xdg-desktop-portal-gnome.service.d

cat > ~/.config/systemd/user/xdg-desktop-portal-gnome.service.d/no-a11y.conf <<'EOF'
[Service]
Environment=GTK_A11Y=none
EOF

systemctl --user daemon-reload
systemctl --user restart xdg-desktop-portal-gnome.service

验证配置生效

1
2
3
4
5
systemctl --user is-active xdg-desktop-portal-gnome.service
systemctl --user show xdg-desktop-portal-gnome.service -p Environment

pid=$(systemctl --user show -p MainPID --value xdg-desktop-portal-gnome.service)
tr '\0' '\n' < /proc/$pid/environ | grep GTK_A11Y

应该看到:

1
2
3
active
Environment=GTK_A11Y=none
GTK_A11Y=none

本机验证截图:

图四

图四:systemctl show 和 /proc/<pid>/environ 都确认 GTK_A11Y=none。

测试

  1. 打开 ToDesk,发起远程连接;
  2. 确认 GNOME 的“共享屏幕”对话框保持显示;
  3. 选中显示器,点击“共享”(Share);
  4. 检查远端是否正常显示画面。

图 1 展示了预期的授权框停留状态。该对话框由 portal ScreenCast 接口触发,与 ToDesk 调用的是同一套 GNOME 授权界面;判断 ToDesk 是否恢复正常,还需结合实际远程连接与日志。

对照图 2,修复后新一轮连接的日志中,不应再出现对该授权框成功执行 action=click 的记录。手动确认后,若会话建立成功,应能看到 PipeWire stream connected successfully,以及持续递增的帧计数。

ToDesk 等待 portal 响应存在超时限制,授权框出现后应及时完成选择与确认。

验证与排错

观察 portal 日志

1
journalctl --user -u xdg-desktop-portal-gnome -f

重点观察新一轮连接中是否仍出现 Session creation inhibited。关闭无障碍桥接并不能保证消除这类错误;若仍出现,应继续排查第 3.2 节提到的会话建立与清理问题。

观察 ToDesk 日志

1
tail -f ~/.local/share/todesk/Logs/session_*.log | grep -E 'wayland|portal|PipeWire'

修复后,新一轮连接中应不再出现对该授权框自动点击成功的记录,例如:

1
portal confirmation attempt=... action=click

手动确认且抓屏成功后,应能看到:

1
2
PipeWire stream connected successfully
screen=530 ... callbacks=... consumed=...

如果授权框仍然一闪而过,先确认配置已加载到当前 portal 进程,再结合日志判断是否仍有自动点击、请求超时或会话失败。

如果远端仍然黑屏

优先检查 journal 里是否还有:

1
Session creation inhibited

如果仍有这类错误,可以先退出并重新打开 ToDesk 客户端,再发起连接;若问题持续,可尝试重启 ToDesk 服务:

1
sudo systemctl restart todeskd.service

重启服务会断开现有远程会话。

回滚

删除本次添加的覆盖配置,并重启 portal 后端:

1
2
3
rm ~/.config/systemd/user/xdg-desktop-portal-gnome.service.d/no-a11y.conf
systemctl --user daemon-reload
systemctl --user restart xdg-desktop-portal-gnome.service

该设置仅作用于 xdg-desktop-portal-gnome 进程,不会全局关闭其他应用的无障碍功能。但它会影响读屏软件访问该进程提供的界面;如果需要通过 Orca 等工具操作这些界面,应恢复无障碍桥接。

本机环境与注意事项

项目 版本 / 值
发行版 Arch Linux(rolling)
内核 7.2.14-arch1-1
桌面 GNOME 51.1,Wayland 会话
合成器 mutter 51.1-1
xdg-desktop-portal 1.22.1-2
xdg-desktop-portal-gnome 51.0-1
xdg-desktop-portal-gtk 1.15.3-2(同时安装,GNOME 会话下 ScreenCast 由 -gnome 后端处理)
PipeWire 1.6.10
ToDesk todesk-bin 4.9.8.1-1(AUR)
向日葵 sunloginclient 16.6.0.32198

需要注意的点:

  1. 适用范围为本文记录的 GNOME 环境。 KDE、Hyprland 使用不同的 portal 后端和界面实现;GTK_A11Y 是 GTK 的设置,不能仅替换服务名就照搬本方案。
  2. 修改范围限于该 portal 进程。 其他应用的无障碍设置不变,但 Orca 等工具将无法通过该进程的无障碍桥接访问其界面。
  3. 授权需要手动完成。 ToDesk 等待响应存在超时限制,请及时选择显示器并确认。
  4. 会话创建失败需要单独排查。 本方案不保证解决 Session creation inhibited;遇到时可先尝试重新连接或重启 todeskd.service。
  5. 自动确认行为值得反馈。 日志显示 ToDesk 会代替用户点击“共享”,使原本等待人工确认的步骤被自动完成。可以将相关日志提交给 ToDesk,反馈这一授权交互问题。
  6. 升级后重新验证。 如果后续版本不再自动点击,可撤销此临时方案,恢复 portal 的无障碍功能。
  7. 日志位置:
    • 用户侧:~/.local/share/todesk/Logs/session_*.log、client*.log、*.xlog
    • 服务侧(root):/var/log/todesk/servicer*.log
    • portal:journalctl --user -u xdg-desktop-portal-gnome

修复脚本

文章同目录附有 fix-todesk-wayland.sh,支持应用修复、检查状态和回滚。在 GNOME Wayland 桌面的终端中,以普通用户执行:

1
2
3
4
5
6
7
8
# 应用修复并验证当前 portal 进程的环境变量
bash fix-todesk-wayland.sh apply

# 检查修复是否生效
bash fix-todesk-wayland.sh status

# 撤销修复
bash fix-todesk-wayland.sh revert

不带参数时默认执行 apply。脚本仅修改当前用户的 xdg-desktop-portal-gnome.service.d/no-a11y.conf;若该文件已有不同内容或为符号链接,会停止并提示手动检查,避免覆盖现有配置。

应用或回滚时会重启 GNOME portal 后端,可能中断正在进行的屏幕共享。应用后需重新发起 ToDesk 连接,并手动确认共享。该方案会关闭此 portal 进程的无障碍桥接,且不保证解决 Session creation inhibited,适用范围与限制见前文。

脚本内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#!/usr/bin/env bash
# GNOME Wayland:阻止 ToDesk 通过 AT-SPI 自动确认屏幕共享。
set -euo pipefail

service=xdg-desktop-portal-gnome.service
config_dir="${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user/${service}.d"
config_file="$config_dir/no-a11y.conf"

usage() {
printf '用法:bash %s [apply|status|revert]\n' "$0"
printf ' apply 应用修复并验证(默认)\n status 检查当前状态\n revert 删除本方案配置并重启 portal\n'
}

expected_config() {
printf '[Service]\nEnvironment=GTK_A11Y=none\n'
}

check_config() {
if [[ -L "$config_file" ]] || { [[ -e "$config_file" ]] && ! cmp -s "$config_file" <(expected_config); }; then
printf '配置已存在且与本方案不同,或为符号链接,请手动检查:%s\n' "$config_file" >&2
exit 1
fi
}

restart_portal() {
systemctl --user daemon-reload
systemctl --user restart "$service"
}

show_status() {
local pid
systemctl --user is-active "$service"
systemctl --user show "$service" -p Environment
pid=$(systemctl --user show "$service" -p MainPID --value)
if [[ "$pid" =~ ^[1-9][0-9]*$ && -r "/proc/$pid/environ" ]]; then
if tr '\0' '\n' < "/proc/$pid/environ" | grep -Fx 'GTK_A11Y=none'; then
printf '已确认:当前 portal 进程使用 GTK_A11Y=none。\n'
else
printf '当前 portal 进程未设置 GTK_A11Y=none。\n' >&2
return 1
fi
else
printf '无法读取当前 portal 进程的环境变量。\n' >&2
return 1
fi
}

action=${1:-apply}
case "$action" in
-h|--help) usage; exit 0 ;;
apply|status|revert) ;;
*) usage >&2; exit 2 ;;
esac

if (( EUID == 0 )); then
printf '请在 GNOME 桌面会话中以普通用户运行,不要使用 sudo。\n' >&2
exit 1
fi

case "$action" in
apply)
desktop=${XDG_CURRENT_DESKTOP:-}
if [[ "${XDG_SESSION_TYPE:-}" != wayland || "${desktop^^}" != *GNOME* ]]; then
printf '未检测到 GNOME Wayland 会话,请在目标桌面的终端中运行。\n' >&2
exit 1
fi
systemctl --user cat "$service" > /dev/null
check_config
printf '将重启 GNOME portal 后端,正在进行的屏幕共享可能中断。\n'
mkdir -p "$config_dir"
expected_config > "$config_file"
restart_portal
show_status
printf '修复已应用。请重新发起 ToDesk 连接,手动选择显示器并点击“共享”。\n'
;;
status)
show_status
;;
revert)
check_config
if [[ -f "$config_file" ]]; then
printf '将删除本方案配置并重启 GNOME portal 后端。\n'
rm -- "$config_file"
restart_portal
printf '已删除:%s\n' "$config_file"
systemctl --user show "$service" -p Environment
else
printf '本方案配置不存在,无需回滚:%s\n' "$config_file"
fi
;;
esac