diff --git a/src/application.rs b/src/application.rs index e251e37..6bd7eda 100644 --- a/src/application.rs +++ b/src/application.rs @@ -27,7 +27,6 @@ mod imp { window.present(); 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("user-agent", crate::USER_AGENT).unwrap(); mpv.set_property("video", false).unwrap(); @@ -44,8 +43,7 @@ mod imp { self, async move { loop { - app.mpv.borrow().wait().await; - app.mpv.borrow_mut().tick(); + app.mpv.borrow_mut().tick().await; } } )); diff --git a/src/mpv.rs b/src/mpv.rs index a97aab5..61a05ee 100644 --- a/src/mpv.rs +++ b/src/mpv.rs @@ -46,11 +46,13 @@ impl std::error::Error for Error {} use std::pin::Pin; use std::ptr::NonNull; +use event_listener::{Event, IntoNotification}; +use std::cell::RefCell; pub struct Handle { inner: NonNull, - _wakeup_send: Pin>>, // the wakeup callback controls this one - wakeup_recv: async_channel::Receiver<()>, + wakeup: Pin>, // the wakeup callback holds a pointer to this + tick_cell: RefCell<()>, observed_property_callbacks: Vec>>>, @@ -63,52 +65,43 @@ pub struct Handle { impl Default for Handle { fn default() -> Self { - Self::create() + Self::new() } } impl Handle { - pub fn create() -> Self { + pub fn new() -> Self { let inner = NonNull::new(unsafe { ffi::mpv_create() }).expect("could not create mpv handle"); - let (wakeup_send, wakeup_recv) = async_channel::bounded(1); - let wakeup_send = Box::pin(wakeup_send); + let wakeup = Box::pin(Event::new()); extern "C" fn wakeup_callback(d: *mut c_void) { - let d = d as *const async_channel::Sender<()>; - let wakeup_send = unsafe { &*d }; - match wakeup_send.try_send(()) { - 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"); - } - } + let d = d as *const Event; + let wakeup = unsafe { &*d }; + wakeup.notify(1_u32.relaxed()); // mpv has its own synchronization, trust it } unsafe { ffi::mpv_set_wakeup_callback( inner.as_ptr(), Some(wakeup_callback), - std::ptr::from_ref::>(&wakeup_send) as *mut c_void, + std::ptr::from_ref::(&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 { inner: inner, - _wakeup_send: wakeup_send, - wakeup_recv: wakeup_recv, + wakeup: wakeup, + tick_cell: RefCell::new(()), 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> { // need to add zero terminator let name = CString::new(name).expect("null bytes in property name"); @@ -143,14 +136,13 @@ impl Handle { Ok(()) } - pub async fn wait(&self) { - match self.wakeup_recv.recv().await { - Ok(()) => {} - Err(async_channel::RecvError) => unreachable!(), - } - } + pub async fn tick(&self) { + // mpv_wait_event is not reentrant!! use a refcell to ensure that + self.tick_cell.borrow_mut(); + + // 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 { let event = unsafe { &*ffi::mpv_wait_event(self.inner.as_ptr(), 0.0) }; match event.event_id { @@ -170,6 +162,8 @@ impl Handle { _ => todo!("event {}", event.event_id), } } + + listener.await; } }