Skip to content

Commit

Permalink
fix: Load systray items that are registered without a path
Browse files Browse the repository at this point in the history
  • Loading branch information
Kage-Yami committed Nov 24, 2024
1 parent 86dc4a4 commit fdfc6cc
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 33 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ All notable changes to eww will be listed here, starting at changes since versio
- Fix values in the `EWW_NET` variable (By: mario-kr)
- Fix the gtk `expander` widget (By: ovalkonia)
- Fix wayland monitor names support (By: dragonnn)
- Load systray items that are registered without a path (By: Kage-Yami)

### Features
- Update rust toolchain to 1.81.0 (By: w-lfchen)
Expand Down
76 changes: 44 additions & 32 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions crates/notifier_host/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ homepage = "https://github.com/elkowar/eww"

[dependencies]
dbusmenu-gtk3 = "0.1.0"
quick-xml = { version = "0.37.1", features = ["serialize"] }
serde = "1.0.215"

gtk.workspace = true
log.workspace = true
Expand Down
66 changes: 65 additions & 1 deletion crates/notifier_host/src/item.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use crate::*;

use gtk::{self, prelude::*};
use serde::Deserialize;
use zbus::fdo::IntrospectableProxy;

/// Recognised values of [`org.freedesktop.StatusNotifierItem.Status`].
///
Expand Down Expand Up @@ -61,7 +63,12 @@ impl Item {
if let Some((addr, path)) = service.split_once('/') {
(addr.to_owned(), format!("/{}", path))
} else if service.starts_with(':') {
(service[0..6].to_owned(), names::ITEM_OBJECT.to_owned())
(
service.to_owned(),
resolve_pathless_address(con, service, "/".to_owned())
.await?
.ok_or_else(|| zbus::Error::Failure(format!("no StatusNotifierItem found for {service}")))?,
)
} else {
return Err(zbus::Error::Address(service.to_owned()));
}
Expand Down Expand Up @@ -105,3 +112,60 @@ impl Item {
load_icon_from_sni(&self.sni, size, scale).await
}
}

#[derive(Deserialize)]
struct DBusNode {
#[serde(default)]
interface: Vec<DBusInterface>,

#[serde(default)]
node: Vec<DBusNode>,

#[serde(rename = "@name")]
name: Option<String>,
}

#[derive(Deserialize)]
struct DBusInterface {
#[serde(rename = "@name")]
name: String,
}

async fn resolve_pathless_address(con: &zbus::Connection, service: &str, path: String) -> zbus::Result<Option<String>> {
let introspection_xml =
IntrospectableProxy::builder(con).destination(service)?.path(path.as_str())?.build().await?.introspect().await?;

let dbus_node =
quick_xml::de::from_str::<DBusNode>(&introspection_xml).map_err(|err| zbus::Error::Failure(err.to_string()))?;

if dbus_node.interface.iter().any(|interface| interface.name == "org.kde.StatusNotifierItem") {
// This item implements the desired interface, so bubble it back up
Ok(Some(path))
} else {
for node in dbus_node.node {
if let Some(name) = node.name {
if name == "StatusNotifierItem" {
// If this exists, then there's a good chance DBus may not think anything
// implements the desired interface, so just bubble this up instead.
return Ok(Some(name));
}

let path = Box::pin(resolve_pathless_address(
con,
service,
// Make sure we don't double-up on the leading slash
format!("{path}/{name}", path = if path == "/" { "" } else { &path }),
))
.await?;

if path.is_some() {
// Return the first item found from a child
return Ok(path);
}
}
}

// No children had the item we want...
Ok(None)
}
}

0 comments on commit fdfc6cc

Please sign in to comment.