fix: 修复 windows 构建

This commit is contained in:
mofeng-git
2026-07-19 23:44:34 +08:00
parent a86df52d9a
commit 6f697e56c8
7 changed files with 278 additions and 177 deletions

View File

@@ -508,9 +508,5 @@ fn device_label(device: &cpal::Device) -> String {
device device
.description() .description()
.map(|desc| desc.to_string()) .map(|desc| desc.to_string())
.or_else(|_| {
#[allow(deprecated)]
device.name()
})
.unwrap_or_else(|_| "Unknown WASAPI capture device".to_string()) .unwrap_or_else(|_| "Unknown WASAPI capture device".to_string())
} }

View File

@@ -142,10 +142,7 @@ fn device_labels(device: &cpal::Device) -> DeviceLabels {
} }
} }
Err(_) => { Err(_) => {
#[allow(deprecated)] let display = "Unknown WASAPI capture device".to_string();
let display = device
.name()
.unwrap_or_else(|_| "Unknown WASAPI capture device".to_string());
DeviceLabels { DeviceLabels {
display: display.clone(), display: display.clone(),
search_text: display, search_text: display,

View File

@@ -355,12 +355,13 @@ pub async fn apply_msd_config(
Ok(()) Ok(())
} }
#[cfg(unix)] pub async fn apply_usb_config(
pub async fn apply_otg_config(
state: &Arc<AppState>, state: &Arc<AppState>,
old_config: &AppConfig, old_config: &AppConfig,
new_config: &AppConfig, new_config: &AppConfig,
) -> Result<()> { ) -> Result<()> {
#[cfg(unix)]
{
let transitioning_away_from_otg = let transitioning_away_from_otg =
old_config.hid.backend == HidBackend::Otg && new_config.hid.backend != HidBackend::Otg; old_config.hid.backend == HidBackend::Otg && new_config.hid.backend != HidBackend::Otg;
@@ -402,6 +403,20 @@ pub async fn apply_otg_config(
ConfigApplyOptions::default(), ConfigApplyOptions::default(),
) )
.await .await
}
#[cfg(not(unix))]
{
apply_hid_config(
state,
&old_config.hid,
&new_config.hid,
&new_config.msd,
&new_config.otg_network,
ConfigApplyOptions::default(),
)
.await
}
} }
pub async fn apply_atx_config( pub async fn apply_atx_config(

View File

@@ -5,8 +5,8 @@ use crate::config::HidConfig;
use crate::error::Result; use crate::error::Result;
use crate::state::AppState; use crate::state::AppState;
use super::otg::update_otg_config_inner; use super::types::HidConfigUpdate;
use super::types::{HidConfigUpdate, OtgConfigUpdate}; use super::usb_update::{stage_hid_config_update, update_usb_config};
pub async fn get_hid_config(State(state): State<Arc<AppState>>) -> Json<HidConfig> { pub async fn get_hid_config(State(state): State<Arc<AppState>>) -> Json<HidConfig> {
Json(state.config.get().hid.clone()) Json(state.config.get().hid.clone())
@@ -16,13 +16,9 @@ pub async fn update_hid_config(
State(state): State<Arc<AppState>>, State(state): State<Arc<AppState>>,
Json(req): Json<HidConfigUpdate>, Json(req): Json<HidConfigUpdate>,
) -> Result<Json<HidConfig>> { ) -> Result<Json<HidConfig>> {
let response = update_otg_config_inner( let config = update_usb_config(&state, move |staged| {
&state, stage_hid_config_update(&mut staged.hid, &req)
OtgConfigUpdate { })
hid: Some(req),
..Default::default()
},
)
.await?; .await?;
Ok(Json(response.hid)) Ok(Json(config.hid))
} }

View File

@@ -15,6 +15,7 @@ mod redfish;
mod rtsp; mod rtsp;
mod rustdesk; mod rustdesk;
mod stream; mod stream;
mod usb_update;
pub(crate) mod video; pub(crate) mod video;
mod vnc; mod vnc;
mod watchdog; mod watchdog;

View File

@@ -4,13 +4,13 @@ use axum::{extract::State, Json};
use serde::Serialize; use serde::Serialize;
use typeshare::typeshare; use typeshare::typeshare;
use crate::config::{HidBackend, HidConfig, MsdConfig, OtgNetworkConfig}; use crate::config::{HidConfig, MsdConfig, OtgNetworkConfig};
use crate::error::{AppError, Result}; use crate::error::Result;
use crate::otg::OtgNetworkStatus; use crate::otg::OtgNetworkStatus;
use crate::state::AppState; use crate::state::AppState;
use super::apply::{apply_otg_config, try_apply_lock};
use super::types::OtgConfigUpdate; use super::types::OtgConfigUpdate;
use super::usb_update::{stage_hid_config_update, update_usb_config};
#[typeshare] #[typeshare]
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
@@ -32,99 +32,23 @@ pub(super) async fn update_otg_config_inner(
state: &Arc<AppState>, state: &Arc<AppState>,
request: OtgConfigUpdate, request: OtgConfigUpdate,
) -> Result<OtgConfigResponse> { ) -> Result<OtgConfigResponse> {
let _guard = try_apply_lock(&state.config_apply_locks.otg, "otg")?; let staged_config = update_usb_config(state, move |staged| {
let requested_ch9329_descriptor = match request.hid.as_ref() {
if let Some(ref update) = request.hid { Some(update) => stage_hid_config_update(&mut staged.hid, update)?,
update.validate()?; None => None,
}
if let Some(ref update) = request.msd {
update.validate()?;
}
let old_config = state.config.get();
let mut staged_config = (*old_config).clone();
let requested_ch9329_descriptor = request.hid.as_ref().and_then(|update| {
update.ch9329_descriptor.as_ref().map(|_| {
let mut hid = staged_config.hid.clone();
update.apply_to(&mut hid);
hid.ch9329_descriptor
})
});
if let Some(ref update) = request.hid {
update.apply_to(&mut staged_config.hid);
}
if requested_ch9329_descriptor.is_some() {
staged_config.hid.ch9329_descriptor = old_config.hid.ch9329_descriptor.clone();
}
if let Some(ref update) = request.msd {
update.apply_to(&mut staged_config.msd);
}
if let Some(ref update) = request.network {
update.apply_to(&mut staged_config.otg_network);
}
staged_config.enforce_invariants();
if staged_config.otg_network.enabled
&& (staged_config.otg_network.device_mac.is_empty()
|| staged_config.otg_network.host_mac.is_empty())
{
let (device_mac, host_mac) =
crate::otg::network::resolved_mac_pair(&staged_config.otg_network);
staged_config.otg_network.device_mac = device_mac;
staged_config.otg_network.host_mac = host_mac;
}
staged_config.hid.validate_otg_functions()?;
staged_config.otg_network.validate()?;
if let Err(error) = apply_otg_config(state, &old_config, &staged_config).await {
return Err(rollback_after_failure(state, &staged_config, &old_config, error, false).await);
}
let descriptor_was_applied = if let Some(ref descriptor) = requested_ch9329_descriptor {
if staged_config.hid.backend == HidBackend::Ch9329 {
match state.hid.apply_ch9329_descriptor(descriptor).await {
Ok(actual) => {
staged_config.hid.ch9329_descriptor = actual.descriptor;
true
}
Err(error) => {
return Err(rollback_after_failure(
state,
&staged_config,
&old_config,
error,
true,
)
.await);
}
}
} else {
false
}
} else {
false
}; };
if let Err(error) = state if let Some(ref update) = request.msd {
.config update.validate()?;
.update(|config| { update.apply_to(&mut staged.msd);
config.hid = staged_config.hid.clone();
config.msd = staged_config.msd.clone();
config.otg_network = staged_config.otg_network.clone();
config.enforce_invariants();
})
.await
{
return Err(rollback_after_failure(
state,
&staged_config,
&old_config,
AppError::Config(format!("Failed to persist OTG config after apply: {error}")),
descriptor_was_applied,
)
.await);
} }
if let Some(ref update) = request.network {
update.apply_to(&mut staged.otg_network);
}
Ok(requested_ch9329_descriptor)
})
.await?;
Ok(OtgConfigResponse { Ok(OtgConfigResponse {
hid: staged_config.hid, hid: staged_config.hid,
@@ -133,34 +57,3 @@ pub(super) async fn update_otg_config_inner(
status: state.otg_service.network_status().await, status: state.otg_service.network_status().await,
}) })
} }
async fn rollback_after_failure(
state: &Arc<AppState>,
failed_config: &crate::config::AppConfig,
old_config: &crate::config::AppConfig,
primary_error: AppError,
restore_descriptor: bool,
) -> AppError {
let mut rollback_errors = Vec::new();
if let Err(error) = apply_otg_config(state, failed_config, old_config).await {
rollback_errors.push(format!("runtime rollback failed: {error}"));
}
if restore_descriptor && old_config.hid.backend == HidBackend::Ch9329 {
if let Err(error) = state
.hid
.apply_ch9329_descriptor(&old_config.hid.ch9329_descriptor)
.await
{
rollback_errors.push(format!("CH9329 descriptor rollback failed: {error}"));
}
}
if rollback_errors.is_empty() {
return primary_error;
}
let message = format!("{primary_error}; {}", rollback_errors.join("; "));
state.otg_service.mark_degraded(message.clone()).await;
AppError::Config(message)
}

View File

@@ -0,0 +1,203 @@
use std::sync::Arc;
use crate::config::{AppConfig, Ch9329DescriptorConfig, HidBackend, HidConfig};
use crate::error::{AppError, Result};
use crate::state::AppState;
use super::apply::{apply_usb_config, try_apply_lock};
use super::types::HidConfigUpdate;
pub(super) fn stage_hid_config_update(
staged_hid: &mut HidConfig,
update: &HidConfigUpdate,
) -> Result<Option<Ch9329DescriptorConfig>> {
update.validate()?;
let old_descriptor = staged_hid.ch9329_descriptor.clone();
update.apply_to(staged_hid);
let requested_descriptor = update
.ch9329_descriptor
.as_ref()
.map(|_| staged_hid.ch9329_descriptor.clone());
if requested_descriptor.is_some() {
staged_hid.ch9329_descriptor = old_descriptor;
}
Ok(requested_descriptor)
}
pub(super) async fn update_usb_config<F>(
state: &Arc<AppState>,
stage_update: F,
) -> Result<AppConfig>
where
F: FnOnce(&mut AppConfig) -> Result<Option<Ch9329DescriptorConfig>>,
{
let _guard = try_apply_lock(&state.config_apply_locks.otg, "otg")?;
let old_config = state.config.get();
let mut staged_config = old_config.as_ref().clone();
let requested_ch9329_descriptor = stage_update(&mut staged_config)?;
staged_config.enforce_invariants();
staged_config.hid.validate_otg_functions()?;
#[cfg(unix)]
{
if staged_config.otg_network.enabled
&& (staged_config.otg_network.device_mac.is_empty()
|| staged_config.otg_network.host_mac.is_empty())
{
let (device_mac, host_mac) =
crate::otg::network::resolved_mac_pair(&staged_config.otg_network);
staged_config.otg_network.device_mac = device_mac;
staged_config.otg_network.host_mac = host_mac;
}
staged_config.otg_network.validate()?;
}
if let Err(error) = apply_usb_config(state, &old_config, &staged_config).await {
return Err(rollback_after_failure(state, &staged_config, &old_config, error, false).await);
}
let descriptor_was_applied = if let Some(ref descriptor) = requested_ch9329_descriptor {
if staged_config.hid.backend == HidBackend::Ch9329 {
match state.hid.apply_ch9329_descriptor(descriptor).await {
Ok(actual) => {
staged_config.hid.ch9329_descriptor = actual.descriptor;
true
}
Err(error) => {
return Err(rollback_after_failure(
state,
&staged_config,
&old_config,
error,
true,
)
.await);
}
}
} else {
false
}
} else {
false
};
if let Err(error) = state
.config
.update(|config| {
config.hid = staged_config.hid.clone();
config.msd = staged_config.msd.clone();
config.otg_network = staged_config.otg_network.clone();
config.enforce_invariants();
})
.await
{
return Err(rollback_after_failure(
state,
&staged_config,
&old_config,
AppError::Config(format!(
"Failed to persist USB configuration after apply: {error}"
)),
descriptor_was_applied,
)
.await);
}
Ok(staged_config)
}
async fn rollback_after_failure(
state: &Arc<AppState>,
failed_config: &AppConfig,
old_config: &AppConfig,
primary_error: AppError,
restore_descriptor: bool,
) -> AppError {
let mut rollback_errors = Vec::new();
if let Err(error) = apply_usb_config(state, failed_config, old_config).await {
rollback_errors.push(format!("runtime rollback failed: {error}"));
}
if restore_descriptor && old_config.hid.backend == HidBackend::Ch9329 {
if let Err(error) = state
.hid
.apply_ch9329_descriptor(&old_config.hid.ch9329_descriptor)
.await
{
rollback_errors.push(format!("CH9329 descriptor rollback failed: {error}"));
}
}
if rollback_errors.is_empty() {
return primary_error;
}
let message = format!("{primary_error}; {}", rollback_errors.join("; "));
#[cfg(unix)]
state.otg_service.mark_degraded(message.clone()).await;
AppError::Config(message)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::config::HidBackend;
use crate::web::handlers::config::types::Ch9329DescriptorConfigUpdate;
fn hid_update() -> HidConfigUpdate {
HidConfigUpdate {
backend: None,
ch9329_port: None,
ch9329_baudrate: None,
ch9329_hybrid_mouse: None,
ch9329_descriptor: None,
otg_udc: None,
otg_descriptor: None,
otg_profile: None,
otg_functions: None,
otg_keyboard_leds: None,
mouse_absolute: None,
}
}
#[test]
fn stages_regular_hid_fields_immediately() {
let mut hid = HidConfig::default();
let mut update = hid_update();
update.backend = Some(HidBackend::Ch9329);
update.ch9329_port = Some("COM7".to_string());
let requested_descriptor = stage_hid_config_update(&mut hid, &update).unwrap();
assert_eq!(hid.backend, HidBackend::Ch9329);
assert_eq!(hid.ch9329_port, "COM7");
assert!(requested_descriptor.is_none());
}
#[test]
fn defers_ch9329_descriptor_until_runtime_apply() {
let mut hid = HidConfig::default();
let old_descriptor = hid.ch9329_descriptor.clone();
let mut update = hid_update();
update.ch9329_descriptor = Some(Ch9329DescriptorConfigUpdate {
vendor_id: Some(0x1234),
product_id: Some(0x5678),
manufacturer: None,
product: None,
serial_number: None,
});
let requested_descriptor = stage_hid_config_update(&mut hid, &update)
.unwrap()
.expect("descriptor update should be deferred");
assert_eq!(hid.ch9329_descriptor, old_descriptor);
assert_eq!(requested_descriptor.vendor_id, 0x1234);
assert_eq!(requested_descriptor.product_id, 0x5678);
}
}