use event listener for mpv wakeup

This commit is contained in:
Erica Z 2024-11-02 16:50:10 +01:00
parent f194e1a608
commit 2aee230606
2 changed files with 26 additions and 34 deletions

View file

@ -27,7 +27,6 @@ mod imp {
window.present(); window.present();
let mut mpv = self.mpv.borrow_mut(); let mut mpv = self.mpv.borrow_mut();
mpv.initialize().expect("could not initialize mpv");
mpv.set_property("audio-client-name", "audrey").unwrap(); mpv.set_property("audio-client-name", "audrey").unwrap();
mpv.set_property("user-agent", crate::USER_AGENT).unwrap(); mpv.set_property("user-agent", crate::USER_AGENT).unwrap();
mpv.set_property("video", false).unwrap(); mpv.set_property("video", false).unwrap();
@ -44,8 +43,7 @@ mod imp {
self, self,
async move { async move {
loop { loop {
app.mpv.borrow().wait().await; app.mpv.borrow_mut().tick().await;
app.mpv.borrow_mut().tick();
} }
} }
)); ));

View file

@ -46,11 +46,13 @@ impl std::error::Error for Error {}
use std::pin::Pin; use std::pin::Pin;
use std::ptr::NonNull; use std::ptr::NonNull;
use event_listener::{Event, IntoNotification};
use std::cell::RefCell;
pub struct Handle { pub struct Handle {
inner: NonNull<ffi::mpv_handle>, inner: NonNull<ffi::mpv_handle>,
_wakeup_send: Pin<Box<async_channel::Sender<()>>>, // the wakeup callback controls this one wakeup: Pin<Box<Event>>, // the wakeup callback holds a pointer to this
wakeup_recv: async_channel::Receiver<()>, tick_cell: RefCell<()>,
observed_property_callbacks: observed_property_callbacks:
Vec<Pin<Box<Box<dyn FnMut(ffi::mpv_format, *mut c_void) + 'static>>>>, Vec<Pin<Box<Box<dyn FnMut(ffi::mpv_format, *mut c_void) + 'static>>>>,
@ -63,52 +65,43 @@ pub struct Handle {
impl Default for Handle { impl Default for Handle {
fn default() -> Self { fn default() -> Self {
Self::create() Self::new()
} }
} }
impl Handle { impl Handle {
pub fn create() -> Self { pub fn new() -> Self {
let inner = let inner =
NonNull::new(unsafe { ffi::mpv_create() }).expect("could not create mpv handle"); NonNull::new(unsafe { ffi::mpv_create() }).expect("could not create mpv handle");
let (wakeup_send, wakeup_recv) = async_channel::bounded(1); let wakeup = Box::pin(Event::new());
let wakeup_send = Box::pin(wakeup_send);
extern "C" fn wakeup_callback(d: *mut c_void) { extern "C" fn wakeup_callback(d: *mut c_void) {
let d = d as *const async_channel::Sender<()>; let d = d as *const Event;
let wakeup_send = unsafe { &*d }; let wakeup = unsafe { &*d };
match wakeup_send.try_send(()) { wakeup.notify(1_u32.relaxed()); // mpv has its own synchronization, trust it
Ok(()) => {}
Err(async_channel::TrySendError::Full(())) => {
// assume the main thread has already been notified
}
Err(async_channel::TrySendError::Closed(())) => {
panic!("wakeup channgel was closed before mpv handle was finalized");
}
}
} }
unsafe { unsafe {
ffi::mpv_set_wakeup_callback( ffi::mpv_set_wakeup_callback(
inner.as_ptr(), inner.as_ptr(),
Some(wakeup_callback), Some(wakeup_callback),
std::ptr::from_ref::<async_channel::Sender<()>>(&wakeup_send) as *mut c_void, std::ptr::from_ref::<Event>(&wakeup) as *mut c_void,
); );
} }
// TODO: maybe we need to set something before initialization, but idk
// also wed need a builder, since "Note that you should avoid doing concurrent accesses on the uninitialized client handle."
Error::from_return_code(unsafe { ffi::mpv_initialize(inner.as_ptr()) }).expect("could not initialize mpv handle");
Self { Self {
inner: inner, inner: inner,
_wakeup_send: wakeup_send, wakeup: wakeup,
wakeup_recv: wakeup_recv, tick_cell: RefCell::new(()),
observed_property_callbacks: vec![], observed_property_callbacks: vec![],
} }
} }
pub fn initialize(&self) -> Result<(), Error> {
Error::from_return_code(unsafe { ffi::mpv_initialize(self.inner.as_ptr()) })
}
pub fn set_property<'a>(&self, name: &str, value: impl FormatValue) -> Result<(), Error> { pub fn set_property<'a>(&self, name: &str, value: impl FormatValue) -> Result<(), Error> {
// need to add zero terminator // need to add zero terminator
let name = CString::new(name).expect("null bytes in property name"); let name = CString::new(name).expect("null bytes in property name");
@ -143,14 +136,13 @@ impl Handle {
Ok(()) Ok(())
} }
pub async fn wait(&self) { pub async fn tick(&self) {
match self.wakeup_recv.recv().await { // mpv_wait_event is not reentrant!! use a refcell to ensure that
Ok(()) => {} self.tick_cell.borrow_mut();
Err(async_channel::RecvError) => unreachable!(),
} // take listener before we drain the event loop, so we don't miss any notifications
} let listener = self.wakeup.listen();
pub fn tick(&mut self) {
loop { loop {
let event = unsafe { &*ffi::mpv_wait_event(self.inner.as_ptr(), 0.0) }; let event = unsafe { &*ffi::mpv_wait_event(self.inner.as_ptr(), 0.0) };
match event.event_id { match event.event_id {
@ -170,6 +162,8 @@ impl Handle {
_ => todo!("event {}", event.event_id), _ => todo!("event {}", event.event_id),
} }
} }
listener.await;
} }
} }