diff --git a/src/application.rs b/src/application.rs index 6bd7eda..af69ed8 100644 --- a/src/application.rs +++ b/src/application.rs @@ -2,11 +2,10 @@ mod imp { use crate::{mpv, ui}; use adw::{prelude::*, subclass::prelude::*}; use gtk::glib; - use std::cell::RefCell; #[derive(Default)] pub struct Application { - mpv: RefCell, + mpv: mpv::Handle, } #[glib::object_subclass] @@ -26,24 +25,23 @@ mod imp { let window = ui::Window::new(self.obj().as_ref()); window.present(); - let mut mpv = self.mpv.borrow_mut(); - mpv.set_property("audio-client-name", "audrey").unwrap(); - mpv.set_property("user-agent", crate::USER_AGENT).unwrap(); - mpv.set_property("video", false).unwrap(); - mpv.set_property("prefetch-playlist", true).unwrap(); - mpv.set_property("gapless-audio", true).unwrap(); + self.mpv + .set_property("audio-client-name", "audrey") + .unwrap(); + self.mpv + .set_property("user-agent", crate::USER_AGENT) + .unwrap(); + self.mpv.set_property("video", false).unwrap(); + self.mpv.set_property("prefetch-playlist", true).unwrap(); + self.mpv.set_property("gapless-audio", true).unwrap(); // TODO: observe properties - mpv.observe_property("time-pos", |time_pos: Option| { - println!("new time pos: {time_pos:?}") - }) - .unwrap(); glib::spawn_future_local(glib::clone!( #[weak(rename_to = app)] self, async move { loop { - app.mpv.borrow_mut().tick().await; + app.mpv.tick().await; } } )); diff --git a/src/mpv.rs b/src/mpv.rs index 61a05ee..0f292c1 100644 --- a/src/mpv.rs +++ b/src/mpv.rs @@ -22,7 +22,7 @@ use std::fmt; impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { fmt::Display::fmt( - unsafe { &CStr::from_ptr(ffi::mpv_error_string(self.0 as c_int)) } + unsafe { CStr::from_ptr(ffi::mpv_error_string(self.0 as c_int)) } .to_str() .unwrap(), f, @@ -34,7 +34,7 @@ impl fmt::Debug for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { f.debug_tuple("Error") .field( - &unsafe { &CStr::from_ptr(ffi::mpv_error_string(self.0 as c_int)) } + &unsafe { CStr::from_ptr(ffi::mpv_error_string(self.0 as c_int)) } .to_str() .unwrap(), ) @@ -44,18 +44,13 @@ impl fmt::Debug for Error { impl std::error::Error for Error {} +use event_listener::{Event, IntoNotification}; use std::pin::Pin; use std::ptr::NonNull; -use event_listener::{Event, IntoNotification}; -use std::cell::RefCell; pub struct Handle { inner: NonNull, wakeup: Pin>, // the wakeup callback holds a pointer to this - tick_cell: RefCell<()>, - - observed_property_callbacks: - Vec>>>, } // The client API is generally fully thread-safe, unless otherwise noted. @@ -91,18 +86,13 @@ impl Handle { // 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"); + Error::from_return_code(unsafe { ffi::mpv_initialize(inner.as_ptr()) }) + .expect("could not initialize mpv handle"); - Self { - inner: inner, - wakeup: wakeup, - tick_cell: RefCell::new(()), - - observed_property_callbacks: vec![], - } + Self { inner, wakeup } } - pub fn set_property<'a>(&self, name: &str, value: impl FormatValue) -> Result<(), Error> { + pub fn set_property(&self, name: &str, value: impl FormatValue) -> Result<(), Error> { // need to add zero terminator let name = CString::new(name).expect("null bytes in property name"); value.into_data(|format, data| { @@ -112,57 +102,23 @@ impl Handle { }) } - // TODO: some way to deregister these maybe?? - pub fn observe_property( - &mut self, - name: &str, - mut cb: impl FnMut(Option) + 'static, - ) -> Result<(), Error> - where - T: FormatValue, - { - let name = CString::new(name).unwrap(); - let cb = Box::new(move |format, data| T::from_data(format, data, |x| cb(x))); - let cb = Box::::from(cb); - let cb = Box::pin(cb); - let userdata = - std::ptr::from_ref::>(&cb) as usize as u64; - - Error::from_return_code(unsafe { - ffi::mpv_observe_property(self.inner.as_ptr(), userdata, name.as_ptr(), T::FORMAT) - })?; - - self.observed_property_callbacks.push(cb); - Ok(()) - } - - 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(); - + fn drain_event_queue(&self) { loop { 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, - ffi::mpv_event_id_MPV_EVENT_PROPERTY_CHANGE => { - let cb = unsafe { - &mut *(event.reply_userdata - as *mut Box) - }; - let data = unsafe { &*(event.data as *mut ffi::mpv_event_property) }; - cb(data.format, data.data); - } - 11 => { /* deprecated, ignore */ } _ => todo!("event {}", event.event_id), } } + } + pub async fn tick(&self) { + // 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; } }