diff --git a/src/application.rs b/src/application.rs index af69ed8..5fd2cff 100644 --- a/src/application.rs +++ b/src/application.rs @@ -36,15 +36,16 @@ mod imp { self.mpv.set_property("gapless-audio", true).unwrap(); // TODO: observe properties - glib::spawn_future_local(glib::clone!( - #[weak(rename_to = app)] - self, - async move { - loop { - app.mpv.tick().await; - } + let self_weak = self.obj().downgrade(); + glib::spawn_future_local(async move { + while let Some(app) = self_weak.upgrade() { + // this song and dance is required so the strong reference to app + // isn't uselessly preserved during the await + let future = app.imp().mpv.tick(); + drop(app); + future.await; } - )); + }); glib::spawn_future_local(async move { let conn = zbus::connection::Builder::session() diff --git a/src/mpv.rs b/src/mpv.rs index 0f292c1..178ade5 100644 --- a/src/mpv.rs +++ b/src/mpv.rs @@ -45,12 +45,14 @@ impl fmt::Debug for Error { impl std::error::Error for Error {} use event_listener::{Event, IntoNotification}; +use std::cell::RefCell; use std::pin::Pin; use std::ptr::NonNull; pub struct Handle { inner: NonNull, wakeup: Pin>, // the wakeup callback holds a pointer to this + wait_event_cell: RefCell<()>, } // The client API is generally fully thread-safe, unless otherwise noted. @@ -89,7 +91,11 @@ impl Handle { Error::from_return_code(unsafe { ffi::mpv_initialize(inner.as_ptr()) }) .expect("could not initialize mpv handle"); - Self { inner, wakeup } + Self { + inner, + wakeup, + wait_event_cell: RefCell::new(()), + } } pub fn set_property(&self, name: &str, value: impl FormatValue) -> Result<(), Error> { @@ -104,7 +110,9 @@ impl Handle { fn drain_event_queue(&self) { loop { + let borrowed = self.wait_event_cell.borrow_mut(); let event = unsafe { &*ffi::mpv_wait_event(self.inner.as_ptr(), 0.0) }; + match event.event_id { ffi::mpv_event_id_MPV_EVENT_NONE => break, @@ -112,14 +120,15 @@ impl Handle { _ => todo!("event {}", event.event_id), } + drop(borrowed); // make sure the borrow is held until here } } - pub async fn tick(&self) { + pub fn tick(&self) -> impl std::future::Future { // take listener before we drain the event queue, so we don't miss any notifications let listener = self.wakeup.listen(); self.drain_event_queue(); - listener.await; + listener } }