Compare commits

..

No commits in common. "a6378b281a066eb8b7a0c1eec0d2bd140f3669c6" and "4e434a79293c6a5b1c54e09318455b7752d34b0a" have entirely different histories.

9 changed files with 110 additions and 117 deletions

View file

@ -1,21 +1,53 @@
use std::path::PathBuf;
fn main() { fn main() {
let meson_build_root =
std::env::var("MESON_BUILD_ROOT").expect("build this through meson please!!"); std::env::var("MESON_BUILD_ROOT").expect("build this through meson please!!");
let out_path = std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap());
// compile blueprints
{
let mut command = std::process::Command::new("blueprint-compiler");
command
.arg("batch-compile")
.arg(out_path.join("resources"))
.arg("resources");
for source in [
"play_queue.blp",
"play_queue_song.blp",
"playbar.blp",
"setup.blp",
"window.blp",
] {
command.arg(format!("resources/{source}"));
}
let output = command.output().unwrap();
assert!(
output.status.success(),
"blueprint-compiler failed with exit status {} and stdout:\n{}",
output.status,
String::from_utf8_lossy(&output.stdout)
);
println!("cargo::rerun-if-changed=resources");
}
glib_build_tools::compile_resources( glib_build_tools::compile_resources(
&["resources", &format!("{meson_build_root}/resources")], &[PathBuf::from("resources"), out_path.join("resources")],
"resources/audrey.gresource.xml", "resources/audrey.gresource.xml",
"audrey.gresource", "audrey.gresource",
); );
println!("cargo::rerun-if-changed=resources/audrey.gresource.xml"); println!("cargo::rerun-if-changed=resources/audrey.gresource.xml");
// TODO: consider using meson to pass include paths // TODO: consider using meson to pass include paths
println!("cargo:rustc-link-lib=mpv"); println!("cargo::rustc-link-lib=mpv");
let bindings = bindgen::Builder::default() let bindings = bindgen::Builder::default()
.header("src/mpv/wrapper.h") .header("src/mpv/wrapper.h")
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
.generate() .generate()
.expect("could not generate bindings for mpv"); .expect("could not generate bindings for mpv");
let out_path = std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap());
bindings bindings
.write_to_file(out_path.join("mpv_ffi.rs")) .write_to_file(out_path.join("mpv_ffi.rs"))
.expect("could not write mpv bindings"); .expect("could not write mpv bindings");

View file

@ -38,7 +38,6 @@ add_project_arguments(
subdir('data') subdir('data')
subdir('po') subdir('po')
subdir('resources')
subdir('src') subdir('src')
if get_option('buildtype') == 'debug' if get_option('buildtype') == 'debug'
@ -60,7 +59,6 @@ custom_target(
'cargo-build', 'cargo-build',
build_by_default: true, build_by_default: true,
build_always_stale: true, build_always_stale: true,
depends: blueprints,
input: config_rs, input: config_rs,
console: true, console: true,
# means nothing (always stale and uncopied), we can't cp since env: drops the /bin/sh wrapper # means nothing (always stale and uncopied), we can't cp since env: drops the /bin/sh wrapper

View file

@ -1,24 +0,0 @@
blueprints = custom_target(
'blueprints',
input: files(
'play_queue.blp',
'play_queue_song.blp',
'playbar.blp',
'setup.blp',
'window.blp',
),
output: [
'play_queue.ui',
'play_queue_song.ui',
'playbar.ui',
'setup.ui',
'window.ui',
],
command: [
find_program('blueprint-compiler'),
'batch-compile',
'@OUTDIR@',
'@CURRENT_SOURCE_DIR@',
'@INPUT@',
],
)

View file

@ -36,7 +36,7 @@ template $AudreyUiPlaybar: Adw.Bin {
xalign: 0; xalign: 0;
halign: start; halign: start;
label: bind template.song as <$AudreyPlaybinSong>.title; label: bind $song_title(template.song) as <string>;
ellipsize: end; ellipsize: end;
} }
@ -46,7 +46,7 @@ template $AudreyUiPlaybar: Adw.Bin {
] ]
xalign: 0; xalign: 0;
label: bind template.song as <$AudreyPlaybinSong>.artist; label: bind $song_artist(template.song) as <string>;
ellipsize: end; ellipsize: end;
} }
@ -56,7 +56,7 @@ template $AudreyUiPlaybar: Adw.Bin {
] ]
xalign: 0; xalign: 0;
label: bind template.song as <$AudreyPlaybinSong>.album; label: bind $song_album(template.song) as <string>;
ellipsize: end; ellipsize: end;
} }
} }
@ -83,7 +83,7 @@ template $AudreyUiPlaybar: Adw.Bin {
name: "seek-scale"; name: "seek-scale";
orientation: horizontal; orientation: horizontal;
width-request: 400; width-request: 400;
sensitive: bind template.idle-active inverted; // sensitive: bind $playbin_active (template.playbin as <$AudreyPlaybin>.state as <$AudreyPlaybinState>) as <bool>;
adjustment: Adjustment { adjustment: Adjustment {
lower: 0; lower: 0;
value: bind template.position; value: bind template.position;
@ -111,35 +111,35 @@ template $AudreyUiPlaybar: Adw.Bin {
Button { Button {
icon-name: "media-skip-backward"; icon-name: "media-skip-backward";
valign: center; valign: center;
sensitive: bind template.idle-active inverted; // sensitive: bind $playbin_active (template.playbin as <$AudreyPlaybin>.state as <$AudreyPlaybinState>) as <bool>;
clicked => $on_skip_backward_clicked() swapped; clicked => $on_skip_backward_clicked() swapped;
} }
Button { Button {
icon-name: "media-seek-backward"; icon-name: "media-seek-backward";
valign: center; valign: center;
sensitive: bind template.idle-active inverted; // sensitive: bind $playbin_active (template.playbin as <$AudreyPlaybin>.state as <$AudreyPlaybinState>) as <bool>;
clicked => $seek_backward() swapped; clicked => $seek_backward() swapped;
} }
Button { Button {
icon-name: bind $play_pause_icon_name(template.idle-active, template.pause) as <string>; icon-name: bind $play_pause_icon_name(template.idle-active, template.pause) as <string>;
valign: center; valign: center;
sensitive: bind $play_pause_sensitive(template.playlist-count) as <bool>; // sensitive: bind $can_press_play (template.playbin as <$AudreyPlaybin>.state as <$AudreyPlaybinState>, template.playbin as <$AudreyPlaybin>.play-queue-length) as <bool>;
clicked => $on_play_pause_clicked() swapped; clicked => $on_play_pause_clicked() swapped;
} }
Button { Button {
icon-name: "media-seek-forward"; icon-name: "media-seek-forward";
valign: center; valign: center;
sensitive: bind template.idle-active inverted; // sensitive: bind $playbin_active (template.playbin as <$AudreyPlaybin>.state as <$AudreyPlaybinState>) as <bool>;
clicked => $seek_forward() swapped; clicked => $seek_forward() swapped;
} }
Button { Button {
icon-name: "media-skip-forward"; icon-name: "media-skip-forward";
valign: center; valign: center;
sensitive: bind template.idle-active inverted; // sensitive: bind $playbin_active (template.playbin as <$AudreyPlaybin>.state as <$AudreyPlaybinState>) as <bool>;
clicked => $on_skip_forward_clicked() swapped; clicked => $on_skip_forward_clicked() swapped;
} }
} }

View file

@ -141,15 +141,14 @@ template $AudreyUiWindow: Adw.ApplicationWindow {
[bottom] [bottom]
$AudreyUiPlaybar playbar { $AudreyUiPlaybar playbar {
song: bind template.song; song: bind template.song;
playing-cover-art: bind template.playing-cover-art; playing_cover_art: bind template.playing_cover_art;
show-cover-art: bind $show_playbar_cover_art(stack.visible-child-name) as <bool>; show_cover_art: bind $show_playbar_cover_art(stack.visible-child-name) as <bool>;
volume: bind template.volume bidirectional; volume: bind template.volume bidirectional;
mute: bind template.mute bidirectional; mute: bind template.mute bidirectional;
pause: bind template.pause bidirectional; pause: bind template.pause bidirectional;
position: bind template.time-pos; position: bind template.time-pos;
duration: bind template.duration; duration: bind template.song as <$AudreyPlaybinSong>.duration;
idle-active: bind template.idle-active; idle-active: bind template.idle-active;
playlist-count: bind template.playlist-count;
} }
} }
} }

34
src/mpv/wait_event.rs Normal file
View file

@ -0,0 +1,34 @@
use std::future::Future;
use std::task::Waker;
pub struct WaitEvent<'a>(&'a Handle);
impl Future for WaitEvent {
type Output = Event;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
// set wakeup before polling, don't miss any wakeup events
{
let waker = self.0.wakeup.lock();
match waker.as_mut() {
None => *waker = cx.waker().clone(),
Some(waker) if !cx.waker().will_wake(waker) => *waker = cx.waker().clone,
_ => {}
}
}
match self.0.wait_event_timeout(0.0) {
Some(event) => Poll::Ready(event),
None => Poll::Pending,
}
}
}
pub(super) extern "C" fn callback(d: *mut c_void) {
let d = d as *const Mutex<Option<Waker>>;
let waker = unsafe { &*d };
let waker = waker.lock().unwrap();
if let Some(waker) = waker.take() {
waker.wake();
}
}

View file

@ -8,7 +8,7 @@ mod imp {
#[template(resource = "/eu/callcc/audrey/play_queue_song.ui")] #[template(resource = "/eu/callcc/audrey/play_queue_song.ui")]
#[properties(wrapper_type = super::Song)] #[properties(wrapper_type = super::Song)]
pub struct Song { pub struct Song {
#[property(type = i32, set = Self::set_playlist_pos)] #[property(type = i64, set = Self::set_playlist_pos)]
_playlist_pos: (), _playlist_pos: (),
#[property(set, get)] #[property(set, get)]
@ -163,9 +163,9 @@ mod imp {
false false
} }
fn set_playlist_pos(&self, playlist_pos: i32) { fn set_playlist_pos(&self, playlist_pos: i64) {
self.obj() self.obj()
.set_current(playlist_pos == self.position.get() as i32); .set_current(playlist_pos == self.position.get() as i64);
} }
} }
} }

View file

@ -18,17 +18,14 @@ mod imp {
#[property(get, set, default = true)] #[property(get, set, default = true)]
show_cover_art: Cell<bool>, show_cover_art: Cell<bool>,
// synced with root window
#[property(get, set)] #[property(get, set)]
_volume: Cell<u32>, _volume: Cell<i64>,
#[property(get, set)] #[property(get, set)]
_mute: Cell<bool>, _mute: Cell<bool>,
#[property(get, set)] #[property(get, set)]
_pause: Cell<bool>, _pause: Cell<bool>,
#[property(get, set)] #[property(get, set)]
_idle_active: Cell<bool>, _idle_active: Cell<bool>,
#[property(get, set)]
_playlist_count: Cell<u32>,
#[property(get, set)] #[property(get, set)]
position: Cell<f64>, position: Cell<f64>,
@ -61,6 +58,21 @@ mod imp {
#[gtk::template_callbacks] #[gtk::template_callbacks]
impl Playbar { impl Playbar {
#[template_callback]
fn song_title(&self, song: Option<&PlaybinSong>) -> Option<String> {
song.map(|song| song.title())
}
#[template_callback]
fn song_artist(&self, song: Option<&PlaybinSong>) -> Option<String> {
song.map(|song| song.artist())
}
#[template_callback]
fn song_album(&self, song: Option<&PlaybinSong>) -> Option<String> {
song.map(|song| song.album())
}
#[template_callback] #[template_callback]
fn format_timestamp(&self, s: f64) -> String { fn format_timestamp(&self, s: f64) -> String {
format!("{:02}:{:02}", (s as i64) / 64, (s as i64) % 60) format!("{:02}:{:02}", (s as i64) / 64, (s as i64) % 60)
@ -152,11 +164,6 @@ mod imp {
} }
} }
#[template_callback]
fn play_pause_sensitive(&self, playlist_count: u32) -> bool {
playlist_count > 0
}
fn window(&self) -> crate::ui::Window { fn window(&self) -> crate::ui::Window {
self.obj().root().unwrap().dynamic_cast().unwrap() self.obj().root().unwrap().dynamic_cast().unwrap()
} }

View file

@ -36,22 +36,18 @@ mod imp {
#[property(get)] #[property(get)]
playlist_model: gio::ListStore, playlist_model: gio::ListStore,
#[property(type = u32, get = Self::volume, set = Self::set_volume)] #[property(type = i64, get = Self::volume, set = Self::set_volume)]
_volume: (), _volume: (),
#[property(type = bool, get = Self::mute, set = Self::set_mute)] #[property(type = bool, get = Self::mute, set = Self::set_mute)]
_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(type = i32, get = Self::playlist_pos)] #[property(type = i64, get = Self::playlist_pos)]
_playlist_pos: (), _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)]
_duration: (), // as reported by mpv, compare with song.duration
#[property(type = bool, get = Self::idle_active)] #[property(type = bool, get = Self::idle_active)]
_idle_active: (), _idle_active: (),
#[property(type = u32, get = Self::playlist_count)]
_playlist_count: (),
} }
impl Default for Window { impl Default for Window {
@ -69,8 +65,6 @@ mod imp {
mpv.observe_property(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(5, "time-pos").unwrap(); mpv.observe_property(5, "time-pos").unwrap();
mpv.observe_property(6, "playlist-count").unwrap();
mpv.observe_property(7, "duration").unwrap();
// "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();
@ -91,9 +85,7 @@ mod imp {
_pause: (), _pause: (),
_playlist_pos: (), _playlist_pos: (),
_time_pos: (), _time_pos: (),
_duration: (),
_idle_active: (), _idle_active: (),
_playlist_count: (),
} }
} }
} }
@ -161,16 +153,6 @@ mod imp {
window.notify("time-pos"); window.notify("time-pos");
} }
6 => {
assert_eq!(event.name, "playlist-count");
window.notify("playlist-count");
}
7 => {
assert_eq!(event.name, "duration");
window.notify("duration");
}
_ => unreachable!(), _ => unreachable!(),
}, },
@ -314,16 +296,12 @@ mod imp {
self.setup.present(Some(self.obj().as_ref())); self.setup.present(Some(self.obj().as_ref()));
} }
fn volume(&self) -> u32 { fn volume(&self) -> i64 {
self.mpv self.mpv.get_property("volume").unwrap()
.get_property::<i64>("volume")
.unwrap()
.try_into()
.unwrap()
} }
fn set_volume(&self, volume: u32) { fn set_volume(&self, volume: i64) {
self.mpv.set_property("volume", volume as i64).unwrap(); self.mpv.set_property("volume", volume).unwrap();
} }
fn mute(&self) -> bool { fn mute(&self) -> bool {
@ -342,12 +320,8 @@ mod imp {
self.mpv.set_property("pause", pause).unwrap(); self.mpv.set_property("pause", pause).unwrap();
} }
fn playlist_pos(&self) -> i32 { fn playlist_pos(&self) -> i64 {
self.mpv self.mpv.get_property("playlist-pos").unwrap()
.get_property::<i64>("playlist-pos")
.unwrap()
.try_into()
.unwrap()
} }
fn time_pos(&self) -> f64 { fn time_pos(&self) -> f64 {
@ -359,37 +333,10 @@ mod imp {
.unwrap() .unwrap()
} }
fn duration(&self) -> f64 {
let duration = match self.mpv.get_property::<f64>("duration") {
Ok(duration) => Ok(Some(duration)),
Err(err) if err.is_property_unavailable() => {
Ok(self.song().as_ref().map(|song| song.duration() as f64))
}
Err(err) => Err(err),
}
.unwrap();
assert_eq!(
duration.map(|f| f as i64),
self.song().as_ref().map(crate::PlaybinSong::duration),
"mpv duration doesn not match subsonic duration (this should probably be a warn)"
);
duration.unwrap_or(0.0) // placeholder
}
fn idle_active(&self) -> bool { fn idle_active(&self) -> bool {
self.mpv.get_property("idle-active").unwrap() self.mpv.get_property("idle-active").unwrap()
} }
fn playlist_count(&self) -> u32 {
self.mpv
.get_property::<i64>("playlist-count")
.unwrap()
.try_into()
.unwrap()
}
fn song(&self) -> Option<PlaybinSong> { fn song(&self) -> Option<PlaybinSong> {
if self.obj().playlist_pos() < 0 { if self.obj().playlist_pos() < 0 {
None None