diff --git a/applets/cosmic-applet-audio/src/input.rs b/applets/cosmic-applet-audio/src/input.rs index c75d2cbe..8c1dc561 100644 --- a/applets/cosmic-applet-audio/src/input.rs +++ b/applets/cosmic-applet-audio/src/input.rs @@ -2,16 +2,16 @@ use gtk4::{prelude::*, Button, Label, ListBox}; use libcosmic_widgets::{relm4::RelmContainerExt, LabeledItem}; use std::rc::Rc; -use crate::pa::{Source, PA}; +use crate::pa::{DeviceInfo, PA}; -pub async fn get_inputs(pa: &PA) -> Vec { +pub async fn get_inputs(pa: &PA) -> Vec { // XXX handle error pa.get_source_info_list() .await .expect("failed to list input devices") } -pub async fn refresh_default_input(pa: &PA, label: &Label) -> Source { +pub async fn refresh_default_input(pa: &PA, label: &Label) -> DeviceInfo { // XXX handle error let default_input = pa .get_default_source() diff --git a/applets/cosmic-applet-audio/src/main.rs b/applets/cosmic-applet-audio/src/main.rs index 068f15f4..7d997433 100644 --- a/applets/cosmic-applet-audio/src/main.rs +++ b/applets/cosmic-applet-audio/src/main.rs @@ -21,12 +21,15 @@ use gtk4::{ Orientation, PositionType, Revealer, RevealerTransitionType, Scale, SelectionMode, Separator, }; use libpulse_binding::{ - context::subscribe::{Facility, InterestMaskSet, Operation}, + context::{ + subscribe::{Facility, InterestMaskSet, Operation}, + FlagSet, State, + }, volume::Volume, }; use mpris2_zbus::metadata::Metadata; use once_cell::sync::Lazy; -use std::rc::Rc; +use std::{cell::RefCell, rc::Rc}; use tokio::runtime::Runtime; static RT: Lazy = Lazy::new(|| Runtime::new().expect("failed to build tokio runtime")); @@ -61,9 +64,20 @@ fn app(application: &Application) { _ => {} } })))); - pa.context - .subscribe(InterestMaskSet::SINK | InterestMaskSet::SOURCE, |_| {}); - let pa = Rc::new(pa); + let pa = Rc::new(RefCell::new(pa)); + pa.borrow_mut() + .context + .set_state_callback(Some(Box::new(clone!(@strong pa => move || { + let mut pa = pa.borrow_mut(); + if pa.context.get_state() == State::Ready { + pa.context + .subscribe(InterestMaskSet::SINK | InterestMaskSet::SOURCE, |_| {}); + } + })))); + pa.borrow_mut() + .context + .connect(None, FlagSet::empty(), None) + .unwrap(); view! { window = ApplicationWindow { set_application: Some(application), @@ -152,18 +166,20 @@ fn app(application: &Application) { glib::MainContext::default().spawn_local( clone!(@weak inputs, @weak current_input, @weak input_volume, @strong pa => async move { while let Some(()) = refresh_input_rx.next().await { - input::refresh_input_widgets(&pa, &inputs); - let default_input = input::refresh_default_input(&pa, ¤t_input); - // XXX volume::update_volume(&default_input, &input_volume); + let pa = pa.borrow(); + input::refresh_input_widgets(&pa, &inputs).await; + let default_input = input::refresh_default_input(&pa, ¤t_input).await; + volume::update_volume(&default_input, &input_volume); } }), ); glib::MainContext::default().spawn_local( clone!(@weak outputs, @weak current_output, @weak output_volume, @strong pa => async move { while let Some(()) = refresh_output_rx.next().await { + let pa = pa.borrow(); output::refresh_output_widgets(&pa, &outputs); - let default_output = output::refresh_default_output(&pa, ¤t_output); - // XXX volume::update_volume(&default_output, &output_volume); + let default_output = output::refresh_default_output(&pa, ¤t_output).await; + volume::update_volume(&default_output, &output_volume); } }), ); diff --git a/applets/cosmic-applet-audio/src/output.rs b/applets/cosmic-applet-audio/src/output.rs index ef9ae407..6684414e 100644 --- a/applets/cosmic-applet-audio/src/output.rs +++ b/applets/cosmic-applet-audio/src/output.rs @@ -2,16 +2,16 @@ use gtk4::{prelude::*, Button, Label, ListBox}; use libcosmic_widgets::{relm4::RelmContainerExt, LabeledItem}; use std::rc::Rc; -use crate::pa::{Sink, PA}; +use crate::pa::{DeviceInfo, PA}; -pub async fn get_outputs(pa: &PA) -> Vec { +pub async fn get_outputs(pa: &PA) -> Vec { // XXX handle error pa.get_sink_info_list() .await .expect("failed to list output devices") } -pub async fn refresh_default_output(pa: &PA, label: &Label) -> Sink { +pub async fn refresh_default_output(pa: &PA, label: &Label) -> DeviceInfo { // XXX handle error let default_output = pa .get_default_sink() diff --git a/applets/cosmic-applet-audio/src/pa.rs b/applets/cosmic-applet-audio/src/pa.rs index ac828b8f..4063cfd0 100644 --- a/applets/cosmic-applet-audio/src/pa.rs +++ b/applets/cosmic-applet-audio/src/pa.rs @@ -2,18 +2,15 @@ use futures::{channel::oneshot, future::poll_fn, task::Poll}; use libpulse_binding::{ callbacks::ListResult, context::{introspect::SinkInfo, Context}, + volume::ChannelVolumes, }; use libpulse_glib_binding::Mainloop; use std::rc::Rc; -pub struct Sink { - pub name: Option, - pub description: Option, -} - -pub struct Source { +pub struct DeviceInfo { pub name: Option, pub description: Option, + pub volume: ChannelVolumes, } pub struct ServerInfo { @@ -45,16 +42,17 @@ impl PA { receiver.await.unwrap() } - pub async fn get_sink_info_list(&self) -> Result, ()> { + pub async fn get_sink_info_list(&self) -> Result, ()> { let (sender, receiver) = oneshot::channel(); let mut sender = Some(sender); let mut items = Some(Vec::new()); self.context .introspect() .get_sink_info_list(move |result| match result { - ListResult::Item(item) => items.as_mut().unwrap().push(Sink { + ListResult::Item(item) => items.as_mut().unwrap().push(DeviceInfo { name: item.name.clone().map(|x| x.into_owned()), description: item.description.clone().map(|x| x.into_owned()), + volume: item.volume, }), ListResult::End => { sender.take().unwrap().send(Ok(items.take().unwrap())); @@ -66,7 +64,7 @@ impl PA { receiver.await.unwrap() } - pub async fn get_default_sink(&self) -> Result { + pub async fn get_default_sink(&self) -> Result { let name = match self.get_server_info().await.default_sink_name { Some(name) => name, None => { @@ -80,9 +78,10 @@ impl PA { .introspect() .get_sink_info_by_name(&name, move |result| match result { ListResult::Item(item) => { - sink = Some(Sink { + sink = Some(DeviceInfo { name: item.name.clone().map(|x| x.into_owned()), description: item.description.clone().map(|x| x.into_owned()), + volume: item.volume, }); } ListResult::End => { @@ -102,16 +101,17 @@ impl PA { } */ - pub async fn get_source_info_list(&self) -> Result, ()> { + pub async fn get_source_info_list(&self) -> Result, ()> { let (sender, receiver) = oneshot::channel(); let mut sender = Some(sender); let mut items = Some(Vec::new()); self.context .introspect() .get_source_info_list(move |result| match result { - ListResult::Item(item) => items.as_mut().unwrap().push(Source { + ListResult::Item(item) => items.as_mut().unwrap().push(DeviceInfo { name: item.name.clone().map(|x| x.into_owned()), description: item.description.clone().map(|x| x.into_owned()), + volume: item.volume, }), ListResult::End => { sender.take().unwrap().send(Ok(items.take().unwrap())); @@ -123,7 +123,7 @@ impl PA { receiver.await.unwrap() } - pub async fn get_default_source(&self) -> Result { + pub async fn get_default_source(&self) -> Result { let name = match self.get_server_info().await.default_source_name { Some(name) => name, None => { @@ -137,9 +137,10 @@ impl PA { .introspect() .get_source_info_by_name(&name, move |result| match result { ListResult::Item(item) => { - source = Some(Source { + source = Some(DeviceInfo { name: item.name.clone().map(|x| x.into_owned()), description: item.description.clone().map(|x| x.into_owned()), + volume: item.volume, }); } ListResult::End => { diff --git a/applets/cosmic-applet-audio/src/volume.rs b/applets/cosmic-applet-audio/src/volume.rs index c52020b2..0cc163f1 100644 --- a/applets/cosmic-applet-audio/src/volume.rs +++ b/applets/cosmic-applet-audio/src/volume.rs @@ -1,8 +1,8 @@ use gtk4::{prelude::*, Scale}; use libpulse_binding::volume::Volume; -/* +use crate::pa::DeviceInfo; + pub fn update_volume(device: &DeviceInfo, scale: &Scale) { scale.set_value((device.volume.avg().0 as f64 / Volume::NORMAL.0 as f64) * 100.); } -*/