# 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& 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 m_prevFrame, m_currFrame; std::atomic m_running; std::atomic m_algorithm; std::atomic 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 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 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 完成 |