many such cases
This commit is contained in:
parent
2aee230606
commit
ad51d16a06
2 changed files with 24 additions and 70 deletions
|
@ -2,11 +2,10 @@ mod imp {
|
||||||
use crate::{mpv, ui};
|
use crate::{mpv, ui};
|
||||||
use adw::{prelude::*, subclass::prelude::*};
|
use adw::{prelude::*, subclass::prelude::*};
|
||||||
use gtk::glib;
|
use gtk::glib;
|
||||||
use std::cell::RefCell;
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Application {
|
pub struct Application {
|
||||||
mpv: RefCell<mpv::Handle>,
|
mpv: mpv::Handle,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[glib::object_subclass]
|
#[glib::object_subclass]
|
||||||
|
@ -26,24 +25,23 @@ mod imp {
|
||||||
let window = ui::Window::new(self.obj().as_ref());
|
let window = ui::Window::new(self.obj().as_ref());
|
||||||
window.present();
|
window.present();
|
||||||
|
|
||||||
let mut mpv = self.mpv.borrow_mut();
|
self.mpv
|
||||||
mpv.set_property("audio-client-name", "audrey").unwrap();
|
.set_property("audio-client-name", "audrey")
|
||||||
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();
|
|
||||||
// TODO: observe properties
|
|
||||||
mpv.observe_property("time-pos", |time_pos: Option<f64>| {
|
|
||||||
println!("new time pos: {time_pos:?}")
|
|
||||||
})
|
|
||||||
.unwrap();
|
.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
|
||||||
|
|
||||||
glib::spawn_future_local(glib::clone!(
|
glib::spawn_future_local(glib::clone!(
|
||||||
#[weak(rename_to = app)]
|
#[weak(rename_to = app)]
|
||||||
self,
|
self,
|
||||||
async move {
|
async move {
|
||||||
loop {
|
loop {
|
||||||
app.mpv.borrow_mut().tick().await;
|
app.mpv.tick().await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
|
|
70
src/mpv.rs
70
src/mpv.rs
|
@ -22,7 +22,7 @@ use std::fmt;
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
fmt::Display::fmt(
|
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()
|
.to_str()
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
f,
|
f,
|
||||||
|
@ -34,7 +34,7 @@ impl fmt::Debug for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
f.debug_tuple("Error")
|
f.debug_tuple("Error")
|
||||||
.field(
|
.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()
|
.to_str()
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
)
|
)
|
||||||
|
@ -44,18 +44,13 @@ impl fmt::Debug for Error {
|
||||||
|
|
||||||
impl std::error::Error for Error {}
|
impl std::error::Error for Error {}
|
||||||
|
|
||||||
|
use event_listener::{Event, IntoNotification};
|
||||||
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: Pin<Box<Event>>, // the wakeup callback holds a pointer to this
|
wakeup: Pin<Box<Event>>, // the wakeup callback holds a pointer to this
|
||||||
tick_cell: RefCell<()>,
|
|
||||||
|
|
||||||
observed_property_callbacks:
|
|
||||||
Vec<Pin<Box<Box<dyn FnMut(ffi::mpv_format, *mut c_void) + 'static>>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The client API is generally fully thread-safe, unless otherwise noted.
|
// 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
|
// 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."
|
// 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 {
|
Self { inner, wakeup }
|
||||||
inner: inner,
|
|
||||||
wakeup: wakeup,
|
|
||||||
tick_cell: RefCell::new(()),
|
|
||||||
|
|
||||||
observed_property_callbacks: vec![],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
// 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");
|
||||||
value.into_data(|format, data| {
|
value.into_data(|format, data| {
|
||||||
|
@ -112,57 +102,23 @@ impl Handle {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: some way to deregister these maybe??
|
fn drain_event_queue(&self) {
|
||||||
pub fn observe_property<T>(
|
|
||||||
&mut self,
|
|
||||||
name: &str,
|
|
||||||
mut cb: impl FnMut(Option<T>) + '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::<dyn FnMut(ffi::mpv_format, *mut c_void)>::from(cb);
|
|
||||||
let cb = Box::pin(cb);
|
|
||||||
let userdata =
|
|
||||||
std::ptr::from_ref::<Box<dyn FnMut(ffi::mpv_format, *mut c_void)>>(&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();
|
|
||||||
|
|
||||||
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 {
|
||||||
ffi::mpv_event_id_MPV_EVENT_NONE => break,
|
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<dyn FnMut(ffi::mpv_format, *mut c_void)>)
|
|
||||||
};
|
|
||||||
let data = unsafe { &*(event.data as *mut ffi::mpv_event_property) };
|
|
||||||
cb(data.format, data.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
11 => { /* deprecated, ignore */ }
|
11 => { /* deprecated, ignore */ }
|
||||||
|
|
||||||
_ => todo!("event {}", event.event_id),
|
_ => 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;
|
listener.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue