TGViewer
V2EX V2EX @pushv2ex · 550 subscribers
Post #238064 3
[分享创造] 开发日志:为了把窗口送回「原来的桌面」,我把 macOS 的私有接口翻了一遍

上一篇发了 LayoutKeeper 之后,有朋友问:恢复窗口位置的工具有好几个,为什么偏偏「连桌面一起恢复」这么难?这篇写写我做这个功能时踩的过程,纯开发日志向。

先说结论:难的不是「移动窗口」,是「知道窗口在哪个桌面」和「让系统允许你把它送回去」。

**第一坑:其他桌面的窗口,系统默认不给你看。**
辅助功能 API ( AX )只能拿到当前桌面的窗口列表。想枚举外接屏上其他桌面的窗口,得拿公开的窗口列表先算出候选,再用远程元素令牌把窗口找回来,而且要校验进程和窗口编号对得上——校验不过的不动,宁可漏恢复,也不能移错别人的窗口。

**第二坑:跨桌面的老接口早就被系统拦了。**
以前把窗口挪到另一个 Space 的老办法,从 macOS 14.5 起就被系统拦截,硬调也没用。真正可用的新接口是 macOS 26 才出现的 SkyLight 桥接操作。所以这也是为什么 LayoutKeeper 的跨桌面恢复要求 macOS 26+:不是我不想兼容旧系统,是旧系统上这条路根本不存在。同一桌面内的位置/大小恢复,macOS 14+ 就能用,不受影响。

**第三坑:调了接口不等于窗口真的过去了。**
私有接口没有文档,返回值也不能全信。我的做法比较笨:每个窗口移动后,回读它实际所在的桌面序号,再回读位置和大小,两次都对上才算成功;对不上的就回滚到移动前,并把这次恢复整体标记为可一键撤销。同名窗口分不清是哪个时(比如开了三个同名文档),直接标「需要确认」让用户选,绝不猜——猜错一次,用户就再也不敢用自动恢复了。

**没做到的也说清:**
- Finder 窗口跨桌面移动不了:身份校验需要进程启动时间,Finder 拿不到,只能跳过(同桌面内调位置没问题)。
- 目前只支持一块外接屏,多屏的桌面归属关系复杂得多,还在想。
- 私有接口依赖系统版本,启动时会自动检测,接口不在就自动关掉跨桌面功能,不会乱移窗口。

好处也有:全程不需要关 SIP 、不需要注入 Dock ,只申请「辅助功能」一个权限——这是我愿意用私有接口的前提,安全性上不能向用户借太多。

项目还是那个:LayoutKeeper ,GPL-3.0 开源,免费。
仓库: https://github.com/tumingke0/LayoutKeeper

做过窗口管理 / Space 相关开发的朋友,欢迎聊聊你们是怎么处理跨桌面窗口枚举的,这块资料实在太少了。
More from @pushv2ex
  1. Oct 14, 2026[又拍云] 又拍云跑路了? 官网在线客服,所有地区的联系电话,邮箱,都搜不到了,x 上面说法人列入被执行人了,还有好多数据在里面,有了解的朋友么
  2. Oct 14, 2026[程序员] 让 AI 分析了一下我帖子里的典型喷子,分重度和重度 不得不说 AI 搞这些事情简直是快狠准,尤其是一句话评价,我真的绷不住,啊哈哈哈哈哈哈 重度 ● crazy0x:…
  3. Oct 14, 2026[分享创造] Transel - 使用 rust + gpui-kit 做了个跨平台的划词翻译项目 Transel 跨平台划词翻译工具。选中文字后按下热键,译文显示在鼠标附近的浮窗…
  4. Oct 14, 2026[Claude] pro 下午被封,用了 20 天 今天下午 5h 额度用满,还差 3h 重置,一直开着 claude code 等恢复结果折在距离 1 个半小时😭
  5. Oct 14, 2026[程序员] 我让 Gemini 4 当高管、DeepSeek 当员工,开了一家软件公司 今天 Gemini-4-Argon 正式发布了,实测它的长程规划和调度决策能力强得让人后背发…
  6. Oct 14, 2026[分享发现] Muse 的内置浏览器 ip 显示国内 想问问佬们, 你们是否也这样吗 这两天发现 muse 好像访问不了外网,就好奇它的浏览器服务器在哪 打开 ping0 发现是内…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →