From 11a78ed2f679f771bf5543c0077e21a806deeecd Mon Sep 17 00:00:00 2001 From: vicrodh <103399283+vicrodh@users.noreply.github.com> Date: Mon, 25 May 2026 17:05:44 -0600 Subject: [PATCH] feat(slint): resolve Auto backend to a concrete one in audio settings (#470) Port #470's UI behavior: add an "Auto" entry at the top of the backend dropdown that, on select, resolves to the best available backend (PipeWire if present, else System) and persists that concrete value. The rebuilt snapshot then moves the dropdown onto the resolved backend, so backend_type is never left null/Auto. Concrete backends keep mapping by their shifted index; System wiring comes from available_backends() (merged backend half). cc --- crates/qbz-slint/src/settings.rs | 48 ++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/crates/qbz-slint/src/settings.rs b/crates/qbz-slint/src/settings.rs index cbd18d298..1204e546a 100644 --- a/crates/qbz-slint/src/settings.rs +++ b/crates/qbz-slint/src/settings.rs @@ -432,8 +432,14 @@ fn build_snapshot( streaming_quality_index: ui_prefs::streaming_quality_index(streaming_quality_key) as i32, sample_rates: MAX_SAMPLE_RATES.iter().map(|(l, _)| l.to_string()).collect(), sample_rate_index: sample_rate_index as i32, - backends: backend_types.iter().map(|t| backend_label(*t)).collect(), - backend_index: backend_index as i32, + // Index 0 is "Auto" (a resolve-and-set action, #470); the concrete + // backends follow. backend_type is always persisted concrete, so the + // current selection is its position shifted by 1 past the Auto entry — + // the dropdown never rests on Auto. + backends: std::iter::once("Auto".to_string()) + .chain(backend_types.iter().map(|t| backend_label(*t))) + .collect(), + backend_index: backend_index as i32 + 1, devices: device_list.labels, device_bp: device_list.bp, device_groups: device_list.groups, @@ -784,15 +790,35 @@ pub async fn handle_select( apply_audio(&ctx, &runtime, Apply::Reinit); } "backend" => { - let backend = ctx - .maps - .lock() - .unwrap_or_else(|e| e.into_inner()) - .backends - .get(index) - .copied(); - let Some(backend) = backend else { - return; + // Dropdown index 0 is "Auto" — a resolve-and-set action (#470), not a + // persisted mode. Pick the best available backend (PipeWire if present, + // else System), persist it concrete, and let the rebuilt snapshot move + // the dropdown onto that backend; backend_type is never left null/Auto. + // Indices >= 1 map to the concrete `maps.backends` list (no Auto entry). + let backend = if index == 0 { + let types = ctx + .maps + .lock() + .unwrap_or_else(|e| e.into_inner()) + .backends + .clone(); + if types.iter().any(|t| *t == AudioBackendType::PipeWire) { + AudioBackendType::PipeWire + } else { + AudioBackendType::SystemDefault + } + } else { + let resolved = ctx + .maps + .lock() + .unwrap_or_else(|e| e.into_inner()) + .backends + .get(index - 1) + .copied(); + let Some(resolved) = resolved else { + return; + }; + resolved }; if let Err(e) = with_audio(&ctx.audio, |s| s.set_backend_type(Some(backend))) { log::error!("[qbz-slint] persist backend failed: {e}");