From a049fb7c6baebc0b36818858657015f1383b30f4 Mon Sep 17 00:00:00 2001 From: khannurien Date: Sat, 19 Sep 2026 13:24:01 +0000 Subject: [PATCH] Re-enumerate outputs through a fresh enumerator on each hotkey press The enumerator created at startup could go stale and keep reporting the device set from back then, so an unplugged DAC stayed in the cycle and a replugged one never came back. A failed enumeration now empties the list instead of keeping the old one. Co-Authored-By: Claude Opus 5 --- src/main.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/main.rs b/src/main.rs index 5274c04..94f606f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -171,7 +171,7 @@ unsafe extern "system" fn wndproc( LRESULT(0) } notify::WM_DEVICES_CHANGED => { - with_app(App::refresh_outputs); + with_app(|app| app.refresh_outputs(&app.enumerator.clone())); LRESULT(0) } WM_SETTINGCHANGE => { @@ -208,9 +208,13 @@ impl App { /// The hotkey press. fn advance(&mut self) { self.reload_config_if_changed(); - self.refresh_outputs(); - let current = audio::default_output(&self.enumerator); + // A fresh enumerator per press: the one created at startup can go stale (the audio + // service restarting is enough) and would keep reporting the devices from back then. + let enumerator = audio::enumerator().unwrap_or_else(|_| self.enumerator.clone()); + self.refresh_outputs(&enumerator); + + let current = audio::default_output(&enumerator); let candidates = cycle::candidates(&self.outputs, &self.config.devices); let Some(target) = cycle::next(&candidates, current.as_ref().map(|c| c.id.as_str())) else { @@ -224,14 +228,13 @@ impl App { } // Report what Windows actually ended up with, not what we asked for. - let confirmed = audio::default_output(&self.enumerator).unwrap_or(target); + let confirmed = audio::default_output(&enumerator).unwrap_or(target); self.osd.show(&confirmed.name, confirmed.form); } - fn refresh_outputs(&mut self) { - if let Ok(outputs) = audio::outputs(&self.enumerator) { - self.outputs = outputs; - } + /// A failed enumeration empties the list: an old list would offer an unplugged device. + fn refresh_outputs(&mut self, enumerator: &IMMDeviceEnumerator) { + self.outputs = audio::outputs(enumerator).unwrap_or_default(); } /// Picks up edits to the config file without a restart, but only re-reads when the