diff --git a/src/application.rs b/src/application.rs index 5fd2cff..4ec016d 100644 --- a/src/application.rs +++ b/src/application.rs @@ -2,10 +2,11 @@ mod imp { use crate::{mpv, ui}; use adw::{prelude::*, subclass::prelude::*}; use gtk::glib; + use std::rc::Rc; #[derive(Default)] pub struct Application { - mpv: mpv::Handle, + mpv: Rc, } #[glib::object_subclass] @@ -36,14 +37,13 @@ mod imp { self.mpv.set_property("gapless-audio", true).unwrap(); // TODO: observe properties - let self_weak = self.obj().downgrade(); + let mpv_weak = Rc::downgrade(&self.mpv); 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; + while let Some(mpv) = mpv_weak.upgrade() { + match mpv.tick() { + None => break, + Some(listener) => listener.await, + } } }); diff --git a/src/main.rs b/src/main.rs index aba1097..4c5511f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,6 +17,8 @@ pub use playbin::Playbin; pub mod subsonic; pub mod subsonic_vala; +mod playbin2; + use gettextrs::{bind_textdomain_codeset, bindtextdomain, setlocale, textdomain, LocaleCategory}; use gtk::prelude::*; use gtk::{gio, glib}; diff --git a/src/mpv.rs b/src/mpv.rs index 1b830bd..87ba7ee 100644 --- a/src/mpv.rs +++ b/src/mpv.rs @@ -44,7 +44,7 @@ impl fmt::Debug for Error { impl std::error::Error for Error {} -use event_listener::{Event, IntoNotification}; +use event_listener::{Event, EventListener, IntoNotification}; use std::cell::RefCell; use std::pin::Pin; use std::ptr::NonNull; @@ -87,7 +87,10 @@ impl Handle { } // set up verbose logging for now - Error::from_return_code(unsafe { ffi::mpv_request_log_messages(inner.as_ptr(), c"v".as_ptr()) }).unwrap(); + Error::from_return_code(unsafe { + ffi::mpv_request_log_messages(inner.as_ptr(), c"v".as_ptr()) + }) + .unwrap(); // 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." @@ -111,9 +114,16 @@ impl Handle { }) } - fn drain_event_queue(&self) { + // TODO: return None on SHUTDOWN possibly? + pub fn tick(&self) -> Option { + // take listener before we drain the event queue, so we don't miss any notifications + let listener = self.wakeup.listen(); + let borrowed = self + .wait_event_cell + .try_borrow_mut() + .expect("Mpv::tick is not reentrant"); + 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 { @@ -126,26 +136,31 @@ impl Handle { let level = unsafe { CStr::from_ptr(data.level) }.to_str().unwrap(); let text = unsafe { CStr::from_ptr(data.text) }.to_str().unwrap(); print!("[{prefix}] {level}: {text}"); - }, + } 11 => { /* deprecated, ignore */ } _ => todo!("event {}", event.event_id), } - drop(borrowed); // make sure the borrow is held until here } - } - 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 + drop(borrowed); // make sure the borrow is held until here + Some(listener) } } impl Drop for Handle { fn drop(&mut self) { + // let any executor ticking tasks know we're ded + self.wakeup.notify(u32::MAX.relaxed()); + + // just in case + unsafe { + ffi::mpv_wait_async_requests(self.inner.as_ptr()); + } + // drain event queue (we're &mut so we know we have exclusive access) + self.tick(); + unsafe { ffi::mpv_destroy(self.inner.as_ptr()); } diff --git a/src/playbin2.rs b/src/playbin2.rs new file mode 100644 index 0000000..6876140 --- /dev/null +++ b/src/playbin2.rs @@ -0,0 +1 @@ +mod imp {}