mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-01-28 08:31:52 +08:00
- 后端:切换事务+transition_id,/stream/mode 返回 switching/transition_id 与实际 codec - 事件:新增 mode_switching/mode_ready,config/webrtc_ready/mode_changed 关联事务 - 编码/格式:扩展 NV21/NV16/NV24/RGB/BGR 输入与转换链路,RKMPP direct input 优化 - 前端:useVideoSession 统一切换,失败回退真实切回 MJPEG,菜单格式同步修复 - 清理:useVideoStream 降级为 MJPEG-only
391 lines
9.2 KiB
Rust
391 lines
9.2 KiB
Rust
//! HID event types for keyboard and mouse
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
/// Keyboard event type
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
|
#[serde(rename_all = "lowercase")]
|
|
pub enum KeyEventType {
|
|
/// Key pressed down
|
|
Down,
|
|
/// Key released
|
|
Up,
|
|
}
|
|
|
|
/// Keyboard modifier flags
|
|
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
|
|
pub struct KeyboardModifiers {
|
|
/// Left Control
|
|
#[serde(default)]
|
|
pub left_ctrl: bool,
|
|
/// Left Shift
|
|
#[serde(default)]
|
|
pub left_shift: bool,
|
|
/// Left Alt
|
|
#[serde(default)]
|
|
pub left_alt: bool,
|
|
/// Left Meta (Windows/Super key)
|
|
#[serde(default)]
|
|
pub left_meta: bool,
|
|
/// Right Control
|
|
#[serde(default)]
|
|
pub right_ctrl: bool,
|
|
/// Right Shift
|
|
#[serde(default)]
|
|
pub right_shift: bool,
|
|
/// Right Alt (AltGr)
|
|
#[serde(default)]
|
|
pub right_alt: bool,
|
|
/// Right Meta
|
|
#[serde(default)]
|
|
pub right_meta: bool,
|
|
}
|
|
|
|
impl KeyboardModifiers {
|
|
/// Convert to USB HID modifier byte
|
|
pub fn to_hid_byte(&self) -> u8 {
|
|
let mut byte = 0u8;
|
|
if self.left_ctrl {
|
|
byte |= 0x01;
|
|
}
|
|
if self.left_shift {
|
|
byte |= 0x02;
|
|
}
|
|
if self.left_alt {
|
|
byte |= 0x04;
|
|
}
|
|
if self.left_meta {
|
|
byte |= 0x08;
|
|
}
|
|
if self.right_ctrl {
|
|
byte |= 0x10;
|
|
}
|
|
if self.right_shift {
|
|
byte |= 0x20;
|
|
}
|
|
if self.right_alt {
|
|
byte |= 0x40;
|
|
}
|
|
if self.right_meta {
|
|
byte |= 0x80;
|
|
}
|
|
byte
|
|
}
|
|
|
|
/// Create from USB HID modifier byte
|
|
pub fn from_hid_byte(byte: u8) -> Self {
|
|
Self {
|
|
left_ctrl: byte & 0x01 != 0,
|
|
left_shift: byte & 0x02 != 0,
|
|
left_alt: byte & 0x04 != 0,
|
|
left_meta: byte & 0x08 != 0,
|
|
right_ctrl: byte & 0x10 != 0,
|
|
right_shift: byte & 0x20 != 0,
|
|
right_alt: byte & 0x40 != 0,
|
|
right_meta: byte & 0x80 != 0,
|
|
}
|
|
}
|
|
|
|
/// Check if any modifier is active
|
|
pub fn any(&self) -> bool {
|
|
self.left_ctrl
|
|
|| self.left_shift
|
|
|| self.left_alt
|
|
|| self.left_meta
|
|
|| self.right_ctrl
|
|
|| self.right_shift
|
|
|| self.right_alt
|
|
|| self.right_meta
|
|
}
|
|
}
|
|
|
|
/// Keyboard event
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct KeyboardEvent {
|
|
/// Event type (down/up)
|
|
#[serde(rename = "type")]
|
|
pub event_type: KeyEventType,
|
|
/// Key code (USB HID usage code or JavaScript key code)
|
|
pub key: u8,
|
|
/// Modifier keys state
|
|
#[serde(default)]
|
|
pub modifiers: KeyboardModifiers,
|
|
/// If true, key is already USB HID code (skip js_to_usb conversion)
|
|
#[serde(default)]
|
|
pub is_usb_hid: bool,
|
|
}
|
|
|
|
impl KeyboardEvent {
|
|
/// Create a key down event (JS keycode, needs conversion)
|
|
pub fn key_down(key: u8, modifiers: KeyboardModifiers) -> Self {
|
|
Self {
|
|
event_type: KeyEventType::Down,
|
|
key,
|
|
modifiers,
|
|
is_usb_hid: false,
|
|
}
|
|
}
|
|
|
|
/// Create a key up event (JS keycode, needs conversion)
|
|
pub fn key_up(key: u8, modifiers: KeyboardModifiers) -> Self {
|
|
Self {
|
|
event_type: KeyEventType::Up,
|
|
key,
|
|
modifiers,
|
|
is_usb_hid: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Mouse button
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
|
#[serde(rename_all = "lowercase")]
|
|
pub enum MouseButton {
|
|
Left,
|
|
Right,
|
|
Middle,
|
|
Back,
|
|
Forward,
|
|
}
|
|
|
|
impl MouseButton {
|
|
/// Convert to USB HID button bit
|
|
pub fn to_hid_bit(&self) -> u8 {
|
|
match self {
|
|
MouseButton::Left => 0x01,
|
|
MouseButton::Right => 0x02,
|
|
MouseButton::Middle => 0x04,
|
|
MouseButton::Back => 0x08,
|
|
MouseButton::Forward => 0x10,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Mouse event type
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
|
#[serde(rename_all = "lowercase")]
|
|
pub enum MouseEventType {
|
|
/// Mouse moved (relative movement)
|
|
Move,
|
|
/// Mouse moved (absolute position)
|
|
MoveAbs,
|
|
/// Button pressed
|
|
Down,
|
|
/// Button released
|
|
Up,
|
|
/// Mouse wheel scroll
|
|
Scroll,
|
|
}
|
|
|
|
/// Mouse event
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct MouseEvent {
|
|
/// Event type
|
|
#[serde(rename = "type")]
|
|
pub event_type: MouseEventType,
|
|
/// X coordinate or delta
|
|
#[serde(default)]
|
|
pub x: i32,
|
|
/// Y coordinate or delta
|
|
#[serde(default)]
|
|
pub y: i32,
|
|
/// Button (for down/up events)
|
|
#[serde(default)]
|
|
pub button: Option<MouseButton>,
|
|
/// Scroll delta (for scroll events)
|
|
#[serde(default)]
|
|
pub scroll: i8,
|
|
}
|
|
|
|
impl MouseEvent {
|
|
/// Create a relative move event
|
|
pub fn move_rel(dx: i32, dy: i32) -> Self {
|
|
Self {
|
|
event_type: MouseEventType::Move,
|
|
x: dx,
|
|
y: dy,
|
|
button: None,
|
|
scroll: 0,
|
|
}
|
|
}
|
|
|
|
/// Create an absolute move event
|
|
pub fn move_abs(x: i32, y: i32) -> Self {
|
|
Self {
|
|
event_type: MouseEventType::MoveAbs,
|
|
x,
|
|
y,
|
|
button: None,
|
|
scroll: 0,
|
|
}
|
|
}
|
|
|
|
/// Create a button down event
|
|
pub fn button_down(button: MouseButton) -> Self {
|
|
Self {
|
|
event_type: MouseEventType::Down,
|
|
x: 0,
|
|
y: 0,
|
|
button: Some(button),
|
|
scroll: 0,
|
|
}
|
|
}
|
|
|
|
/// Create a button up event
|
|
pub fn button_up(button: MouseButton) -> Self {
|
|
Self {
|
|
event_type: MouseEventType::Up,
|
|
x: 0,
|
|
y: 0,
|
|
button: Some(button),
|
|
scroll: 0,
|
|
}
|
|
}
|
|
|
|
/// Create a scroll event
|
|
pub fn scroll(delta: i8) -> Self {
|
|
Self {
|
|
event_type: MouseEventType::Scroll,
|
|
x: 0,
|
|
y: 0,
|
|
button: None,
|
|
scroll: delta,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Combined HID event (keyboard or mouse)
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
#[serde(tag = "device", rename_all = "lowercase")]
|
|
pub enum HidEvent {
|
|
Keyboard(KeyboardEvent),
|
|
Mouse(MouseEvent),
|
|
Consumer(ConsumerEvent),
|
|
}
|
|
|
|
/// Consumer control event (multimedia keys)
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct ConsumerEvent {
|
|
/// Consumer control usage code (e.g., 0x00CD for Play/Pause)
|
|
pub usage: u16,
|
|
}
|
|
|
|
/// USB HID keyboard report (8 bytes)
|
|
#[derive(Debug, Clone, Default)]
|
|
pub struct KeyboardReport {
|
|
/// Modifier byte
|
|
pub modifiers: u8,
|
|
/// Reserved byte
|
|
pub reserved: u8,
|
|
/// Key codes (up to 6 simultaneous keys)
|
|
pub keys: [u8; 6],
|
|
}
|
|
|
|
impl KeyboardReport {
|
|
/// Convert to bytes for USB HID
|
|
pub fn to_bytes(&self) -> [u8; 8] {
|
|
[
|
|
self.modifiers,
|
|
self.reserved,
|
|
self.keys[0],
|
|
self.keys[1],
|
|
self.keys[2],
|
|
self.keys[3],
|
|
self.keys[4],
|
|
self.keys[5],
|
|
]
|
|
}
|
|
|
|
/// Add a key to the report
|
|
pub fn add_key(&mut self, key: u8) -> bool {
|
|
for slot in &mut self.keys {
|
|
if *slot == 0 {
|
|
*slot = key;
|
|
return true;
|
|
}
|
|
}
|
|
false // All slots full
|
|
}
|
|
|
|
/// Remove a key from the report
|
|
pub fn remove_key(&mut self, key: u8) {
|
|
for slot in &mut self.keys {
|
|
if *slot == key {
|
|
*slot = 0;
|
|
}
|
|
}
|
|
// Compact the array
|
|
self.keys.sort_by(|a, b| b.cmp(a));
|
|
}
|
|
|
|
/// Clear all keys
|
|
pub fn clear(&mut self) {
|
|
self.modifiers = 0;
|
|
self.keys = [0; 6];
|
|
}
|
|
}
|
|
|
|
/// USB HID mouse report
|
|
#[derive(Debug, Clone, Default)]
|
|
pub struct MouseReport {
|
|
/// Button state
|
|
pub buttons: u8,
|
|
/// X movement (-127 to 127)
|
|
pub x: i8,
|
|
/// Y movement (-127 to 127)
|
|
pub y: i8,
|
|
/// Wheel movement (-127 to 127)
|
|
pub wheel: i8,
|
|
}
|
|
|
|
impl MouseReport {
|
|
/// Convert to bytes for USB HID (relative mouse)
|
|
pub fn to_bytes_relative(&self) -> [u8; 4] {
|
|
[self.buttons, self.x as u8, self.y as u8, self.wheel as u8]
|
|
}
|
|
|
|
/// Convert to bytes for USB HID (absolute mouse)
|
|
pub fn to_bytes_absolute(&self, x: u16, y: u16) -> [u8; 6] {
|
|
[
|
|
self.buttons,
|
|
(x & 0xFF) as u8,
|
|
(x >> 8) as u8,
|
|
(y & 0xFF) as u8,
|
|
(y >> 8) as u8,
|
|
self.wheel as u8,
|
|
]
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_modifier_conversion() {
|
|
let mods = KeyboardModifiers {
|
|
left_ctrl: true,
|
|
left_shift: true,
|
|
..Default::default()
|
|
};
|
|
assert_eq!(mods.to_hid_byte(), 0x03);
|
|
|
|
let mods2 = KeyboardModifiers::from_hid_byte(0x03);
|
|
assert!(mods2.left_ctrl);
|
|
assert!(mods2.left_shift);
|
|
assert!(!mods2.left_alt);
|
|
}
|
|
|
|
#[test]
|
|
fn test_keyboard_report() {
|
|
let mut report = KeyboardReport::default();
|
|
assert!(report.add_key(0x04)); // 'A'
|
|
assert!(report.add_key(0x05)); // 'B'
|
|
assert_eq!(report.keys[0], 0x04);
|
|
assert_eq!(report.keys[1], 0x05);
|
|
|
|
report.remove_key(0x04);
|
|
assert_eq!(report.keys[0], 0x05);
|
|
}
|
|
}
|