More work on mpris controls: buttons now work

This commit is contained in:
Ian Douglas Scott 2021-08-26 11:00:43 -07:00
parent 0ccab6df13
commit 1145729a9e
2 changed files with 93 additions and 26 deletions

View file

@ -5,7 +5,8 @@ use gtk4::{
prelude::*,
subclass::prelude::*,
};
use std::cell::RefCell;
use once_cell::unsync::OnceCell;
use std::{cell::RefCell, collections::HashMap};
use crate::deref_cell::DerefCell;
@ -15,6 +16,8 @@ pub struct MprisControlsInner {
backward_button: DerefCell<gtk4::Button>,
play_pause_button: DerefCell<gtk4::Button>,
forward_button: DerefCell<gtk4::Button>,
dbus: OnceCell<DBus>,
players: RefCell<HashMap<String, Player>>,
}
#[glib::object_subclass]
@ -32,17 +35,17 @@ impl ObjectImpl for MprisControlsInner {
fn constructed(&self, obj: &MprisControls) {
let backward_button = cascade! {
gtk4::Button::from_icon_name(Some("media-skip-backward-symbolic"));
..connect_clicked(|_| {});
..connect_clicked(clone!(@strong obj => move |_| obj.call("Previous")));
};
let play_pause_button = cascade! {
gtk4::Button::from_icon_name(Some("media-playback-start-symbolic"));
..connect_clicked(|_| {});
..connect_clicked(clone!(@strong obj => move |_| obj.call("PlayPause")));
};
let forward_button = cascade! {
gtk4::Button::from_icon_name(Some("media-skip-forward-symbolic"));
..connect_clicked(|_| {});
..connect_clicked(clone!(@strong obj => move |_| obj.call("Next")));
};
let box_ = cascade! {
@ -53,31 +56,41 @@ impl ObjectImpl for MprisControlsInner {
..append(&forward_button);
};
/*
glib::MainContext::default().spawn_local(clone!(@strong obj => async move {
// XXX unwrap
let dbus = DBus::new().await.unwrap();
dbus.connect_name_owner_changed(|a, b, c| {
println!("{:?}", (a, b, c));
});
for name in dbus.list_names().await.unwrap() {
if !name.starts_with("org.mpris.MediaPlayer2.") {
continue;
let dbus = match DBus::new().await {
Ok(dbus) => dbus,
Err(err) => {
eprintln!("Failed to connect to 'org.freedesktop.DBus': {}", err);
return;
}
let player = Player::new(&name).await.unwrap();
};
println!("{}", name);
let status = player.playback_status();
let metadata = player.metadata().unwrap();
let title = metadata.title();
let album = metadata.album();
let artist = metadata.artist();
let art = metadata.arturl();
println!("{:?}", (title, art));
dbus.connect_name_owner_changed(clone!(@strong obj => move |name, old, new| {
if name.starts_with("org.mpris.MediaPlayer2.") {
if !old.is_empty() {
obj.player_removed(&name);
}
if !new.is_empty() {
glib::MainContext::default().spawn_local(clone!(@strong obj => async move {
obj.player_added(&name).await;
}));
}
}
}));
match dbus.list_names().await {
Ok(names) => for name in names {
if name.starts_with("org.mpris.MediaPlayer2.") {
glib::MainContext::default().spawn_local(clone!(@strong obj => async move {
obj.player_added(&name).await;
}));
}
}
Err(err) => eprintln!("Failed to call 'ListNames: {}'", err)
}
std::mem::forget(dbus);
let _ = obj.inner().dbus.set(dbus);
}));
*/
self.box_.set(box_);
self.backward_button.set(backward_button);
@ -85,7 +98,7 @@ impl ObjectImpl for MprisControlsInner {
self.forward_button.set(forward_button);
}
fn dispose(&self, obj: &MprisControls) {
fn dispose(&self, _obj: &MprisControls) {
self.box_.unparent();
}
}
@ -101,6 +114,52 @@ impl MprisControls {
pub fn new() -> Self {
glib::Object::new(&[]).unwrap()
}
fn inner(&self) -> &MprisControlsInner {
MprisControlsInner::from_instance(self)
}
fn player(&self) -> Option<Player> {
// XXX correctly choose which player to show
self.inner().players.borrow().values().next().cloned()
}
async fn player_added(&self, name: &str) {
let player = match Player::new(&name).await {
Ok(player) => player,
Err(err) => {
eprintln!("Failed to connect to '{}': {}", name, err);
return;
}
};
let status = player.playback_status();
let metadata = player.metadata().unwrap(); // XXX unwrap
let title = metadata.title();
let album = metadata.album();
let artist = metadata.artist();
let arturl = metadata.arturl();
println!("{:?}", (status, title, album, artist, arturl));
self.inner()
.players
.borrow_mut()
.insert(name.to_owned(), player);
}
fn player_removed(&self, name: &str) {
self.inner().players.borrow_mut().remove(name);
}
fn call(&self, method: &'static str) {
glib::MainContext::default().spawn_local(clone!(@strong self as self_ => async move {
if let Some(player) = self_.player() {
if let Err(err) = player.call(method).await {
eprintln!("Failed to call '{}': {}", method, err);
}
}
}));
}
}
struct Metadata(glib::VariantDict);
@ -127,6 +186,7 @@ impl Metadata {
}
}
#[derive(Clone)]
struct Player(gio::DBusProxy);
impl Player {
@ -143,6 +203,13 @@ impl Player {
Ok(Self(proxy))
}
async fn call(&self, method: &str) -> Result<(), glib::Error> {
self.0
.call_future(method, None, gio::DBusCallFlags::NONE, 1000)
.await?;
Ok(())
}
fn property<T: glib::FromVariant>(&self, prop: &str) -> Option<T> {
self.0.cached_property(prop)?.get()
}

View file

@ -55,7 +55,7 @@ impl ObjectImpl for TimeButtonInner {
obj.update_time();
}
fn dispose(&self, obj: &TimeButton) {
fn dispose(&self, _obj: &TimeButton) {
self.menu_button.unparent();
}
}