Files
SimpleRemoter/docs/macOS_Support_Design.md
2026-08-21 14:15:31 +02:00

577 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# macOS 远程桌面客户端设计文档
## 1. 概述
### 1.1 目标
在不影响现有 Windows/Linux 功能的基础上,为 YAMA 项目添加 macOS 客户端支持,前期实现远程桌面控制功能。
### 1.2 功能范围Phase 1
| 功能 | 优先级 | 状态 | 说明 |
|-----|--------|------|------|
| 连接服务器 | P0 | ⏳ Stub | 复用现有网络协议 |
| 屏幕捕获 | P0 | ✅ 完成 | CGDisplayCreateImage 实现 |
| 鼠标控制 | P0 | ✅ 完成 | CGEvent 实现 |
| 键盘控制 | P0 | ✅ 完成 | CGEvent + VK映射 |
| 光标同步 | P1 | ⏳ 待实现 | 显示当前光标类型 |
| 多显示器 | P2 | ⏳ 待实现 | 支持多屏幕选择 |
### 1.3 参考实现
- **Linux 客户端** (`linux/`): 架构相似,协议兼容
- **RustDesk**: 成熟的跨平台远程桌面开源项目
- **Sunshine**: 高性能游戏串流项目
---
## 2. 架构设计
### 2.1 目录结构(已实现)
```
YAMA/
├── common/ # 跨平台共享(已有)
│ ├── commands.h # 协议定义
│ └── ...
├── client/ # Windows 客户端(不动)
├── linux/ # Linux 客户端(不动)
└── macos/ # macOS 客户端Phase 1 完成)
├── CMakeLists.txt # 构建配置
├── build.sh # 一键编译脚本
├── README.txt # 使用说明
├── IOCPClient.h # 网络客户端 stub
├── main.mm # 入口点 + 系统信息
├── Permissions.h # 权限检查声明
├── Permissions.mm # 权限检查实现
├── ScreenHandler.h # 屏幕捕获声明
├── ScreenHandler.mm # 屏幕捕获实现
├── InputHandler.h # 输入模拟声明
├── InputHandler.mm # 输入模拟实现
├── SystemManager.h # 进程管理声明
└── SystemManager.mm # 进程管理实现
```
### 2.2 模块依赖关系
```
┌─────────────────────────────────────────────────┐
│ main.mm │
│ (入口、权限检查、系统信息收集) │
└─────────────┬───────────────────────────────────┘
┌─────────┼─────────┬─────────────┐
▼ ▼ ▼ ▼
┌───────┐ ┌───────┐ ┌───────┐ ┌────────────┐
│Screen │ │Input │ │System │ │IOCPClient │
│Handler│ │Handler│ │Manager│ │(网络 stub) │
└───┬───┘ └───┬───┘ └───────┘ └────────────┘
│ │
▼ ▼
┌─────────────────────────────────┐
│ macOS 系统框架 │
│ CoreGraphics (截屏) │
│ Carbon (键码定义) │
│ ApplicationServices (权限) │
└─────────────────────────────────┘
```
---
## 3. 技术选型
### 3.1 屏幕捕获方案(已选定)
| 方案 | 最低版本 | 性能 | 复杂度 | 选择 |
|-----|---------|------|--------|-----|
| `CGDisplayCreateImage` | 10.5 | 中 | 低 | **✅ 已采用** |
| `CGDisplayStream` | 10.8 | 中 | 中 | 备选 |
| `ScreenCaptureKit` | 12.3 | 高 | 高 | 未来考虑 |
**选择 CGDisplayCreateImage**
- 实现简单,兼容性好
- 与 Linux 端 `XGetImage` 方案一致
- 足以支持 10-30 FPS 传输
### 3.2 输入模拟方案(已实现)
| 功能 | API | 实现状态 |
|-----|-----|---------|
| 鼠标移动 | `CGEventCreateMouseEvent` | ✅ 完成 |
| 鼠标点击 | `CGEventCreateMouseEvent` | ✅ 完成 |
| 鼠标拖动 | `kCGEventLeftMouseDragged` | ✅ 完成 |
| 鼠标滚轮 | `CGEventCreateScrollWheelEvent` | ✅ 完成 |
| 键盘输入 | `CGEventCreateKeyboardEvent` | ✅ 完成 |
| 事件发送 | `CGEventPost(kCGHIDEventTap)` | ✅ 完成 |
### 3.3 VK 键码映射(已实现)
Windows VK 码到 macOS CGKeyCode 的完整映射:
| 按键类型 | VK 范围 | macOS 映射 |
|---------|---------|-----------|
| 字母 A-Z | 0x41-0x5A | kVK_ANSI_A - kVK_ANSI_Z |
| 数字 0-9 | 0x30-0x39 | kVK_ANSI_0 - kVK_ANSI_9 |
| 小键盘 | 0x60-0x69 | kVK_ANSI_Keypad0 - 9 |
| F1-F12 | 0x70-0x7B | kVK_F1 - kVK_F12 |
| 方向键 | 0x25-0x28 | kVK_LeftArrow 等 |
| 修饰键 | 0x10-0x12 | kVK_Shift/Control/Option |
| Win 键 | 0x5B-0x5C | kVK_Command |
### 3.4 差分算法(与 Linux 一致)
| 算法 | ID | 说明 |
|-----|-----|------|
| ALGORITHM_GRAY | 0 | 灰度压缩 |
| ALGORITHM_DIFF | 1 | BGRA 差分 |
| ALGORITHM_H264 | 2 | 硬件编码(未实现)|
| ALGORITHM_RGB565 | 3 | RGB565 压缩 |
### 3.5 macOS 版本支持
| 版本 | 代号 | 发布年份 | 支持状态 |
|-----|------|---------|---------|
| 10.13 | High Sierra | 2017 | ✅ 目标最低版本 |
| 10.14 | Mojave | 2018 | ✅ 支持 |
| 10.15 | Catalina | 2019 | ✅ 支持(需权限) |
| 11.x | Big Sur | 2020 | ✅ 支持 |
| 12.x | Monterey | 2021 | ✅ 支持 |
| 13.x | Ventura | 2022 | ✅ 支持 |
| 14.x | Sonoma | 2023 | ✅ 支持 |
**注意**macOS 10.15+ 需要用户授予屏幕录制权限。
---
## 4. 权限处理
### 4.1 所需权限
| 权限 | 用途 | 最低要求版本 | 授权方式 |
|-----|------|------------|---------|
| **屏幕录制** | 捕获屏幕内容 | 10.15 | 系统偏好设置 > 隐私 > 屏幕录制 |
| **辅助功能** | 模拟键盘/鼠标输入 | 所有版本 | 系统偏好设置 > 隐私 > 辅助功能 |
### 4.2 权限检查实现Permissions.mm
```objc
// 检查屏幕录制权限
bool Permissions::checkScreenCapture() {
if (@available(macOS 10.15, *)) {
CGImageRef image = CGWindowListCreateImage(
CGRectMake(0, 0, 1, 1),
kCGWindowListOptionOnScreenOnly,
kCGNullWindowID,
kCGWindowImageDefault
);
if (image == nil) return false;
CGImageRelease(image);
return true;
}
return true; // 10.15 之前不需要权限
}
// 检查辅助功能权限
bool Permissions::checkAccessibility() {
return AXIsProcessTrusted();
}
// 请求辅助功能权限(弹出系统对话框)
void Permissions::requestAccessibility() {
NSDictionary *options = @{
(__bridge id)kAXTrustedCheckOptionPrompt: @YES
};
AXIsProcessTrustedWithOptions((__bridge CFDictionaryRef)options);
}
```
### 4.3 权限引导流程
```
程序启动
检查屏幕录制权限 ──否──▶ 打开系统偏好设置
检查辅助功能权限 ──否──▶ 弹出授权对话框
正常运行
```
---
## 5. 核心代码实现
### 5.1 ScreenHandler已实现
```objc
// ScreenHandler.h
class ScreenHandler {
public:
ScreenHandler();
~ScreenHandler();
bool init(); // 初始化
void start(IOCPClient* client, uint64_t id); // 开始捕获
void stop(); // 停止捕获
int getWidth() const;
int getHeight() const;
void sendBitmapInfo(); // 发送屏幕信息
void onReceive(const uint8_t* data, size_t size);
void applyQualityLevel(int8_t level, bool persist);
private:
bool captureScreen(std::vector<uint8_t>& buffer); // 截屏
void sendFirstScreen(); // 发送首帧
void sendDiffFrame(); // 发送差分帧
uint32_t compareBitmap(...); // 差分计算
void convertBGRAtoGray(...); // 灰度转换
void convertBGRAtoRGB565(...); // RGB565转换
void captureLoop(); // 捕获循环
// 成员变量
IOCPClient* m_client;
CGDirectDisplayID m_displayID;
BITMAPINFOHEADER_MAC m_bmpHeader;
std::vector<uint8_t> m_prevFrame, m_currFrame;
std::atomic<bool> m_running;
std::atomic<uint8_t> m_algorithm;
std::atomic<int> m_maxFPS;
};
```
### 5.2 InputHandler已实现
```objc
// InputHandler.h
class InputHandler {
public:
InputHandler();
~InputHandler();
bool init(); // 初始化(检查权限)
void handleInputEvent(const MSG64_MAC* msg); // 处理输入事件
bool hasAccessibilityPermission() const;
private:
void handleMouseMove(int x, int y);
void handleMouseButton(CGMouseButton button, bool down, int x, int y);
void handleMouseDoubleClick(CGMouseButton button, int x, int y);
void handleMouseWheel(int delta);
void handleKeyEvent(uint32_t vkCode, bool down);
static CGKeyCode vkToMacKeyCode(uint32_t vk); // VK 到 macOS 键码映射
bool m_hasPermission;
CGPoint m_lastMousePos;
bool m_leftButtonDown, m_rightButtonDown, m_middleButtonDown;
};
```
### 5.3 SystemManager已实现
```objc
// SystemManager.h
class SystemManager {
public:
SystemManager(IOCPClient* client, uint64_t clientID);
~SystemManager();
void onReceive(const uint8_t* data, size_t size);
private:
void sendProcessList(); // 进程列表
void killProcesses(const uint8_t* data, size_t size);
void sendWindowsList(); // 窗口列表
static std::vector<pid_t> getAllPids(); // 枚举进程
static std::string getProcessName(pid_t pid);
static std::string getProcessPath(pid_t pid);
};
```
### 5.4 main.mm已实现
主要功能:
- 命令行参数解析
- 权限检查和引导
- 系统信息收集主机名、OS版本、CPU、内存等
- 初始化各处理器
- 主事件循环
```objc
int main(int argc, const char* argv[]) {
@autoreleasepool {
// 1. 解析命令行参数
// 2. 检查权限
// 3. 收集系统信息
// 4. 初始化 ScreenHandler, InputHandler
// 5. 主循环
}
return 0;
}
```
---
## 6. 构建配置
### 6.1 CMakeLists.txt
```cmake
cmake_minimum_required(VERSION 3.15)
project(ghost_macos)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13")
set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64") # Universal Binary
set(SOURCES
main.mm
ScreenHandler.mm
InputHandler.mm
SystemManager.mm
Permissions.mm
)
add_executable(ghost ${SOURCES})
target_include_directories(ghost PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
# 链接 macOS 框架
find_library(COCOA_FRAMEWORK Cocoa REQUIRED)
find_library(COREGRAPHICS_FRAMEWORK CoreGraphics REQUIRED)
find_library(IOKIT_FRAMEWORK IOKit REQUIRED)
find_library(IOSURFACE_FRAMEWORK IOSurface REQUIRED)
find_library(APPLICATIONSERVICES_FRAMEWORK ApplicationServices REQUIRED)
find_library(SECURITY_FRAMEWORK Security REQUIRED)
find_library(CARBON_FRAMEWORK Carbon REQUIRED)
target_link_libraries(ghost PRIVATE
${COCOA_FRAMEWORK}
${COREGRAPHICS_FRAMEWORK}
${IOKIT_FRAMEWORK}
${IOSURFACE_FRAMEWORK}
${APPLICATIONSERVICES_FRAMEWORK}
${SECURITY_FRAMEWORK}
${CARBON_FRAMEWORK}
)
target_compile_options(ghost PRIVATE -Wall -Wextra -fobjc-arc)
```
### 6.2 编译命令
```bash
# 方式 1: 使用脚本
chmod +x build.sh
./build.sh
# 方式 2: 手动编译
mkdir build && cd build
cmake ..
make -j$(sysctl -n hw.ncpu)
# 输出: build/bin/ghost
```
---
## 7. 实现进度
### Phase 1: 基础框架 ✅ 完成
| 任务 | 状态 | 产出 |
|-----|------|------|
| 创建目录结构 | ✅ | `macos/` 目录 |
| CMakeLists.txt | ✅ | 构建配置 |
| Permissions.h/mm | ✅ | 权限检查 |
| ScreenHandler.h/mm | ✅ | 屏幕捕获 |
| InputHandler.h/mm | ✅ | 输入模拟 |
| SystemManager.h/mm | ✅ | 进程管理 |
| main.mm | ✅ | 入口点 |
| IOCPClient.h | ✅ | 网络 stub |
### Phase 2: 网络集成 ⏳ 待实现
| 任务 | 状态 | 说明 |
|-----|------|------|
| IOCPClient 实现 | ⏳ | 复用 client/IOCPClient |
| 登录信息发送 | ⏳ | LOGIN_INFOR 结构 |
| 心跳保活 | ⏳ | Heartbeat 机制 |
| 命令分发 | ⏳ | DataProcess 回调 |
### Phase 3: 功能完善 ⏳ 待实现
| 任务 | 状态 |
|-----|------|
| 光标类型同步 | ⏳ |
| 多显示器支持 | ⏳ |
| 文件传输 | ⏳ |
| 剪贴板同步 | ⏳ |
### Phase 4: 代码保护 ⏳ 待实现
| 任务 | 状态 |
|-----|------|
| 服务端密钥对生成 | ⏳ |
| 服务端签名逻辑 | ⏳ |
| 闭源库 libYamaCore | ⏳ |
| 客户端绑定验证 | ⏳ |
---
## 8. 测试指南
### 8.1 编译前准备
```bash
# 安装 Xcode Command Line Tools
xcode-select --install
# 安装 CMake (通过 Homebrew)
brew install cmake
```
### 8.2 编译运行
```bash
# 获取代码
git clone <repo-url>
cd YAMA/macos
# 编译
./build.sh
# 运行
./build/bin/ghost [server_ip] [port]
```
### 8.3 首次运行权限授予
1. **屏幕录制权限**
- 系统偏好设置 > 隐私与安全性 > 屏幕录制
- 添加 `ghost` 可执行文件
2. **辅助功能权限**
- 系统偏好设置 > 隐私与安全性 > 辅助功能
- 添加 `ghost` 可执行文件
### 8.4 预期输出
```
=== macOS Ghost Client (Phase 1) ===
Server: 192.168.0.55:6543
Checking permissions...
Collecting system information...
Hostname: MacBook-Pro.local
OS: macOS 14.0.0
CPU: Apple M1 (0 MHz, 8 cores)
Memory: 16.0 GB
User: username
Resolution: 1:2560x1600
Executable: /path/to/ghost
Start Time: 2024-04-29 12:00:00
Initializing handlers...
Screen handler initialized: 2560x1600
Input handler initialized
=== Phase 1 Status ===
Screen Capture: READY
Input Control: READY
Network: STUB (Phase 2)
======================
Starting main loop (Ctrl+C to exit)...
```
---
## 9. 常见问题
### 9.1 编译问题
| 问题 | 原因 | 解决方案 |
|-----|------|---------|
| 找不到 Foundation.h | 缺少 Xcode CLT | `xcode-select --install` |
| CMake 找不到 | 未安装 CMake | `brew install cmake` |
| 架构不匹配 | Universal Binary 问题 | 检查 CMAKE_OSX_ARCHITECTURES |
### 9.2 运行时问题
| 问题 | 原因 | 解决方案 |
|-----|------|---------|
| 程序无法打开 | 未签名 | `xattr -d com.apple.quarantine ghost` |
| Screen Capture: NEED PERMISSION | 无录屏权限 | 系统偏好设置授权 |
| Input Control: NEED PERMISSION | 无辅助功能权限 | 系统偏好设置授权 |
### 9.3 权限重置
```bash
# 如果权限状态混乱,可重置
tccutil reset ScreenCapture
tccutil reset Accessibility
```
---
## 10. 代码保护机制(待实现)
### 10.1 保护策略
核心思路:客户端内嵌服务端公钥,连接时验证服务端身份。
```
┌─────────────────────┐ ┌─────────────────────┐
│ macOS Client │ │ Your Server │
│ │ 1. 挑战请求 │ │
│ ┌─────────────────┐ │ ─────────────────▶ │ ┌─────────────────┐ │
│ │ libYamaCore.a │ │ │ │ 服务端私钥 │ │
│ │ (闭源) │ │ ◀───────────────── │ │ │ │
│ │ 内嵌服务端公钥 │ │ 2. 签名响应 │ └─────────────────┘ │
│ │ │ │ │ │
│ │ 验证签名 ─────▶ 匹配 ──▶ 正常工作 │
│ │ ─────▶ 不匹配 ──▶ 拒绝连接 │
│ └─────────────────┘ │ │ │
└─────────────────────┘ └─────────────────────┘
```
### 10.2 实施步骤
1. 生成服务端密钥对
2. 服务端添加签名逻辑
3. 创建闭源库 libYamaCore
4. 嵌入公钥编译
5. 客户端集成验证
---
## 11. 参考资料
### 11.1 官方文档
- [CGDisplayCreateImage](https://developer.apple.com/documentation/coregraphics/1454434-cgdisplaycreateimage)
- [CGEvent Reference](https://developer.apple.com/documentation/coregraphics/cgevent)
- [Carbon Events.h](https://developer.apple.com/documentation/carbon/events_h)
- [Accessibility API](https://developer.apple.com/documentation/applicationservices/axuielement_h)
### 11.2 开源项目参考
- [RustDesk macOS](https://github.com/rustdesk/rustdesk/tree/master/src/platform/macos)
- [Sunshine Screen Capture](https://github.com/LizardByte/Sunshine/tree/master/src/platform/macos)
---
## 12. 修订历史
| 日期 | 版本 | 修改内容 |
|-----|------|---------|
| 2026-04-28 | 1.0 | 初始版本(设计文档) |
| 2026-04-29 | 1.1 | 更新为实现文档Phase 1 完成 |