mirror of
				https://github.com/Zedfrigg/ironbar.git
				synced 2025-11-04 07:21:54 +01:00 
			
		
		
		
	refactor(tray): complete client rewrite
This commit is contained in:
		
					parent
					
						
							
								c7b6ee8bc0
							
						
					
				
			
			
				commit
				
					
						004ea76da5
					
				
			
		
					 9 changed files with 193 additions and 246 deletions
				
			
		
							
								
								
									
										36
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										36
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							| 
						 | 
					@ -101,12 +101,6 @@ dependencies = [
 | 
				
			||||||
 "windows-sys 0.48.0",
 | 
					 "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]]
 | 
					[[package]]
 | 
				
			||||||
name = "async-broadcast"
 | 
					name = "async-broadcast"
 | 
				
			||||||
version = "0.5.1"
 | 
					version = "0.5.1"
 | 
				
			||||||
| 
						 | 
					@ -3117,18 +3111,13 @@ dependencies = [
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "system-tray"
 | 
					name = "system-tray"
 | 
				
			||||||
version = "0.1.5"
 | 
					version = "0.2.0"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "a456e3e6cbd396f1a3a91f8f74d1fdcf2bde85c97afe174442c367f4749fc09b"
 | 
					checksum = "82a053bfb84b11f5eb8655a762ba826a2524d02a2f355b0fd6fce4125272f2e0"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "anyhow",
 | 
					 | 
				
			||||||
 "byteorder",
 | 
					 | 
				
			||||||
 "chrono",
 | 
					 | 
				
			||||||
 "log",
 | 
					 | 
				
			||||||
 "serde",
 | 
					 "serde",
 | 
				
			||||||
 "thiserror",
 | 
					 "thiserror",
 | 
				
			||||||
 "tokio",
 | 
					 "tokio",
 | 
				
			||||||
 "tokio-stream",
 | 
					 | 
				
			||||||
 "tracing",
 | 
					 "tracing",
 | 
				
			||||||
 "zbus",
 | 
					 "zbus",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
| 
						 | 
					@ -3157,18 +3146,18 @@ dependencies = [
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "thiserror"
 | 
					name = "thiserror"
 | 
				
			||||||
version = "1.0.56"
 | 
					version = "1.0.58"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad"
 | 
					checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "thiserror-impl",
 | 
					 "thiserror-impl",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "thiserror-impl"
 | 
					name = "thiserror-impl"
 | 
				
			||||||
version = "1.0.56"
 | 
					version = "1.0.58"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
 | 
					checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "proc-macro2",
 | 
					 "proc-macro2",
 | 
				
			||||||
 "quote 1.0.35",
 | 
					 "quote 1.0.35",
 | 
				
			||||||
| 
						 | 
					@ -3243,7 +3232,6 @@ dependencies = [
 | 
				
			||||||
 "signal-hook-registry",
 | 
					 "signal-hook-registry",
 | 
				
			||||||
 "socket2 0.5.5",
 | 
					 "socket2 0.5.5",
 | 
				
			||||||
 "tokio-macros",
 | 
					 "tokio-macros",
 | 
				
			||||||
 "tracing",
 | 
					 | 
				
			||||||
 "windows-sys 0.48.0",
 | 
					 "windows-sys 0.48.0",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3268,17 +3256,6 @@ dependencies = [
 | 
				
			||||||
 "tokio",
 | 
					 "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]]
 | 
					[[package]]
 | 
				
			||||||
name = "tokio-util"
 | 
					name = "tokio-util"
 | 
				
			||||||
version = "0.7.7"
 | 
					version = "0.7.7"
 | 
				
			||||||
| 
						 | 
					@ -4144,7 +4121,6 @@ dependencies = [
 | 
				
			||||||
 "serde_repr",
 | 
					 "serde_repr",
 | 
				
			||||||
 "sha1",
 | 
					 "sha1",
 | 
				
			||||||
 "static_assertions",
 | 
					 "static_assertions",
 | 
				
			||||||
 "tokio",
 | 
					 | 
				
			||||||
 "tracing",
 | 
					 "tracing",
 | 
				
			||||||
 "uds_windows",
 | 
					 "uds_windows",
 | 
				
			||||||
 "winapi",
 | 
					 "winapi",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -131,7 +131,7 @@ mpris = { version = "2.0.1", optional = true }
 | 
				
			||||||
sysinfo = { version = "0.29.11", optional = true }
 | 
					sysinfo = { version = "0.29.11", optional = true }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# tray
 | 
					# tray
 | 
				
			||||||
system-tray = { version = "0.1.5", optional = true }
 | 
					 system-tray = { version = "0.2.0", optional = true }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# upower
 | 
					# upower
 | 
				
			||||||
upower_dbus = { version = "0.3.2", optional = true }
 | 
					upower_dbus = { version = "0.3.2", optional = true }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,3 +1,4 @@
 | 
				
			||||||
 | 
					use crate::Ironbar;
 | 
				
			||||||
use std::sync::Arc;
 | 
					use std::sync::Arc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(feature = "clipboard")]
 | 
					#[cfg(feature = "clipboard")]
 | 
				
			||||||
| 
						 | 
					@ -9,7 +10,7 @@ pub mod music;
 | 
				
			||||||
#[cfg(feature = "notifications")]
 | 
					#[cfg(feature = "notifications")]
 | 
				
			||||||
pub mod swaync;
 | 
					pub mod swaync;
 | 
				
			||||||
#[cfg(feature = "tray")]
 | 
					#[cfg(feature = "tray")]
 | 
				
			||||||
pub mod system_tray;
 | 
					pub mod tray;
 | 
				
			||||||
#[cfg(feature = "upower")]
 | 
					#[cfg(feature = "upower")]
 | 
				
			||||||
pub mod upower;
 | 
					pub mod upower;
 | 
				
			||||||
#[cfg(feature = "volume")]
 | 
					#[cfg(feature = "volume")]
 | 
				
			||||||
| 
						 | 
					@ -30,7 +31,7 @@ pub struct Clients {
 | 
				
			||||||
    #[cfg(feature = "notifications")]
 | 
					    #[cfg(feature = "notifications")]
 | 
				
			||||||
    notifications: Option<Arc<swaync::Client>>,
 | 
					    notifications: Option<Arc<swaync::Client>>,
 | 
				
			||||||
    #[cfg(feature = "tray")]
 | 
					    #[cfg(feature = "tray")]
 | 
				
			||||||
    tray: Option<Arc<system_tray::TrayEventReceiver>>,
 | 
					    tray: Option<Arc<tray::Client>>,
 | 
				
			||||||
    #[cfg(feature = "upower")]
 | 
					    #[cfg(feature = "upower")]
 | 
				
			||||||
    upower: Option<Arc<zbus::fdo::PropertiesProxy<'static>>>,
 | 
					    upower: Option<Arc<zbus::fdo::PropertiesProxy<'static>>>,
 | 
				
			||||||
    #[cfg(feature = "volume")]
 | 
					    #[cfg(feature = "volume")]
 | 
				
			||||||
| 
						 | 
					@ -85,11 +86,17 @@ impl Clients {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[cfg(feature = "tray")]
 | 
					    #[cfg(feature = "tray")]
 | 
				
			||||||
    pub fn tray(&mut self) -> Arc<system_tray::TrayEventReceiver> {
 | 
					    pub fn tray(&mut self) -> Arc<tray::Client> {
 | 
				
			||||||
 | 
					        // TODO: Error handling here isn't great - should throw a user-friendly error
 | 
				
			||||||
        self.tray
 | 
					        self.tray
 | 
				
			||||||
            .get_or_insert_with(|| {
 | 
					            .get_or_insert_with(|| {
 | 
				
			||||||
                Arc::new(crate::await_sync(async {
 | 
					                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()
 | 
					            .clone()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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<String, (Box<StatusNotifierItem>, Option<TrayMenu>)>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[derive(Debug)]
 | 
					 | 
				
			||||||
pub struct TrayEventReceiver {
 | 
					 | 
				
			||||||
    tx: mpsc::Sender<NotifierItemCommand>,
 | 
					 | 
				
			||||||
    b_tx: broadcast::Sender<NotifierItemMessage>,
 | 
					 | 
				
			||||||
    _b_rx: broadcast::Receiver<NotifierItemMessage>,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    tray: Arc<Mutex<Tray>>,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl TrayEventReceiver {
 | 
					 | 
				
			||||||
    async fn new() -> system_tray::error::Result<Self> {
 | 
					 | 
				
			||||||
        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<NotifierItemMessage>>(())
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Ok(Self {
 | 
					 | 
				
			||||||
            tx,
 | 
					 | 
				
			||||||
            b_tx,
 | 
					 | 
				
			||||||
            _b_rx: b_rx,
 | 
					 | 
				
			||||||
            tray,
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub fn subscribe(
 | 
					 | 
				
			||||||
        &self,
 | 
					 | 
				
			||||||
    ) -> (
 | 
					 | 
				
			||||||
        mpsc::Sender<NotifierItemCommand>,
 | 
					 | 
				
			||||||
        broadcast::Receiver<NotifierItemMessage>,
 | 
					 | 
				
			||||||
    ) {
 | 
					 | 
				
			||||||
        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);
 | 
					 | 
				
			||||||
							
								
								
									
										4
									
								
								src/clients/tray.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								src/clients/tray.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,4 @@
 | 
				
			||||||
 | 
					use crate::register_client;
 | 
				
			||||||
 | 
					pub use system_tray::client::Client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					register_client!(Client, tray);
 | 
				
			||||||
| 
						 | 
					@ -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.
 | 
					/// Diff change type and associated info.
 | 
				
			||||||
#[derive(Debug, Clone)]
 | 
					#[derive(Debug, Clone)]
 | 
				
			||||||
pub enum Diff {
 | 
					pub enum Diff {
 | 
				
			||||||
    Add(MenuItemInfo),
 | 
					    Add(MenuItem),
 | 
				
			||||||
    Update(i32, MenuItemDiff),
 | 
					    Update(i32, MenuItemDiff),
 | 
				
			||||||
    Remove(i32),
 | 
					    Remove(i32),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -12,7 +12,7 @@ pub enum Diff {
 | 
				
			||||||
#[derive(Debug, Clone)]
 | 
					#[derive(Debug, Clone)]
 | 
				
			||||||
pub struct MenuItemDiff {
 | 
					pub struct MenuItemDiff {
 | 
				
			||||||
    /// Text of the item,
 | 
					    /// Text of the item,
 | 
				
			||||||
    pub label: Option<String>,
 | 
					    pub label: Option<Option<String>>,
 | 
				
			||||||
    /// Whether the item can be activated or not.
 | 
					    /// Whether the item can be activated or not.
 | 
				
			||||||
    pub enabled: Option<bool>,
 | 
					    pub enabled: Option<bool>,
 | 
				
			||||||
    /// True if the item is visible in the menu.
 | 
					    /// True if the item is visible in the menu.
 | 
				
			||||||
| 
						 | 
					@ -29,7 +29,7 @@ pub struct MenuItemDiff {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl MenuItemDiff {
 | 
					impl MenuItemDiff {
 | 
				
			||||||
    fn new(old: &MenuItemInfo, new: &MenuItemInfo) -> Self {
 | 
					    fn new(old: &MenuItem, new: &MenuItem) -> Self {
 | 
				
			||||||
        macro_rules! diff {
 | 
					        macro_rules! diff {
 | 
				
			||||||
            ($field:ident) => {
 | 
					            ($field:ident) => {
 | 
				
			||||||
                if old.$field == new.$field {
 | 
					                if old.$field == new.$field {
 | 
				
			||||||
| 
						 | 
					@ -70,7 +70,7 @@ impl MenuItemDiff {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Gets a diff set between old and new state.
 | 
					/// Gets a diff set between old and new state.
 | 
				
			||||||
pub fn get_diffs(old: &[MenuItemInfo], new: &[MenuItemInfo]) -> Vec<Diff> {
 | 
					pub fn get_diffs(old: &[MenuItem], new: &[MenuItem]) -> Vec<Diff> {
 | 
				
			||||||
    let mut diffs = vec![];
 | 
					    let mut diffs = vec![];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for new_item in new {
 | 
					    for new_item in new {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,5 @@
 | 
				
			||||||
use crate::image::ImageProvider;
 | 
					use crate::image::ImageProvider;
 | 
				
			||||||
 | 
					use crate::modules::tray::interface::TrayMenu;
 | 
				
			||||||
use color_eyre::{Report, Result};
 | 
					use color_eyre::{Report, Result};
 | 
				
			||||||
use glib::ffi::g_strfreev;
 | 
					use glib::ffi::g_strfreev;
 | 
				
			||||||
use glib::translate::ToGlibPtr;
 | 
					use glib::translate::ToGlibPtr;
 | 
				
			||||||
| 
						 | 
					@ -10,7 +11,6 @@ use std::collections::HashSet;
 | 
				
			||||||
use std::ffi::CStr;
 | 
					use std::ffi::CStr;
 | 
				
			||||||
use std::os::raw::{c_char, c_int};
 | 
					use std::os::raw::{c_char, c_int};
 | 
				
			||||||
use std::ptr;
 | 
					use std::ptr;
 | 
				
			||||||
use system_tray::message::tray::StatusNotifierItem;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Gets the GTK icon theme search paths by calling the FFI function.
 | 
					/// Gets the GTK icon theme search paths by calling the FFI function.
 | 
				
			||||||
/// Conveniently returns the result as a `HashSet`.
 | 
					/// Conveniently returns the result as a `HashSet`.
 | 
				
			||||||
| 
						 | 
					@ -38,17 +38,23 @@ fn get_icon_theme_search_paths(icon_theme: &IconTheme) -> HashSet<String> {
 | 
				
			||||||
    paths
 | 
					    paths
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn get_image(item: &StatusNotifierItem, icon_theme: &IconTheme, size: u32) -> Result<Image> {
 | 
					pub fn get_image(
 | 
				
			||||||
    get_image_from_icon_name(item, icon_theme, size).or_else(|_| get_image_from_pixmap(item, size))
 | 
					    item: &TrayMenu,
 | 
				
			||||||
 | 
					    icon_theme: &IconTheme,
 | 
				
			||||||
 | 
					    size: u32,
 | 
				
			||||||
 | 
					    prefer_icons: bool,
 | 
				
			||||||
 | 
					) -> Result<Image> {
 | 
				
			||||||
 | 
					    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
 | 
					/// Attempts to get a GTK `Image` component
 | 
				
			||||||
/// for the status notifier item's icon.
 | 
					/// for the status notifier item's icon.
 | 
				
			||||||
fn get_image_from_icon_name(
 | 
					fn get_image_from_icon_name(item: &TrayMenu, icon_theme: &IconTheme, size: u32) -> Result<Image> {
 | 
				
			||||||
    item: &StatusNotifierItem,
 | 
					 | 
				
			||||||
    icon_theme: &IconTheme,
 | 
					 | 
				
			||||||
    size: u32,
 | 
					 | 
				
			||||||
) -> Result<Image> {
 | 
					 | 
				
			||||||
    if let Some(path) = item.icon_theme_path.as_ref() {
 | 
					    if let Some(path) = item.icon_theme_path.as_ref() {
 | 
				
			||||||
        if !path.is_empty() && !get_icon_theme_search_paths(icon_theme).contains(path) {
 | 
					        if !path.is_empty() && !get_icon_theme_search_paths(icon_theme).contains(path) {
 | 
				
			||||||
            icon_theme.append_search_path(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())
 | 
					        icon_theme.lookup_icon(icon_name, size as i32, IconLookupFlags::empty())
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let pixbuf = icon_info.unwrap().load_icon()?;
 | 
					    if let Some(icon_info) = icon_info {
 | 
				
			||||||
 | 
					        let pixbuf = icon_info.load_icon()?;
 | 
				
			||||||
    let image = Image::new();
 | 
					        let image = Image::new();
 | 
				
			||||||
    ImageProvider::create_and_load_surface(&pixbuf, &image)?;
 | 
					        ImageProvider::create_and_load_surface(&pixbuf, &image)?;
 | 
				
			||||||
    Ok(image)
 | 
					        Ok(image)
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        Err(Report::msg("could not find icon"))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Attempts to get an image from the item pixmap.
 | 
					/// Attempts to get an image from the item pixmap.
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
/// The pixmap is supplied in ARGB32 format,
 | 
					/// The pixmap is supplied in ARGB32 format,
 | 
				
			||||||
/// which has 8 bits per sample and a bit stride of `4*width`.
 | 
					/// which has 8 bits per sample and a bit stride of `4*width`.
 | 
				
			||||||
fn get_image_from_pixmap(item: &StatusNotifierItem, size: u32) -> Result<Image> {
 | 
					fn get_image_from_pixmap(item: &TrayMenu, size: u32) -> Result<Image> {
 | 
				
			||||||
    const BITS_PER_SAMPLE: i32 = 8;
 | 
					    const BITS_PER_SAMPLE: i32 = 8;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let pixmap = item
 | 
					    let pixmap = item
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,10 +1,11 @@
 | 
				
			||||||
use crate::modules::tray::diff::{Diff, MenuItemDiff};
 | 
					use super::diff::{Diff, MenuItemDiff};
 | 
				
			||||||
use crate::{spawn, try_send};
 | 
					use crate::{spawn, try_send};
 | 
				
			||||||
use gtk::prelude::*;
 | 
					use gtk::prelude::*;
 | 
				
			||||||
use gtk::{CheckMenuItem, Image, Label, Menu, MenuItem, SeparatorMenuItem};
 | 
					use gtk::{CheckMenuItem, Image, Label, Menu, MenuItem, SeparatorMenuItem};
 | 
				
			||||||
use std::collections::HashMap;
 | 
					use std::collections::HashMap;
 | 
				
			||||||
use system_tray::message::menu::{MenuItem as MenuItemInfo, MenuType, ToggleState, ToggleType};
 | 
					use system_tray::client::ActivateRequest;
 | 
				
			||||||
use system_tray::message::NotifierItemCommand;
 | 
					use system_tray::item::{IconPixmap, StatusNotifierItem};
 | 
				
			||||||
 | 
					use system_tray::menu::{MenuItem as MenuItemInfo, MenuType, ToggleState, ToggleType};
 | 
				
			||||||
use tokio::sync::mpsc;
 | 
					use tokio::sync::mpsc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Calls a method on the underlying widget,
 | 
					/// Calls a method on the underlying widget,
 | 
				
			||||||
| 
						 | 
					@ -49,37 +50,47 @@ macro_rules! call {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Main tray icon to show on the bar
 | 
					/// Main tray icon to show on the bar
 | 
				
			||||||
pub(crate) struct TrayMenu {
 | 
					pub(crate) struct TrayMenu {
 | 
				
			||||||
    pub(crate) widget: MenuItem,
 | 
					    pub widget: MenuItem,
 | 
				
			||||||
    menu_widget: Menu,
 | 
					    menu_widget: Menu,
 | 
				
			||||||
    image_widget: Option<Image>,
 | 
					    image_widget: Option<Image>,
 | 
				
			||||||
    label_widget: Option<Label>,
 | 
					    label_widget: Option<Label>,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    menu: HashMap<i32, TrayMenuItem>,
 | 
					    menu: HashMap<i32, TrayMenuItem>,
 | 
				
			||||||
    state: Vec<MenuItemInfo>,
 | 
					    state: Vec<MenuItemInfo>,
 | 
				
			||||||
    icon_name: Option<String>,
 | 
					
 | 
				
			||||||
 | 
					    pub title: Option<String>,
 | 
				
			||||||
 | 
					    pub icon_name: Option<String>,
 | 
				
			||||||
 | 
					    pub icon_theme_path: Option<String>,
 | 
				
			||||||
 | 
					    pub icon_pixmap: Option<Vec<IconPixmap>>,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    tx: mpsc::Sender<i32>,
 | 
					    tx: mpsc::Sender<i32>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl TrayMenu {
 | 
					impl TrayMenu {
 | 
				
			||||||
    pub fn new(tx: mpsc::Sender<NotifierItemCommand>, address: String, path: String) -> Self {
 | 
					    pub fn new(
 | 
				
			||||||
 | 
					        tx: mpsc::Sender<ActivateRequest>,
 | 
				
			||||||
 | 
					        address: String,
 | 
				
			||||||
 | 
					        item: StatusNotifierItem,
 | 
				
			||||||
 | 
					    ) -> Self {
 | 
				
			||||||
        let widget = MenuItem::new();
 | 
					        let widget = MenuItem::new();
 | 
				
			||||||
        widget.style_context().add_class("item");
 | 
					        widget.style_context().add_class("item");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let (item_tx, mut item_rx) = mpsc::channel(8);
 | 
					        let (item_tx, mut item_rx) = mpsc::channel(8);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        spawn(async move {
 | 
					        if let Some(menu) = item.menu {
 | 
				
			||||||
            while let Some(id) = item_rx.recv().await {
 | 
					            spawn(async move {
 | 
				
			||||||
                try_send!(
 | 
					                while let Some(id) = item_rx.recv().await {
 | 
				
			||||||
                    tx,
 | 
					                    try_send!(
 | 
				
			||||||
                    NotifierItemCommand::MenuItemClicked {
 | 
					                        tx,
 | 
				
			||||||
                        submenu_id: id,
 | 
					                        ActivateRequest {
 | 
				
			||||||
                        menu_path: path.clone(),
 | 
					                            submenu_id: id,
 | 
				
			||||||
                        notifier_address: address.clone(),
 | 
					                            menu_path: menu.clone(),
 | 
				
			||||||
                    }
 | 
					                            address: address.clone(),
 | 
				
			||||||
                );
 | 
					                        }
 | 
				
			||||||
            }
 | 
					                    );
 | 
				
			||||||
        });
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let menu = Menu::new();
 | 
					        let menu = Menu::new();
 | 
				
			||||||
        widget.set_submenu(Some(&menu));
 | 
					        widget.set_submenu(Some(&menu));
 | 
				
			||||||
| 
						 | 
					@ -90,7 +101,10 @@ impl TrayMenu {
 | 
				
			||||||
            image_widget: None,
 | 
					            image_widget: None,
 | 
				
			||||||
            label_widget: None,
 | 
					            label_widget: None,
 | 
				
			||||||
            state: vec![],
 | 
					            state: vec![],
 | 
				
			||||||
            icon_name: None,
 | 
					            title: item.title,
 | 
				
			||||||
 | 
					            icon_name: item.icon_name,
 | 
				
			||||||
 | 
					            icon_theme_path: item.icon_theme_path,
 | 
				
			||||||
 | 
					            icon_pixmap: item.icon_pixmap,
 | 
				
			||||||
            menu: HashMap::new(),
 | 
					            menu: HashMap::new(),
 | 
				
			||||||
            tx: item_tx,
 | 
					            tx: item_tx,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -112,6 +126,18 @@ impl TrayMenu {
 | 
				
			||||||
            .set_label(text);
 | 
					            .set_label(text);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Shows the label, using its current text.
 | 
				
			||||||
 | 
					    /// The image is hidden if present.
 | 
				
			||||||
 | 
					    pub fn show_label(&self) {
 | 
				
			||||||
 | 
					        if let Some(image) = &self.image_widget {
 | 
				
			||||||
 | 
					            image.hide();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if let Some(label) = &self.label_widget {
 | 
				
			||||||
 | 
					            label.show();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Updates the image, and shows it in favour of the label.
 | 
					    /// Updates the image, and shows it in favour of the label.
 | 
				
			||||||
    pub fn set_image(&mut self, image: &Image) {
 | 
					    pub fn set_image(&mut self, image: &Image) {
 | 
				
			||||||
        if let Some(label) = &self.label_widget {
 | 
					        if let Some(label) = &self.label_widget {
 | 
				
			||||||
| 
						 | 
					@ -134,6 +160,7 @@ impl TrayMenu {
 | 
				
			||||||
                    let item = TrayMenuItem::new(&info, self.tx.clone());
 | 
					                    let item = TrayMenuItem::new(&info, self.tx.clone());
 | 
				
			||||||
                    call!(self.menu_widget, add, item.widget);
 | 
					                    call!(self.menu_widget, add, item.widget);
 | 
				
			||||||
                    self.menu.insert(item.id, item);
 | 
					                    self.menu.insert(item.id, item);
 | 
				
			||||||
 | 
					                    // self.widget.show_all();
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                Diff::Update(id, info) => {
 | 
					                Diff::Update(id, info) => {
 | 
				
			||||||
                    if let Some(item) = self.menu.get_mut(&id) {
 | 
					                    if let Some(item) = self.menu.get_mut(&id) {
 | 
				
			||||||
| 
						 | 
					@ -209,12 +236,15 @@ impl TrayMenuItem {
 | 
				
			||||||
            (MenuType::Separator, _) => TrayMenuWidget::Separator(SeparatorMenuItem::new()),
 | 
					            (MenuType::Separator, _) => TrayMenuWidget::Separator(SeparatorMenuItem::new()),
 | 
				
			||||||
            (MenuType::Standard, ToggleType::Checkmark) => {
 | 
					            (MenuType::Standard, ToggleType::Checkmark) => {
 | 
				
			||||||
                let widget = CheckMenuItem::builder()
 | 
					                let widget = CheckMenuItem::builder()
 | 
				
			||||||
                    .label(info.label.as_str())
 | 
					 | 
				
			||||||
                    .visible(info.visible)
 | 
					                    .visible(info.visible)
 | 
				
			||||||
                    .sensitive(info.enabled)
 | 
					                    .sensitive(info.enabled)
 | 
				
			||||||
                    .active(info.toggle_state == ToggleState::On)
 | 
					                    .active(info.toggle_state == ToggleState::On)
 | 
				
			||||||
                    .build();
 | 
					                    .build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if let Some(label) = &info.label {
 | 
				
			||||||
 | 
					                    widget.set_label(label);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                add_submenu!(menu, widget);
 | 
					                add_submenu!(menu, widget);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
| 
						 | 
					@ -230,11 +260,14 @@ impl TrayMenuItem {
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            (MenuType::Standard, _) => {
 | 
					            (MenuType::Standard, _) => {
 | 
				
			||||||
                let widget = MenuItem::builder()
 | 
					                let widget = MenuItem::builder()
 | 
				
			||||||
                    .label(&info.label)
 | 
					 | 
				
			||||||
                    .visible(info.visible)
 | 
					                    .visible(info.visible)
 | 
				
			||||||
                    .sensitive(info.enabled)
 | 
					                    .sensitive(info.enabled)
 | 
				
			||||||
                    .build();
 | 
					                    .build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if let Some(label) = &info.label {
 | 
				
			||||||
 | 
					                    widget.set_label(label);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                add_submenu!(menu, widget);
 | 
					                add_submenu!(menu, widget);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
| 
						 | 
					@ -265,6 +298,7 @@ impl TrayMenuItem {
 | 
				
			||||||
    /// applying the submenu diffs to any further submenu items.
 | 
					    /// applying the submenu diffs to any further submenu items.
 | 
				
			||||||
    fn apply_diff(&mut self, diff: MenuItemDiff) {
 | 
					    fn apply_diff(&mut self, diff: MenuItemDiff) {
 | 
				
			||||||
        if let Some(label) = diff.label {
 | 
					        if let Some(label) = diff.label {
 | 
				
			||||||
 | 
					            let label = label.unwrap_or_default();
 | 
				
			||||||
            match &self.widget {
 | 
					            match &self.widget {
 | 
				
			||||||
                TrayMenuWidget::Separator(widget) => widget.set_label(&label),
 | 
					                TrayMenuWidget::Separator(widget) => widget.set_label(&label),
 | 
				
			||||||
                TrayMenuWidget::Standard(widget) => widget.set_label(&label),
 | 
					                TrayMenuWidget::Standard(widget) => widget.set_label(&label),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,19 +2,21 @@ mod diff;
 | 
				
			||||||
mod icon;
 | 
					mod icon;
 | 
				
			||||||
mod interface;
 | 
					mod interface;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::clients::system_tray::TrayEventReceiver;
 | 
					use crate::clients::tray;
 | 
				
			||||||
use crate::config::CommonConfig;
 | 
					use crate::config::CommonConfig;
 | 
				
			||||||
use crate::modules::tray::diff::get_diffs;
 | 
					use crate::modules::tray::diff::get_diffs;
 | 
				
			||||||
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext};
 | 
					use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext};
 | 
				
			||||||
use crate::{glib_recv, spawn};
 | 
					use crate::{glib_recv, lock, send_async, spawn};
 | 
				
			||||||
use color_eyre::Result;
 | 
					use color_eyre::{Report, Result};
 | 
				
			||||||
use gtk::{prelude::*, PackDirection};
 | 
					use gtk::{prelude::*, PackDirection};
 | 
				
			||||||
use gtk::{IconTheme, MenuBar};
 | 
					use gtk::{IconTheme, MenuBar};
 | 
				
			||||||
use interface::TrayMenu;
 | 
					use interface::TrayMenu;
 | 
				
			||||||
use serde::Deserialize;
 | 
					use serde::Deserialize;
 | 
				
			||||||
use std::collections::HashMap;
 | 
					use std::collections::HashMap;
 | 
				
			||||||
use system_tray::message::{NotifierItemCommand, NotifierItemMessage};
 | 
					use system_tray::client::Event;
 | 
				
			||||||
 | 
					use system_tray::client::{ActivateRequest, UpdateEvent};
 | 
				
			||||||
use tokio::sync::mpsc;
 | 
					use tokio::sync::mpsc;
 | 
				
			||||||
 | 
					use tracing::{debug, error, warn};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Deserialize, Clone)]
 | 
					#[derive(Debug, Deserialize, Clone)]
 | 
				
			||||||
pub struct TrayModule {
 | 
					pub struct TrayModule {
 | 
				
			||||||
| 
						 | 
					@ -49,8 +51,8 @@ where
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Module<MenuBar> for TrayModule {
 | 
					impl Module<MenuBar> for TrayModule {
 | 
				
			||||||
    type SendMessage = NotifierItemMessage;
 | 
					    type SendMessage = Event;
 | 
				
			||||||
    type ReceiveMessage = NotifierItemCommand;
 | 
					    type ReceiveMessage = ActivateRequest;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn name() -> &'static str {
 | 
					    fn name() -> &'static str {
 | 
				
			||||||
        "tray"
 | 
					        "tray"
 | 
				
			||||||
| 
						 | 
					@ -64,26 +66,39 @@ impl Module<MenuBar> for TrayModule {
 | 
				
			||||||
    ) -> Result<()> {
 | 
					    ) -> Result<()> {
 | 
				
			||||||
        let tx = context.tx.clone();
 | 
					        let tx = context.tx.clone();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let client = context.client::<TrayEventReceiver>();
 | 
					        let client = context.client::<tray::Client>();
 | 
				
			||||||
 | 
					        let mut tray_rx = client.subscribe();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let (tray_tx, mut tray_rx) = client.subscribe();
 | 
					        let initial_items = lock!(client.items()).clone();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // listen to tray updates
 | 
					        // listen to tray updates
 | 
				
			||||||
        spawn(async move {
 | 
					        spawn(async move {
 | 
				
			||||||
            while let Ok(message) = tray_rx.recv().await {
 | 
					            for (key, (item, menu)) in initial_items.into_iter() {
 | 
				
			||||||
                tx.send(ModuleUpdateEvent::Update(message)).await?;
 | 
					                send_async!(
 | 
				
			||||||
 | 
					                    tx,
 | 
				
			||||||
 | 
					                    ModuleUpdateEvent::Update(Event::Add(key.clone(), item.into()))
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if let Some(menu) = menu.clone() {
 | 
				
			||||||
 | 
					                    send_async!(
 | 
				
			||||||
 | 
					                        tx,
 | 
				
			||||||
 | 
					                        ModuleUpdateEvent::Update(Event::Update(key, UpdateEvent::Menu(menu)))
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            Ok::<(), mpsc::error::SendError<ModuleUpdateEvent<Self::SendMessage>>>(())
 | 
					            while let Ok(message) = tray_rx.recv().await {
 | 
				
			||||||
 | 
					                send_async!(tx, ModuleUpdateEvent::Update(message))
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // send tray commands
 | 
					        // send tray commands
 | 
				
			||||||
        spawn(async move {
 | 
					        spawn(async move {
 | 
				
			||||||
            while let Some(cmd) = rx.recv().await {
 | 
					            while let Some(cmd) = rx.recv().await {
 | 
				
			||||||
                tray_tx.send(cmd).await?;
 | 
					                client.activate(cmd).await?;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            Ok::<(), mpsc::error::SendError<NotifierItemCommand>>(())
 | 
					            Ok::<_, Report>(())
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
| 
						 | 
					@ -114,7 +129,7 @@ impl Module<MenuBar> for TrayModule {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // listen for UI updates
 | 
					            // listen for UI updates
 | 
				
			||||||
            glib_recv!(context.subscribe(), update =>
 | 
					            glib_recv!(context.subscribe(), update =>
 | 
				
			||||||
                on_update(update, &container, &mut menus, &icon_theme, self.icon_size, &context.controller_tx)
 | 
					                on_update(update, &container, &mut menus, &icon_theme, self.icon_size, self.prefer_theme_icons, &context.controller_tx)
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -128,52 +143,81 @@ impl Module<MenuBar> for TrayModule {
 | 
				
			||||||
/// Handles UI updates as callback,
 | 
					/// Handles UI updates as callback,
 | 
				
			||||||
/// getting the diff since the previous update and applying it to the menu.
 | 
					/// getting the diff since the previous update and applying it to the menu.
 | 
				
			||||||
fn on_update(
 | 
					fn on_update(
 | 
				
			||||||
    update: NotifierItemMessage,
 | 
					    update: Event,
 | 
				
			||||||
    container: &MenuBar,
 | 
					    container: &MenuBar,
 | 
				
			||||||
    menus: &mut HashMap<Box<str>, TrayMenu>,
 | 
					    menus: &mut HashMap<Box<str>, TrayMenu>,
 | 
				
			||||||
    icon_theme: &IconTheme,
 | 
					    icon_theme: &IconTheme,
 | 
				
			||||||
    icon_size: u32,
 | 
					    icon_size: u32,
 | 
				
			||||||
    tx: &mpsc::Sender<NotifierItemCommand>,
 | 
					    prefer_icons: bool,
 | 
				
			||||||
 | 
					    tx: &mpsc::Sender<ActivateRequest>,
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
    match update {
 | 
					    match update {
 | 
				
			||||||
        NotifierItemMessage::Update {
 | 
					        Event::Add(address, item) => {
 | 
				
			||||||
            item,
 | 
					            debug!("Received new tray item at '{address}': {item:?}");
 | 
				
			||||||
            address,
 | 
					 | 
				
			||||||
            menu,
 | 
					 | 
				
			||||||
        } => {
 | 
					 | 
				
			||||||
            if let (Some(menu_opts), Some(menu_path)) = (menu, &item.menu) {
 | 
					 | 
				
			||||||
                let submenus = menu_opts.submenus;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let mut menu_item = menus.remove(address.as_str()).unwrap_or_else(|| {
 | 
					            let mut menu_item = TrayMenu::new(tx.clone(), address.clone(), *item);
 | 
				
			||||||
                    let item = TrayMenu::new(tx.clone(), address.clone(), menu_path.to_string());
 | 
					            container.add(&menu_item.widget);
 | 
				
			||||||
                    container.add(&item.widget);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    item
 | 
					            match icon::get_image(&menu_item, icon_theme, icon_size, prefer_icons) {
 | 
				
			||||||
                });
 | 
					                Ok(image) => menu_item.set_image(&image),
 | 
				
			||||||
 | 
					                Err(_) => {
 | 
				
			||||||
                let label = item.title.as_ref().unwrap_or(&address);
 | 
					                    let label = menu_item.title.clone().unwrap_or(address.clone());
 | 
				
			||||||
                if let Some(label_widget) = menu_item.label_widget() {
 | 
					                    menu_item.set_label(&label)
 | 
				
			||||||
                    label_widget.set_label(label);
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if item.icon_name.as_ref() != menu_item.icon_name() {
 | 
					            menu_item.widget.show();
 | 
				
			||||||
                    match icon::get_image(&item, icon_theme, icon_size) {
 | 
					            menus.insert(address.into(), menu_item);
 | 
				
			||||||
                        Ok(image) => menu_item.set_image(&image),
 | 
					        }
 | 
				
			||||||
                        Err(_) => menu_item.set_label(label),
 | 
					        Event::Update(address, update) => {
 | 
				
			||||||
                    };
 | 
					            debug!("Received tray update for '{address}': {update:?}");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let Some(menu_item) = menus.get_mut(address.as_str()) else {
 | 
				
			||||||
 | 
					                error!("Attempted to update menu at '{address}' but could not find it");
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            match update {
 | 
				
			||||||
 | 
					                UpdateEvent::AttentionIcon(_icon) => {
 | 
				
			||||||
 | 
					                    warn!("received unimplemented NewAttentionIcon event");
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					                UpdateEvent::Icon(icon) => {
 | 
				
			||||||
 | 
					                    if icon.as_ref() != menu_item.icon_name() {
 | 
				
			||||||
 | 
					                        match icon::get_image(menu_item, icon_theme, icon_size, prefer_icons) {
 | 
				
			||||||
 | 
					                            Ok(image) => menu_item.set_image(&image),
 | 
				
			||||||
 | 
					                            Err(_) => menu_item.show_label(),
 | 
				
			||||||
 | 
					                        };
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let diffs = get_diffs(menu_item.state(), &submenus);
 | 
					                    menu_item.set_icon_name(icon);
 | 
				
			||||||
                menu_item.apply_diffs(diffs);
 | 
					                }
 | 
				
			||||||
                menu_item.widget.show();
 | 
					                UpdateEvent::OverlayIcon(_icon) => {
 | 
				
			||||||
 | 
					                    warn!("received unimplemented NewOverlayIcon event");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                UpdateEvent::Status(_status) => {
 | 
				
			||||||
 | 
					                    warn!("received unimplemented NewStatus event");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                UpdateEvent::Title(title) => {
 | 
				
			||||||
 | 
					                    if let Some(label_widget) = menu_item.label_widget() {
 | 
				
			||||||
 | 
					                        label_widget.set_label(&title.unwrap_or_default());
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                // UpdateEvent::Tooltip(_tooltip) => {
 | 
				
			||||||
 | 
					                //     warn!("received unimplemented NewAttentionIcon event");
 | 
				
			||||||
 | 
					                // }
 | 
				
			||||||
 | 
					                UpdateEvent::Menu(menu) => {
 | 
				
			||||||
 | 
					                    debug!("received new menu for '{}'", address);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                menu_item.set_state(submenus);
 | 
					                    let diffs = get_diffs(menu_item.state(), &menu.submenus);
 | 
				
			||||||
                menu_item.set_icon_name(item.icon_name);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                menus.insert(address.into(), menu_item);
 | 
					                    menu_item.apply_diffs(diffs);
 | 
				
			||||||
 | 
					                    menu_item.set_state(menu.submenus);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        NotifierItemMessage::Remove { address } => {
 | 
					        Event::Remove(address) => {
 | 
				
			||||||
 | 
					            debug!("Removing tray item at '{address}'");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if let Some(menu) = menus.get(address.as_str()) {
 | 
					            if let Some(menu) = menus.get(address.as_str()) {
 | 
				
			||||||
                container.remove(&menu.widget);
 | 
					                container.remove(&menu.widget);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue