From 1139e349285c39112a03bd1c27f52f4a492e8b68 Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Thu, 12 Feb 2026 16:52:25 +0100 Subject: [PATCH] fix(sound): only consider active routes when setting volume and mute-ness --- crates/cosmic-pipewire/src/lib.rs | 72 +++++++++++++++++++-------- crates/cosmic-pipewire/src/profile.rs | 2 +- subscriptions/sound/src/lib.rs | 29 +++++++---- 3 files changed, 71 insertions(+), 32 deletions(-) diff --git a/crates/cosmic-pipewire/src/lib.rs b/crates/cosmic-pipewire/src/lib.rs index 2f899ae..d9e5dac 100644 --- a/crates/cosmic-pipewire/src/lib.rs +++ b/crates/cosmic-pipewire/src/lib.rs @@ -66,17 +66,18 @@ fn run_service( let registry = core.get_registry_rc()?; let state = Rc::new(RefCell::new(State { - nodes: IntMap::new(), + main_loop: main_loop.downgrade(), proxies: Proxies { devices: IntMap::new(), metadata: IntMap::new(), nodes: IntMap::new(), }, + nodes: IntMap::new(), + active_routes: IntMap::new(), routes: IntMap::new(), node_devices: IntMap::new(), node_card_profile_device: IntMap::new(), node_props: IntMap::new(), - main_loop: main_loop.downgrade(), on_event, })); @@ -207,7 +208,9 @@ fn run_service( match param_type { ParamType::EnumProfile => { if let Some(profile) = Profile::from_pod(pod) { - state.borrow_mut().add_profile(device_id, profile); + state + .borrow_mut() + .add_profile(device_id, index, profile); } } @@ -423,7 +426,7 @@ pub enum Event { /// A new node was detected. AddNode(Node), /// A profile was enumerated - AddProfile(DeviceId, Profile), + AddProfile(DeviceId, u32, Profile), /// A route was enumerated AddRoute(DeviceId, u32, Route), /// The default sink was changed. @@ -496,13 +499,21 @@ struct Proxies { } struct State { - nodes: IntMap)>, - pub(self) proxies: Proxies, - routes: IntMap>, - node_devices: IntMap, - node_props: IntMap, - node_card_profile_device: IntMap, main_loop: MainLoopWeak, + /// Stores pipewire objects that we are monitoring. + pub(self) proxies: Proxies, + /// Associates the pipewire ID of a node to its node and device IDs. + nodes: IntMap)>, + /// Routes which are currently in use by devices. + active_routes: IntMap>, + /// Routes which are supported by devices. + routes: IntMap>, + /// Associates node objects to their device objects. + node_devices: IntMap, + /// Additional properties of nodes for managing volume, mute, etc. + node_props: IntMap, + /// Associates a node with a card profile device for matching nodes to routes. + node_card_profile_device: IntMap, /// Handle events and exit the loop when `true` is returned. on_event: Box, } @@ -513,6 +524,16 @@ impl State { } fn active_route(&mut self, id: DeviceId, index: u32, route: Route) { + // Keep a record of routes attached to a device for setting properties. + // This will overwrite routes on updates to + let routes = self.active_routes.entry(id).or_default(); + if routes.len() < index as usize + 1 { + let additional = (index as usize + 1) - routes.capacity(); + routes.reserve_exact(additional); + routes.extend(std::iter::repeat(Route::default()).take(additional)); + } + routes[index as usize] = route.clone(); + self.on_event(Event::ActiveRoute(id, index, route)); } @@ -554,8 +575,8 @@ impl State { self.on_event(Event::AddNode(node)); } - fn add_profile(&mut self, id: DeviceId, profile: Profile) { - self.on_event(Event::AddProfile(id, profile)); + fn add_profile(&mut self, id: DeviceId, index: u32, profile: Profile) { + self.on_event(Event::AddProfile(id, index, profile)); } fn add_route(&mut self, id: DeviceId, index: u32, route: Route) { @@ -592,8 +613,8 @@ impl State { self.on_event(Event::DefaultSource(name)); } - fn node_route(&self, device_id: DeviceId, route_device: i32) -> Option<&Route> { - self.routes + fn active_node_route(&self, device_id: DeviceId, route_device: i32) -> Option<&Route> { + self.active_routes .get(device_id)? .iter() .find(|r| r.devices.contains(&route_device)) @@ -682,14 +703,14 @@ impl State { fn set_mute_node(&self, id: NodeId, mute: bool) { // Prefer to mute the device instead of the node. // Muting a node will not emit a notification. - if let Some((&device_id, &route_device)) = self + if let Some((&device_id, &card_profile_device)) = self .node_devices .get(id) .zip(self.node_card_profile_device.get(id)) { - let route_device = route_device as i32; - if let Some(route) = self.node_route(device_id, route_device) { - self.set_mute(device_id, route_device, route, mute); + let card_profile_device = card_profile_device as i32; + if let Some(route) = self.active_node_route(device_id, card_profile_device) { + self.set_mute(device_id, card_profile_device, route, mute); return; }; } @@ -762,14 +783,21 @@ impl State { }; // Prefer to change the volume of the device instead of the node. - if let Some((&device_id, &route_device)) = self + if let Some((&device_id, &card_profile_device)) = self .node_devices .get(id) .zip(self.node_card_profile_device.get(id)) { - let route_device = route_device as i32; - if let Some(route) = self.node_route(device_id, route_device) { - self.set_volume(device_id, props, route_device, route, volume, balance); + let card_profile_device = card_profile_device as i32; + if let Some(route) = self.active_node_route(device_id, card_profile_device) { + self.set_volume( + device_id, + props, + card_profile_device, + route, + volume, + balance, + ); return; }; } diff --git a/crates/cosmic-pipewire/src/profile.rs b/crates/cosmic-pipewire/src/profile.rs index d2b01d0..aabad61 100644 --- a/crates/cosmic-pipewire/src/profile.rs +++ b/crates/cosmic-pipewire/src/profile.rs @@ -4,7 +4,7 @@ use crate::{Availability, spa_utils::string_from_pod}; use libspa::pod::Pod; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default)] pub struct Profile { pub index: i32, pub priority: i32, diff --git a/subscriptions/sound/src/lib.rs b/subscriptions/sound/src/lib.rs index e129c59..b99e766 100644 --- a/subscriptions/sound/src/lib.rs +++ b/subscriptions/sound/src/lib.rs @@ -526,7 +526,7 @@ impl Model { } } - pipewire::Event::AddProfile(id, profile) => { + pipewire::Event::AddProfile(id, index, profile) => { if let Some(p) = self.active_profiles.get_mut(id) { if p.index == profile.index { *p = profile.clone(); @@ -534,16 +534,17 @@ impl Model { } let profiles = self.device_profiles.entry(id).or_default(); - for p in profiles.iter_mut() { - if p.index == profile.index { - *p = profile; - - self.update_ui_profiles(); - return; - } + if profiles.len() < index as usize + 1 { + let additional = (index as usize + 1) - profiles.capacity(); + profiles.reserve_exact(additional); + profiles.extend(std::iter::repeat_n( + pipewire::Profile::default(), + additional, + )); } - profiles.push(profile); + profiles[index as usize] = profile; + self.update_ui_profiles(); } @@ -676,12 +677,22 @@ impl Model { fn add_route(&mut self, id: DeviceId, index: u32, route: pipewire::Route) { self.update_device_route_name(&route, id); + + tracing::debug!(target: "sound", + "Device {} added route {} ({:?}); {:?}", + id, + route.name, + route.direction, + route.available + ); + let routes = self.device_routes.entry(id).or_default(); if routes.len() < index as usize + 1 { let additional = (index as usize + 1) - routes.capacity(); routes.reserve_exact(additional); routes.extend(std::iter::repeat_n(pipewire::Route::default(), additional)); } + routes[index as usize] = route; }