T3 Code 非官方中文翻译不是官方产品,也不是 T3 Tools 官方文档。源提交 d2c25950b4d9英文原文GitHub 源文件
Skip to content

Linux 窗口捕获 ​

仅限 Wayland。每次捕获都在 LinuxSnapShot.ts 中选定一个后端(Hyprland 辅助程序、Niri IPC、KDE 辅助程序、以活动窗口为目标的 Screenshot portal、GNOME 扩展,然后是 Electron 选择器),失败、取消或被拒绝时,绝不回退到另一个后端。过期的 NIRI_SOCKET,或一个可达的 GNOME 扩展,都不得据此改选其他桌面的后端;会话检测来自 XDG_CURRENT_DESKTOP,位于 linuxCaptureSession.ts。

模块边界 ​

dbus-next 只能经由动态导入到达。linuxCaptureSession.ts 存放纯辅助函数(会话检测、Niri 绑定文本、portal 按键映射、PNG 读取),这样主进程就能回答「这是哪个桌面」,而不必在 macOS 或 Windows 上加载 D-Bus 客户端。

patches/dbus-next@0.10.2.patch 会移除 usocket(其可选的原生 Unix-FD 传输),并连接到 unix:path= 与 unix:abstract= 两条总线,连接经由 Node 的 net。我们从不传递 Unix 描述符,而上游对抽象套接字没有 net 回退;这类套接字由 dbus-launch 在 systemd 之外产生。升级 dbus-next 时请保留这份补丁。

无障碍身份 ​

Screenshot portal 返回的是 PNG URI,没有窗口身份,因此 portal 捕获和选择器捕获都不携带无障碍数据。不要根据随后获得焦点的窗口,或猜测出来的标题来推断这些数据。

原生后端会提供 PID、标题和帧。AT-SPI 按 PID,再加上唯一的标题与尺寸来匹配。在 Wayland 上,即使合成器知道真实位置,AT-SPI 对窗口屏幕位置报告的仍是 (0, 0),因此只比较宽度和高度;只有当 AT-SPI 根节点与合成器帧一致,且至少一个后代报告了不同位置时,后代坐标才可信。否则边界取 null,而不是原点为零的矩形。

标题匹配会忽略开头单独的一帧盲文 CLI 转轮,因为终端会在捕获与查找之间改掉它。KDE 同时提供 frameGeometry 和 clientGeometry;两者中任一已经校验过的尺寸都可以接受,绝不要使用装饰高度容差。

在 GNOME 上,浏览器的无障碍桥接必须在浏览器启动之前具备 org.gnome.desktop.interface toolkit-accessibility。不要在捕获过程中切换它。

KDE ​

KWin 授权 org.kde.KWin.ScreenShot2 的方式,是把调用方 PID 的可执行文件解析到它的应用程序注册表,因此辅助程序要安装在 XDG data home 下的稳定路径,并带有隐藏的 desktop 条目,而不是从 AppImage 挂载点运行。有两个陷阱:

  • X-KDE-DBUS-Restricted-Interfaces 使用 KConfig 的逗号分隔列表语法。末尾的分号会变成接口名的一部分,KWin 会拒绝它。
  • KService 的缓存键包含搜索路径和区域设置。请直接使用 kbuildsycoca6 刷新,并通过 systemd-run --user 再刷新一次;只在 AppImage 环境里刷新,可能让 KWin 读到过期的权限。

就绪与否的检查方法是:用一个无效 ID 调用 CaptureWindow,并期望得到 InvalidWindow。这次调用发生在 KWin 的授权检查之后。磁盘上有文件,绝不表示已经就绪。

Plasma 会消耗 Shift 来处理上档数字,并产生一个标点 keysym,因此绑定成功的 Ctrl+Shift+2 在某些布局上可能永远不会触发。可以用字母组合键绕过。通用的修复需要能感知布局的编码。

Hyprland ​

辅助程序先用 hyprland-toplevel-mapping-v1 把 foreign-toplevel 句柄映射为完整的 64 位窗口地址,再通过 hyprland-toplevel-export-v1 导出。绝不要截断该地址,也不要按标题挑选窗口。

同意被拒绝,或适用 no_screen_share 规则时,Hyprland 0.56.2 会渲染一张拒绝访问的纹理,而不是让导出帧失败。ready 并不是一次权限授予,也不得靠检查像素来猜测是否已经授予。参见 ScreenshareFrame。

Hyprland 的 GlobalShortcuts portal 注册的是动作,不是按键组合。shortcutActionRegistered 与 shortcutRegistered 是两回事;界面中任何地方都不得声称这些按键已被预留。

捆绑的协议 XML 随辅助程序一并提供,因为其 BSD 许可证要求附上该声明。

Niri ​

Niri 没有实现 global-shortcut portal。捕获启用期间,应用在会话总线上拥有 <app-id>.SnapShot,并导出 com.t3tools.SnapShot.Capture;配置中的绑定会启动 gdbus 来调用它。开发用的应用 ID 与打包后的应用 ID 使用不同的名称,这样开发构建就不会抢走用户的绑定。

Niri 不提供全局截图原点,因此 AT-SPI 匹配只使用逻辑尺寸和标题。

配置编辑 ​

CaptureShortcutConfig 会编辑用户的点文件。不变量如下:打开设置时绝不读取该文件;审阅更改才表示同意读取;渲染进程只会经由受信任的桌面 IPC 看到修改前与修改后的文本;保存发送的是提案 ID,桌面端在写入之前,会对照预览时的那份快照,重新校验字节、inode、文件模式以及 include。写入经过暂存临时文件和原子重命名,并在原文件旁边留下备份。Niri 会先校验暂存文件。

修饰键的序列化会显式写出 Linux 的 Ctrl,绝不会使用跨平台的 mod 别名。

GNOME 扩展 ​

源码位于 apps/desktop/gnome-extension,UUID 为 snap-shot@t3.codes。GNOME 只在登录时才会发现新安装的扩展,因此设置流程会把「已安装,需要注销」和「已发现,但已禁用」区分开,并比较已加载版本与已安装版本。

扩展信任同一连接上拥有 com.t3tools.T3Code.SnapShot(或 .Development 变体)的调用方。这是 GNOME 的受信任会话客户端模式,并不是针对用户总线上的恶意进程做身份验证。

Electron 不会在 Wayland 上为覆盖窗口定位,因此闪光和飞入作为扩展内部的 Shell actor 运行,坐标相对于 T3 的内容区域。Electron 44 的会话恢复路径可能跳过重新绑定,并在注销时遗留回调,所以 PortalCaptureShortcut 自己持有 portal 会话,而不是使用 Electron 的 global-shortcut API。

GNOME 50 移除了 Meta.is_wayland_compositor。Shell 的内部实现会随主版本变化;在把某个版本加入 metadata.json 之前,请先逐一验证。

非官方译文 · 源提交 d2c25950b4d9 · 不代表 T3 Tools