Files
One-KVM/docs/modules/events.md
mofeng-git a8a3b6c66b feat: 添加 RustDesk 协议支持和项目文档
- 新增 RustDesk 模块,支持与 RustDesk 客户端连接
  - 实现会合服务器协议和 P2P 连接
  - 支持 NaCl 加密和密钥交换
  - 添加视频帧和 HID 事件适配器
- 添加 Protobuf 协议定义 (message.proto, rendezvous.proto)
- 新增完整项目文档
  - 各功能模块文档 (video, hid, msd, otg, webrtc 等)
  - hwcodec 和 RustDesk 协议技术报告
  - 系统架构和技术栈文档
- 更新 Web 前端 RustDesk 配置界面和 API
2025-12-31 18:59:52 +08:00

8.8 KiB

Events 模块文档

1. 模块概述

Events 模块提供事件总线功能,用于模块间通信和状态广播。

1.1 主要功能

  • 事件发布/订阅
  • 多订阅者广播
  • WebSocket 事件推送
  • 状态变更通知

1.2 文件结构

src/events/
└── mod.rs              # EventBus 实现

2. 架构设计

┌─────────────────────────────────────────────────────────────────────────────┐
│                          Event System                                        │
└─────────────────────────────────────────────────────────────────────────────┘

┌────────────────┐ ┌────────────────┐ ┌────────────────┐
│     Video      │ │      HID       │ │     Audio      │
│    Module      │ │    Module      │ │    Module      │
└───────┬────────┘ └───────┬────────┘ └───────┬────────┘
        │                  │                  │
        │   publish()      │   publish()      │   publish()
        └──────────────────┼──────────────────┘
                           │
                           ▼
                ┌─────────────────────┐
                │      EventBus       │
                │ (broadcast channel) │
                └──────────┬──────────┘
                           │
         ┌─────────────────┼─────────────────┐
         │                 │                 │
         │   subscribe()   │   subscribe()   │
         ▼                 ▼                 ▼
┌────────────────┐ ┌────────────────┐ ┌────────────────┐
│   WebSocket    │ │   DeviceInfo   │ │   Internal     │
│   Handler      │ │  Broadcaster   │ │    Tasks       │
└────────────────┘ └────────────────┘ └────────────────┘

3. 核心组件

3.1 EventBus

pub struct EventBus {
    /// 广播发送器
    tx: broadcast::Sender<SystemEvent>,
}

impl EventBus {
    /// 创建事件总线
    pub fn new() -> Self {
        let (tx, _) = broadcast::channel(1024);
        Self { tx }
    }

    /// 发布事件
    pub fn publish(&self, event: SystemEvent) {
        let _ = self.tx.send(event);
    }

    /// 订阅事件
    pub fn subscribe(&self) -> broadcast::Receiver<SystemEvent> {
        self.tx.subscribe()
    }
}

3.2 SystemEvent

#[derive(Clone, Debug, Serialize)]
pub enum SystemEvent {
    // 视频事件
    StreamStateChanged {
        state: String,
        device: Option<String>,
        resolution: Option<Resolution>,
        fps: Option<f32>,
    },

    VideoDeviceChanged {
        added: Vec<String>,
        removed: Vec<String>,
    },

    // HID 事件
    HidStateChanged {
        backend: String,
        initialized: bool,
        keyboard_connected: bool,
        mouse_connected: bool,
        mouse_mode: String,
        error: Option<String>,
    },

    // MSD 事件
    MsdStateChanged {
        mode: String,
        connected: bool,
        image: Option<String>,
        error: Option<String>,
    },

    MsdDownloadProgress {
        download_id: String,
        downloaded: u64,
        total: u64,
        speed: u64,
    },

    // ATX 事件
    AtxStateChanged {
        power_on: bool,
        last_action: Option<String>,
        error: Option<String>,
    },

    // 音频事件
    AudioStateChanged {
        enabled: bool,
        streaming: bool,
        device: Option<String>,
        error: Option<String>,
    },

    // 配置事件
    ConfigChanged {
        section: String,
    },

    // 设备信息汇总
    DeviceInfo {
        video: VideoInfo,
        hid: HidInfo,
        msd: MsdInfo,
        atx: AtxInfo,
        audio: AudioInfo,
    },

    // 系统错误
    SystemError {
        module: String,
        severity: String,
        message: String,
    },

    // RustDesk 事件
    RustDeskStatusChanged {
        status: String,
        device_id: Option<String>,
        error: Option<String>,
    },

    RustDeskConnectionOpened {
        connection_id: String,
        peer_id: String,
    },

    RustDeskConnectionClosed {
        connection_id: String,
        peer_id: String,
        reason: String,
    },
}

4. 设备信息广播器

main.rs 中启动的后台任务:

pub fn spawn_device_info_broadcaster(
    state: Arc<AppState>,
    events: Arc<EventBus>,
) -> JoinHandle<()> {
    tokio::spawn(async move {
        let mut rx = events.subscribe();
        let mut debounce = tokio::time::interval(Duration::from_millis(100));
        let mut pending = false;

        loop {
            tokio::select! {
                // 收到事件
                result = rx.recv() => {
                    if result.is_ok() {
                        pending = true;
                    }
                }

                // 防抖定时器
                _ = debounce.tick() => {
                    if pending {
                        pending = false;
                        // 收集设备信息
                        let device_info = state.get_device_info().await;
                        // 广播
                        events.publish(SystemEvent::DeviceInfo(device_info));
                    }
                }
            }
        }
    })
}

5. WebSocket 事件推送

pub async fn ws_handler(
    ws: WebSocketUpgrade,
    State(state): State<Arc<AppState>>,
) -> impl IntoResponse {
    ws.on_upgrade(|socket| handle_ws(socket, state))
}

async fn handle_ws(mut socket: WebSocket, state: Arc<AppState>) {
    let mut rx = state.events.subscribe();

    loop {
        tokio::select! {
            // 发送事件给客户端
            result = rx.recv() => {
                if let Ok(event) = result {
                    let json = serde_json::to_string(&event).unwrap();
                    if socket.send(Message::Text(json)).await.is_err() {
                        break;
                    }
                }
            }

            // 接收客户端消息
            msg = socket.recv() => {
                match msg {
                    Some(Ok(Message::Close(_))) | None => break,
                    _ => {}
                }
            }
        }
    }
}

6. 使用示例

6.1 发布事件

// 视频模块发布状态变更
events.publish(SystemEvent::StreamStateChanged {
    state: "streaming".to_string(),
    device: Some("/dev/video0".to_string()),
    resolution: Some(Resolution { width: 1920, height: 1080 }),
    fps: Some(30.0),
});

// HID 模块发布状态变更
events.publish(SystemEvent::HidStateChanged {
    backend: "otg".to_string(),
    initialized: true,
    keyboard_connected: true,
    mouse_connected: true,
    mouse_mode: "absolute".to_string(),
    error: None,
});

6.2 订阅事件

let mut rx = events.subscribe();

loop {
    match rx.recv().await {
        Ok(SystemEvent::StreamStateChanged { state, .. }) => {
            println!("Stream state: {}", state);
        }
        Ok(SystemEvent::HidStateChanged { backend, .. }) => {
            println!("HID backend: {}", backend);
        }
        Err(_) => break,
    }
}

7. 前端事件处理

// 连接 WebSocket
const ws = new WebSocket('/api/ws');

ws.onmessage = (event) => {
    const data = JSON.parse(event.data);

    switch (data.type) {
        case 'StreamStateChanged':
            updateStreamStatus(data);
            break;
        case 'HidStateChanged':
            updateHidStatus(data);
            break;
        case 'MsdStateChanged':
            updateMsdStatus(data);
            break;
        case 'DeviceInfo':
            updateAllDevices(data);
            break;
    }
};

8. 最佳实践

8.1 事件粒度

  • 使用细粒度事件便于精确更新
  • DeviceInfo 用于初始化和定期同步

8.2 防抖

  • 使用 100ms 防抖避免事件风暴
  • 合并多个快速变更

8.3 错误处理

  • 发布失败静默忽略 (fire-and-forget)
  • 订阅者断开自动清理