From 004ea76da5af3e8750e5a02a8780f62337b06844 Mon Sep 17 00:00:00 2001 From: Jake Stanger Date: Fri, 29 Mar 2024 00:23:44 +0000 Subject: [PATCH 1/2] refactor(tray): complete client rewrite --- Cargo.lock | 36 ++------- Cargo.toml | 2 +- src/clients/mod.rs | 15 +++- src/clients/system_tray.rs | 127 ------------------------------- src/clients/tray.rs | 4 + src/modules/tray/diff.rs | 10 +-- src/modules/tray/icon.rs | 37 +++++---- src/modules/tray/interface.rs | 76 +++++++++++++------ src/modules/tray/mod.rs | 138 ++++++++++++++++++++++------------ 9 files changed, 196 insertions(+), 249 deletions(-) delete mode 100644 src/clients/system_tray.rs create mode 100644 src/clients/tray.rs diff --git a/Cargo.lock b/Cargo.lock index 05956ad4..c6d55ffd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -101,12 +101,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "anyhow" -version = "1.0.70" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" - [[package]] name = "async-broadcast" version = "0.5.1" @@ -3117,18 +3111,13 @@ dependencies = [ [[package]] name = "system-tray" -version = "0.1.5" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a456e3e6cbd396f1a3a91f8f74d1fdcf2bde85c97afe174442c367f4749fc09b" +checksum = "82a053bfb84b11f5eb8655a762ba826a2524d02a2f355b0fd6fce4125272f2e0" dependencies = [ - "anyhow", - "byteorder", - "chrono", - "log", "serde", "thiserror", "tokio", - "tokio-stream", "tracing", "zbus", ] @@ -3157,18 +3146,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.56" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.56" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote 1.0.35", @@ -3243,7 +3232,6 @@ dependencies = [ "signal-hook-registry", "socket2 0.5.5", "tokio-macros", - "tracing", "windows-sys 0.48.0", ] @@ -3268,17 +3256,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-stream" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - [[package]] name = "tokio-util" version = "0.7.7" @@ -4144,7 +4121,6 @@ dependencies = [ "serde_repr", "sha1", "static_assertions", - "tokio", "tracing", "uds_windows", "winapi", diff --git a/Cargo.toml b/Cargo.toml index c7639a47..23ab177c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -131,7 +131,7 @@ mpris = { version = "2.0.1", optional = true } sysinfo = { version = "0.29.11", optional = true } # tray -system-tray = { version = "0.1.5", optional = true } + system-tray = { version = "0.2.0", optional = true } # upower upower_dbus = { version = "0.3.2", optional = true } diff --git a/src/clients/mod.rs b/src/clients/mod.rs index fa9c876b..a3899b2b 100644 --- a/src/clients/mod.rs +++ b/src/clients/mod.rs @@ -1,3 +1,4 @@ +use crate::Ironbar; use std::sync::Arc; #[cfg(feature = "clipboard")] @@ -9,7 +10,7 @@ pub mod music; #[cfg(feature = "notifications")] pub mod swaync; #[cfg(feature = "tray")] -pub mod system_tray; +pub mod tray; #[cfg(feature = "upower")] pub mod upower; #[cfg(feature = "volume")] @@ -30,7 +31,7 @@ pub struct Clients { #[cfg(feature = "notifications")] notifications: Option>, #[cfg(feature = "tray")] - tray: Option>, + tray: Option>, #[cfg(feature = "upower")] upower: Option>>, #[cfg(feature = "volume")] @@ -85,11 +86,17 @@ impl Clients { } #[cfg(feature = "tray")] - pub fn tray(&mut self) -> Arc { + pub fn tray(&mut self) -> Arc { + // TODO: Error handling here isn't great - should throw a user-friendly error self.tray .get_or_insert_with(|| { Arc::new(crate::await_sync(async { - system_tray::create_client().await + let service_name = + format!("{}-{}", env!("CARGO_CRATE_NAME"), Ironbar::unique_id()); + + tray::Client::new(&service_name) + .await + .expect("to be able to start client") })) }) .clone() diff --git a/src/clients/system_tray.rs b/src/clients/system_tray.rs deleted file mode 100644 index aec5c0a6..00000000 --- a/src/clients/system_tray.rs +++ /dev/null @@ -1,127 +0,0 @@ -use crate::{arc_mut, lock, register_client, send, spawn, Ironbar}; -use color_eyre::Report; -use std::collections::BTreeMap; -use std::sync::{Arc, Mutex}; -use system_tray::message::menu::TrayMenu; -use system_tray::message::tray::StatusNotifierItem; -use system_tray::message::{NotifierItemCommand, NotifierItemMessage}; -use system_tray::StatusNotifierWatcher; -use tokio::sync::{broadcast, mpsc}; -use tracing::{debug, error, trace}; - -type Tray = BTreeMap, Option)>; - -#[derive(Debug)] -pub struct TrayEventReceiver { - tx: mpsc::Sender, - b_tx: broadcast::Sender, - _b_rx: broadcast::Receiver, - - tray: Arc>, -} - -impl TrayEventReceiver { - async fn new() -> system_tray::error::Result { - let id = format!("ironbar-{}", Ironbar::unique_id()); - - let (tx, rx) = mpsc::channel(16); - let (b_tx, b_rx) = broadcast::channel(64); - - let tray = StatusNotifierWatcher::new(rx).await?; - let mut host = Box::pin(tray.create_notifier_host(&id)).await?; - - let tray = arc_mut!(BTreeMap::new()); - - { - let b_tx = b_tx.clone(); - let tray = tray.clone(); - - spawn(async move { - while let Ok(message) = host.recv().await { - trace!("Received message: {message:?}"); - - send!(b_tx, message.clone()); - let mut tray = lock!(tray); - match message { - NotifierItemMessage::Update { - address, - item, - menu, - } => { - debug!("Adding/updating item with address '{address}'"); - tray.insert(address, (item, menu)); - } - NotifierItemMessage::Remove { address } => { - debug!("Removing item with address '{address}'"); - tray.remove(&address); - } - } - } - - Ok::<(), broadcast::error::SendError>(()) - }); - } - - Ok(Self { - tx, - b_tx, - _b_rx: b_rx, - tray, - }) - } - - pub fn subscribe( - &self, - ) -> ( - mpsc::Sender, - broadcast::Receiver, - ) { - let tx = self.tx.clone(); - let b_rx = self.b_tx.subscribe(); - - let tray = lock!(self.tray).clone(); - for (address, (item, menu)) in tray { - let update = NotifierItemMessage::Update { - address, - item, - menu, - }; - send!(self.b_tx, update); - } - - (tx, b_rx) - } -} - -/// Attempts to create a new `TrayEventReceiver` instance, -/// retrying a maximum of 10 times before panicking the thread. -pub async fn create_client() -> TrayEventReceiver { - const MAX_RETRIES: i32 = 10; - - // sometimes this can fail - let mut retries = 0; - - let value = loop { - retries += 1; - - let tray = Box::pin(TrayEventReceiver::new()).await; - - match tray { - Ok(tray) => break Some(tray), - Err(err) => error!( - "{:?}", - Report::new(err).wrap_err(format!( - "Failed to create StatusNotifierWatcher (attempt {retries})" - )) - ), - } - - if retries == MAX_RETRIES { - break None; - } - }; - - value.expect("Failed to create StatusNotifierWatcher") -} - -register_client!(TrayEventReceiver, tray); diff --git a/src/clients/tray.rs b/src/clients/tray.rs new file mode 100644 index 00000000..3b84815e --- /dev/null +++ b/src/clients/tray.rs @@ -0,0 +1,4 @@ +use crate::register_client; +pub use system_tray::client::Client; + +register_client!(Client, tray); diff --git a/src/modules/tray/diff.rs b/src/modules/tray/diff.rs index 25e27b69..dd756afc 100644 --- a/src/modules/tray/diff.rs +++ b/src/modules/tray/diff.rs @@ -1,9 +1,9 @@ -use system_tray::message::menu::{MenuItem as MenuItemInfo, ToggleState}; +use system_tray::menu::{MenuItem, ToggleState}; /// Diff change type and associated info. #[derive(Debug, Clone)] pub enum Diff { - Add(MenuItemInfo), + Add(MenuItem), Update(i32, MenuItemDiff), Remove(i32), } @@ -12,7 +12,7 @@ pub enum Diff { #[derive(Debug, Clone)] pub struct MenuItemDiff { /// Text of the item, - pub label: Option, + pub label: Option>, /// Whether the item can be activated or not. pub enabled: Option, /// True if the item is visible in the menu. @@ -29,7 +29,7 @@ pub struct MenuItemDiff { } impl MenuItemDiff { - fn new(old: &MenuItemInfo, new: &MenuItemInfo) -> Self { + fn new(old: &MenuItem, new: &MenuItem) -> Self { macro_rules! diff { ($field:ident) => { if old.$field == new.$field { @@ -70,7 +70,7 @@ impl MenuItemDiff { } /// Gets a diff set between old and new state. -pub fn get_diffs(old: &[MenuItemInfo], new: &[MenuItemInfo]) -> Vec { +pub fn get_diffs(old: &[MenuItem], new: &[MenuItem]) -> Vec { let mut diffs = vec![]; for new_item in new { diff --git a/src/modules/tray/icon.rs b/src/modules/tray/icon.rs index e0a1c5b9..3918dfb5 100644 --- a/src/modules/tray/icon.rs +++ b/src/modules/tray/icon.rs @@ -1,4 +1,5 @@ use crate::image::ImageProvider; +use crate::modules::tray::interface::TrayMenu; use color_eyre::{Report, Result}; use glib::ffi::g_strfreev; use glib::translate::ToGlibPtr; @@ -10,7 +11,6 @@ use std::collections::HashSet; use std::ffi::CStr; use std::os::raw::{c_char, c_int}; use std::ptr; -use system_tray::message::tray::StatusNotifierItem; /// Gets the GTK icon theme search paths by calling the FFI function. /// Conveniently returns the result as a `HashSet`. @@ -38,17 +38,23 @@ fn get_icon_theme_search_paths(icon_theme: &IconTheme) -> HashSet { paths } -pub fn get_image(item: &StatusNotifierItem, icon_theme: &IconTheme, size: u32) -> Result { - get_image_from_icon_name(item, icon_theme, size).or_else(|_| get_image_from_pixmap(item, size)) +pub fn get_image( + item: &TrayMenu, + icon_theme: &IconTheme, + size: u32, + prefer_icons: bool, +) -> Result { + if !prefer_icons && item.icon_pixmap.is_some() { + get_image_from_pixmap(item, size) + } else { + get_image_from_icon_name(item, icon_theme, size) + .or_else(|_| get_image_from_pixmap(item, size)) + } } /// Attempts to get a GTK `Image` component /// for the status notifier item's icon. -fn get_image_from_icon_name( - item: &StatusNotifierItem, - icon_theme: &IconTheme, - size: u32, -) -> Result { +fn get_image_from_icon_name(item: &TrayMenu, icon_theme: &IconTheme, size: u32) -> Result { if let Some(path) = item.icon_theme_path.as_ref() { if !path.is_empty() && !get_icon_theme_search_paths(icon_theme).contains(path) { icon_theme.append_search_path(path); @@ -59,18 +65,21 @@ fn get_image_from_icon_name( icon_theme.lookup_icon(icon_name, size as i32, IconLookupFlags::empty()) }); - let pixbuf = icon_info.unwrap().load_icon()?; - - let image = Image::new(); - ImageProvider::create_and_load_surface(&pixbuf, &image)?; - Ok(image) + if let Some(icon_info) = icon_info { + let pixbuf = icon_info.load_icon()?; + let image = Image::new(); + ImageProvider::create_and_load_surface(&pixbuf, &image)?; + Ok(image) + } else { + Err(Report::msg("could not find icon")) + } } /// Attempts to get an image from the item pixmap. /// /// The pixmap is supplied in ARGB32 format, /// which has 8 bits per sample and a bit stride of `4*width`. -fn get_image_from_pixmap(item: &StatusNotifierItem, size: u32) -> Result { +fn get_image_from_pixmap(item: &TrayMenu, size: u32) -> Result { const BITS_PER_SAMPLE: i32 = 8; let pixmap = item diff --git a/src/modules/tray/interface.rs b/src/modules/tray/interface.rs index d35a1bff..4a182e90 100644 --- a/src/modules/tray/interface.rs +++ b/src/modules/tray/interface.rs @@ -1,10 +1,11 @@ -use crate::modules::tray::diff::{Diff, MenuItemDiff}; +use super::diff::{Diff, MenuItemDiff}; use crate::{spawn, try_send}; use gtk::prelude::*; use gtk::{CheckMenuItem, Image, Label, Menu, MenuItem, SeparatorMenuItem}; use std::collections::HashMap; -use system_tray::message::menu::{MenuItem as MenuItemInfo, MenuType, ToggleState, ToggleType}; -use system_tray::message::NotifierItemCommand; +use system_tray::client::ActivateRequest; +use system_tray::item::{IconPixmap, StatusNotifierItem}; +use system_tray::menu::{MenuItem as MenuItemInfo, MenuType, ToggleState, ToggleType}; use tokio::sync::mpsc; /// Calls a method on the underlying widget, @@ -49,37 +50,47 @@ macro_rules! call { /// Main tray icon to show on the bar pub(crate) struct TrayMenu { - pub(crate) widget: MenuItem, + pub widget: MenuItem, menu_widget: Menu, image_widget: Option, label_widget: Option