Skip to content

Commit

Permalink
Refactor host
Browse files Browse the repository at this point in the history
  • Loading branch information
ralismark committed Mar 9, 2024
1 parent 19a9241 commit 38ec238
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 23 deletions.
8 changes: 3 additions & 5 deletions crates/eww/src/widgets/systray.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ async fn dbus_session() -> zbus::Result<&'static DBusSession> {
let con = zbus::Connection::session().await?;
notifier_host::Watcher::new().attach_to(&con).await?;

let name = notifier_host::attach_new_wellknown_name(&con).await?;
let snw = notifier_host::register_to_watcher(&con, &name).await?;
let (_, snw) = notifier_host::register_as_host(&con).await?;

Ok(DBusSession {
snw,
Expand Down Expand Up @@ -68,9 +67,8 @@ pub fn spawn_systray(menubar: &gtk::MenuBar, props: &Props) {
};

systray.menubar.show();
if let Err(e) = notifier_host::run_host_forever(&mut systray, &s.snw).await {
log::error!("notifier host error: {}", e);
}
let e = notifier_host::run_host(&mut systray, &s.snw).await;
log::error!("notifier host error: {}", e);
});

// stop the task when the widget is dropped
Expand Down
70 changes: 52 additions & 18 deletions crates/notifier_host/src/host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,34 @@ use crate::*;

use zbus::export::ordered_stream::{self, OrderedStreamExt};

/// Trait for system tray implementations, to be notified of changes to what items are in the tray.
pub trait Host {
/// Called when an item is added to the tray. This is also called for all existing items when
/// starting [`run_host`].
fn add_item(&mut self, id: &str, item: Item);

/// Called when an item is removed from the tray.
fn remove_item(&mut self, id: &str);
}

/// Add a new well-known name of format `org.freedesktop.StatusNotifierHost-{pid}-{nr}` for this connection.
pub async fn attach_new_wellknown_name(con: &zbus::Connection) -> zbus::Result<zbus::names::WellKnownName<'static>> {
// TODO We aren't really thinking about what happens when we shut down a host. Currently, we don't
// provide a way to unregister as a host.
//
// It would also be good to combine `register_as_host` and `run_host`, so that we're only
// registered while we're running.

/// Register this DBus connection as a StatusNotifierHost (i.e. system tray).
///
/// This associates with the DBus connection new name of the format
/// `org.freedesktop.StatusNotifierHost-{pid}-{nr}`, and registers it to active
/// StatusNotifierWatcher. The name and the StatusNotifierWatcher proxy are returned.
///
/// You still need to call [`run_host`] to have the instance of [`Host`] be notified of new and
/// removed items.
pub async fn register_as_host(con: &zbus::Connection) -> zbus::Result<(zbus::names::WellKnownName<'static>, proxy::StatusNotifierWatcherProxy<'static>)> {
let snw = proxy::StatusNotifierWatcherProxy::new(con).await?;

// get a well-known name
let pid = std::process::id();
let mut i = 0;
let wellknown = loop {
Expand All @@ -26,33 +47,46 @@ pub async fn attach_new_wellknown_name(con: &zbus::Connection) -> zbus::Result<z
InQueue => unreachable!("request_name_with_flags returned InQueue even though we specified DoNotQueue"),
};
};
Ok(wellknown)
}

pub async fn register_to_watcher(
con: &zbus::Connection,
name: &zbus::names::WellKnownName<'_>,
) -> zbus::Result<proxy::StatusNotifierWatcherProxy<'static>> {
// register ourself to StatusNotifierWatcher
let snw = proxy::StatusNotifierWatcherProxy::new(con).await?;
snw.register_status_notifier_host(name).await?;
Ok(snw)
// register it to the StatusNotifierWatcher, so that they know there is a systray on the system
snw.register_status_notifier_host(&wellknown).await?;

Ok((wellknown, snw))
}

pub async fn run_host_forever(host: &mut dyn Host, snw: &proxy::StatusNotifierWatcherProxy<'static>) -> zbus::Result<()> {
/// Run the Host forever, calling its methods as signals are received from the StatusNotifierWatcher.
///
/// Before calling this, you should have called [`register_as_host`] (which returns an instance of
/// [`proxy::StatusNotifierWatcherProxy`]).
///
/// This async function runs forever, and only returns if it gets an error! As such, it is
/// recommended to call this via something like `tokio::spawn` that runs this in the
/// background.
pub async fn run_host(host: &mut dyn Host, snw: &proxy::StatusNotifierWatcherProxy<'static>) -> zbus::Error
{
// Replacement for ? operator since we're not returning a Result.
macro_rules! try_ {
($e:expr) => {
match $e {
Ok(x) => x,
Err(e) => return e,
}
}
}

enum ItemEvent {
NewItem(proxy::StatusNotifierItemRegistered),
GoneItem(proxy::StatusNotifierItemUnregistered),
}

// start listening to these streams
let new_items = snw.receive_status_notifier_item_registered().await?;
let gone_items = snw.receive_status_notifier_item_unregistered().await?;
let new_items = try_!(snw.receive_status_notifier_item_registered().await);
let gone_items = try_!(snw.receive_status_notifier_item_unregistered().await);

let mut item_names = std::collections::HashSet::new();

// initial items first
for svc in snw.registered_status_notifier_items().await? {
for svc in try_!(snw.registered_status_notifier_items().await) {
match Item::from_address(snw.connection(), &svc).await {
Ok(item) => {
item_names.insert(svc.to_owned());
Expand All @@ -71,7 +105,7 @@ pub async fn run_host_forever(host: &mut dyn Host, snw: &proxy::StatusNotifierWa
while let Some(ev) = ev_stream.next().await {
match ev {
ItemEvent::NewItem(sig) => {
let svc = sig.args()?.service;
let svc = try_!(sig.args()).service;
if item_names.contains(svc) {
log::info!("Got duplicate new item: {:?}", svc);
} else {
Expand All @@ -87,7 +121,7 @@ pub async fn run_host_forever(host: &mut dyn Host, snw: &proxy::StatusNotifierWa
}
}
ItemEvent::GoneItem(sig) => {
let svc = sig.args()?.service;
let svc = try_!(sig.args()).service;
if item_names.remove(svc) {
host.remove_item(svc);
}
Expand Down

0 comments on commit 38ec238

Please sign in to comment.