fix(sound): set route before setting default to fix headsets
This commit is contained in:
parent
a5366a5cb8
commit
079a1fbb55
4 changed files with 78 additions and 61 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -1828,7 +1828,6 @@ name = "cosmic-settings-sound-subscription"
|
||||||
version = "1.0.0-beta6"
|
version = "1.0.0-beta6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cosmic-pipewire",
|
"cosmic-pipewire",
|
||||||
"crossbeam-queue",
|
|
||||||
"futures",
|
"futures",
|
||||||
"intmap",
|
"intmap",
|
||||||
"libcosmic",
|
"libcosmic",
|
||||||
|
|
|
||||||
|
|
@ -530,10 +530,6 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_node(&mut self, id: PipewireId, node: Node) {
|
fn add_node(&mut self, id: PipewireId, node: Node) {
|
||||||
eprintln!(
|
|
||||||
"adding node {} with card.profile.device {:?}",
|
|
||||||
node.object_id, node.card_profile_device
|
|
||||||
);
|
|
||||||
// Map the device's pipewire ID to its device ID
|
// Map the device's pipewire ID to its device ID
|
||||||
if let Some(entry) = self.proxies.nodes.get_mut(id) {
|
if let Some(entry) = self.proxies.nodes.get_mut(id) {
|
||||||
entry.0 = node.object_id;
|
entry.0 = node.object_id;
|
||||||
|
|
@ -573,7 +569,6 @@ impl State {
|
||||||
}
|
}
|
||||||
routes[index as usize] = route.clone();
|
routes[index as usize] = route.clone();
|
||||||
|
|
||||||
eprintln!("add route on device {id}[{index}]: {}", route.name);
|
|
||||||
self.on_event(Event::AddRoute(id, index, route));
|
self.on_event(Event::AddRoute(id, index, route));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -590,12 +585,10 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_sink(&mut self, name: String) {
|
fn default_sink(&mut self, name: String) {
|
||||||
eprintln!("default sink set to {name}");
|
|
||||||
self.on_event(Event::DefaultSink(name));
|
self.on_event(Event::DefaultSink(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_source(&mut self, name: String) {
|
fn default_source(&mut self, name: String) {
|
||||||
eprintln!("default source set to {name}");
|
|
||||||
self.on_event(Event::DefaultSource(name));
|
self.on_event(Event::DefaultSource(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -813,7 +806,6 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_profile(&mut self, id: DeviceId, index: u32, save: bool) {
|
fn set_profile(&mut self, id: DeviceId, index: u32, save: bool) {
|
||||||
eprintln!("set profile {id}[{index}]: {save}");
|
|
||||||
let Some(device) = self.device(id) else {
|
let Some(device) = self.device(id) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
@ -854,7 +846,6 @@ impl State {
|
||||||
volume: f32,
|
volume: f32,
|
||||||
balance: Option<f32>,
|
balance: Option<f32>,
|
||||||
) {
|
) {
|
||||||
eprintln!("set volume on {id} route device {route_device}");
|
|
||||||
let Some(device) = self.device(id) else {
|
let Some(device) = self.device(id) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ publish = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cosmic-pipewire = { path = "../../crates/cosmic-pipewire" }
|
cosmic-pipewire = { path = "../../crates/cosmic-pipewire" }
|
||||||
crossbeam-queue = "0.3.12"
|
|
||||||
futures = "0.3.31"
|
futures = "0.3.31"
|
||||||
intmap = "3.1.2"
|
intmap = "3.1.2"
|
||||||
libcosmic = { git = "https://github.com/pop-os/libcosmic", default-features = false }
|
libcosmic = { git = "https://github.com/pop-os/libcosmic", default-features = false }
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,14 @@
|
||||||
use cosmic::Task;
|
use cosmic::Task;
|
||||||
use cosmic::iced_futures::MaybeSend;
|
use cosmic::iced_futures::MaybeSend;
|
||||||
use cosmic_pipewire as pipewire;
|
use cosmic_pipewire as pipewire;
|
||||||
use futures::{FutureExt, SinkExt, Stream};
|
use futures::{SinkExt, Stream};
|
||||||
use intmap::IntMap;
|
use intmap::IntMap;
|
||||||
use pipewire::Availability;
|
use pipewire::Availability;
|
||||||
use std::{process::Stdio, sync::Arc, time::Duration};
|
use std::{
|
||||||
|
process::Stdio,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
pub type DeviceId = u32;
|
pub type DeviceId = u32;
|
||||||
pub type NodeId = u32;
|
pub type NodeId = u32;
|
||||||
|
|
@ -16,46 +20,37 @@ pub type RouteId = u32;
|
||||||
|
|
||||||
pub fn watch() -> impl Stream<Item = Message> + MaybeSend + 'static {
|
pub fn watch() -> impl Stream<Item = Message> + MaybeSend + 'static {
|
||||||
cosmic::iced_futures::stream::channel(1, |mut emitter| async move {
|
cosmic::iced_futures::stream::channel(1, |mut emitter| async move {
|
||||||
let (cancel_tx, mut cancel_rx) = futures::channel::oneshot::channel::<()>();
|
|
||||||
let events = Arc::new(crossbeam_queue::SegQueue::new());
|
|
||||||
|
|
||||||
_ = emitter
|
|
||||||
.send(
|
|
||||||
Message::SubHandle(Arc::new(SubscriptionHandle {
|
|
||||||
cancel_tx,
|
|
||||||
pipewire: pipewire::run({
|
|
||||||
let events = events.clone();
|
|
||||||
move |event| {
|
|
||||||
events.push(event);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
}))
|
|
||||||
.into(),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let mut timer = tokio::time::interval(Duration::from_millis(64));
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
futures::select! {
|
let (cancel_tx, cancel_rx) = futures::channel::oneshot::channel::<()>();
|
||||||
_ = timer.tick().fuse() => {
|
let sender = Arc::new((Mutex::new(Vec::new()), tokio::sync::Notify::const_new()));
|
||||||
if !events.is_empty() {
|
let receiver = sender.clone();
|
||||||
let mut batched = Vec::with_capacity(events.len());
|
|
||||||
while let Some(event) = events.pop() {
|
|
||||||
batched.push(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = emitter
|
_ = emitter
|
||||||
.send(Message::Server(Arc::from(batched)))
|
.send(
|
||||||
.await;
|
Message::SubHandle(Arc::new(SubscriptionHandle {
|
||||||
|
cancel_tx,
|
||||||
|
pipewire: pipewire::run(move |event| {
|
||||||
|
sender.0.lock().unwrap().push(event);
|
||||||
|
sender.1.notify_one();
|
||||||
|
}),
|
||||||
|
}))
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let forwarder = Box::pin(async {
|
||||||
|
loop {
|
||||||
|
_ = receiver.1.notified().await;
|
||||||
|
let events = std::mem::take(&mut *receiver.0.lock().unwrap());
|
||||||
|
if !events.is_empty() {
|
||||||
|
_ = emitter.send(Message::Server(Arc::from(events))).await;
|
||||||
|
tokio::time::sleep(Duration::from_millis(64)).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
_ = &mut cancel_rx => break,
|
futures::future::select(cancel_rx, forwarder).await;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
futures::future::pending::<Message>().await;
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -73,6 +68,7 @@ pub struct Model {
|
||||||
device_ids: IntMap<NodeId, DeviceId>,
|
device_ids: IntMap<NodeId, DeviceId>,
|
||||||
node_names: IntMap<NodeId, String>,
|
node_names: IntMap<NodeId, String>,
|
||||||
card_profile_devices: IntMap<NodeId, u32>,
|
card_profile_devices: IntMap<NodeId, u32>,
|
||||||
|
node_route_indexes: IntMap<NodeId, i32>,
|
||||||
|
|
||||||
device_names: IntMap<DeviceId, String>,
|
device_names: IntMap<DeviceId, String>,
|
||||||
device_profiles: IntMap<DeviceId, Vec<pipewire::Profile>>,
|
device_profiles: IntMap<DeviceId, Vec<pipewire::Profile>>,
|
||||||
|
|
@ -240,13 +236,28 @@ impl Model {
|
||||||
self.set_default_sink_id(node_id);
|
self.set_default_sink_id(node_id);
|
||||||
|
|
||||||
// Use pactl if the node is not a device node.
|
// Use pactl if the node is not a device node.
|
||||||
let virtual_sink_name: Option<String> = if self.device_ids.contains_key(node_id) {
|
let virtual_sink_name: Option<String> =
|
||||||
None
|
if let Some(device) = self.device_ids.get(node_id).cloned() {
|
||||||
} else if let Some(name) = self.node_names.get(node_id) {
|
// Get route index of the selected node and apply it to the device.
|
||||||
Some(name.clone())
|
if let Some((card_profile_device, route_index)) = self
|
||||||
} else {
|
.card_profile_devices
|
||||||
None
|
.get(node_id)
|
||||||
};
|
.cloned()
|
||||||
|
.zip(self.node_route_indexes.get(node_id).cloned())
|
||||||
|
{
|
||||||
|
self.pipewire_send(pipewire::Request::SetRoute(
|
||||||
|
device,
|
||||||
|
card_profile_device,
|
||||||
|
route_index as u32,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
} else if let Some(name) = self.node_names.get(node_id) {
|
||||||
|
Some(name.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
tokio::task::spawn(async move {
|
tokio::task::spawn(async move {
|
||||||
if let Some(node_name) = virtual_sink_name {
|
if let Some(node_name) = virtual_sink_name {
|
||||||
|
|
@ -304,13 +315,28 @@ impl Model {
|
||||||
self.set_default_source_id(node_id);
|
self.set_default_source_id(node_id);
|
||||||
|
|
||||||
// Use pactl if the node is not a device node.
|
// Use pactl if the node is not a device node.
|
||||||
let virtual_source_name: Option<String> = if self.device_ids.contains_key(node_id) {
|
let virtual_source_name: Option<String> =
|
||||||
None
|
if let Some(device) = self.device_ids.get(node_id).cloned() {
|
||||||
} else if let Some(name) = self.node_names.get(node_id) {
|
// Get route index of the selected node and apply it to the device.
|
||||||
Some(name.clone())
|
if let Some((card_profile_device, route_index)) = self
|
||||||
} else {
|
.card_profile_devices
|
||||||
None
|
.get(node_id)
|
||||||
};
|
.cloned()
|
||||||
|
.zip(self.node_route_indexes.get(node_id).cloned())
|
||||||
|
{
|
||||||
|
self.pipewire_send(pipewire::Request::SetRoute(
|
||||||
|
device,
|
||||||
|
card_profile_device,
|
||||||
|
route_index as u32,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
} else if let Some(name) = self.node_names.get(node_id) {
|
||||||
|
Some(name.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
tokio::task::spawn(async move {
|
tokio::task::spawn(async move {
|
||||||
if let Some(node_name) = virtual_source_name {
|
if let Some(node_name) = virtual_source_name {
|
||||||
|
|
@ -771,7 +797,9 @@ impl Model {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
tracing::debug!(target: "sound", "matched route {} on {}: {}", route.index, id, route.description);
|
||||||
devices[pos] = [&route.description, " - ", device_name].concat();
|
devices[pos] = [&route.description, " - ", device_name].concat();
|
||||||
|
self.node_route_indexes.insert(node, route.index);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue