diff --git a/old-panel/src/config.rs b/old-panel/src/config.rs deleted file mode 100644 index 735540f2..00000000 --- a/old-panel/src/config.rs +++ /dev/null @@ -1,16 +0,0 @@ -use std::str::FromStr; -use toml_edit::{Document, Table, Array, TomlError}; - -struct Buttons<'a>(&'a Table); - -struct Config(Document); - -impl Config { - fn new(s: &str) -> Result { - Ok(Self(Document::from_str(s)?)) - } - - fn buttons(&self) -> Option { - Some(Buttons(self.0.as_table().get("buttons")?.as_table()?)) - } -} diff --git a/old-panel/src/main.rs b/old-panel/src/main.rs index 5e9b34a4..0835b9ca 100644 --- a/old-panel/src/main.rs +++ b/old-panel/src/main.rs @@ -2,8 +2,6 @@ use gtk4::{glib, prelude::*}; mod application; mod deref_cell; -mod mpris; -mod mpris_player; mod popover_container; mod time_button; mod window; diff --git a/old-panel/src/mpris.rs b/old-panel/src/mpris.rs deleted file mode 100644 index ff3cd228..00000000 --- a/old-panel/src/mpris.rs +++ /dev/null @@ -1,134 +0,0 @@ -use cascade::cascade; -use futures::stream::StreamExt; -use gtk4::{ - glib::{self, clone}, - prelude::*, - subclass::prelude::*, -}; -use once_cell::unsync::OnceCell; -use std::{cell::RefCell, collections::HashMap}; -use zbus::fdo::DBusProxy; - -use crate::deref_cell::DerefCell; -use crate::mpris_player::MprisPlayer; - -#[derive(Default)] -pub struct MprisControlsInner { - listbox: DerefCell, - dbus: OnceCell>, - players: RefCell>, -} - -#[glib::object_subclass] -impl ObjectSubclass for MprisControlsInner { - const NAME: &'static str = "S76MprisControls"; - type ParentType = gtk4::Widget; - type Type = MprisControls; - - fn class_init(klass: &mut Self::Class) { - klass.set_layout_manager_type::(); - } -} - -impl ObjectImpl for MprisControlsInner { - fn constructed(&self, obj: &MprisControls) { - let listbox = cascade! { - gtk4::ListBox::new(); - ..set_parent(obj); - }; - - glib::MainContext::default().spawn_local(clone!(@strong obj => async move { - let (dbus, mut name_owner_changed_stream) = match async { - let connection = zbus::Connection::session().await?; - let dbus = DBusProxy::new(&connection).await?; - let stream = dbus.receive_name_owner_changed().await?; - Ok::<_, zbus::Error>((dbus, stream)) - }.await { - Ok(value) => value, - Err(err) => { - eprintln!("Failed to connect to 'org.freedesktop.DBus': {}", err); - return; - } - }; - - glib::MainContext::default().spawn_local(clone!(@strong obj => async move { - while let Some(evt) = name_owner_changed_stream.next().await { - let args = match evt.args() { - Ok(args) => args, - Err(_) => { continue; }, - }; - if args.name.starts_with("org.mpris.MediaPlayer2.") { - if !args.old_owner.is_none() { - obj.player_removed(&args.name); - } - if !args.new_owner.is_none() { - obj.player_added(&args.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) - } - - let _ = obj.inner().dbus.set(dbus); - })); - - self.listbox.set(listbox); - } - - fn dispose(&self, _obj: &MprisControls) { - self.listbox.unparent(); - } -} - -impl WidgetImpl for MprisControlsInner {} - -glib::wrapper! { - pub struct MprisControls(ObjectSubclass) - @extends gtk4::Widget; -} - -impl MprisControls { - pub fn new() -> Self { - glib::Object::new(&[]).unwrap() - } - - fn inner(&self) -> &MprisControlsInner { - MprisControlsInner::from_instance(self) - } - - async fn player_added(&self, name: &str) { - let player = match MprisPlayer::new(&name).await { - Ok(player) => player, - Err(err) => { - eprintln!("Failed to connect to '{}': {}", name, err); - return; - } - }; - - let row = cascade! { - gtk4::ListBoxRow::new(); - ..set_selectable(false); - ..set_child(Some(&player)); - }; - self.inner().listbox.append(&row); - - self.inner() - .players - .borrow_mut() - .insert(name.to_owned(), player.clone()); - } - - fn player_removed(&self, name: &str) { - self.inner().players.borrow_mut().remove(name); - } -} diff --git a/old-panel/src/mpris_player.rs b/old-panel/src/mpris_player.rs deleted file mode 100644 index 19b47146..00000000 --- a/old-panel/src/mpris_player.rs +++ /dev/null @@ -1,264 +0,0 @@ -use cascade::cascade; -use futures::StreamExt; -use gtk4::{ - gdk_pixbuf, gio, - glib::{self, clone}, - pango, - prelude::*, - subclass::prelude::*, -}; -use std::{cell::RefCell, collections::HashMap}; -use zbus::dbus_proxy; -use zvariant::OwnedValue; - -use crate::deref_cell::DerefCell; - -#[derive(Default)] -pub struct MprisPlayerInner { - box_: DerefCell, - backward_button: DerefCell, - play_pause_button: DerefCell, - forward_button: DerefCell, - player: DerefCell>, - image: DerefCell, - image_uri: RefCell>, - title_label: DerefCell, - artist_label: DerefCell, -} - -#[glib::object_subclass] -impl ObjectSubclass for MprisPlayerInner { - const NAME: &'static str = "S76MprisPlayer"; - type ParentType = gtk4::Widget; - type Type = MprisPlayer; - - fn class_init(klass: &mut Self::Class) { - klass.set_layout_manager_type::(); - } -} - -impl ObjectImpl for MprisPlayerInner { - fn constructed(&self, obj: &MprisPlayer) { - let image = cascade! { - gtk4::Image::new(); - ..set_pixel_size(64); - }; - - let title_label = cascade! { - gtk4::Label::new(None); - ..set_halign(gtk4::Align::Start); - ..set_ellipsize(pango::EllipsizeMode::End); - ..set_max_width_chars(20); - ..set_attributes(Some(&cascade! { - pango::AttrList::new(); - ..insert(pango::AttrInt::new_weight(pango::Weight::Bold)); - })); - }; - - let artist_label = cascade! { - gtk4::Label::new(None); - ..set_halign(gtk4::Align::Start); - ..set_ellipsize(pango::EllipsizeMode::End); - ..set_max_width_chars(20); - }; - - let backward_button = cascade! { - gtk4::Button::from_icon_name("media-skip-backward-symbolic"); - ..connect_clicked(clone!(@strong obj => move |_| obj.call("Previous"))); - }; - - let play_pause_button = cascade! { - gtk4::Button::from_icon_name("media-playback-start-symbolic"); - ..connect_clicked(clone!(@strong obj => move |_| obj.call("PlayPause"))); - }; - - let forward_button = cascade! { - gtk4::Button::from_icon_name("media-skip-forward-symbolic"); - ..connect_clicked(clone!(@strong obj => move |_| obj.call("Next"))); - }; - - let box_ = cascade! { - gtk4::Box::new(gtk4::Orientation::Horizontal, 6); - ..set_parent(obj); - ..append(&image); - ..append(&cascade! { - gtk4::Box::new(gtk4::Orientation::Vertical, 0); - ..append(&title_label); - ..append(&artist_label); - ..append(&cascade! { - gtk4::Box::new(gtk4::Orientation::Horizontal, 0); - ..set_valign(gtk4::Align::Start); - ..append(&backward_button); - ..append(&play_pause_button); - ..append(&forward_button); - }); - }); - }; - - self.box_.set(box_); - self.backward_button.set(backward_button); - self.play_pause_button.set(play_pause_button); - self.forward_button.set(forward_button); - self.image.set(image); - self.title_label.set(title_label); - self.artist_label.set(artist_label); - } - - fn dispose(&self, _obj: &MprisPlayer) { - self.box_.unparent(); - } -} - -impl WidgetImpl for MprisPlayerInner {} - -glib::wrapper! { - pub struct MprisPlayer(ObjectSubclass) - @extends gtk4::Widget; -} - -impl MprisPlayer { - pub async fn new(name: &str) -> zbus::Result { - let obj = glib::Object::new::(&[]).unwrap(); - - let connection = zbus::Connection::session().await?; - let player = PlayerProxy::builder(&connection) - .destination(name.to_string())? - .build() - .await?; - - let mut status_stream = player.receive_playback_status_changed().await; - let mut metadata_stream = player.receive_metadata_changed().await; - - obj.inner().player.set(player); - - glib::MainContext::default().spawn_local(clone!(@strong obj => async move { - while status_stream.next().await.is_some() { - obj.update_status(); - } - })); - glib::MainContext::default().spawn_local(clone!(@strong obj => async move { - while metadata_stream.next().await.is_some() { - obj.update_metadata(); - } - })); - - Ok(obj) - } - - fn inner(&self) -> &MprisPlayerInner { - MprisPlayerInner::from_instance(self) - } - - fn call(&self, method: &'static str) { - glib::MainContext::default().spawn_local(clone!(@strong self as self_ => async move { - if let Err(err) = self_.inner().player.call::<_, _, ()>(method, &()).await { - eprintln!("Failed to call '{}': {}", method, err); - } - })); - } - - async fn update_arturl(&self, arturl: Option<&str>) { - let mut image_uri = self.inner().image_uri.borrow_mut(); - if image_uri.as_deref() == arturl { - return; - } - *image_uri = arturl.map(String::from); - drop(image_uri); - - let pixbuf = async { - // TODO: Security? - let file = gio::File::for_uri(&arturl?); - let stream = file.read_future(glib::PRIORITY_DEFAULT).await.ok()?; - gdk_pixbuf::Pixbuf::from_stream_future(&stream).await.ok() - } - .await; - if let Some(pixbuf) = pixbuf { - self.inner().image.set_from_pixbuf(Some(&pixbuf)); - } - } - - fn update_status(&self) { - let status = match self.inner().player.cached_playback_status() { - Ok(Some(status)) => status, - _ => return, - }; - - let play_pause_icon = if status == "Playing" { - "media-playback-pause-symbolic" - } else { - "media-playback-start-symbolic" - }; - - self.inner() - .play_pause_button - .set_icon_name(play_pause_icon); - } - - fn update_metadata(&self) { - let metadata = match self.inner().player.cached_metadata() { - Ok(Some(metadata)) => metadata, - _ => return, - }; - - let title = metadata.title().unwrap_or_else(|| String::new()); - // XXX correct way to handle multiple? - let artist = metadata - .artist() - .and_then(|x| x.get(0).cloned()) - .unwrap_or_default(); - - let _album = metadata.album(); // TODO - - let arturl = metadata.arturl(); - glib::MainContext::default().spawn_local(clone!(@strong self as self_ => async move { - self_.update_arturl(arturl.as_deref()).await; - })); - - self.inner().title_label.set_label(&title); - self.inner().artist_label.set_label(&artist); - } -} - -pub struct Metadata(HashMap); - -impl TryFrom for Metadata { - type Error = zbus::Error; - - fn try_from(value: OwnedValue) -> zbus::Result { - Ok(Self(value.try_into()?)) - } -} - -impl Metadata { - fn lookup<'a, T: TryFrom>(&self, key: &str) -> Option { - T::try_from(self.0.get(key)?.clone()).ok() - } - - fn title(&self) -> Option { - self.lookup("xesam:title") - } - - fn album(&self) -> Option { - self.lookup("xesam:album") - } - - fn artist(&self) -> Option> { - self.lookup("xesam:artist") - } - - fn arturl(&self) -> Option { - self.lookup("mpris:artUrl") - } -} - -#[dbus_proxy( - interface = "org.mpris.MediaPlayer2.Player", - default_path = "/org/mpris/MediaPlayer2" -)] -trait Player { - #[dbus_proxy(property)] - fn metadata(&self) -> zbus::Result; - - #[dbus_proxy(property)] - fn playback_status(&self) -> zbus::Result; -} diff --git a/old-panel/src/time_button.rs b/old-panel/src/time_button.rs index 85f64fbb..027847ff 100644 --- a/old-panel/src/time_button.rs +++ b/old-panel/src/time_button.rs @@ -8,7 +8,6 @@ use gtk4::{ use crate::application::PanelApp; use crate::deref_cell::DerefCell; -use crate::mpris::MprisControls; use crate::popover_container::PopoverContainer; #[derive(Default)] @@ -16,7 +15,6 @@ pub struct TimeButtonInner { calendar: DerefCell, button: DerefCell, label: DerefCell, - left_box: DerefCell, } #[glib::object_subclass] @@ -50,17 +48,11 @@ impl ObjectImpl for TimeButtonInner { ..set_child(Some(&label)); }; - let left_box = cascade! { - gtk4::Box::new(gtk4::Orientation::Vertical, 0); - ..append(&MprisControls::new()); - }; - cascade! { PopoverContainer::new(&button); ..set_parent(obj); ..popover().set_child(Some(&cascade! { gtk4::Box::new(gtk4::Orientation::Horizontal, 0); - ..append(&left_box); ..append(&calendar); })); ..popover().connect_show(clone!(@strong obj => move |_| obj.opening())); @@ -70,7 +62,6 @@ impl ObjectImpl for TimeButtonInner { self.calendar.set(calendar); self.button.set(button); self.label.set(label); - self.left_box.set(left_box); // TODO: better way to do this? glib::timeout_add_seconds_local(