audio: WIP code for a volume scale
This commit is contained in:
parent
eabe58f62c
commit
33178a9a78
3 changed files with 104 additions and 4 deletions
|
|
@ -11,6 +11,7 @@ mod pa;
|
||||||
use pa::PA;
|
use pa::PA;
|
||||||
mod task;
|
mod task;
|
||||||
mod volume;
|
mod volume;
|
||||||
|
mod volume_scale;
|
||||||
|
|
||||||
use futures::{channel::mpsc, stream::StreamExt};
|
use futures::{channel::mpsc, stream::StreamExt};
|
||||||
use gtk4::{
|
use gtk4::{
|
||||||
|
|
|
||||||
|
|
@ -197,7 +197,11 @@ impl PA {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn set_sink_volume_by_name(&self, name: &str, volume: &ChannelVolumes) -> bool {
|
pub fn set_sink_volume_by_name(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
volume: &ChannelVolumes,
|
||||||
|
) -> PAFut<bool, impl ?Sized> {
|
||||||
PAFut::new(|waker| {
|
PAFut::new(|waker| {
|
||||||
self.introspect().set_sink_volume_by_name(
|
self.introspect().set_sink_volume_by_name(
|
||||||
name,
|
name,
|
||||||
|
|
@ -205,10 +209,13 @@ impl PA {
|
||||||
Some(Box::new(move |success| waker.wake(success))),
|
Some(Box::new(move |success| waker.wake(success))),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn set_source_volume_by_name(&self, name: &str, volume: &ChannelVolumes) -> bool {
|
pub fn set_source_volume_by_name(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
volume: &ChannelVolumes,
|
||||||
|
) -> PAFut<bool, impl ?Sized> {
|
||||||
PAFut::new(|waker| {
|
PAFut::new(|waker| {
|
||||||
self.introspect().set_source_volume_by_name(
|
self.introspect().set_source_volume_by_name(
|
||||||
name,
|
name,
|
||||||
|
|
@ -216,6 +223,5 @@ impl PA {
|
||||||
Some(Box::new(move |success| waker.wake(success))),
|
Some(Box::new(move |success| waker.wake(success))),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
93
applets/cosmic-applet-audio/src/volume_scale.rs
Normal file
93
applets/cosmic-applet-audio/src/volume_scale.rs
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
use gtk4::{glib, prelude::*};
|
||||||
|
use libpulse_binding::volume::{ChannelVolumes, Volume};
|
||||||
|
use std::{
|
||||||
|
cell::{Cell, RefCell},
|
||||||
|
future::Future,
|
||||||
|
pin::Pin,
|
||||||
|
rc::Rc,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::PA;
|
||||||
|
|
||||||
|
// Component
|
||||||
|
|
||||||
|
struct VolumeScale {
|
||||||
|
scale: gtk4::Scale,
|
||||||
|
name: Rc<RefCell<Option<String>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VolumeScale {
|
||||||
|
fn new(pa: PA, sink: bool) {
|
||||||
|
let name: Rc<RefCell<Option<String>>> = Rc::new(RefCell::new(None));
|
||||||
|
let scale = gtk4::Scale::with_range(gtk4::Orientation::Horizontal, 0., 100., 1.);
|
||||||
|
let updater = Updater::new(move |value: f64| {
|
||||||
|
let name = name.clone();
|
||||||
|
let pa = pa.clone();
|
||||||
|
async move {
|
||||||
|
let mut volumes = ChannelVolumes::default();
|
||||||
|
volumes.set(0, Volume((value * 100.) as _)); // XXX ?
|
||||||
|
|
||||||
|
let name_ref = name.borrow();
|
||||||
|
if let Some(name) = name_ref.as_deref() {
|
||||||
|
if sink {
|
||||||
|
let fut = pa.set_sink_volume_by_name(name, &volumes);
|
||||||
|
drop(name_ref);
|
||||||
|
fut.await;
|
||||||
|
} else {
|
||||||
|
let fut = pa.set_source_volume_by_name(name, &volumes);
|
||||||
|
drop(name_ref);
|
||||||
|
fut.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
scale.connect_change_value(move |_scale, _scroll, value| {
|
||||||
|
updater.update(value);
|
||||||
|
gtk4::Inhibit(false)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_value(&self, value: f64) {
|
||||||
|
self.scale.set_value(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_name(&self, name: Option<String>) {
|
||||||
|
*self.name.borrow_mut() = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform an asynchronous update operation without queuing more than one set.
|
||||||
|
struct Updater<T: 'static> {
|
||||||
|
updating: Rc<Cell<bool>>,
|
||||||
|
value: Rc<Cell<Option<T>>>,
|
||||||
|
update_fn: Rc<dyn Fn(T) -> Pin<Box<dyn Future<Output = ()> + 'static>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: 'static> Updater<T> {
|
||||||
|
fn new<Fut: Future<Output = ()> + 'static, F: Fn(T) -> Fut + 'static>(f: F) -> Self {
|
||||||
|
let value = Rc::new(Cell::new(None));
|
||||||
|
let updating = Rc::new(Cell::new(false));
|
||||||
|
let update_fn =
|
||||||
|
Rc::new(move |value| Box::pin(f(value)) as Pin<Box<dyn Future<Output = ()>>>);
|
||||||
|
Self {
|
||||||
|
updating,
|
||||||
|
value,
|
||||||
|
update_fn,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&self, value: T) {
|
||||||
|
self.value.set(Some(value));
|
||||||
|
if self.updating.replace(true) == false {
|
||||||
|
let value = self.value.clone();
|
||||||
|
let updating = self.updating.clone();
|
||||||
|
let update_fn = self.update_fn.clone();
|
||||||
|
glib::MainContext::default().spawn_local(async move {
|
||||||
|
while let Some(value) = value.take() {
|
||||||
|
update_fn(value).await;
|
||||||
|
}
|
||||||
|
updating.set(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue