ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

OpenScreen macOS 原生光标捕获测试管线:Helper 构建、冒烟测试与 Sidecar 验证实战

OpenScreen macOS 原生光标捕获测试管线:Helper 构建、冒烟测试与 Sidecar 验证实战 OpenScreen macOS 原生光标捕获测试管线Helper 构建、冒烟测试与 Sidecar 验证实战【免费下载链接】openscreenCreate stunning demos for free. Open-source, no subscriptions, no watermarks, and free for commercial use. An alternative to Screen Studio.项目地址: https://gitcode.com/GitHub_Trending/open/openscreenOpenScreen 在 macOS 上通过一个独立的 Swift helper 子进程openscreen-macos-cursor-helper捕获真实系统光标位图并让位图光标进入编辑器与导出管线。本文基于仓库内的测试文档 macOS native cursor test pipeline完整覆盖 helper 的工作原理、NDJSON 协议格式、构建与冒烟测试命令、macOS 权限矩阵、分优先级P0/P1/P2的手工测试清单以及如何通过.cursor.jsonsidecar 文件判定一次录制是否健康并结合 Swift 源码 与 TypeScript 会话实现 说明这些测试项背后的真实调用链帮助你在 macOS 上独立搭建、诊断和回归验证这条原生光标捕获链路。Helper 的工作原理轮询、去重与点击事件cursor helperopenscreen-macos-cursor-helper在录制期间作为 Electron 的子进程运行它做五件事以配置的采样间隔轮询NSCursor.currentSystem拿到当前活动的 AppKit 系统光标把每个光标图像编码为 PNG并计算 SHA-256 内容哈希作为稳定的资产 idassetId每个唯一的光标形状在整场录制中只发送一次完整的 base64 位图负载后续采样只携带assetId从而保持 stdout 体量很小通过CGEventTaplisten-only 模式跟踪左键按下/抬起事件为每个样本打上interactionType标签在获得辅助功能Accessibility授权时使用 Accessibility API 检测text/pointer亲和affordance即输入框/链接/按钮等角色这类形状会用仓库自带的高质量 SVG 替换原始位图渲染。在 main.swift 中可以直接看到这套实现currentCursorAsset()函数先用NSCursor.currentSystem取光标转NSBitmapImageRep后编码 PNG再用SHA256.hash(data: png)生成 idscaleFactor由pixelsWide / pointSize.width推出Retina 上为 2.0hotspot 坐标会乘以scaleFactor转成像素单位供渲染端再除回点point尺寸。主循环里的emittedAssetIds: SetString保证即使用户在 arrow → text → arrow 之间来回切换同一形状也只序列化一次整个采样循环包裹在autoreleasepool中避免长录制时 Cocoa 对象堆积导致内存增长这也是后文 P2 长录制内存检查项的依据。NDJSON 采样协议helper 通过 stdout 输出按行分隔的 JSONNDJSON每行一个事件{ type: ready, timestampMs: 1234567890, accessibilityTrusted: true, mouseTapReady: true } { type: sample, timestampMs: 1234567891, assetId: a7472..., asset: { id: a7472..., imageDataUrl: data:image/png;base64,..., width: 64, height: 64, hotspotX: 16, hotspotY: 16, scaleFactor: 2.0 }, cursorType: null, leftButtonDown: false, leftButtonPressed: false, leftButtonReleased: false } { type: sample, timestampMs: 1234567924, assetId: a7472..., cursorType: null, leftButtonDown: false, leftButtonPressed: false, leftButtonReleased: false }注意asset字段只在某个assetId首次出现时携带。各字段含义字段说明type: readyhelper 启动完成的握手事件accessibilityTrusted指示辅助功能是否已授权mouseTapReady指示CGEventTap是否建立成功assetId光标位图的 SHA-256 内容哈希是位图资产的稳定标识asset仅首见时出现imageDataUrlbase64 PNG、像素宽高、像素单位 hotspot、scaleFactorcursorTypeAccessibility 探测到的亲和类型text/pointer无授权或非亲和形状时为nullleftButtonDown/leftButtonPressed/leftButtonReleased当前左键状态CGEventSource.buttonState、本采样周期内是否发生按下/抬起TypeScript 端的 MacNativeCursorRecordingSession 按行解析这些事件把唯一资产收集进assets的 Map并在stop()时输出provider: native当且仅当至少捕获到一个位图否则为provider: none纯位置遥测。该数据结构与 contracts.ts 中的CursorRecordingData/NativeCursorAsset接口一一对应version: 2、provider、samples[]归一化坐标cx/cy、visible、interactionType、可选assetId/cursorType、assets[]platform: darwin、hotspot、scaleFactor。会话层还有一个值得注意的细节captureSample()里会统计连续越界样本数达到OUTSIDE_HIDE_THRESHOLD 333ms 间隔下约 100ms才把visible置为false——短暂滑出屏幕的快划会被渲染端按 clip-path 裁到画布边缘而不是突然消失。这正是后文多显示器测试项要验证的行为。构建 helper构建命令一条npm run build:native:mac它会同时构建两个 Swift helperopenscreen-screencapturekit-helper与openscreen-macos-cursor-helper并复制到两个位置electron/native/screencapturekit/build/—— 本地 dev server 使用electron/native/bin/darwin-arm64/或darwin-x64/—— 打包packaged构建使用。对应实现是 build-macos-screencapturekit-helper.mjs它先用xcodebuild -version检查完整 Xcode 是否处于激活状态只有 Command Line Tools 会因缺少 SwiftPM 需要的 SDK/平台元数据而失败然后对每个目标架构执行swift build -c release --arch arch再把产物拷入上述两处并chmod 0755。构建脚本还支持OPENSCREEN_MAC_HELPER_ARCHS环境变量按架构矩阵构建CI 用每个架构产出独立的单架构二进制、分放在各自的darwin-arch目录不生成 fat binary。如果构建报错提示缺少 SDK 元数据切换到完整 Xcode 并接受许可sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer sudo xcodebuild -license accept另外注意 Package.swift 声明了platforms: [.macOS(.v13)]即 helper 本身要求 macOS 13Ventura及以上与后文原生录制的可用性规则一致。直接冒烟测试 helper在启动整个应用之前可以先单独运行 cursor helper 观察原始输出BINelectron/native/screencapturekit/build/openscreen-macos-cursor-helper ($BIN {sampleIntervalMs:100} PID$!; sleep 2; kill $PID) | head -20helper 把命令行第一个参数作为 JSON 请求解析字段sampleIntervalMs可选缺省 33ms且被max(8, ...)钳制到不低于 8ms。预期第一行输出{type:ready,mouseTapReady:true,accessibilityTrusted:false,timestampMs:...}accessibilityTrusted: false在开发/未签名构建中是正常现象——它意味着 text/pointer 亲和检测被禁用但基于NSCursor的原生位图捕获仍然工作。预期采样行{type:sample,assetId:a7472...,asset:{id:a7472...,imageDataUrl:data:image/png;base64,...,width:64,height:64,hotspotX:26,hotspotY:16,scaleFactor:2.0},...} {type:sample,assetId:a7472...,...}在 helper 运行期间把光标移到一个文本输入框上方需已授权 Accessibility应看到出现一个新的assetId且位图不同。对应源码路径currentCursorType()先检查AXIsProcessTrusted()未授权直接返回nil授权后调用AXUIElementCopyElementAtPosition取鼠标下的元素沿最多 5 层父链向上找角色——文本类角色AXTextField/AXTextArea/AXTextView/AXComboBox返回text指针类角色AXLink、AXButton、AXMenuButton、AXCheckBox、AXTab、AXMenuItem等返回pointer其余情况返回nil渲染端回退到原生捕获的位图——这正是默认箭头与自定义光标能以真实图像呈现的机制。让应用使用自定义 helper 二进制在本地诊断时可以用环境变量把会话指向自编译的 helperexport OPENSCREEN_MAC_CURSOR_HELPER_EXE/path/to/openscreen-macos-cursor-helper npm run dev从 macNativeCursorRecordingSession.ts 的helperCandidates()可以看到完整的解析优先级OPENSCREEN_MAC_CURSOR_HELPER_EXE环境变量electron/native/screencapturekit/build/openscreen-macos-cursor-helperdev 路径electron/native/bin/arch/openscreen-macos-cursor-helper源码树内的打包路径resources/electron/native/bin/arch/...打包应用的process.resourcesPath下。按顺序探测第一个可执行X_OK的文件全部失败则返回null会话转入仅位置回退模式startPositionOnlyFallback()只记录鼠标坐标、不捕获位图。另外会话启动时会用READY_TIMEOUT_MS 5_0005 秒等待ready事件超时或子进程提前退出都会触发同样的回退并打印[cursor-macos] falling back to position-only cursor telemetry警告。macOS 权限两项独立授权这条链路需要两个相互独立的权限权限作用授权位置Screen RecordingScreenCaptureKit 视频捕获System Settings → Privacy Security → Screen System Audio Recording → Electron ✅Accessibilitytext/pointer光标类型检测affordance 提示System Settings → Privacy Security → Accessibility → Electron ✅Screen Recording 是必需的没有它录制根本不会开始原生录制的三项可用性规则之一。Accessibility 是可选的没有它cursorType永远为null所有光标都从捕获到的位图渲染不做 SVG 替换。对非 text/pointer 形状而言这不是质量损失而是预期的回退行为。授予任一权限后必须完全退出并重启dev server——getMediaAccessStatus按进程缓存结果。还有一个更可靠的信号文档特别指出在未签名的 dev 构建中getMediaAccessStatus(accessibility)可能不反映实际开关状态应以 helper 在ready事件中上报的accessibilityTrusted为准对应源码中requestAccessibilityTrust()调用AXIsProcessTrustedWithOptions的探测结果。手工测试清单以下清单按优先级组织覆盖核心捕获、亲和替换、热点对齐、点击检测、优雅降级、多显示器与长录制内存。P0 — 核心位图捕获录制一段短视频打开编辑器确认默认箭头光标是真实系统箭头而不是仓库自带的 SVG 近似图。在悬停网页浏览器时录制确认自定义 CSS 光标如cursor: grab、cursor: crosshair以其实际形状出现。导出 MP4确认导出视频中的光标渲染正确。导出 GIF做同样的检查。P1 — 亲和替换需要 Accessibility授予 Accessibility 权限并重启应用。录制悬停文本输入框确认 text I-beam 使用的是仓库自带 SVG 版本比系统位图更精致。录制悬停链接/按钮确认 pointer 手型使用自带 SVG。P1 — 热点对齐Retina在 Retina 显示器上录制一次对小型按钮的精确点击在编辑器中确认光标尖端与实际点击点重合。helper 上报scaleFactor: 2.0渲染器会把像素尺寸和 hotspot 除以该值恢复为点point尺寸——即 Swift 端hotSpot.x * scaleFactor的逆运算。P1 — 点击检测录制若干次左键单击确认编辑器中每次点击都触发点击回弹click-bounce动画。确认录制会话 sidecarvideoPath.cursor.json内的cursorRecordingData中存在interactionType: click与mouseup事件。sidecar 中interactionType的判定在 macNativeCursorRecordingSession.ts 的captureSample()里本周期出现按下leftButtonPressed或状态由松变按记为click出现抬起或由按变松记为mouseup其余为move。click/mouseup成对出现意味着CGEventTap事件流完整。P2 — 优雅降级移走两份构建产物位置的 helper 二进制后开始录制会话应当以provider: none成功仅位置遥测、渲染默认箭头之后再恢复两份二进制ARCH$([ $(uname -m) arm64 ] echo darwin-arm64 || echo darwin-x64) mv electron/native/screencapturekit/build/openscreen-macos-cursor-helper /tmp/cursor-helper-build mv electron/native/bin/$ARCH/openscreen-macos-cursor-helper /tmp/cursor-helper-bin # ... 开始录制然后恢复 mv /tmp/cursor-helper-bin electron/native/bin/$ARCH/openscreen-macos-cursor-helper mv /tmp/cursor-helper-build electron/native/screencapturekit/build/openscreen-macos-cursor-helper撤销 Accessibility 授权确认录制仍可用、光标从位图渲染无 SVG 替换。这一项验证的正是findMacCursorHelperPath()全部候选失败后startPositionOnlyFallback()的路径回退模式按同一采样间隔持续记录鼠标位置stop()时因assets为空而输出provider: none。P2 — 多显示器录制期间把光标移到副显示器确认光标被裁切clip到画布边缘、而不是快速划动时突然消失并在连续约 100ms 越界后隐藏。对应实现即前文提到的consecutiveOutsideSamples计数与OUTSIDE_HIDE_THRESHOLD 3短暂越界走 clip-path 裁切持续越界才置visible: false避免多屏移动产生的残影光标与运动拖尾。P2 — 长录制内存在浏览器、终端、编辑器之间切换并录制 3–5 分钟。helper 不应内存增长因为每次循环通过autoreleasepool排空 Cocoa 对象。用 Activity Monitor 观察openscreen-macos-cursor-helper的 RSS 应在头几秒后保持平稳。健康录制的 Sidecar 长什么样检查与录制视频一同写出的光标 sidecar 文件视频保存为/tmp/rec.mp4时sidecar 即/tmp/rec.mp4.cursor.jsonIPC 层以${videoPath}.cursor.json生成该路径见 handlers.ts{ version: 2, provider: native, assets: [ { id: a7472..., platform: darwin, imageDataUrl: data:image/png;base64,..., width: 64, height: 64, hotspotX: 26.0, hotspotY: 16.0, scaleFactor: 2.0 } ], samples: [ { timeMs: 0, cx: 0.42, cy: 0.38, visible: true, assetId: a7472..., interactionType: move }, ... ] }判读标准provider: native且assets非空 → 位图捕获处于活动状态provider: none且assets: []→ helper 未找到或在发出ready之前就退出了可结合启动日志中的falling back to position-only警告定位。samples中的cx/cy是相对录制显示区边界的归一化坐标0–1由screen.getCursorScreenPoint()减去显示区原点再除以宽高得到并做clamp(0,1)。原生 macOS 捕获后端与视频/光标分离OpenScreen 在 macOS 上会把录制路由到 ScreenCaptureKit helperopenscreen-screencapturekit-helper使真实系统光标不出现在视频帧内光标的位置与位图由 cursor helper 独立捕获最终在编辑器与导出管线中合成。这条路由的可用性规则为macOS 13Ventura或更新版本openscreen-screencapturekit-helper二进制存在已授予 Screen Recording 权限。构建两个 helper 仍是npm run build:native:mac本地诊断自定义 cursor helper 二进制时同样使用OPENSCREEN_MAC_CURSOR_HELPER_EXE环境变量叠加npm run dev。这个视频去光标、位图单独采样、后期合成的架构意味着两条链路可以独立调试视频侧问题看 SCK helper 的warning/error事件光标侧问题看 cursor helper 的 NDJSON 输出与 sidecar 文件。已知限制Intelx86_64Mac分发的 helper 构建目标是darwin-arm64。Intel Mac 需要在目标机器上用npm run build:native:mac从源码构建构建脚本会按process.arch选择x86_64并输出darwin-x64目录。未签名/dev 构建中的 AccessibilitygetMediaAccessStatus(accessibility)对 dev 模式下的未签名 Electron 可能不反映开关状态。helper 始终会自行探测并在ready事件中上报accessibilityTrusted——应把它当作权威信号。应用自定义光标CGS 层NSCursor.currentSystem捕获的是活动 AppKit 光标某些游戏或 GPU 加速应用通过 CoreGraphics/CGS 层设置的光标在这里可能不可见这是 macOS API 的已知限制。小结这条测试管线的核心思路是分层验证先用 standalone 冒烟命令确认 helper 的 NDJSON 协议与ready/sample事件再用 sidecar 文件核对provider、assets、interactionType是否完整最后按 P0→P2 清单覆盖位图真实性、SVG 亲和替换、Retina 热点对齐、点击事件、降级行为、多显示器与长录制内存。每一步的预期值都能直接对应到 main.swift 的采样实现与 macNativeCursorRecordingSession.ts 的解析/降级逻辑使得任何一步失败时都能快速区分是 helper 侧Swift 进程、权限还是会话侧TS 解析、路径解析的问题。【免费下载链接】openscreenCreate stunning demos for free. Open-source, no subscriptions, no watermarks, and free for commercial use. An alternative to Screen Studio.项目地址: https://gitcode.com/GitHub_Trending/open/openscreen创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表