peetzweg/opendisplay
GitHub: peetzweg/opendisplay
开源免费的 Sidecar 替代方案,将 iPhone/iPad 作为 Mac 的第二显示器,支持 USB/WiFi 低延迟连接、Retina HiDPI 显示和触摸输入。
Stars: 745 | Forks: 61

# OpenDisplay
**将你闲置的 Apple 设备变成 Mac 的第二显示器 —— 免费、开源、无需订阅。**
目前支持 iPhone 和 iPad,未来计划支持闲置的 MacBook。作为 Apple Sidecar、Duet Display 和 Luna Display 的自托管替代方案:真正的屏幕扩展(不仅仅是镜像),支持 Retina 级清晰度,可通过 USB 或 WiFi 连接,并支持触摸和滚动输入。
[官网](https://peetzweg.github.io/opendisplay/) · [快速开始](#quick-start) · [工作原理](#how-it-works) · [常见问题](#faq) · [参与贡献](#contributing)
## 为什么开发 OpenDisplay
将 iPhone 或 iPad 变成 Mac 的外接显示器,这是一个已经解决的问题 —— 但现有的每个选项都有其局限性:
- **Apple Sidecar** 是免费的,但要求两台设备登录*相同的 Apple ID*,完全不支持 iPhone,且仅在受支持的硬件组合上有效。
- **Duet Display** 转向了订阅制。
- **Luna Display** 需要额外的硬件加密狗。
OpenDisplay 填补了这一空白:提供了一种**免费、开源、无需账号、无需加密狗**的方式,让你已拥有的 iOS 设备作为真正的第二显示器使用。如果你打算自己写一个 —— 别了!不如在这里贡献代码;那些最困难的部分(虚拟显示器创建、低延迟 H.264 pipeline、USB 传输、输入注入)都已经实现了。
## 功能
- 🖥️ **真正的屏幕扩展** —— macOS 会将该设备视为真正的第二显示器(可以将窗口拖拽过去,在系统设置中排列),而不是镜像。镜像也可作为一种模式使用。
- 🔌 **USB 有线连接实现最低延迟** —— 通过 macOS 内置的 `usbmuxd` 经由 Lightning/USB-C 数据线进行流传输;即插即用,无需网络,没有 WiFi 抖动,也不需要辅助工具。
- 📶 **零配置 WiFi** —— iPhone 通过 Bonjour 进行广播;只需在 Mac 上的下拉菜单中选择它即可。
- 🔍 **Retina / HiDPI** —— 虚拟显示器与设备屏幕逐像素匹配(@2x),因此文字非常清晰。
- 👆 **内置触摸输入** —— 你的 iPhone 将成为 macOS 的触摸屏:**点击即单击**,**拖拽即拖拽**,以及手感像触控板一样的**双指滚动**。(Apple Pencil 的支持已在路线图中。)
- 🔄 **竖屏或横屏** —— 旋转设备,虚拟显示器会以原生分辨率自动重建为垂直显示器。
- ⚡ **低延迟 pipeline** —— 硬件 H.264 编码(VideoToolbox,实时模式,无 B 帧),TCP_NODELAY,带有 keyframe 恢复机制的丢帧背压,以及通过 `AVSampleBufferDisplayLayer` 进行解码和渲染。
- 🔒 **自托管且隐私安全** —— 你的屏幕内容永远不会经过任何人的服务器。两个小应用,一个 TCP 连接,就这么简单。
## 工作原理
```
MAC (sender) iPHONE / iPAD (receiver)
CGVirtualDisplay ← macOS believes a monitor is attached
→ ScreenCaptureKit (capture the virtual display)
→ VideoToolbox H.264 (hardware, real-time)
→ TCP [4-byte length][Annex B frame] ═══════→ NWListener :9000
→ AVSampleBufferDisplayLayer
← JSON control messages (hello, touch, scroll) ═══
→ CGEvent injection (click / drag / scroll)
```
**手机监听,Mac 连接** —— 这种顺序使得完全相同的代码可以通过 USB(通过每台 macOS 安装中内置的 `usbmuxd` daemon)和 WiFi 工作。手机会广播其原生屏幕尺寸;Mac 会以在 points 上刚好是一半的大小(@2x HiDPI)创建一个 `CGVirtualDisplay`,并将像素流传输回去。
`CGVirtualDisplay` 是一个 **CoreGraphics 私有 API**(与 BetterDisplay 和 DeskPad 使用的相同) —— 这正是该项目无法在 App Store 上发布而只能存在于 GitHub 上的原因。
## 安装
你需要**两个应用**:一个 Mac 应用(负责捕获和发送)和一个 iOS 应用(负责接收和显示)。
### 预编译下载
从[最新版本](https://github.com/peetzweg/opendisplay/releases/latest)中获取 `OpenDisplay.dmg`。
该应用使用 Developer ID 证书签名并经过了 Apple 的公证,因此在 macOS 14+ 上只需双击即可打开 —— 没有 Gatekeeper 警告。打开 `.dmg` 文件并将应用拖入“应用程序”文件夹即可。
### iPhone 应用
- **TestFlight**(推荐):在 [testflight.apple.com/join/3NYaY11c](https://testflight.apple.com/join/3NYaY11c) 加入公开测试版。
- **从源码构建**:在 Xcode 中打开项目,在“签名”下选择你免费的 Apple ID,然后点击运行。大约需要 2 分钟。
## 快速开始(基于源码)
### 前置条件
```
brew install xcodegen # project generation
```
需要 Xcode 15+ 以及免费或付费的 Apple 开发者账号(用于将 iOS 应用侧载到你的设备上)。
### 构建
```
git clone https://github.com/peetzweg/opendisplay.git
cd opendisplay
echo "DEVELOPMENT_TEAM=YOURTEAMID" > .env # your Apple team ID, for signing
./generate.sh # runs xcodegen with your .env
xcodebuild -project OpenSidecar.xcodeproj -scheme OpenSidecarMac \
-configuration Debug -derivedDataPath build build
xcodebuild -project OpenSidecar.xcodeproj -scheme OpenSidecariOS \
-configuration Debug -destination 'generic/platform=iOS' \
-derivedDataPath build -allowProvisioningUpdates build
```
(或者在 Xcode 中打开 `OpenSidecar.xcodeproj`,并在每个 target 上点击运行。你的团队 ID 可以在 [developer.apple.com/account](https://developer.apple.com/account) 的“Membership”下找到,或者直接在 Xcode 的“签名”面板中选择你的团队。)
### 运行(USB —— 推荐)
1. 在 iPhone 上安装并打开 **OpenDisplay**(它会在 9000 端口上监听)。
2. 在 Mac 上,运行 `./run.sh`(或者直接打开应用)—— 它会直接与 macOS 内置的 `usbmuxd` 通信,并通过数据线自动连接。不需要任何隧道工具。
3. 当 macOS 询问时,授予**屏幕录制**(用于捕获)和**辅助功能**(用于触摸)权限 —— 每项仅需一次。
4. 将一个窗口拖到你的新显示器上。大功告成。
### 运行(WiFi)
打开 iPhone 应用,然后在 Mac 应用的“连接”菜单中选择**“iPhone (WiFi)”**。通过 Bonjour 自动发现。USB 的延迟更低;而 WiFi 的优势在于无需数据线。
### 权限核对清单
macOS 和 iOS 对此应用所需的若干权限进行了限制 —— 大多数会在首次使用时提示,但如果被拒绝或遗漏,有些会**静默失败**。Mac 应用会显示一个实时的权限状态面板;iPhone 应用包含一个设置屏幕(摇晃手机,或在闲置时点击“Settings & Help”)。
| 位置 | 权限 | 需要用于 | 如果缺失 |
|---|---|---|---|
| Mac | Screen Recording | 捕获屏幕 | 手机端显示黑屏 |
| Mac | Accessibility | 触摸/滚动输入 | 点击无反应 |
| Mac | **Local Network** | WiFi 发现 | “连接”菜单中无设备 |
| iPhone | **Local Network** | WiFi 发现 | Mac 无法找到手机 |
以上所有选项均位于系统设置(Mac)/ 设置(iPhone)的**隐私与安全性**下。Local Network 选项仅在 WiFi 模式下需要 —— USB 模式无需此权限。如果从未出现提示,请手动切换开关,或者强制退出并重新打开应用。
## 常见问题
**为什么我会在菜单栏中看到紫色的屏幕录制指示器?**
这是 macOS 针对任何捕获屏幕的应用显示的隐私指示器 —— Duet、Luna、OBS 和 Zoom 也会触发它。Apple Sidecar 之所以不会触发,仅仅是因为它是在操作系统内部实现的,而不是基于公开的捕获 API。它不能(也不应该)被应用隐藏;这正是 macOS 告知你正在进行屏幕捕获的方式。
**Mac 应用没有在“连接”菜单中显示我的 iPhone(WiFi)。**
双方都需要 **Local Network** 权限,而且如果缺少该权限,双方都会*静默失败*:请检查 Mac **和** iPhone 上的“隐私与安全性 -> Local Network”,确保两者在同一 WiFi 网络下,并保持 iPhone 应用在前台打开。USB 模式不受影响。
**支持 iPad 吗?** 接收端应用是通用的(iPhone + iPad);iPad 使用相同的代码库。针对 iPad 的特定优化(Pencil、压感)已在路线图中。
**为什么使用 H.264 而不是 HEVC/AV1?** 硬件 H.264 编码/解码的通用速度非常快,延迟也极佳。HEVC 已作为计划选项,以提供更好的压缩质量。
**我的屏幕内容会被发送到任何地方吗?** 不会。只在你 Mac 和设备之间通过数据线或局域网建立一条直接的 TCP 连接。没有服务器,没有账号,没有数据分析。完整细节 —— 包括应用在本地存储了什么,以及当前 WiFi 加密的注意事项 —— 请参见[隐私页面](https://peetzweg.github.io/opendisplay/privacy.html)。
**遵循什么许可证?我可以 fork 它或用于商业用途吗?**
[GPL-3.0](LICENSE)。可自由使用、学习和改编 —— 也包括商业用途。如果你发布了修改后的版本,它必须在相同的许可证下保持开源,并保留原始署名,这样改进就能回馈社区,而不是流入封闭的 fork 中。(v0.4.x 及之前的版本使用 MIT 许可证,且仍可根据该条款使用。)
**macOS 更新会破坏它吗?** 有可能 —— `CGVirtualDisplay` 是私有 API。同样的风险也适用于所有虚拟显示器产品。捕获/流式传输 pipeline 本身仅使用公开的 API。
**音频呢?** 暂不在范围之内。
## 对比
| | OpenDisplay | Apple Sidecar | Duet Display | Luna Display |
|---|---|---|---|---|
| 价格 | **免费、开源** | 免费 | 订阅制 | $$$ + 加密狗 |
| iPhone 作为显示器 | ✅ | ❌(仅限 iPad) | ✅ | ✅ |
| 不同的 Apple ID | ✅ | ❌ | ✅ | ✅ |
| 有线(USB) | ✅ | ✅ | ✅ | ❌ |
| 真正的扩展 | ✅ | ✅ | ✅ | ✅ |
| 触摸输入 | ✅ | ✅ | ✅ | ✅ |
| 自托管/可审计 | ✅ | — | ❌ | ❌ |
## 路线图
作为[路线图 issue](https://github.com/peetzweg/opendisplay/issues?q=is%3Aissue+is%3Aopen+label%3Aroadmap) 进行跟踪 —— 如果你想贡献,欢迎认领!
**连接与分发**
- [#16](https://github.com/peetzweg/opendisplay/issues/16) 带有配对码的加密 WiFi 传输
- [ ] iOS 应用的 App Store 发布 + 经过公证的 Mac 下载
**输入**
- [#4](https://github.com/peetzweg/opendisplay/issues/4) 支持压感和倾斜的 Apple Pencil
- [#5](https://github.com/peetzweg/opendisplay/issues/5) 右键点击和多点触控手势
- [#6](https://github.com/peetzweg/opendisplay/issues/6) 硬件键盘透传
- [#7](https://github.com/peetzweg/opendisplay/issues/7) 屏幕上的修饰键侧边栏
**显示与媒体**
- [#9](https://github.com/peetzweg/opendisplay/issues/9) 分辨率与质量设置
- [#10](https://github.com/peetzweg/opendisplay/issues/10) HEVC 编码
- [#12](https://github.com/peetzweg/opendisplay/issues/12) 音频转发
- [#17](https://github.com/peetzweg/opendisplay/issues/17) macOS 接收端 —— 将另一台 Mac 用作显示器
**体验**
- [#11](https://github.com/peetzweg/opendisplay/issues/11) 带有自动连接功能的菜单栏应用模式
- [#13](https://github.com/peetzweg/opendisplay/issues/13) 电池与生命周期感知
**探索性**
- [#14](https://github.com/peetzweg/opendisplay/issues/14) 超越局域网的远程访问
- [#15](https://github.com/peetzweg/opendisplay/issues/15) 其他客户端平台
已完成:预编译版本、内置 USB 连接(无需辅助工具)、通过 Bonjour 实现的 WiFi 连接、竖屏模式、触摸 + 双指滚动、性能监控覆盖层、iPad 支持、同时连接多个设备([#8](https://github.com/peetzweg/opendisplay/issues/8) —— 每个连接的设备都会成为其独立的扩展显示器)。
## 自动更新(macOS 应用)
macOS 应用使用 [Sparkle](https://sparkle-project.org) 进行自我更新 ——
这是一个开源框架,**而不是**托管服务。更新检查仅访问我们自己的基础设施:
- 该应用读取一个托管在落地页网站上的 **appcast** 订阅源:
`https://opendisplay.app/appcast.xml`(位于 `project.yml` 中的 `SUFeedURL`)。
- 发布工作流(`.github/workflows/release.yml`,`build-mac` 作业)
对经过公证的 `OpenDisplay.dmg` 运行 Sparkle 的 `generate_appcast`,
使用 EdDSA 密钥对其进行签名,将结果提交给 `public/appcast.xml`,
并触发 Pages 部署 —— 因此发布的订阅源会将下载链接指向 GitHub Release 的 assets。
- Sparkle 在安装前会验证 EdDSA 签名和 Apple 的公证。该应用会在后台自动检查
(`SUEnableAutomaticChecks`),并在菜单栏窗口的**退出**旁边提供一个手动的**“检查更新…”**
按钮。
### 维护者前置条件(在自动更新上线之前)
自动更新功能已**搭建好脚手架但尚未激活**,直到签名密钥配置到位。私钥**绝对不会**被提交 —— 它仅作为 CI 密钥存在。要启用它:
1. **生成密钥对。** 运行一次 Sparkle 的 `generate_keys`(它包含在 Sparkle SPM artifact bundle 以及 `bin/generate_keys` 下的发布 tarball 中)。它会打印出一个**公**钥,并将**私**钥存储在你的登录 keychain 中。
2. **钥 ->** 将其粘贴到 `project.yml` 中的 `SUPublicEDKey` 里(替换掉 `REPLACE_WITH_SUPUBLICEDKEY_FROM_generate_keys` 占位符),然后重新运行 `xcodegen generate` 并提交。
3. **私钥 ->** 将其添加为 `SPARKLE_PRIVATE_KEY` GitHub Actions 密钥(如有需要,可使用 `generate_keys -x private_key.pem` 导出)。在缺少此密钥时,`release.yml` 中的 appcast 步代会平滑地空操作,因此在准备好之前,版本发布仍会正常工作。
4. **首次 appcast 将在两个密钥都设置完成后的下一次发布时自动发布**。确认 `https://opendisplay.app/appcast.xml` 可以解析后,然后**在真正签名/公证的构建版本上测试完整的更新流程**(检查 -> 下载 -> 验证 -> 重启)—— 这无法在 CI 中验证。
## 许可证
[GPL-3.0](LICENSE) —— 版权所有 (c) 2026 Philip Poloczek。
免费使用、学习和改编。如果你分发修改后的版本,它必须在相同的许可证下保持开源,并保留原始署名 —— 这样改进就能回馈所有人,而不是流入封闭的 fork 中。(v0.4.x 及之前的版本使用 MIT 许可证;这些版本仍可根据 MIT 条款使用。)
*关键词:iPhone 第二显示器 Mac,iPad 外接显示器,免费 Sidecar 替代方案,Duet Display 替代方案,开源 macOS 屏幕扩展,使用 iPhone 作为副屏,Mac 虚拟显示器,USB 第二显示器。*
标签:H.264, iOS, usbmuxd, 屏幕扩展, 虚拟显示器