ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

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

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-codecua-driver 是一款跨平台的计算机操作Computer Use驱动其 Wayland 支持在 GNOME/Mutter 上依赖一个随仓库分发的辅助扩展cua WinRectswinrectscua。它运行在 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/winrectscua/extension.js元数据声明支持 GNOME Shell 45–50扩展版本为 8见 metadata.json。会话总线 API 一览扩展在会话总线上以org.cua.WinRects名称导出对象路径为/org/cua/WinRects。extension.js中的 D-Bus 接口定义IFACE常量完整列出了所有方法方法签名作用GetVersion()出参uint返回浏览器/光标敏感的 API 版本号GetRects()出参sJSON 字符串返回每个窗口的 frame 几何与 surface-buffer 原点Capture()出参sbase64 PNG通过 Shell 截图 API 捕获合成器 stageActivate(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.js的enable()中可以看到具体实现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()的稳定序列号pid、titlex/y/w/hw.get_frame_rect()的窗口 frame 矩形屏幕几何buffer_x/buffer_yw.get_buffer_rect()的 surface-buffer 原点focused、minimized、visible、stacking等状态位。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的实现专门说明了为何必须边运行边排空 stdoutbase64 PNG 很容易超过管道约 64 KiB 的容量若先等子进程退出再读会因管道写满而死锁。信任模型谁可以调用这个特权接口扩展运行在 Shell 特权上下文因此 cua-driver 对调用边界格外严格。shell_helper.rs中的shell_owner()做了完整的合成器归属证明流程通过 D-Bus 守护进程的GetNameOwner解析org.cua.WinRects的不可变唯一名称形如:1.204而不是直接使用公共名称通过GetConnectionUnixProcessID/GetConnectionUnixUser拿到持有者的 PID 与 UID要求UID 等于当前用户通过is_trusted_gnome_shell(pid)验证进程是系统安装的 gnome-shell/proc/pid/comm为gnome-shell、/proc/pid/exe解析出的可执行文件名也是gnome-shell且该二进制UID 为 0root、权限掩码0o022为 0即系统级安装、非用户可写针对敏感调用再校验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 winrectscua # 应显示 State: ACTIVEinstall.sh的细节值得注意目标安装目录为$XDG_DATA_HOME默认~/.local/share下的gnome-shell/extensions/winrectscua复制metadata.json与extension.js两个文件通过gsettings get org.gnome.shell enabled-extensions读取当前启用集合用 Python 解析后追加winrectscua保留已有扩展再gsettings set写回脚本明确提示GNOME Shell 只在会话启动时扫描扩展因此必须注销/登录或重启会话一次。cua-driver 在运行时通过wayland::shell_helperavailable()即探测shell_owner自动检测该扩展。语义光标v8 契约与 12 种动作状态自 helper v8 起合成器光标的语义状态与跨平台内置光标主题cua.default保持一致。extension.js中定义了 12 个动作状态集合ACTIONSidle、observe、click、drag、scroll、text、key、navigate、app、transfer、record、system其中ONE_SHOT_ACTIONS {click, key, navigate, app, system}会在ACTION_DURATIONS播完后自动回到idle。该扩展的渲染是纯 Cairo 实现的CANVAS_SIZE 128、DISPLAY_SIZE 42、ACTOR_SIZE 112通过traceCursorBody绘制光标轮廓drawCursorGlowShape以 36 层渐变笔触生成辉光预先渲染到GLOW_SURFACE_SCALE 3的离屏 surface 以提高性能drawActionCue则按动作类型绘制不同的动画提示点击缩放、拖拽偏移、滚动箭头、文字竖线、键盘按键、导航双箭头、应用网格、传输、录制圆环、系统齿轮等。MoveCursor使用Clutter.AnimationMode.EASE_OUT_CUBIC以 480ms 平滑滑动ClickPulse则瞬间定位并触发点击脉冲。Delivery 与 Target 上下文以宿主拥有的芯片chips形式显示在会话徽章中而不是指针相对的主题图案——这是 v8 重构的要点drawBadgeChip可绘制background、foreground、ax、pixel、browser、desktop六种字形配合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;、SetCursorState、SetCursorColor、SetSessionLabel、drawBadgeChip、badgeStyle等并断言不包含旧版_badgeIdentity、_badgeDot、drawModifiers等遗留标识。README 明确说明若仍加载旧版 helpercua-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.rs、libei.rs、sway_ipc.rs等模块。KDE Plasma Wayland需要等价的、可寻址目标的 KWin 激活适配器目前尚未提供。文档特别强调仅 portal 可达性是不够的因为 RemoteDesktop/libei 输入对合成器焦点是全局的——这正是 WinRects 式精确激活 焦点验证不可替代的原因。测试与验证依据Rust 侧客户端在 shell_helper.rs 内置了完整的单元测试覆盖parses_and_filters_shell_windows解析并按 PID 过滤 JSON 窗口列表验证标题含撇号Sentinels 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.json的shell-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),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进