Merge pull request #766 from JakeStanger/fix/tray-fixes

A whole load of tray fixes 🎉
This commit is contained in:
Jake Stanger 2024-11-09 19:08:43 +00:00 committed by GitHub
commit ed338e948c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 224 additions and 67 deletions

166
Cargo.lock generated
View File

@ -17,6 +17,12 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "adler2"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
[[package]]
name = "ahash"
version = "0.8.3"
@ -214,7 +220,7 @@ checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba"
dependencies = [
"proc-macro2",
"quote 1.0.35",
"syn 2.0.85",
"syn 2.0.87",
]
[[package]]
@ -231,7 +237,7 @@ checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842"
dependencies = [
"proc-macro2",
"quote 1.0.35",
"syn 2.0.85",
"syn 2.0.87",
]
[[package]]
@ -279,7 +285,7 @@ dependencies = [
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"miniz_oxide 0.6.2",
"object",
"rustc-demangle",
]
@ -374,7 +380,7 @@ dependencies = [
"glib",
"libc",
"once_cell",
"thiserror",
"thiserror 1.0.58",
]
[[package]]
@ -399,7 +405,7 @@ dependencies = [
"polling 3.3.1",
"rustix 0.38.28",
"slab",
"thiserror",
"thiserror 1.0.58",
]
[[package]]
@ -485,7 +491,7 @@ dependencies = [
"heck 0.5.0",
"proc-macro2",
"quote 1.0.35",
"syn 2.0.85",
"syn 2.0.87",
]
[[package]]
@ -561,6 +567,15 @@ dependencies = [
"libc",
]
[[package]]
name = "crc32fast"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
dependencies = [
"cfg-if",
]
[[package]]
name = "crossbeam-channel"
version = "0.5.8"
@ -811,7 +826,7 @@ checksum = "5e9a1f9f7d83e59740248a6e14ecf93929ade55027844dfcea78beafccc15745"
dependencies = [
"proc-macro2",
"quote 1.0.35",
"syn 2.0.85",
"syn 2.0.87",
]
[[package]]
@ -861,6 +876,15 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
[[package]]
name = "fdeflate"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07c6f4c64c1d33a3111c4466f7365ebdcc37c5bd1ea0d62aae2e3d722aacbedb"
dependencies = [
"simd-adler32",
]
[[package]]
name = "field-offset"
version = "0.3.5"
@ -883,6 +907,16 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "flate2"
version = "1.0.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0"
dependencies = [
"crc32fast",
"miniz_oxide 0.8.0",
]
[[package]]
name = "fnv"
version = "1.0.7"
@ -1018,7 +1052,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
dependencies = [
"proc-macro2",
"quote 1.0.35",
"syn 2.0.85",
"syn 2.0.87",
]
[[package]]
@ -1143,7 +1177,7 @@ checksum = "913dce4c5f06c2ea40fc178c06f777ac89fc6b1383e90c254fafb1abe4ba3c82"
dependencies = [
"proc-macro2",
"quote 1.0.35",
"syn 2.0.85",
"syn 2.0.87",
"uuid",
]
@ -1180,7 +1214,7 @@ dependencies = [
"once_cell",
"pin-project-lite",
"smallvec",
"thiserror",
"thiserror 1.0.58",
]
[[package]]
@ -1216,7 +1250,7 @@ dependencies = [
"memchr",
"once_cell",
"smallvec",
"thiserror",
"thiserror 1.0.58",
]
[[package]]
@ -1230,7 +1264,7 @@ dependencies = [
"proc-macro-error",
"proc-macro2",
"quote 1.0.35",
"syn 2.0.85",
"syn 2.0.87",
]
[[package]]
@ -1331,7 +1365,7 @@ dependencies = [
"proc-macro-error",
"proc-macro2",
"quote 1.0.35",
"syn 2.0.85",
"syn 2.0.87",
]
[[package]]
@ -1540,7 +1574,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5dd8ce4c182ce77e485918f49262425ee51a2746fe97f14084869aeff2fbc38e"
dependencies = [
"quote 1.0.35",
"syn 2.0.85",
"syn 2.0.87",
]
[[package]]
@ -1671,6 +1705,7 @@ dependencies = [
"mpris",
"nix 0.29.0",
"notify",
"png",
"regex",
"reqwest",
"schemars",
@ -1753,7 +1788,7 @@ dependencies = [
"pest",
"pest_derive",
"serde",
"thiserror",
"thiserror 1.0.58",
]
[[package]]
@ -1895,6 +1930,16 @@ dependencies = [
"adler",
]
[[package]]
name = "miniz_oxide"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
dependencies = [
"adler2",
"simd-adler32",
]
[[package]]
name = "mio"
version = "1.0.1"
@ -1940,7 +1985,7 @@ checksum = "8fe50e71b3206a46eff95e96549aa60953dc072baffaa04b71415024f8f254d2"
dependencies = [
"futures",
"mpd_client",
"thiserror",
"thiserror 1.0.58",
"tokio",
"tracing",
]
@ -1980,7 +2025,7 @@ dependencies = [
"derive_is_enum_variant",
"enum-kinds",
"from_variants",
"thiserror",
"thiserror 1.0.58",
]
[[package]]
@ -2155,7 +2200,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote 1.0.35",
"syn 2.0.85",
"syn 2.0.87",
]
[[package]]
@ -2253,7 +2298,7 @@ version = "2.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1acb4a4365a13f749a93f1a094a7805e5cfa0955373a9de860d962eaa3a5fe5a"
dependencies = [
"thiserror",
"thiserror 1.0.58",
"ucd-trie",
]
@ -2277,7 +2322,7 @@ dependencies = [
"pest_meta",
"proc-macro2",
"quote 1.0.35",
"syn 2.0.85",
"syn 2.0.87",
]
[[package]]
@ -2308,7 +2353,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
dependencies = [
"proc-macro2",
"quote 1.0.35",
"syn 2.0.85",
"syn 2.0.87",
]
[[package]]
@ -2329,6 +2374,19 @@ version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
[[package]]
name = "png"
version = "0.17.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52f9d46a34a05a6a57566bc2bfae066ef07585a6e3fa30fbbdff5936380623f0"
dependencies = [
"bitflags 1.3.2",
"crc32fast",
"fdeflate",
"flate2",
"miniz_oxide 0.8.0",
]
[[package]]
name = "polling"
version = "2.7.0"
@ -2531,7 +2589,7 @@ checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
dependencies = [
"getrandom",
"redox_syscall 0.2.16",
"thiserror",
"thiserror 1.0.58",
]
[[package]]
@ -2778,7 +2836,7 @@ dependencies = [
"proc-macro2",
"quote 1.0.35",
"serde_derive_internals",
"syn 2.0.85",
"syn 2.0.87",
]
[[package]]
@ -2839,7 +2897,7 @@ checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766"
dependencies = [
"proc-macro2",
"quote 1.0.35",
"syn 2.0.85",
"syn 2.0.87",
]
[[package]]
@ -2850,7 +2908,7 @@ checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3"
dependencies = [
"proc-macro2",
"quote 1.0.35",
"syn 2.0.85",
"syn 2.0.87",
]
[[package]]
@ -2873,7 +2931,7 @@ checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab"
dependencies = [
"proc-macro2",
"quote 1.0.35",
"syn 2.0.85",
"syn 2.0.87",
]
[[package]]
@ -2960,6 +3018,12 @@ dependencies = [
"libc",
]
[[package]]
name = "simd-adler32"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
[[package]]
name = "slab"
version = "0.4.8"
@ -2989,7 +3053,7 @@ dependencies = [
"log",
"memmap2",
"rustix 0.38.28",
"thiserror",
"thiserror 1.0.58",
"wayland-backend",
"wayland-client",
"wayland-csd-frame",
@ -3081,7 +3145,7 @@ checksum = "44b43b4059d825ccc04adf9726f944d0e3aa20938f4cff3b5c6b53198afcd6b3"
dependencies = [
"serde",
"serde_json",
"thiserror",
"thiserror 1.0.58",
]
[[package]]
@ -3108,9 +3172,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.85"
version = "2.0.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56"
checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
dependencies = [
"proc-macro2",
"quote 1.0.35",
@ -3165,12 +3229,12 @@ dependencies = [
[[package]]
name = "system-tray"
version = "0.2.0"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82a053bfb84b11f5eb8655a762ba826a2524d02a2f355b0fd6fce4125272f2e0"
checksum = "66066cf85f8a4985ae5a40ca4387036e4f870d95fbf113ffaab714796785d0f2"
dependencies = [
"serde",
"thiserror",
"thiserror 2.0.0",
"tokio",
"tracing",
"zbus",
@ -3195,7 +3259,16 @@ version = "1.0.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297"
dependencies = [
"thiserror-impl",
"thiserror-impl 1.0.58",
]
[[package]]
name = "thiserror"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15291287e9bff1bc6f9ff3409ed9af665bec7a5fc8ac079ea96be07bca0e2668"
dependencies = [
"thiserror-impl 2.0.0",
]
[[package]]
@ -3206,7 +3279,18 @@ checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
dependencies = [
"proc-macro2",
"quote 1.0.35",
"syn 2.0.85",
"syn 2.0.87",
]
[[package]]
name = "thiserror-impl"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22efd00f33f93fa62848a7cab956c3d38c8d43095efda1decfc2b3a5dc0b8972"
dependencies = [
"proc-macro2",
"quote 1.0.35",
"syn 2.0.87",
]
[[package]]
@ -3291,7 +3375,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
dependencies = [
"proc-macro2",
"quote 1.0.35",
"syn 2.0.85",
"syn 2.0.87",
]
[[package]]
@ -3446,7 +3530,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf"
dependencies = [
"crossbeam-channel",
"thiserror",
"thiserror 1.0.58",
"time",
"tracing-subscriber",
]
@ -3459,7 +3543,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote 1.0.35",
"syn 2.0.85",
"syn 2.0.87",
]
[[package]]
@ -3584,7 +3668,7 @@ dependencies = [
"serde",
"serde_json",
"serde_yaml",
"thiserror",
"thiserror 1.0.58",
"toml 0.8.12",
"tracing",
]
@ -3736,7 +3820,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote 1.0.35",
"syn 2.0.85",
"syn 2.0.87",
"wasm-bindgen-shared",
]
@ -3770,7 +3854,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
dependencies = [
"proc-macro2",
"quote 1.0.35",
"syn 2.0.85",
"syn 2.0.87",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]

View File

@ -68,7 +68,7 @@ notifications = ["zbus"]
sys_info = ["sysinfo", "regex"]
tray = ["system-tray"]
tray = ["system-tray", "png"]
upower = ["upower_dbus", "zbus", "futures-lite"]
@ -148,7 +148,8 @@ futures-signals = { version = "0.3.34", optional = true }
sysinfo = { version = "0.29.11", optional = true }
# tray
system-tray = { version = "0.2.0", optional = true }
system-tray = { version = "0.3.0", optional = true }
png = { version = "0.17.14", optional = true }
# upower
upower_dbus = { version = "0.3.2", optional = true }

View File

@ -1,4 +1,4 @@
use crate::{await_sync, Ironbar};
use crate::await_sync;
use color_eyre::Result;
use std::path::Path;
use std::rc::Rc;
@ -151,9 +151,7 @@ impl Clients {
let client = match &self.tray {
Some(client) => client.clone(),
None => {
let service_name = format!("{}-{}", env!("CARGO_CRATE_NAME"), Ironbar::unique_id());
let client = await_sync(async { tray::Client::new(&service_name).await })?;
let client = await_sync(async { tray::Client::new().await })?;
let client = Arc::new(client);
self.tray.replace(client.clone());
client

View File

@ -464,7 +464,7 @@ where
/// Blocks on a `Future` until it resolves.
///
/// This is not an `async` operation
/// so can be used outside of an async function.
/// so can be used outside an async function.
///
/// Use sparingly, as this risks blocking the UI thread!
/// Prefer async functions wherever possible.

View File

@ -18,7 +18,9 @@ pub struct MenuItemDiff {
/// True if the item is visible in the menu.
pub visible: Option<bool>,
/// Icon name of the item, following the freedesktop.org icon spec.
// pub icon_name: Option<Option<String>>,
pub icon_name: Option<Option<String>>,
/// PNG icon data.
pub icon_data: Option<Option<Vec<u8>>>,
/// Describe the current state of a "togglable" item. Can be one of:
/// - Some(true): on
/// - Some(false): off
@ -52,7 +54,8 @@ impl MenuItemDiff {
label: diff!(&label),
enabled: diff!(enabled),
visible: diff!(visible),
// icon_name: diff!(&icon_name),
icon_name: diff!(&icon_name),
icon_data: diff!(&icon_data),
toggle_state: diff!(toggle_state),
submenu: get_diffs(&old.submenu, &new.submenu),
}

View File

@ -7,6 +7,7 @@ use gtk::ffi::gtk_icon_theme_get_search_path;
use gtk::gdk_pixbuf::{Colorspace, InterpType, Pixbuf};
use gtk::prelude::IconThemeExt;
use gtk::{IconLookupFlags, IconTheme, Image};
use png::ColorType;
use std::collections::HashSet;
use std::ffi::CStr;
use std::os::raw::{c_char, c_int};
@ -125,3 +126,36 @@ fn get_image_from_pixmap(item: &TrayMenu, size: u32) -> Result<Image> {
ImageProvider::create_and_load_surface(&pixbuf, &image)?;
Ok(image)
}
pub struct PngData<'a>(pub &'a [u8]);
impl TryFrom<PngData<'_>> for Image {
type Error = Report;
fn try_from(value: PngData) -> std::result::Result<Self, Self::Error> {
let data = value.0;
let decoder = png::Decoder::new(data);
let mut reader = decoder.read_info()?;
let mut buf = vec![0; reader.output_buffer_size()];
let info = reader.next_frame(&mut buf)?;
let bytes = glib::Bytes::from(&buf[..info.buffer_size()]);
let has_alpha = matches!(info.color_type, ColorType::Rgba | ColorType::GrayscaleAlpha);
let row_stride_multiplier = if has_alpha { 4 } else { 3 };
let pixbuf = Pixbuf::from_bytes(
&bytes,
Colorspace::Rgb,
has_alpha,
info.bit_depth as i32,
info.width as i32,
info.height as i32,
(info.width * row_stride_multiplier) as i32,
);
let image = Image::new();
ImageProvider::create_and_load_surface(&pixbuf, &image)?;
Ok(image)
}
}

View File

@ -1,13 +1,19 @@
use super::diff::{Diff, MenuItemDiff};
use crate::image::ImageProvider;
use crate::modules::tray::icon::PngData;
use crate::{spawn, try_send};
use glib::Propagation;
use gtk::prelude::*;
use gtk::{CheckMenuItem, Image, Label, Menu, MenuItem, SeparatorMenuItem};
use gtk::{
CheckMenuItem, Container, IconTheme, Image, Label, Menu, MenuItem, Orientation,
SeparatorMenuItem,
};
use std::collections::HashMap;
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;
use tracing::{error, warn};
/// Calls a method on the underlying widget,
/// passing in a single argument.
@ -214,6 +220,37 @@ enum TrayMenuWidget {
Checkbox(CheckMenuItem),
}
fn setup_item<W>(widget: &W, info: &MenuItemInfo)
where
W: IsA<MenuItem> + IsA<Container>,
{
let container = gtk::Box::new(Orientation::Horizontal, 10);
widget.add(&container);
if let Some(icon) = &info.icon_name {
// TODO: Get theme here
let image = Image::new();
match ImageProvider::parse(icon, &IconTheme::new(), true, 24)
.map(|provider| provider.load_into_image(image.clone()))
{
Some(Ok(())) => container.add(&image),
_ => warn!("Failed to load icon: {icon}"),
}
}
if let Some(icon_data) = &info.icon_data {
match Image::try_from(PngData(icon_data.as_slice())) {
Ok(image) => container.add(&image),
Err(err) => error!("{err:?}"),
};
}
let label = Label::new(info.label.as_deref());
container.add(&label);
container.show_all();
}
impl TrayMenuItem {
fn new(info: &MenuItemInfo, tx: mpsc::Sender<i32>) -> Self {
let mut submenu = HashMap::new();
@ -234,7 +271,9 @@ impl TrayMenuItem {
}
let widget = match (info.menu_type, info.toggle_type) {
(MenuType::Separator, _) => TrayMenuWidget::Separator(SeparatorMenuItem::new()),
(MenuType::Separator, _) => {
TrayMenuWidget::Separator(SeparatorMenuItem::builder().visible(true).build())
}
(MenuType::Standard, ToggleType::Checkmark) => {
let widget = CheckMenuItem::builder()
.visible(info.visible)
@ -242,10 +281,7 @@ impl TrayMenuItem {
.active(info.toggle_state == ToggleState::On)
.build();
if let Some(label) = &info.label {
widget.set_label(label);
}
setup_item(&widget, info);
add_submenu!(menu, widget);
{
@ -266,10 +302,7 @@ impl TrayMenuItem {
.sensitive(info.enabled)
.build();
if let Some(label) = &info.label {
widget.set_label(label);
}
setup_item(&widget, info);
add_submenu!(menu, widget);
{
@ -310,9 +343,13 @@ impl TrayMenuItem {
}
// TODO: Image support
// if let Some(icon_name) = diff.icon_name {
//
// }
if let Some(_icon_name) = diff.icon_name {
warn!("received unimplemented menu icon update");
}
if let Some(_icon_data) = diff.icon_data {
warn!("received unimplemented menu icon update");
}
if let Some(enabled) = diff.enabled {
match &self.widget {

View File

@ -16,7 +16,7 @@ use std::collections::HashMap;
use system_tray::client::Event;
use system_tray::client::{ActivateRequest, UpdateEvent};
use tokio::sync::mpsc;
use tracing::{debug, error, warn};
use tracing::{debug, error, trace, warn};
#[derive(Debug, Deserialize, Clone)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
@ -198,7 +198,8 @@ fn on_update(
menus.insert(address.into(), menu_item);
}
Event::Update(address, update) => {
debug!("Received tray update for '{address}': {update:?}");
debug!("Received tray update for '{address}'");
trace!("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");
@ -211,13 +212,12 @@ fn on_update(
}
UpdateEvent::Icon(icon) => {
if icon.as_ref() != menu_item.icon_name() {
menu_item.set_icon_name(icon);
match icon::get_image(menu_item, icon_theme, icon_size, prefer_icons) {
Ok(image) => menu_item.set_image(&image),
Err(_) => menu_item.show_label(),
};
}
menu_item.set_icon_name(icon);
}
UpdateEvent::OverlayIcon(_icon) => {
warn!("received unimplemented NewOverlayIcon event");