news 2026/9/15 3:30:17

cua-driver 的 WinRects GNOME Shell 辅助扩展:在 Mutter Wayland 上获取像素坐标、精确窗口激活与合成器级 Agent 光标

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
cua-driver 的 WinRects GNOME Shell 辅助扩展:在 Mutter Wayland 上获取像素坐标、精确窗口激活与合成器级 Agent 光标

cua-driver 的 WinRects GNOME Shell 辅助扩展:在 Mutter Wayland 上获取像素坐标、精确窗口激活与合成器级 Agent 光标

【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code

cua-driver 是一款跨平台的计算机操作(Computer Use)驱动,其 Wayland 支持在 GNOME/Mutter 上依赖一个随仓库分发的辅助扩展cua WinRectswinrects@cua)。它运行在 GNOME Shell 的特权上下文中,通过会话总线上的org.cua.WinRects接口,为 cua-driver 提供普通 Wayland 客户端无法获得的全局能力:窗口像素坐标、精确目标窗口激活、合成器 stage 截图,以及在合成器上绘制 Agent 光标。读完本文,你将完整掌握该扩展的 D-Bus API 契约、信任验证模型、安装与降级行为,以及它如何与 cua-driver 的 Rust 侧客户端(shell_helper.rs)协同工作。

为什么普通 Wayland 客户端做不到,而扩展可以

Wayland 的安全模型与 X11 完全不同:普通客户端既拿不到窗口在屏幕上的全局坐标(GNOME 的org.gnome.Shell.Introspect.GetWindows出于隐私考虑默认拒绝),也没有 X11 那种全局输入/绘制能力。Mutter 还缺少 wlroots 系合成器提供的zwlr_layer_shell_v1等协议,因此客户端无法在屏幕指定坐标放置覆盖层。

解决这些问题的唯一路径是从合成器内部提供能力——这正是 WinRects 扩展存在的意义。它在 Shell 的特权上下文内运行,因此无需 xdg-desktop-portal 授权(与 libei/RemoteDesktop 的 portal 流程不同)。该扩展的完整源码位于 packages/cua-driver/wayland-helper/winrects@cua/extension.js,元数据声明支持 GNOME Shell 45–50,扩展版本为 8(见 metadata.json)。

会话总线 API 一览

扩展在会话总线上以org.cua.WinRects名称导出,对象路径为/org/cua/WinRectsextension.js中的 D-Bus 接口定义(IFACE常量)完整列出了所有方法:

方法签名作用
GetVersion()出参uint返回浏览器/光标敏感的 API 版本号
GetRects()出参s(JSON 字符串)返回每个窗口的 frame 几何与 surface-buffer 原点
Capture()出参s(base64 PNG)通过 Shell 截图 API 捕获合成器 stage
Activate(id)入参uint,出参b激活一个 Shell stable-sequence 窗口并报告是否被接受
MoveCursor(x, y)入参i32, i32将 Agent 光标(Clutter actor)移动到屏幕坐标
ClickPulse(x, y)入参i32, i32光标立即跳到目标并播放点击脉冲动画
HideCursor()无参隐藏光标与徽章
SetCursorState(action, delivery, target, active)入参s, s, s, b渲染与跨平台cua.default光标主题一致的 12 种语义动作状态
SetCursorColor(fill_color)入参s#RRGGBB应用会话级光标填充色并重建辉光
SetSessionLabel(label)入参s设置光标旁会话徽章显示的文本

extension.jsenable()中可以看到具体实现:Gio.DBusExportedObject.wrapJSObject(IFACE, this)包装导出对象,通过Gio.bus_own_name(Gio.BusType.SESSION, 'org.cua.WinRects', Gio.BusNameOwnerFlags.REPLACE, ...)抢占会话总线名称,并以GLib.timeout_add(..., 33, ...)驱动每约 33ms 的光标重绘帧循环。

核心方法原理

GetRects:窗口几何与 AT-SPI 坐标重建

这是整个像素坐标体系的基础。GetRects()extension.js中通过global.get_window_actors()枚举窗口,按global.display.sort_windows_by_stacking()排序,为每个窗口输出:

  • idw.get_stable_sequence()的稳定序列号;
  • pidtitle
  • x/y/w/hw.get_frame_rect()窗口 frame 矩形(屏幕几何);
  • buffer_x/buffer_yw.get_buffer_rect()的 surface-buffer 原点;
  • focusedminimizedvisiblestacking等状态位。

frame 与 buffer 原点的分离是本设计的关键:GTK 客户端侧阴影(client-side shadow)导致 surface-buffer 矩形比可视 frame 更大。cua-driver 使用 frame 原点(屏幕坐标)与 AT-SPI 的CoordType::Window逐控件坐标相加:screen = origin + window_xy,这是 X11 上_GTK_FRAME_EXTENTS重建方案的 GNOME 对应物——因为在 Mutter 上 AT-SPI 的CoordType::Screen对每个控件都返回(0,0)

这一点在 Rust 侧有明确的工程注释(shell_helper.rs 的window_origin_for_pid):如果使用更大的 surface-buffer 矩形,浮动窗口的像素动作会因阴影偏移而错过目标。模块内测试accessibility_origin_matches_the_frame_cropped_screenshot专门验证了 frame 原点与裁剪截图的对应关系。

Activate:精确窗口激活与焦点验证

ActivateAsync(id)get_stable_sequence()精确查找窗口并调用target.activate(global.get_current_time()),随后延迟 100ms 检查global.display.focus_window === target才返回true

cua-driver 侧的使用方式更加严谨:with_focused_window()(Rust)会先通过trusted_shell_windows记录激活前的聚焦窗口快照,激活目标窗口、执行有界操作后,再把之前聚焦的 Shell 窗口恢复并验证。在发送焦点绑定的 portal/libei 输入之前,cua-driver 会通过第二次GetRects快照验证焦点,防止输入泄漏到用户当前碰巧聚焦的任意应用中。若窗口未激活或未验证成功,cua-driver 会拒绝发送焦点绑定输入,而不是向未验证目标注入。

Capture:合成器 stage 截图

CaptureAsync通过new Shell.Screenshot()shooter.screenshot(false, stream)捕获 stage,并将 PNG 编码为 base64 返回。注意include_cursor参数显式为false——源码注释说明:GNOME 50 的 stage-content 捕获会在绘制后复制真实光标精灵,远程/无头指针 seat 可能暴露 0x0 精灵导致 Shell 崩溃,因此禁用光标捕获,改由 cua-driver 自行绘制 Agent 光标。

Rust 侧screenshot_display()使用 5 秒超时调用Capturewait_timeout的实现专门说明了为何必须边运行边排空 stdout:base64 PNG 很容易超过管道约 64 KiB 的容量,若先等子进程退出再读会因管道写满而死锁。

信任模型:谁可以调用这个"特权"接口

扩展运行在 Shell 特权上下文,因此 cua-driver 对调用边界格外严格。shell_helper.rs中的shell_owner()做了完整的合成器归属证明流程:

  1. 通过 D-Bus 守护进程的GetNameOwner解析org.cua.WinRects不可变唯一名称(形如:1.204),而不是直接使用公共名称;
  2. 通过GetConnectionUnixProcessID/GetConnectionUnixUser拿到持有者的 PID 与 UID,要求UID 等于当前用户
  3. 通过is_trusted_gnome_shell(pid)验证进程是系统安装的 gnome-shell/proc/<pid>/commgnome-shell/proc/<pid>/exe解析出的可执行文件名也是gnome-shell,且该二进制UID 为 0(root)、权限掩码0o022为 0(即系统级安装、非用户可写);
  4. 针对敏感调用,再校验GetVersion()返回的 API 版本。

之所以调用唯一名称而非公共名称,是为了关闭竞态:公共名称可能被同会话的其他进程在验证与激活之间替换,而唯一名称不可变。文档强调:浏览器相关设置与授权(browser setup/consent)持有更严格的边界——helper API v4 或更新必须由已验证的 GNOME Shell 所有者提供BROWSER_HELPER_API_VERSION = 4)。从源码结构看,这是为了防止伪装成 helper 的进程窃取浏览器授权数据。

安装与验证

安装脚本位于 packages/cua-driver/wayland-helper/install.sh。README 给出的标准流程为:

~/.cua-driver/packages/current/wayland-helper/install.sh # 从源码检出使用时,直接在本目录执行 ./install.sh # 然后注销并重新登录一次(GNOME 仅在会话启动时扫描扩展) gnome-extensions info winrects@cua # 应显示 State: ACTIVE

install.sh的细节值得注意:

  • 目标安装目录为$XDG_DATA_HOME(默认~/.local/share)下的gnome-shell/extensions/winrects@cua
  • 复制metadata.jsonextension.js两个文件;
  • 通过gsettings get org.gnome.shell enabled-extensions读取当前启用集合,用 Python 解析后追加winrects@cua(保留已有扩展),再gsettings set写回;
  • 脚本明确提示:GNOME Shell 只在会话启动时扫描扩展,因此必须注销/登录(或重启会话)一次。

cua-driver 在运行时通过wayland::shell_helperavailable()即探测shell_owner)自动检测该扩展。

语义光标:v8 契约与 12 种动作状态

自 helper v8 起,合成器光标的语义状态与跨平台内置光标主题cua.default保持一致。extension.js中定义了 12 个动作状态集合ACTIONSidleobserveclickdragscrolltextkeynavigateapptransferrecordsystem,其中ONE_SHOT_ACTIONS = {click, key, navigate, app, system}会在ACTION_DURATIONS播完后自动回到idle

该扩展的渲染是纯 Cairo 实现的:CANVAS_SIZE = 128DISPLAY_SIZE = 42ACTOR_SIZE = 112,通过traceCursorBody绘制光标轮廓,drawCursorGlowShape以 36 层渐变笔触生成辉光(预先渲染到GLOW_SURFACE_SCALE = 3的离屏 surface 以提高性能),drawActionCue则按动作类型绘制不同的动画提示(点击缩放、拖拽偏移、滚动箭头、文字竖线、键盘按键、导航双箭头、应用网格、传输、录制圆环、系统齿轮等)。MoveCursor使用Clutter.AnimationMode.EASE_OUT_CUBIC以 480ms 平滑滑动,ClickPulse则瞬间定位并触发点击脉冲。

Delivery 与 Target 上下文以宿主拥有的芯片(chips)形式显示在会话徽章中,而不是指针相对的主题图案——这是 v8 重构的要点:drawBadgeChip可绘制backgroundforegroundaxpixelbrowserdesktop六种字形,配合SetSessionLabel的文本标签、BADGE_HOLD_SECONDS = 2.0的保持时间与 0.4s 淡出动画。徽章配色由badgeStyle(fillColor)从会话填充色生成渐变背景、边框与光晕(源码注释说明 rim 携带会话身份,与 Rust 渲染器的paint_session_badge对齐)。

SetCursorColor只接受/^#([0-9a-fA-F]{6})$/格式的#RRGGBB,非法值静默忽略;应用后重建辉光 surface、更新徽章样式与芯片,并始终保留白色指针描边setPaper白色外轮廓)。

v8 的版本门槛在两端都有强制约束:Rust 侧SEMANTIC_CURSOR_API_VERSION = 8semantic_cursor_available()要求 owner 版本 ≥ 8 才调用SetCursorState;模块测试bundled_helper_v8_uses_host_owned_modifier_badge_chips会编译期读取并断言扩展源码包含 v8 的全部特征(return 8;SetCursorStateSetCursorColorSetSessionLabeldrawBadgeChipbadgeStyle等),并断言不包含旧版_badgeIdentity_badgeDotdrawModifiers等遗留标识。README 明确说明:若仍加载旧版 helper,cua-driver 不会绘制其遗留光标,需重新运行安装器并重载 GNOME 会话。

无 helper 时的降级行为

WinRects 是**尽力而为(best-effort)**的组件。shell_helper.rs的模块注释明确说明:若扩展未安装/未启用,调用返回None/ no-op,调用方保持原有行为。具体到能力矩阵:

  • AT-SPI 语义操作(AX):即使没有 helper 依然工作;
  • 像素几何、Shell 光标、安全的前台 portal 输入:不可用;
  • 焦点绑定输入:cua-driver 会拒绝注入,而不是向未验证目标发送。

文档(action-support.md)记载了 GNOME/Mutter 的真实验证结果:在真实 GNOME 46 Wayland 会话上,WinRects helper 提供稳定窗口 id、frame/buffer 几何、堆叠顺序、已验证激活、stage 截图与合成器光标,配合 AT-SPI 语义动作与持久 portal/libei 会话,完整 GTK3 矩阵 31/31 通过;没有 helper 时,绑定目标的前台输入会明确拒绝。

与其他合成器生态的对比

  • wlroots 系合成器(Sway、labwc):不需要该扩展。cua-driver 在那边使用foreign-toplevel激活、virtual-pointer输入与layer-shell(见 wayland 目录 下的ext_toplevel.rslibei.rssway_ipc.rs等模块)。
  • KDE Plasma Wayland:需要等价的、可寻址目标的 KWin 激活适配器,目前尚未提供。文档特别强调:仅 portal 可达性是不够的,因为 RemoteDesktop/libei 输入对合成器焦点是全局的——这正是 WinRects 式"精确激活 + 焦点验证"不可替代的原因。

测试与验证依据

Rust 侧客户端在 shell_helper.rs 内置了完整的单元测试,覆盖:

  • parses_and_filters_shell_windows:解析并按 PID 过滤 JSON 窗口列表,验证标题含撇号("Sentinel's window")时也能健壮解析(先取首个[到末个],绕过 GVariant 包装);
  • accessibility_origin_matches_the_frame_cropped_screenshot:验证使用 frame 原点(而非 buffer 原点)与截图裁剪一致;
  • marks_minimized_shell_windows_off_screen:最小化窗口被标记为屏幕外;
  • parses_dbus_owner_and_numeric_identity:解析:1.204唯一名称与(uint32 6079,)数值(注释特别说明为何不能直接全文搜索数字——会把类型标注里的32误解析出来);
  • preserves_exact_focus_from_shell_snapshot:焦点快照保持精确;
  • bundled_helper_v8_uses_host_owned_modifier_badge_chips:编译期引用并断言扩展源码/元数据符合 v8 契约。

扩展侧行为(帧循环、动画、芯片、GetRects字段)可直接在 extension.js 中逐行核验。关于cua.default主题与 12 种语义状态的跨平台一致性,可进一步参考 cursor-themes.md。

限制与适用前提

  • 仅适用于GNOME Shell 45–50 的 Wayland 会话metadata.jsonshell-version声明),X11 会话无需此扩展(走 X11 原生路径);
  • 安装后必须注销/登录一次才能加载扩展;
  • 缺少扩展时,像素操作与合成器光标能力自动缺失,且安全相关操作选择拒绝而非冒险注入
  • KDE Plasma Wayland 尚无对应适配器;wlroots 系合成器走另一套协议栈。

【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 3:29:03

基于SSM的出版社教材服务网站:从毕设选题到答辩的全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/15 3:27:29

1100张老鼠图像如何训练YOLOv8?小目标检测调优实战

简介&#xff1a;这是一份面向目标检测任务的老鼠图像数据集&#xff0c;共包含约1100张已标注图片&#xff0c;采用YOLO标注格式&#xff0c;类别仅“老鼠”一类&#xff0c;适合需要训练老鼠检测模型、开展YOLO系列改进实验或进行迁移学习的研究者与开发者。资源包共2000个文…

作者头像 李华
网站建设 2026/9/15 3:26:28

服务器故障排查清单:12种常见问题定位与处理全指南

做服务器运维这些年&#xff0c;我最怕听到的一句话不是“服务器挂了”&#xff0c;而是电话那头补一句“你自己看吧&#xff0c;我啥也没动”。半夜两点的机房告警&#xff0c;周末的微信轰炸&#xff0c;新手接手一台来历不明的服务器&#xff0c;面对的往往是一个黑盒加一堆…

作者头像 李华
网站建设 2026/9/15 3:26:09

AI时代CLI工具复兴:高效开发与自动化实践

1. AI Agent时代CLI复兴现象解析最近半年在开发者社区观察到一个有趣现象&#xff1a;当各大科技公司都在为AI Agent开发华丽的图形界面时&#xff0c;一批以Codex CLI、Gemini CLI为代表的新型命令行工具却逆势崛起。我的团队在开发AI辅助编程工具时&#xff0c;最初也设计了完…

作者头像 李华
网站建设 2026/9/15 3:24:52

嵌入式低功耗策略:收益量化、风险权衡与平衡之道

做嵌入式这些年&#xff0c;我最深的体会是&#xff1a;低功耗策略这项工作是典型的“表面越简单&#xff0c;背后越复杂”。一块电池、一颗MCU、一个无线模组&#xff0c;看起来只要让设备多睡一会儿就能省电&#xff0c;可真把功耗曲线打出来&#xff0c;你会发现每一微安都在…

作者头像 李华
网站建设 2026/9/15 3:24:43

万岳网校源码深度拆解:直播课堂与多端部署实战指南

简介&#xff1a;2024最新万岳开源网校源码&#xff0c;是一套面向教育培训机构、职业院校及独立讲师的开源在线教学系统。采用原生语言开发、多端互通&#xff0c;集教学、学习、管理、互动、营销于一体&#xff0c;支持视频直播、语音直播、PPT直播&#xff0c;覆盖大班课、小…

作者头像 李华