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

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 完成