Files
qiming/qimingclaw/specs/agent-client/vendorsdoc/enigo.md

32 KiB
Raw Blame History

enigo

项目概述

跨平台键盘鼠标输入模拟库,支持 Linux (X11/Wayland)、Windows、macOS 和 BSD。

GitHub: https://github.com/enigo-rs/enigo 本地路径: vendors/enigo

目录结构

enigo/
├── src/
│   ├── lib.rs                    # 公共 API 和 Trait 定义
│   ├── platform.rs               # 平台抽象
│   ├── keycodes.rs               # 键码定义
│   ├── agent.rs                  # Token/Agent 模式
│   ├── errors.rs                 # 错误定义
│   ├── tests/                    # 测试用例
│   │   ├── keyboard.rs
│   │   ├── mouse.rs
│   │   └── text.rs
│   ├── linux/                    # Linux 实现
│   │   ├── mod.rs                # 模块入口
│   │   ├── x11.rs                # X11 实现 (x11rb)
│   │   ├── xdo.rs                # xdo 实现
│   │   ├── wayland.rs            # Wayland 实现
│   │   ├── libei.rs              # libei 实现
│   │   ├── keymap2.rs            # 键码映射
│   │   └── keyboard.rs           # 键盘布局
│   ├── macos/                    # macOS 实现
│   │   ├── mod.rs
│   │   ├── macos_impl.rs         # CoreGraphics 实现
│   │   └── key_layout.rs         # 键盘布局
│   ├── win/                      # Windows 实现
│   │   ├── mod.rs
│   │   ├── win_impl.rs           # SendInput 实现
│   │   └── key_codes.rs          # 虚拟键码
│   └── freebsd/                  # FreeBSD 实现
│       └── mod.rs
├── Cargo.toml
└── README.md

核心依赖

[dependencies]
# 平台 API
windows = "0.48"
core-foundation = "0.9"
core-graphics = "0.22"
objc2 = "0.5"

# Linux 平台
x11rb = { version = "0.13", optional = true }
wayland-client = { version = "0.31", optional = true }
ashpd = { version = "0.4", optional = true }
reis = { version = "0.3", optional = true }
libei = { version = "0.3", optional = true }

# 运行时支持
smol = { version = "2.0", optional = true }
tokio = { version = "1.0", optional = true, features = ["rt", "rt-multi-thread", "time"] }

[features]
default = ["smol-runtime"]
smol-runtime = ["smol", "reis"]
tokio-runtime = ["tokio", "reis"]
x11rb = ["x11rb", "reis"]
xdo = ["xdo"]
wayland = ["wayland-client", "reis", "ashpd"]
libei = ["libei", "reis"]

核心 Trait 和结构体

Keyboard Trait

// src/lib.rs

/// 键盘操作 Trait
pub trait Keyboard {
    /// 发送文本Unicode 支持)
    fn text(&mut self, text: &str) -> InputResult<()>;

    /// 发送单个按键
    fn key(&mut self, key: Key, direction: Direction) -> InputResult<()>;

    /// 发送原始键码
    fn raw(&mut self, keycode: u16, direction: Direction) -> InputResult<()>;

    /// 获取可打印字符
    fn get_char(&self, key: Key, direction: Direction) -> Option<char>;
}

Mouse Trait

/// 鼠标操作 Trait
pub trait Mouse {
    /// 鼠标按钮点击
    fn button(&mut self, button: Button, direction: Direction) -> InputResult<()>;

    /// 移动鼠标到绝对位置
    fn move_mouse(&mut self, x: i32, y: i32, coordinate: Coordinate) -> InputResult<()>;

    /// 移动鼠标(相对位移)
    fn move_mouse_relative(&mut self, x: i32, y: i32) -> InputResult<()>;

    /// 滚动
    fn scroll(&mut self, length: i32, axis: Axis) -> InputResult<()>;

    /// 水平滚动
    fn scroll_x(&mut self, length: i32) -> InputResult<()> {
        self.scroll(length, Axis::Horizontal)
    }

    /// 垂直滚动
    fn scroll_y(&mut self, length: i32) -> InputResult<()> {
        self.scroll(length, Axis::Vertical)
    }

    /// 获取主显示器尺寸
    fn main_display(&self) -> InputResult<(i32, i32)>;

    /// 获取鼠标当前位置
    fn location(&self) -> InputResult<(i32, i32)>;
}

主结构体

/// 主入口结构体
pub struct Enigo {
    keyboard: Box<dyn Keyboard + Send>,
    mouse: Box<dyn Mouse + Send>,
}

impl Enigo {
    /// 创建新的 Enigo 实例
    pub fn new(settings: &Settings) -> Self {
        let keyboard = create_keyboard(settings);
        let mouse = create_mouse(settings);
        Self { keyboard, mouse }
    }
}

pub struct Settings {
    /// 跳过的初始化检查
    pub skip_init: bool,
    /// 键盘设置
    pub keyboard: KeyboardSettings,
    /// 鼠标设置
    pub mouse: MouseSettings,
}

impl Default for Settings {
    fn default() -> Self {
        Self {
            skip_init: false,
            keyboard: KeyboardSettings::default(),
            mouse: MouseSettings::default(),
        }
    }
}

Key 键码定义

// src/keycodes.rs

/// 特殊键枚举
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Key {
    // 修饰键
    Control,
    Alt,
    Shift,
    AltGr,
    Super,      // Windows/Cmd

    // 功能键
    Function(Fn),  // F1-F24

    // 布局相关字符
    Layout(char),  // 基于当前键盘布局的字符

    // Unicode 字符
    Unicode(char),

    // 特殊键
    Space,
    Enter,
    Tab,
    Escape,
    Backspace,
    Delete,
    Insert,
    Home,
    End,
    PageUp,
    PageDown,

    // 方向键
    ArrowUp,
    ArrowDown,
    ArrowLeft,
    ArrowRight,

    // 数字行
    F1, F2, F3, F4, F5, F6,
    F7, F8, F9, F10, F11, F12,
    F13, F14, F15, F16,
    F17, F18, F19, F20,
    F21, F22, F23, F24,

    // 音频键
    VolumeUp,
    VolumeDown,
    VolumeMute,

    // 媒体键
    MediaNextTrack,
    MediaPreviousTrack,
    MediaPlay,
    MediaStop,

    // 小键盘
    NumLock,
    Numpad0, Numpad1, Numpad2, Numpad3, Numpad4,
    Numpad5, Numpad6, Numpad7, Numpad8, Numpad9,
    NumpadAdd,
    NumpadSubtract,
    NumpadMultiply,
    NumpadDivide,
    NumpadEnter,
    NumpadDecimal,
}

pub enum Fn {
    F1, F2, F3, F4, F5, F6,
    F7, F8, F9, F10, F11, F12,
    F13, F14, F15, F16,
    F17, F18, F19, F20,
    F21, F22, F23, F24,
}

/// 转换为虚拟键码
impl From<Key> for KeyCode<'_> {
    fn from(key: Key) -> Self {
        match key {
            Key::Control => Self::Control,
            Key::Alt => Self::Alt,
            Key::Shift => Self::Shift,
            Key::Super => Self::Super,
            // ...
        }
    }
}

Direction 和 Button

/// 按键方向
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Direction {
    Click,   // 点击(按下 + 释放)
    Down,    // 按下
    Up,      // 释放
}

/// 鼠标按钮
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Button {
    Left,
    Right,
    Middle,
    Back,     // 浏览器后退
    Forward,  // 浏览器前进
}

/// 滚动轴
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Axis {
    Horizontal,
    Vertical,
}

/// 坐标系统
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Coordinate {
    Absolute,   // 绝对坐标(屏幕位置)
    Relative,   // 相对坐标(位移)
}

平台实现详情

Windows 实现

// src/win/win_impl.rs

use windows::Win32::UI::Input::KeyboardAndMouse::{
    SendInput, INPUT, INPUT_KEYBOARD, KEYBDEVENTFLAGS,
    VkKeyScanW, MapVirtualKeyExW, ToUnicodeEx,
};

pub struct EnigoWindows {
    // 键盘布局
    layout: HKL,
    // 状态
    state: Vec<u8>,
}

impl Keyboard for EnigoWindows {
    fn key(&mut self, key: Key, direction: Direction) -> InputResult<()> {
        let vk = key_to_virtual_key(key, self.layout)?;
        let flags = match direction {
            Direction::Down => KEYBDEVENTFLAGS::empty(),
            Direction::Up => KEYBDEVENTFLAGS::KEYEVENTF_KEYUP,
            Direction::Click => {
                self.key(key, Direction::Down)?;
                self.key(key, Direction::Up)?;
                return Ok(());
            }
        };

        let input = INPUT {
            type_: INPUT_TYPE::INPUT_KEYBOARD,
            Anonymous: input_ki!(vk, 0, flags),
        };

        unsafe {
            SendInput(&[input], std::mem::size_of::<INPUT>() as i32);
        }

        Ok(())
    }

    fn text(&mut self, text: &str) -> InputResult<()> {
        // 使用 ToUnicodeEx 处理 Unicode 输入
        let context = 0; // 不获取上下文
        let flags = 0;

        for c in text.chars() {
            let mut buffer = [0u16; 5];
            let len = unsafe {
                ToUnicodeEx(
                    VkKeyScanW(c as u16),
                    0,  // 扫描码
                    &mut [0u8; 256],
                    buffer.as_mut_ptr(),
                    buffer.len() as i32,
                    flags,
                    self.layout,
                )
            };

            if len > 0 {
                for i in 0..len as usize {
                    let vk = buffer[i];
                    self.key(Key::Unicode(c), Direction::Down)?;
                    self.key(Key::Unicode(c), Direction::Up)?;
                }
            }
        }

        Ok(())
    }
}

impl Mouse for EnigoWindows {
    fn move_mouse(&mut self, x: i32, y: i32, coordinate: Coordinate) -> InputResult<()> {
        match coordinate {
            Coordinate::Absolute => {
                // 绝对位置 - 使用 MOUSEINPUT
                let input = INPUT {
                    type_: INPUT_TYPE::INPUT_MOUSE,
                    Anonymous: input_mi!(x, y, 0, MOUSEEVENTF_MOVE, 0),
                };
                unsafe {
                    SendInput(&[input], std::mem::size_of::<INPUT>() as i32);
                }
            }
            Coordinate::Relative => {
                // 相对位移
                let input = INPUT {
                    type_: INPUT_TYPE::INPUT_MOUSE,
                    Anonymous: input_mi!(x, y, 0, MOUSEEVENTF_MOVE, 0),
                };
                unsafe {
                    SendInput(&[input], std::mem::size_of::<INPUT>() as i32);
                }
            }
        }

        Ok(())
    }

    fn button(&mut self, button: Button, direction: Direction) -> InputResult<()> {
        let (flags, data) = match (button, direction) {
            (Button::Left, Direction::Down) => (MOUSEEVENTF_LEFTDOWN, 0),
            (Button::Left, Direction::Up) => (MOUSEEVENTF_LEFTUP, 0),
            (Button::Right, Direction::Down) => (MOUSEEVENTF_RIGHTDOWN, 0),
            (Button::Right, Direction::Up) => (MOUSEEVENTF_RIGHTUP, 0),
            (Button::Middle, Direction::Down) => (MOUSEEVENTF_MIDDLEDOWN, 0),
            (Button::Middle, Direction::Up) => (MOUSEEVENTF_MIDDLEUP, 0),
            _ => return Err(Error::Unimplemented),
        };

        let input = INPUT {
            type_: INPUT_TYPE::INPUT_MOUSE,
            Anonymous: input_mi!(0, 0, 0, flags, data),
        };

        unsafe {
            SendInput(&[input], std::mem::size_of::<INPUT>() as i32);
        }

        Ok(())
    }

    fn scroll(&mut self, length: i32, axis: Axis) -> InputResult<()> {
        let flags = match axis {
            Axis::Vertical => MOUSEEVENTF_WHEEL,
            Axis::Horizontal => MOUSEEVENTF_HWHEEL,
        };

        let input = INPUT {
            type_: INPUT_TYPE::INPUT_MOUSE,
            Anonymous: input_mi!(0, 0, length as i16, flags, 0),
        };

        unsafe {
            SendInput(&[input], std::mem::size_of::<INPUT>() as i32);
        }

        Ok(())
    }
}

macOS 实现

// src/macos/macos_impl.rs

use core_foundation::base::{TCFType, CFTypeRef};
use core_graphics::display::{CGDisplay, CGPoint, CGRect};
use objc2::rc::Retained;
use objc2::runtime::{AnyClass, ProtocolObject};
use objc2_foundation::{NSInteger, NSUInteger};

pub struct EnigoMac {
    // CGEventSource
    source: Retained<ProtocolObject<dyn CGEventSourceProtocol>>,
    // 键盘布局
    layout: TISInputSourceRef,
}

impl Keyboard for EnigoMac {
    fn key(&mut self, key: Key, direction: Direction) -> InputResult<()> {
        let (keycode, flags) = key_to_keycode_and_flags(key, self.layout)?;

        let event_type = match direction {
            Direction::Down => CGEventType::KeyDown,
            Direction::Up => CGEventType::KeyUp,
            Direction::Click => {
                self.key(key, Direction::Down)?;
                self.key(key, Direction::Up)?;
                return Ok(());
            }
        };

        let event = CGEvent::new_keyboard_event(self.source.as_ref(), event_type, keycode, true)?;
        event.set_flags(flags);

        unsafe {
            CGEventPost(CGEventSourceStateID::HIDState, event.as_ref());
        }

        Ok(())
    }

    fn text(&mut self, text: &str) -> InputResult<()> {
        for c in text.chars() {
            let event = CGEvent::new_keyboard_event(
                self.source.as_ref(),
                CGEventType::KeyDown,
                c as u64,
                true,
            )?;

            // 设置 Unicode 内容
            event.set_keyboard_type(0); // 使用当前键盘类型
            event.set_string(c.to_string());

            unsafe {
                CGEventPost(CGEventSourceStateID::HIDState, event.as_ref());
            }
        }

        Ok(())
    }
}

impl Mouse for EnigoMac {
    fn button(&mut self, button: Button, direction: Direction) -> InputResult<()> {
        let (button_num, event_type) = match (button, direction) {
            (Button::Left, Direction::Down) => (0, CGEventType::LeftMouseDown),
            (Button::Left, Direction::Up) => (0, CGEventType::LeftMouseUp),
            (Button::Right, Direction::Down) => (1, CGEventType::RightMouseDown),
            (Button::Right, Direction::Up) => (1, CGEventType::RightMouseUp),
            (Button::Middle, Direction::Down) => (2, CGEventType::OtherMouseDown),
            (Button::Middle, Direction::Up) => (2, CGEventType::OtherMouseUp),
            _ => return Err(Error::Unimplemented),
        };

        let point = CGEvent::get_location(self.mouse_event.as_ref().unwrap());
        let event = CGEvent::new_mouse_event(
            self.source.as_ref(),
            event_type,
            point,
            button_num,
        )?;

        unsafe {
            CGEventPost(CGEventSourceStateID::HIDState, event.as_ref());
        }

        Ok(())
    }

    fn move_mouse(&mut self, x: i32, y: i32, coordinate: Coordinate) -> InputResult<()> {
        let point = CGPoint::new(x as f64, y as f64);

        let event_type = match coordinate {
            Coordinate::Absolute => CGEventType::MouseMoved,
            Coordinate::Relative => CGEventType::MouseMoved,
        };

        let event = CGEvent::new_mouse_event(self.source.as_ref(), event_type, point, 0)?;

        unsafe {
            CGEventPost(CGEventSourceStateID::HIDState, event.as_ref());
        }

        Ok(())
    }

    fn scroll(&mut self, length: i32, axis: Axis) -> InputResult<()> {
        let (scroll_type, wheel_count) = match axis {
            Axis::Vertical => (CGEventType::Wheel, 1),
            Axis::Horizontal => (CGEventType::Wheel, 2),
        };

        let point = CGEvent::get_location(self.mouse_event.as_ref().unwrap());
        let event = CGEvent::new_scroll_event(
            self.source.as_ref(),
            scroll_type,
            CGScrollEventUnit::Pixel,
            wheel_count as i32,
            length as i32,
            0,
        )?;

        unsafe {
            CGEventPost(CGEventSourceStateID::HIDState, event.as_ref());
        }

        Ok(())
    }
}

Linux X11 实现

// src/linux/x11.rs

use x11rb::connection::Connection;
use x11rb::protocol::xproto::*;
use x11rb::wrapper::ConnectionExt as _;

pub struct EnigoX11<'a, C: Connection> {
    conn: &'a C,
    screen: usize,
    root: Window,
    // 键盘状态
    keyboard_state: Vec<u8>,
    // 修饰键状态
    modifiers: ModifiersState,
}

impl<C: Connection> Keyboard for EnigoX11<'_, C> {
    fn key(&mut self, key: Key, direction: Direction) -> InputResult<()> {
        let keycode = self.key_to_keycode(key)?;

        let event_detail = match direction {
            Direction::Down => keycode,
            Direction::Up => keycode + 8, // X11: keyup = keycode + 8
            Direction::Click => {
                self.key(key, Direction::Down)?;
                self.key(key, Direction::Up)?;
                return Ok(());
            }
        };

        let event = InputEvent::new(
            self.conn.get_input_focus().reply()?.focus,
            event_detail,
            self.root,
            state: self.get_current_state(),
            event_type: match direction {
                Direction::Down => EventType::KEY_PRESS,
                Direction::Up => EventType::KEY_RELEASE,
                _ => unreachable!(),
            },
        );

        self.conn.send_event(false, event, EventType::KEY_PRESS as u8)?;

        Ok(())
    }
}

impl<C: Connection> Mouse for EnigoX11<'_, C> {
    fn move_mouse(&mut self, x: i32, y: i32, coordinate: Coordinate) -> InputResult<()> {
        let (root_x, root_y) = match coordinate {
            Coordinate::Absolute => {
                // 转换屏幕坐标
                (x, y)
            }
            Coordinate::Relative => {
                // 相对位移需要获取当前位置
                let (current_x, _) = self.query_pointer()?;
                (current_x + x, y)
            }
        };

        let event = InputEvent::new(
            self.root,
            EventType::MOTION_NOTIFY,
            self.root,
            root_x as u16,
            root_y as u16,
            state: self.get_current_state(),
        );

        self.conn.send_event(false, event, EventType::MOTION_NOTIFY as u8)?;

        Ok(())
    }

    fn button(&mut self, button: Button, direction: Direction) -> InputResult<()> {
        let button_code = button_to_x11(button)?;

        let event = InputEvent::new(
            self.root,
            match direction {
                Direction::Down => EventType::BUTTON_PRESS,
                Direction::Up => EventType::BUTTON_RELEASE,
                Direction::Click => {
                    self.button(button, Direction::Down)?;
                    self.button(button, Direction::Up)?;
                    return Ok(());
                }
            },
            self.root,
            0, 0,
            state: self.get_current_state(),
            detail: button_code,
        );

        self.conn.send_event(false, event, EventType::BUTTON_PRESS as u8)?;

        Ok(())
    }

    fn scroll(&mut self, length: i32, axis: Axis) -> InputResult<()> {
        let button = match axis {
            Axis::Vertical => {
                if length > 0 { ButtonCode::4 } else { ButtonCode::5 }
            }
            Axis::Horizontal => {
                if length > 0 { ButtonCode::6 } else { ButtonCode::7 }
            }
        };

        let event = InputEvent::new(
            self.root,
            EventType::BUTTON_PRESS,
            self.root,
            0, 0,
            state: self.get_current_state(),
            detail: button as u16,
        );

        self.conn.send_event(false, event, EventType::BUTTON_PRESS as u8)?;

        Ok(())
    }
}

使用示例

use enigo::{Enigo, Key, Button, Direction, Coordinate, KeyboardControllable, MouseControllable};

fn main() {
    let mut enigo = Enigo::new(&enigo::Settings::default());

    // 键盘模拟
    enigo.key_click(Key::Space);
    enigo.key_down(Key::Control);
    enigo.key_click(Key::C);
    enigo.key_up(Key::Control);

    // 文本输入
    enigo.text("Hello, World!");

    // 鼠标移动(绝对位置)
    enigo.move_mouse(100, 200, Coordinate::Absolute);

    // 鼠标移动(相对位移)
    enigo.move_mouse_relative(10, 10);

    // 鼠标点击
    enigo.click(Button::Left);
    enigo.click(Button::Right);

    // 鼠标按下/释放
    enigo.button(Button::Left, Direction::Down);
    enigo.button(Button::Left, Direction::Up);

    // 滚动
    enigo.scroll(5, Axis::Vertical);   // 向下滚动
    enigo.scroll(-3, Axis::Vertical);  // 向上滚动
    enigo.scroll(2, Axis::Horizontal); // 向右滚动

    // 组合键
    enigo.key_down(Key::Shift);
    enigo.text("a");
    enigo.key_up(Key::Shift);  // 输入 "A"

    // 快捷键Ctrl+S 保存
    enigo.key_down(Key::Control);
    enigo.key_click(Key::S);
    enigo.key_up(Key::Control);
}

可复用代码

模块 文件 用途
平台抽象 src/platform.rs 统一的平台接口
键码映射 src/keycodes.rs 跨平台键码转换
Windows 实现 src/win/ Windows API
macOS 实现 src/macos/ Core Graphics
Linux 实现 src/linux/ X11/Wayland

在本项目中的使用

用于实现远程桌面控制时的键盘鼠标模拟:

agent-server-admin (远程控制端)
    │
    ├── 输入事件 --> hbb_common (Protocol Buffers)
    │                      │
    └── 屏幕画面 <─── qiming-rustdesk (scrap)
                              │
                              └── enigo (输入模拟)
                                      │
                                      └── 客户端本地执行输入

与 agent-client 集成场景

场景1远程桌面输入模拟

// agent-client 接收远程输入事件并执行

use enigo::{Enigo, Key, Button, Direction, Coordinate};
use std::sync::{Arc, Mutex};

pub struct RemoteInputHandler {
    enigo: Mutex<Enigo>,
    // 输入队列(处理高频输入)
    input_queue: Arc<Mutex<Vec<InputEvent>>>,
    // 处理任务
    _task: JoinHandle<()>,
}

pub enum InputEvent {
    Keyboard {
        key: Key,
        direction: Direction,
    },
    Text {
        text: String,
    },
    Mouse {
        button: Option<Button>,
        direction: Option<Direction>,
        x: Option<i32>,
        y: Option<i32>,
        scroll_x: Option<i32>,
        scroll_y: Option<i32>,
    },
}

impl RemoteInputHandler {
    pub fn new() -> Result<Self> {
        let enigo = Enigo::new(&enigo::Settings::default());
        let input_queue = Arc::new(Mutex::new(Vec::new()));

        let this = Self {
            enigo: Mutex::new(enigo),
            input_queue: input_queue.clone(),
            _task: tokio::spawn(Self::process_loop(input_queue)),
        };

        Ok(this)
    }

    /// 处理远程输入事件
    pub fn handle_input(&self, event: InputEvent) {
        let mut queue = self.input_queue.lock().unwrap();
        queue.push(event);
    }

    /// 执行键盘事件
    pub fn key_event(&self, key: Key, direction: Direction) {
        let mut enigo = self.enigo.lock().unwrap();
        enigo.key(key, direction).ok();
    }

    /// 执行文本输入
    pub fn text_input(&self, text: &str) {
        let mut enigo = self.enigo.lock().unwrap();
        enigo.text(text).ok();
    }

    /// 执行鼠标事件
    pub fn mouse_event(
        &self,
        button: Option<Button>,
        direction: Option<Direction>,
        x: Option<i32>,
        y: Option<i32>,
        scroll_x: Option<i32>,
        scroll_y: Option<i32>,
    ) {
        let mut enigo = self.enigo.lock().unwrap();

        // 鼠标移动
        if let (Some(x), Some(y)) = (x, y) {
            enigo.move_mouse(x, y, Coordinate::Absolute).ok();
        }

        // 鼠标点击
        if let (Some(btn), Some(dir)) = (button, direction) {
            enigo.button(btn, dir).ok();
        }

        // 滚动
        if let Some(y) = scroll_y {
            enigo.scroll(y, enigo::Axis::Vertical).ok();
        }
        if let Some(x) = scroll_x {
            enigo.scroll(x, enigo::Axis::Horizontal).ok();
        }
    }

    /// 组合键处理Ctrl+C, Ctrl+V 等)
    pub fn shortcut(&self, modifier: Key, key: Key) {
        let mut enigo = self.enigo.lock().unwrap();
        enigo.key(modifier, Direction::Down).ok();
        enigo.key(key, Direction::Click).ok();
        enigo.key(modifier, Direction::Up).ok();
    }

    /// 获取鼠标位置
    pub fn get_mouse_position(&self) -> (i32, i32) {
        let enigo = self.enigo.lock().unwrap();
        enigo.location().unwrap_or((0, 0))
    }

    /// 获取主显示器尺寸
    pub fn get_display_size(&self) -> (i32, i32) {
        let enigo = self.enigo.lock().unwrap();
        enigo.main_display().unwrap_or((1920, 1080))
    }

    /// 输入处理循环
    async fn process_loop(input_queue: Arc<Mutex<Vec<InputEvent>>>) {
        let mut interval = tokio::time::interval(Duration::from_millis(16)); // ~60fps

        loop {
            interval.tick().await;

            let events = {
                let mut queue = input_queue.lock().unwrap();
                std::mem::take(&mut *queue)
            };

            for event in events {
                // 根据事件类型执行输入
                match event {
                    InputEvent::Keyboard { key, direction } => {
                        // 执行键盘输入
                    }
                    InputEvent::Text { text } => {
                        // 执行文本输入
                    }
                    InputEvent::Mouse { button, direction, x, y, scroll_x, scroll_y } => {
                        // 执行鼠标输入
                    }
                }
            }
        }
    }
}

场景2从 Protocol Buffers 消息解析输入

// 解析来自 qiming-rustdesk 的 protobuf 输入消息

use hbb_common::protobuf::RemoteInput;

// 从 protobuf 消息转换为 enigo 输入事件
impl From<RemoteInput> for InputEvent {
    fn from(msg: RemoteInput) -> Self {
        match msg.input_type {
            Some(InputType::Key(key)) => InputEvent::Keyboard {
                key: convert_key(key.name()),
                direction: convert_direction(key.down()),
            },
            Some(InputType::Text(text)) => InputEvent::Text {
                text: text.content(),
            },
            Some(InputType::Mouse(mouse)) => InputEvent::Mouse {
                button: mouse.button().map(convert_button),
                direction: mouse.down().map(convert_direction),
                x: if mouse.x() != 0 { Some(mouse.x() as i32) } else { None },
                y: if mouse.y() != 0 { Some(mouse.y() as i32) } else { None },
                scroll_x: None, // 需要从 protobuf 定义中添加
                scroll_y: None,
            },
            Some(InputType::Scroll(scroll)) => InputEvent::Mouse {
                button: None,
                direction: None,
                x: None,
                y: None,
                scroll_x: Some(scroll.x() as i32),
                scroll_y: Some(scroll.y() as i32),
            },
            _ => unreachable!(),
        }
    }
}

/// 转换键码
fn convert_key(name: &str) -> Key {
    match name {
        "Control" => Key::Control,
        "Alt" => Key::Alt,
        "Shift" => Key::Shift,
        "Super" => Key::Super,
        "Enter" => Key::Enter,
        "Tab" => Key::Tab,
        "Escape" => Key::Escape,
        "Backspace" => Key::Backspace,
        "Delete" => Key::Delete,
        "ArrowUp" => Key::ArrowUp,
        "ArrowDown" => Key::ArrowDown,
        "ArrowLeft" => Key::ArrowLeft,
        "ArrowRight" => Key::ArrowRight,
        _ => Key::Layout(name.chars().next().unwrap_or(' ')),
    }
}

/// 转换鼠标按钮
fn convert_button(btn: MouseButton) -> Button {
    match btn {
        MouseButton::Left => Button::Left,
        MouseButton::Right => Button::Right,
        MouseButton::Middle => Button::Middle,
        MouseButton::Back => Button::Back,
        MouseButton::Forward => Button::Forward,
        _ => Button::Left,
    }
}

/// 转换按键方向
fn convert_direction(down: bool) -> Direction {
    if down { Direction::Down } else { Direction::Up }
}

场景3输入事件序列化与传输

// 输入事件序列化为 protobuf 用于网络传输

#[derive(Debug, Clone)]
pub struct SerializedInput {
    pub event_type: String,
    pub key: Option<String>,
    pub text: Option<String>,
    pub button: Option<String>,
    pub direction: Option<String>,
    pub x: Option<i32>,
    pub y: Option<i32>,
    pub scroll_x: Option<i32>,
    pub scroll_y: Option<i32>,
}

impl SerializedInput {
    pub fn from_input_event(event: &InputEvent) -> Self {
        match event {
            InputEvent::Keyboard { key, direction } => Self {
                event_type: "key".to_string(),
                key: Some(format!("{:?}", key)),
                text: None,
                button: None,
                direction: Some(format!("{:?}", direction)),
                x: None,
                y: None,
                scroll_x: None,
                scroll_y: None,
            },
            InputEvent::Text { text } => Self {
                event_type: "text".to_string(),
                key: None,
                text: Some(text.clone()),
                button: None,
                direction: None,
                x: None,
                y: None,
                scroll_x: None,
                scroll_y: None,
            },
            InputEvent::Mouse { button, direction, x, y, scroll_x, scroll_y } => Self {
                event_type: "mouse".to_string(),
                key: None,
                text: None,
                button: button.map(|b| format!("{:?}", b)),
                direction: direction.map(|d| format!("{:?}", d)),
                x: *x,
                y: *y,
                scroll_x: *scroll_x,
                scroll_y: *scroll_y,
            },
        }
    }
}

场景4平台特定的权限处理

// 不同平台的输入权限获取

#[cfg(target_os = "macos")]
mod macos_permissions {
    use crate::Result;

    pub fn request_accessibility() -> bool {
        // macOS 需要用户授权辅助功能权限
        // 可以通过 AXIsProcessTrustedWithOptions() 检查
        // 需要引导用户到系统设置中启用
        unsafe {
            let options: CFDictionaryRef = core_foundation::base::CFDictionaryCreateMutable(
                core_foundation::base::kCFAllocatorDefault,
                0,
                &core_foundation::base::kCFTypeDictionaryKeyCallBacks,
                &core_foundation::base::kCFTypeDictionaryValueCallBacks,
            );

            let key = core_foundation::string::CFStringCreateWithCString(
                core_foundation::base::kCFAllocatorDefault,
                "AXTrustedCheckPurposeOptionKey\0".as_ptr() as *const _,
                core_foundation::base::kCFStringEncodingUTF8,
            );

            let value = core_foundation::boolean::kCFBooleanTrue;

            CFDictionarySetValue(options, key as *const _, value as *const _);
            let trusted = AXIsProcessTrustedWithOptions(options);

            CFRelease(options as *const _);
            trusted
        }
    }
}

#[cfg(target_os = "windows")]
mod windows_permissions {
    pub fn check_accessibility() -> bool {
        // Windows 需要启用"允许应用控制鼠标键盘"
        // 通过 OpenInputDesktop 检查
        true
    }
}

#[cfg(target_os = "linux")]
mod linux_permissions {
    pub fn check_accessibility() -> bool {
        // Linux 需要 X11/Wayland 权限
        // 可以通过 WAYLAND_DISPLAY 环境变量检查
        std::env::var("WAYLAND_DISPLAY").is_ok()
    }
}

场景5输入频率限制与平滑

// 防止高频输入导致的问题

pub struct InputRateLimiter {
    // 上一条输入的时间戳
    last_input: AtomicU64,
    // 最小输入间隔(毫秒)
    min_interval: u64,
    // 输入平滑因子
    smoothing: f32,
    // 累积的位移
    pending_movement: AtomicI32,
}

impl InputRateLimiter {
    pub fn new(min_interval_ms: u64) -> Self {
        Self {
            last_input: AtomicU64::new(0),
            min_interval: min_interval_ms,
            smoothing: 0.8,
            pending_movement: AtomicI32::new(0),
        }
    }

    /// 检查是否可以执行输入
    pub fn can_input(&self) -> bool {
        let now = current_time_millis();
        let last = self.last_input.load(Ordering::SeqCst);
        (now - last) >= self.min_interval
    }

    /// 执行带速率限制的输入
    pub fn execute_with_rate_limit<F, T>(&self, f: F) -> Option<T>
    where
        F: FnOnce() -> T,
    {
        if self.can_input() {
            self.last_input.store(current_time_millis(), Ordering::SeqCst);
            Some(f())
        } else {
            None
        }
    }

    /// 平滑鼠标移动
    pub fn smooth_mouse_move(&self, target_x: i32, target_y: i32, current_x: i32, current_y: i32) -> (i32, i32) {
        let dx = target_x - current_x;
        let dy = target_y - current_y;

        // 应用平滑因子
        let smooth_x = (dx as f32 * self.smoothing) as i32;
        let smooth_y = (dy as f32 * self.smoothing) as i32;

        // 累积未移动的距离
        let pending = self.pending_movement.load(Ordering::SeqCst);
        self.pending_movement.store(pending + dx - smooth_y, Ordering::SeqCst);

        (smooth_x, smooth_y)
    }
}