Compare commits
No commits in common. "5dc1ed221b74e8067ba88f8b591d35c5f65e715c" and "3940392f2d4f22b87f4d440016a6773b416a3199" have entirely different histories.
5dc1ed221b
...
3940392f2d
5 changed files with 60 additions and 157 deletions
|
@ -130,7 +130,8 @@ template $AudreyUiPlaybar: Adw.Bin {
|
||||||
Button {
|
Button {
|
||||||
icon-name: "media-seek-backward";
|
icon-name: "media-seek-backward";
|
||||||
valign: center;
|
valign: center;
|
||||||
action-name: "app.seek-backward";
|
sensitive: bind template.idle-active inverted;
|
||||||
|
clicked => $seek_backward() swapped;
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
|
@ -143,7 +144,8 @@ template $AudreyUiPlaybar: Adw.Bin {
|
||||||
Button {
|
Button {
|
||||||
icon-name: "media-seek-forward";
|
icon-name: "media-seek-forward";
|
||||||
valign: center;
|
valign: center;
|
||||||
action-name: "app.seek-forward";
|
sensitive: bind template.idle-active inverted;
|
||||||
|
clicked => $seek_forward() swapped;
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
|
|
|
@ -55,12 +55,7 @@ pub struct LogMessageEvent {
|
||||||
pub struct PropertyEvent {
|
pub struct PropertyEvent {
|
||||||
pub reply_userdata: u64,
|
pub reply_userdata: u64,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub value: Option<PropertyEventValue>,
|
//pub value: PropertyEventValue,
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum PropertyEventValue {
|
|
||||||
Int64(i64),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use super::event::{
|
use super::event::{
|
||||||
EndFileEvent, EndFileReason, HookEvent, LogMessageEvent, PropertyEvent, PropertyEventValue,
|
EndFileEvent, EndFileReason, HookEvent, LogMessageEvent, PropertyEvent, StartFileEvent,
|
||||||
StartFileEvent,
|
|
||||||
};
|
};
|
||||||
use super::{ffi, Error, Event as MpvEvent, GetProperty, SetProperty};
|
use super::{ffi, Error, Event as MpvEvent, GetProperty, SetProperty};
|
||||||
use event_listener::{Event, EventListener, IntoNotification};
|
use event_listener::{Event, EventListener, IntoNotification};
|
||||||
|
@ -141,18 +140,6 @@ impl Handle {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn observe_property_i64(&self, reply_userdata: u64, name: &str) -> Result<(), Error> {
|
|
||||||
let name = CString::new(name).expect("null bytes in property name");
|
|
||||||
Error::from_return_code(unsafe {
|
|
||||||
ffi::mpv_observe_property(
|
|
||||||
self.inner.as_ptr(),
|
|
||||||
reply_userdata,
|
|
||||||
name.as_ptr(),
|
|
||||||
ffi::mpv_format_MPV_FORMAT_INT64,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unobserve_property(&self, registered_reply_userdata: u64) -> Result<u32, Error> {
|
pub fn unobserve_property(&self, registered_reply_userdata: u64) -> Result<u32, Error> {
|
||||||
let rc =
|
let rc =
|
||||||
unsafe { ffi::mpv_unobserve_property(self.inner.as_ptr(), registered_reply_userdata) };
|
unsafe { ffi::mpv_unobserve_property(self.inner.as_ptr(), registered_reply_userdata) };
|
||||||
|
@ -257,15 +244,6 @@ impl Handle {
|
||||||
name: unsafe { CStr::from_ptr(data.name) }
|
name: unsafe { CStr::from_ptr(data.name) }
|
||||||
.to_string_lossy()
|
.to_string_lossy()
|
||||||
.into(),
|
.into(),
|
||||||
value: match data.format {
|
|
||||||
ffi::mpv_format_MPV_FORMAT_NONE => None,
|
|
||||||
ffi::mpv_format_MPV_FORMAT_INT64 => {
|
|
||||||
Some(PropertyEventValue::Int64(unsafe {
|
|
||||||
*(data.data as *mut i64)
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
_ => todo!(),
|
|
||||||
},
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -111,6 +111,28 @@ mod imp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[template_callback]
|
||||||
|
fn seek_backward(&self) {
|
||||||
|
// 10 seconds
|
||||||
|
let mut new_position = self.window().time_pos() - 10.0;
|
||||||
|
if new_position < 0.0 {
|
||||||
|
new_position = 0.0;
|
||||||
|
}
|
||||||
|
self.window().seek(new_position);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[template_callback]
|
||||||
|
fn seek_forward(&self) {
|
||||||
|
// 10 seconds
|
||||||
|
let new_position = self.window().time_pos() + 10.0;
|
||||||
|
if new_position > self.window().duration() {
|
||||||
|
// just seek to the next track
|
||||||
|
self.on_skip_forward_clicked();
|
||||||
|
} else {
|
||||||
|
self.window().seek(new_position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[template_callback]
|
#[template_callback]
|
||||||
fn on_play_pause_clicked(&self, _button: >k::Button) {
|
fn on_play_pause_clicked(&self, _button: >k::Button) {
|
||||||
if self.window().idle_active() {
|
if self.window().idle_active() {
|
||||||
|
|
158
src/ui/window.rs
158
src/ui/window.rs
|
@ -15,9 +15,9 @@ mod imp {
|
||||||
pub(super) enum State {
|
pub(super) enum State {
|
||||||
Idle,
|
Idle,
|
||||||
FileLoading,
|
FileLoading,
|
||||||
FileLoaded, // internal
|
FileLoaded, // internal?
|
||||||
Active,
|
Active,
|
||||||
FileEnded, // internal
|
FileEnded, // internal?
|
||||||
Seeking,
|
Seeking,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,8 +61,8 @@ mod imp {
|
||||||
_mute: (),
|
_mute: (),
|
||||||
#[property(type = bool, get = Self::pause, set = Self::set_pause)]
|
#[property(type = bool, get = Self::pause, set = Self::set_pause)]
|
||||||
_pause: (),
|
_pause: (),
|
||||||
#[property(get)]
|
#[property(type = i64, get = Self::playlist_pos)]
|
||||||
playlist_pos: Cell<i64>,
|
_playlist_pos: (),
|
||||||
#[property(type = f64, get = Self::time_pos)]
|
#[property(type = f64, get = Self::time_pos)]
|
||||||
_time_pos: (),
|
_time_pos: (),
|
||||||
#[property(type = f64, get = Self::duration)]
|
#[property(type = f64, get = Self::duration)]
|
||||||
|
@ -97,7 +97,7 @@ mod imp {
|
||||||
mpv.set_property("vid", false).unwrap();
|
mpv.set_property("vid", false).unwrap();
|
||||||
mpv.set_property("prefetch-playlist", true).unwrap();
|
mpv.set_property("prefetch-playlist", true).unwrap();
|
||||||
|
|
||||||
mpv.observe_property_i64(3, "playlist-pos").unwrap();
|
mpv.observe_property(3, "playlist-pos").unwrap();
|
||||||
mpv.observe_property(4, "idle-active").unwrap();
|
mpv.observe_property(4, "idle-active").unwrap();
|
||||||
mpv.observe_property(6, "playlist-count").unwrap();
|
mpv.observe_property(6, "playlist-count").unwrap();
|
||||||
mpv.observe_property(7, "duration").unwrap();
|
mpv.observe_property(7, "duration").unwrap();
|
||||||
|
@ -105,7 +105,7 @@ mod imp {
|
||||||
// "Useful to drain property changes before a new file is loaded."
|
// "Useful to drain property changes before a new file is loaded."
|
||||||
mpv.add_hook(0, "on_before_start_file", 0).unwrap();
|
mpv.add_hook(0, "on_before_start_file", 0).unwrap();
|
||||||
// "Useful to drain property changes after a file has finished."
|
// "Useful to drain property changes after a file has finished."
|
||||||
mpv.add_hook(0, "on_after_end_file", 0).unwrap();
|
mpv.add_hook(0, "o_after_start_file", 0).unwrap();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
state: Cell::new(State::Idle),
|
state: Cell::new(State::Idle),
|
||||||
|
@ -126,7 +126,7 @@ mod imp {
|
||||||
_volume: (),
|
_volume: (),
|
||||||
_mute: (),
|
_mute: (),
|
||||||
_pause: (),
|
_pause: (),
|
||||||
playlist_pos: Cell::new(-1),
|
_playlist_pos: (),
|
||||||
_time_pos: (),
|
_time_pos: (),
|
||||||
_duration: (),
|
_duration: (),
|
||||||
_idle_active: (),
|
_idle_active: (),
|
||||||
|
@ -170,66 +170,6 @@ mod imp {
|
||||||
fn constructed(&self) {
|
fn constructed(&self) {
|
||||||
self.parent_constructed();
|
self.parent_constructed();
|
||||||
|
|
||||||
use gio::ActionEntry;
|
|
||||||
|
|
||||||
let action_seek_backward = ActionEntry::builder("seek-backward")
|
|
||||||
.activate(glib::clone!(
|
|
||||||
#[weak(rename_to = window)]
|
|
||||||
self.obj(),
|
|
||||||
move |_, _, _| {
|
|
||||||
let new_position = window.time_pos() - 10.0;
|
|
||||||
if new_position < 0.0 {
|
|
||||||
window.seek(0.0);
|
|
||||||
} else {
|
|
||||||
window.seek(new_position);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let action_seek_forward = ActionEntry::builder("seek-forward")
|
|
||||||
.activate(glib::clone!(
|
|
||||||
#[weak(rename_to = window)]
|
|
||||||
self.obj(),
|
|
||||||
move |_, _, _| {
|
|
||||||
let new_position = window.time_pos() + 10.0;
|
|
||||||
if new_position > window.duration() {
|
|
||||||
// just seek to the next track
|
|
||||||
if window.playlist_pos() + 1 < window.playlist_count() {
|
|
||||||
window.playlist_next();
|
|
||||||
} else {
|
|
||||||
window.playlist_play_index(None);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
window.seek(new_position);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let actions = gio::SimpleActionGroup::new();
|
|
||||||
actions.add_action_entries([action_seek_backward, action_seek_forward]);
|
|
||||||
self.obj().insert_action_group("app", Some(&actions));
|
|
||||||
|
|
||||||
let playlist_not_empty = gtk::ClosureExpression::with_callback(
|
|
||||||
[gtk::ObjectExpression::new(self.obj().as_ref())
|
|
||||||
.chain_property::<super::Window>("playlist-count")],
|
|
||||||
|values| {
|
|
||||||
let playlist_count: i64 = values[1].get().unwrap();
|
|
||||||
playlist_count > 0
|
|
||||||
},
|
|
||||||
);
|
|
||||||
playlist_not_empty.bind(
|
|
||||||
&actions.lookup_action("seek-backward").unwrap(),
|
|
||||||
"enabled",
|
|
||||||
None::<&glib::Object>,
|
|
||||||
);
|
|
||||||
playlist_not_empty.bind(
|
|
||||||
&actions.lookup_action("seek-forward").unwrap(),
|
|
||||||
"enabled",
|
|
||||||
None::<&glib::Object>,
|
|
||||||
);
|
|
||||||
|
|
||||||
let settings = gio::Settings::new(crate::APP_ID);
|
let settings = gio::Settings::new(crate::APP_ID);
|
||||||
settings.bind("mute", self.obj().as_ref(), "mute").build();
|
settings.bind("mute", self.obj().as_ref(), "mute").build();
|
||||||
settings
|
settings
|
||||||
|
@ -265,69 +205,40 @@ mod imp {
|
||||||
let mpv_event_loop_handle = glib::spawn_future_local(async move {
|
let mpv_event_loop_handle = glib::spawn_future_local(async move {
|
||||||
loop {
|
loop {
|
||||||
let window = window.upgrade().unwrap();
|
let window = window.upgrade().unwrap();
|
||||||
|
let listener = window.imp().mpv.wakeup_listener();
|
||||||
|
|
||||||
// only send property change notifications after the event queue is drained
|
// only send property change notifications after the event queue is drained
|
||||||
let freeze_notify = window.freeze_notify();
|
let freeze_notify = window.freeze_notify();
|
||||||
event!(
|
|
||||||
Level::TRACE,
|
|
||||||
"before event loop, state is {:?}",
|
|
||||||
window.imp().state.get()
|
|
||||||
);
|
|
||||||
|
|
||||||
let listener = loop {
|
while let Some(event) = window.imp().mpv.wait_event(0.0) {
|
||||||
let listener = window.imp().mpv.wakeup_listener();
|
use crate::mpv::Event;
|
||||||
|
|
||||||
while let Some(event) = window.imp().mpv.wait_event(0.0) {
|
match event {
|
||||||
use crate::mpv::Event;
|
Event::PropertyChange(event) => window.imp().on_property_change(event),
|
||||||
|
Event::Hook(event) => window.imp().on_hook(event),
|
||||||
|
Event::LogMessage(event) => window.imp().on_log_message(event),
|
||||||
|
|
||||||
match event {
|
Event::StartFile(_) => window.imp().on_start_file(),
|
||||||
Event::PropertyChange(event) => {
|
Event::FileLoaded => window.imp().on_file_loaded(),
|
||||||
window.imp().on_property_change(event)
|
Event::PlaybackRestart => window.imp().on_playback_restart(),
|
||||||
}
|
Event::Seek => window.imp().on_seek(),
|
||||||
Event::Hook(event) => window.imp().on_hook(event),
|
Event::EndFile(event) => window.imp().on_end_file(event),
|
||||||
Event::LogMessage(event) => window.imp().on_log_message(event),
|
|
||||||
|
|
||||||
Event::StartFile(_) => window.imp().on_start_file(),
|
Event::Unknown(_) => {
|
||||||
Event::FileLoaded => window.imp().on_file_loaded(),
|
// either deprecated or future, ignore
|
||||||
Event::PlaybackRestart => window.imp().on_playback_restart(),
|
|
||||||
Event::Seek => window.imp().on_seek(),
|
|
||||||
Event::EndFile(event) => window.imp().on_end_file(event),
|
|
||||||
|
|
||||||
Event::Unknown(_) => {
|
|
||||||
// either deprecated or future, ignore
|
|
||||||
}
|
|
||||||
|
|
||||||
Event::AudioReconfig => {
|
|
||||||
// "This is relatively uninteresting, because there is no such thing as audio output embedding."
|
|
||||||
// ^ ignore
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => event!(Level::DEBUG, "unhandled {event:?}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match window.imp().state.get() {
|
|
||||||
State::FileLoaded | State::FileEnded => {
|
|
||||||
// really annoying intermediary states we're not really interested
|
|
||||||
// in seeing outside of this loop
|
|
||||||
// just block until it's done lol
|
|
||||||
use event_listener::Listener;
|
|
||||||
event!(Level::INFO, "blocking");
|
|
||||||
listener.wait();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => break listener,
|
Event::AudioReconfig => {
|
||||||
|
// "This is relatively uninteresting, because there is no such thing as audio output embedding."
|
||||||
|
// ^ ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => event!(Level::DEBUG, "unhandled {event:?}"),
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
// send property change notifications now
|
// send property change notifications now
|
||||||
drop(freeze_notify);
|
drop(freeze_notify);
|
||||||
event!(
|
|
||||||
Level::TRACE,
|
|
||||||
"after event loop, state is {:?}",
|
|
||||||
window.imp().state.get()
|
|
||||||
);
|
|
||||||
|
|
||||||
drop(window);
|
drop(window);
|
||||||
listener.await;
|
listener.await;
|
||||||
}
|
}
|
||||||
|
@ -468,6 +379,10 @@ mod imp {
|
||||||
self.mpv.set_property("pause", pause).unwrap();
|
self.mpv.set_property("pause", pause).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn playlist_pos(&self) -> i64 {
|
||||||
|
self.mpv.get_property("playlist-pos").unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
fn time_pos(&self) -> f64 {
|
fn time_pos(&self) -> f64 {
|
||||||
if let Some(queued_seek) = self.queued_seek.get() {
|
if let Some(queued_seek) = self.queued_seek.get() {
|
||||||
// counterfeit time-pos while the seek is ongoing
|
// counterfeit time-pos while the seek is ongoing
|
||||||
|
@ -552,17 +467,9 @@ mod imp {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_property_change(&self, event: crate::mpv::event::PropertyEvent) {
|
fn on_property_change(&self, event: crate::mpv::event::PropertyEvent) {
|
||||||
use crate::mpv::event::PropertyEventValue;
|
|
||||||
|
|
||||||
match event.reply_userdata {
|
match event.reply_userdata {
|
||||||
3 => {
|
3 => {
|
||||||
assert_eq!(event.name, "playlist-pos");
|
assert_eq!(event.name, "playlist-pos");
|
||||||
let value = match event.value {
|
|
||||||
Some(PropertyEventValue::Int64(i)) => i,
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
self.playlist_pos.set(value);
|
|
||||||
event!(Level::TRACE, "playlist-pos is now {value}");
|
|
||||||
self.obj().notify("playlist-pos");
|
self.obj().notify("playlist-pos");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -743,7 +650,6 @@ mod imp {
|
||||||
}
|
}
|
||||||
self.state.set(State::Seeking);
|
self.state.set(State::Seeking);
|
||||||
|
|
||||||
self.obj().notify("time-pos");
|
|
||||||
self.buffering_start();
|
self.buffering_start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue