mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-11-01 10:20:50 +08:00
feat(hid): 支持 CH9329 + OTG 组合模式
CH9329 负责键鼠,OTG 提供虚拟媒体、USB 网络和音频;分别配置设备、报告运行状态,并在 OTG 配置变更和回滚时保留未变化的 CH9329 实例。 补齐双设备校验、串口冲突检查、初始化引导和功能入口,修复组合模式下鼠标兼容选项缺失。 验证:独立功能版本的 591 项 Rust 库测试、前端生产构建、3 项引导逻辑测试及 2 组浏览器回归通过。未进行目标 KVM 硬件验证。
This commit is contained in:
@@ -37,6 +37,27 @@ pub enum HidBackendType {
|
||||
None,
|
||||
}
|
||||
|
||||
impl From<&crate::config::HidConfig> for HidBackendType {
|
||||
fn from(config: &crate::config::HidConfig) -> Self {
|
||||
use crate::config::HidBackend;
|
||||
match config.backend {
|
||||
HidBackend::Otg => Self::Otg {
|
||||
macos_drag: config.mouse_macos_drag,
|
||||
},
|
||||
HidBackend::Ch9329 | HidBackend::Ch9329Otg => Self::Ch9329 {
|
||||
port: config.ch9329_port.clone(),
|
||||
baud_rate: config.ch9329_baudrate,
|
||||
hybrid_mouse: config.ch9329_hybrid_mouse,
|
||||
macos_drag: config.mouse_macos_drag,
|
||||
},
|
||||
HidBackend::Bluetooth => Self::Bluetooth {
|
||||
config: config.bluetooth.clone(),
|
||||
},
|
||||
HidBackend::None => Self::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HidBackendType {
|
||||
pub fn name_str(&self) -> &str {
|
||||
match self {
|
||||
|
||||
@@ -767,6 +767,60 @@ fn merge_pending_move(pending: &mut Option<MouseEvent>, event: MouseEvent) {
|
||||
#[cfg(test)]
|
||||
mod queue_tests {
|
||||
use super::*;
|
||||
#[cfg(unix)]
|
||||
#[tokio::test]
|
||||
async fn composite_otg_updates_and_rollback_preserve_hid_instance() {
|
||||
let mut old = crate::config::AppConfig::default();
|
||||
old.hid.backend = crate::config::HidBackend::Ch9329;
|
||||
old.enforce_invariants();
|
||||
let otg = Arc::new(OtgService::new());
|
||||
let controller = Arc::new(HidController::new(
|
||||
HidBackendType::from(&old.hid),
|
||||
Some(otg.clone()),
|
||||
));
|
||||
let pressed = Arc::new(AtomicBool::new(true));
|
||||
let (runtime, _) = tokio::sync::watch::channel(());
|
||||
let backend: Arc<dyn HidBackend> = Arc::new(TestBackend {
|
||||
pressed: pressed.clone(),
|
||||
reset_done: Arc::new(tokio::sync::Notify::new()),
|
||||
runtime,
|
||||
});
|
||||
*controller.backend.write().await = Some(backend.clone());
|
||||
let data = tempfile::tempdir().unwrap();
|
||||
let coordinator = crate::runtime::UsbCoordinator::new(
|
||||
controller.clone(),
|
||||
otg,
|
||||
Arc::new(RwLock::new(None)),
|
||||
Arc::new(RwLock::new(None)),
|
||||
Arc::new(EventBus::new()),
|
||||
data.path().into(),
|
||||
);
|
||||
let mut new = old.clone();
|
||||
new.hid.backend = crate::config::HidBackend::Ch9329Otg;
|
||||
new.hid.otg_udc = Some("test.udc".into());
|
||||
new.hid.otg_descriptor.product = "Advanced USB".into();
|
||||
new.hid.otg_keyboard_leds = true;
|
||||
coordinator.apply_config(&old, &new).await.unwrap();
|
||||
assert!(Arc::ptr_eq(
|
||||
controller.backend.read().await.as_ref().unwrap(),
|
||||
&backend
|
||||
));
|
||||
coordinator.apply_config(&new, &old).await.unwrap();
|
||||
assert!(Arc::ptr_eq(
|
||||
controller.backend.read().await.as_ref().unwrap(),
|
||||
&backend
|
||||
));
|
||||
assert!(pressed.load(Ordering::Acquire));
|
||||
// Invalid OTG configuration must not touch the healthy input backend.
|
||||
new.uac.enabled = true;
|
||||
new.uac.sample_rate = 8000;
|
||||
assert!(coordinator.apply_config(&old, &new).await.is_err());
|
||||
assert!(Arc::ptr_eq(
|
||||
controller.backend.read().await.as_ref().unwrap(),
|
||||
&backend
|
||||
));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn screen_resolution_updates_backend_and_retains_valid_dimensions() {
|
||||
#[cfg(unix)]
|
||||
|
||||
Reference in New Issue
Block a user