SpinWin: 通过 ScreenCaptureKit 可视化旋转 macOS 窗口

SpinWin: 通过 ScreenCaptureKit 可视化旋转 macOS 窗口

SpinWin 使 macOS 上的窗口可视化旋转

SpinWin 是一个 macOS 菜单栏实用工具,允许用户以任何角度(包括 90 度增量、任意角度或连续旋转)来可视化旋转或旋转任何应用程序窗口。由于 macOS 没有提供旋转另一应用程序窗口的公共 API,SpinWin 通过捕获窗口的可视缓冲区并在变换的覆盖窗口中渲染它来实现此效果。

技术实现:通过捕获和覆盖伪造旋转

SpinWin 通过实现一个三步流程来模拟窗口旋转,从而绕过 macOS 中缺少公共旋转 API 的限制:

  1. 离屏定位:应用程序使用 Accessibility API 将选定的目标窗口移出屏幕。窗口保持活动状态并继续渲染,使用户即使看不见也能捕获它。
  2. 连续捕获:使用 ScreenCaptureKitSCStreamdesktopIndependentWindow 过滤器),SpinWin 持续捕获窗口的内容。此方法确保仅捕获特定窗口的缓冲区,防止覆盖镜像自身。
  3. 层变换:捕获的帧被绘制到一个透明且无边框的覆盖窗口中。该窗口定位在原始窗口所在位置,并使用 CALayer 变换进行旋转。覆盖窗口会自动调整大小以适应旋转内容的边界框以防止裁剪,并在连续旋转期间使用对角线大小的正方形。

操作要求和权限

要使 SpinWin 正常工作,需要两个特定的 macOS 系统权限:

  • 屏幕录制:通过 ScreenCaptureKit 捕获窗口内容所必需。
  • 辅助功能:将源窗口移出屏幕并在停止旋转时恢复到其原始位置所必需。

当前技术限制

SpinWin 是一种可视化模拟,由于 macOS API 限制,存在若干固有限制:

  • 非交互式覆盖:覆盖是一个实时可视化馈送;它不支持交互。点击和按键无法传递给隐藏的源窗口。
  • Mission Control 和 Exposé 泄漏:由于隐藏的窗口是停在屏幕外的真实窗口,它在 Mission Control 和 Exposé 中仍以未旋转的状态可见。没有公共 API 能在不最小化窗口的情况下将特定窗口从这些视图中排除,而最小化会停止捕获过程。
  • 渲染节流:一些应用程序可能在窗口被移出屏幕时限制其渲染,这可能导致捕获馈送的帧率下降。
  • 调整大小时的静态占用空间:如果源窗口在旋转时自行调整大小,覆盖将保持原始占用空间。捕获将变为带黑边,直到旋转停止并重新开始。

项目架构

SpinWin 代码库被组织成若干专门的组件:

Component Responsibility
main.swift 应用入口和菜单栏策略
AppDelegate.swift 管理菜单栏项和交互逻辑
Permissions.swift 处理屏幕录制和辅助功能请求
WindowPicker.swift 提供全屏窗口选择和旋转选项
RotationManager.swift 跟踪每个窗口的活动旋转会话
RotationSession.swift 协调隐藏-捕获-覆盖工作流程
CaptureEngine.swift 管理 ScreenCaptureKit 流和 IOSurface
OverlayWindow.swift 处理透明旋转覆盖和拖动
AccessibilityWindowMover.swift 管理将源窗口移出屏幕并恢复它们

社区视角

用户指出,虽然该工具主要是一种新奇事物,但它可能为特定应用提供实用价值,例如通常允许画布旋转的绘图应用。一位开发者指出它与早期 Linux 桌面环境中的“摇晃窗口”相似:

这让我想起了当时摇晃窗口在 Linux 中是热门话题。那时它很好玩,现在看来又有趣了。

Sources