17 KiB
17 KiB
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)
// 检查屏幕录制权限
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(已实现)
// 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(已实现)
// 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(已实现)
// 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、内存等)
- 初始化各处理器
- 主事件循环
int main(int argc, const char* argv[]) {
@autoreleasepool {
// 1. 解析命令行参数
// 2. 检查权限
// 3. 收集系统信息
// 4. 初始化 ScreenHandler, InputHandler
// 5. 主循环
}
return 0;
}
6. 构建配置
6.1 CMakeLists.txt
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 编译命令
# 方式 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 编译前准备
# 安装 Xcode Command Line Tools
xcode-select --install
# 安装 CMake (通过 Homebrew)
brew install cmake
8.2 编译运行
# 获取代码
git clone <repo-url>
cd YAMA/macos
# 编译
./build.sh
# 运行
./build/bin/ghost [server_ip] [port]
8.3 首次运行权限授予
-
屏幕录制权限:
- 系统偏好设置 > 隐私与安全性 > 屏幕录制
- 添加
ghost可执行文件
-
辅助功能权限:
- 系统偏好设置 > 隐私与安全性 > 辅助功能
- 添加
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 权限重置
# 如果权限状态混乱,可重置
tccutil reset ScreenCapture
tccutil reset Accessibility
10. 代码保护机制(待实现)
10.1 保护策略
核心思路:客户端内嵌服务端公钥,连接时验证服务端身份。
┌─────────────────────┐ ┌─────────────────────┐
│ macOS Client │ │ Your Server │
│ │ 1. 挑战请求 │ │
│ ┌─────────────────┐ │ ─────────────────▶ │ ┌─────────────────┐ │
│ │ libYamaCore.a │ │ │ │ 服务端私钥 │ │
│ │ (闭源) │ │ ◀───────────────── │ │ │ │
│ │ 内嵌服务端公钥 │ │ 2. 签名响应 │ └─────────────────┘ │
│ │ │ │ │ │
│ │ 验证签名 ─────▶ 匹配 ──▶ 正常工作 │
│ │ ─────▶ 不匹配 ──▶ 拒绝连接 │
│ └─────────────────┘ │ │ │
└─────────────────────┘ └─────────────────────┘
10.2 实施步骤
- 生成服务端密钥对
- 服务端添加签名逻辑
- 创建闭源库 libYamaCore
- 嵌入公钥编译
- 客户端集成验证
11. 参考资料
11.1 官方文档
11.2 开源项目参考
12. 修订历史
| 日期 | 版本 | 修改内容 |
|---|---|---|
| 2026-04-28 | 1.0 | 初始版本(设计文档) |
| 2026-04-29 | 1.1 | 更新为实现文档,Phase 1 完成 |