audio: Use custom, cancellable, single threaded future type

This commit is contained in:
Ian Douglas Scott 2022-07-21 13:16:08 -07:00
parent 35a2d6905b
commit 0aaa0dd74d
2 changed files with 152 additions and 107 deletions

View file

@ -0,0 +1,63 @@
use libpulse_binding::operation::Operation;
use std::{
cell::RefCell,
future::{self, Future},
pin::Pin,
rc::Rc,
task::{self, Poll, Waker},
};
struct PAFutInner<T> {
res: Option<T>,
waker: Option<Waker>,
}
pub struct PAFutWaker<T>(Rc<RefCell<PAFutInner<T>>>);
impl<T> PAFutWaker<T> {
pub fn wake(&self, res: T) {
let mut inner = self.0.borrow_mut();
inner.res = Some(res);
if let Some(waker) = inner.waker.take() {
waker.wake();
}
}
}
pub struct PAFut<T, F: ?Sized> {
inner: Rc<RefCell<PAFutInner<T>>>,
operation: Operation<F>,
}
impl<T, F: ?Sized> PAFut<T, F> {
pub fn new(cb: impl FnOnce(PAFutWaker<T>) -> Operation<F>) -> Self {
let inner = Rc::new(RefCell::new(PAFutInner {
res: None,
waker: None,
}));
let operation = cb(PAFutWaker(inner.clone()));
Self { inner, operation }
}
}
impl<T, F: ?Sized> Future for PAFut<T, F> {
type Output = T;
fn poll(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll<Self::Output> {
let mut inner = self.inner.borrow_mut();
if let Some(res) = inner.res.take() {
Poll::Ready(res)
} else {
inner.waker = Some(cx.waker().clone());
Poll::Pending
}
}
}
impl<T, F: ?Sized> Drop for PAFut<T, F> {
fn drop(&mut self) {
self.operation.cancel();
}
}

View file

@ -1,4 +1,3 @@
use futures::{channel::oneshot, future::poll_fn, task::Poll};
use gtk4::glib; use gtk4::glib;
use libpulse_binding::{ use libpulse_binding::{
callbacks::ListResult, callbacks::ListResult,
@ -12,10 +11,13 @@ use libpulse_binding::{
}; };
use libpulse_glib_binding::Mainloop; use libpulse_glib_binding::Mainloop;
use std::{ use std::{
cell::{Ref, RefCell}, cell::RefCell,
rc::Rc, rc::Rc,
}; };
mod future;
use future::{PAFut, PAFutWaker};
pub struct DeviceInfo { pub struct DeviceInfo {
pub name: Option<String>, pub name: Option<String>,
pub description: Option<String>, pub description: Option<String>,
@ -90,37 +92,33 @@ impl PA {
} }
pub async fn get_server_info(&self) -> ServerInfo { pub async fn get_server_info(&self) -> ServerInfo {
let (sender, receiver) = oneshot::channel(); PAFut::new(|waker| {
let mut sender = Some(sender); self.introspect().get_server_info(move |info| {
self.introspect().get_server_info(move |info| { waker.wake(ServerInfo {
sender.take().unwrap().send(ServerInfo { default_sink_name: info.default_sink_name.clone().map(|x| x.into_owned()),
default_sink_name: info.default_sink_name.clone().map(|x| x.into_owned()), default_source_name: info.default_source_name.clone().map(|x| x.into_owned()),
default_source_name: info.default_source_name.clone().map(|x| x.into_owned()), });
}); })
}); })
receiver.await.unwrap() .await
} }
pub async fn get_sink_info_list(&self) -> Result<Vec<DeviceInfo>, ()> { pub async fn get_sink_info_list(&self) -> Result<Vec<DeviceInfo>, ()> {
let (sender, receiver) = oneshot::channel();
let mut sender = Some(sender);
let mut items = Some(Vec::new()); let mut items = Some(Vec::new());
self.introspect() PAFut::new(|waker| {
.get_sink_info_list(move |result| match result { self.introspect()
ListResult::Item(item) => items.as_mut().unwrap().push(DeviceInfo { .get_sink_info_list(move |result| match result {
name: item.name.clone().map(|x| x.into_owned()), ListResult::Item(item) => items.as_mut().unwrap().push(DeviceInfo {
description: item.description.clone().map(|x| x.into_owned()), name: item.name.clone().map(|x| x.into_owned()),
volume: item.volume, description: item.description.clone().map(|x| x.into_owned()),
index: item.index, volume: item.volume,
}), index: item.index,
ListResult::End => { }),
sender.take().unwrap().send(Ok(items.take().unwrap())); ListResult::End => waker.wake(Ok(items.take().unwrap())),
} ListResult::Error => waker.wake(Err(())),
ListResult::Error => { })
sender.take().unwrap().send(Err(())); })
} .await
});
receiver.await.unwrap()
} }
pub async fn get_default_sink(&self) -> Result<DeviceInfo, ()> { pub async fn get_default_sink(&self) -> Result<DeviceInfo, ()> {
@ -130,27 +128,23 @@ impl PA {
return Err(()); return Err(());
} }
}; };
let (sender, receiver) = oneshot::channel();
let mut sender = Some(sender);
let mut sink = None; let mut sink = None;
self.introspect() PAFut::new(|waker| {
.get_sink_info_by_name(&name, move |result| match result { self.introspect()
ListResult::Item(item) => { .get_sink_info_by_name(&name, move |result| match result {
sink = Some(DeviceInfo { ListResult::Item(item) => {
name: item.name.clone().map(|x| x.into_owned()), sink = Some(DeviceInfo {
description: item.description.clone().map(|x| x.into_owned()), name: item.name.clone().map(|x| x.into_owned()),
volume: item.volume, description: item.description.clone().map(|x| x.into_owned()),
index: item.index, volume: item.volume,
}); index: item.index,
} });
ListResult::End => { }
sender.take().unwrap().send(sink.take().ok_or(())); ListResult::End => waker.wake(sink.take().ok_or(())),
} ListResult::Error => waker.wake(Err(())),
ListResult::Error => { })
sender.take().unwrap().send(Err(())); })
} .await
});
receiver.await.unwrap()
} }
// XXX async wait and handle error // XXX async wait and handle error
@ -163,25 +157,21 @@ impl PA {
} }
pub async fn get_source_info_list(&self) -> Result<Vec<DeviceInfo>, ()> { pub async fn get_source_info_list(&self) -> Result<Vec<DeviceInfo>, ()> {
let (sender, receiver) = oneshot::channel();
let mut sender = Some(sender);
let mut items = Some(Vec::new()); let mut items = Some(Vec::new());
self.introspect() PAFut::new(|waker| {
.get_source_info_list(move |result| match result { self.introspect()
ListResult::Item(item) => items.as_mut().unwrap().push(DeviceInfo { .get_source_info_list(move |result| match result {
name: item.name.clone().map(|x| x.into_owned()), ListResult::Item(item) => items.as_mut().unwrap().push(DeviceInfo {
description: item.description.clone().map(|x| x.into_owned()), name: item.name.clone().map(|x| x.into_owned()),
volume: item.volume, description: item.description.clone().map(|x| x.into_owned()),
index: item.index, volume: item.volume,
}), index: item.index,
ListResult::End => { }),
sender.take().unwrap().send(Ok(items.take().unwrap())); ListResult::End => waker.wake(Ok(items.take().unwrap())),
} ListResult::Error => waker.wake(Err(())),
ListResult::Error => { })
sender.take().unwrap().send(Err(())); })
} .await
});
receiver.await.unwrap()
} }
pub async fn get_default_source(&self) -> Result<DeviceInfo, ()> { pub async fn get_default_source(&self) -> Result<DeviceInfo, ()> {
@ -191,52 +181,44 @@ impl PA {
return Err(()); return Err(());
} }
}; };
let (sender, receiver) = oneshot::channel();
let mut sender = Some(sender);
let mut source = None; let mut source = None;
self.introspect() PAFut::new(|waker| {
.get_source_info_by_name(&name, move |result| match result { self.introspect()
ListResult::Item(item) => { .get_source_info_by_name(&name, move |result| match result {
source = Some(DeviceInfo { ListResult::Item(item) => {
name: item.name.clone().map(|x| x.into_owned()), source = Some(DeviceInfo {
description: item.description.clone().map(|x| x.into_owned()), name: item.name.clone().map(|x| x.into_owned()),
volume: item.volume, description: item.description.clone().map(|x| x.into_owned()),
index: item.index, volume: item.volume,
}); index: item.index,
} });
ListResult::End => { }
sender.take().unwrap().send(source.take().ok_or(())); ListResult::End => waker.wake(source.take().ok_or(())),
} ListResult::Error => waker.wake(Err(())),
ListResult::Error => { })
sender.take().unwrap().send(Err(())); })
} .await
});
receiver.await.unwrap()
} }
pub async fn set_sink_volume_by_name(&self, name: &str, volume: &ChannelVolumes) -> bool { pub async fn set_sink_volume_by_name(&self, name: &str, volume: &ChannelVolumes) -> bool {
let (sender, receiver) = oneshot::channel(); PAFut::new(|waker| {
let mut sender = Some(sender); self.introspect().set_sink_volume_by_name(
self.introspect().set_sink_volume_by_name( name,
name, volume,
volume, Some(Box::new(move |success| waker.wake(success))),
Some(Box::new(move |success| { )
sender.take().unwrap().send(success); })
})), .await
);
receiver.await.unwrap()
} }
pub async fn set_source_volume_by_name(&self, name: &str, volume: &ChannelVolumes) -> bool { pub async fn set_source_volume_by_name(&self, name: &str, volume: &ChannelVolumes) -> bool {
let (sender, receiver) = oneshot::channel(); PAFut::new(|waker| {
let mut sender = Some(sender); self.introspect().set_source_volume_by_name(
self.introspect().set_source_volume_by_name( name,
name, volume,
volume, Some(Box::new(move |success| waker.wake(success))),
Some(Box::new(move |success| { )
sender.take().unwrap().send(success); })
})), .await
);
receiver.await.unwrap()
} }
} }