Files
SimpleRemoter/docs/macOS_Support_Design.md
T

17 KiB
Raw Blame History

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 首次运行权限授予

  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 权限重置

# 如果权限状态混乱,可重置
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 官方文档

11.2 开源项目参考


12. 修订历史

日期 版本 修改内容
2026-04-28 1.0 初始版本(设计文档)
2026-04-29 1.1 更新为实现文档,Phase 1 完成