Skip to content

Commit

Permalink
Avoid duplicate dbus connections
Browse files Browse the repository at this point in the history
  • Loading branch information
ralismark committed Apr 10, 2023
1 parent 132b18c commit b043801
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 33 deletions.
38 changes: 32 additions & 6 deletions crates/eww/src/widgets/systray.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,19 +56,45 @@ impl notifier_host::Host for Host {
}
}

pub fn maintain_menubar(menubar: gtk::MenuBar) {
// TODO avoid having too many zbus::Connection instances
struct DBusGlobalState {
con: zbus::Connection,
name: zbus::names::WellKnownName<'static>,
}

menubar.show_all();
glib::MainContext::default().spawn_local(async move {
async fn dbus_state() -> std::sync::Arc<DBusGlobalState> {
use tokio::sync::Mutex;
use std::sync::{Weak, Arc};
use once_cell::sync::Lazy;
static DBUS_STATE: Lazy<Mutex<Weak<DBusGlobalState>>> = Lazy::new(Default::default);

let mut dbus_state = DBUS_STATE.lock().await;
if let Some(state) = dbus_state.upgrade() {
state
} else {
// TODO error handling?
let con = zbus::Connection::session().await.unwrap();
notifier_host::watcher_on(&con).await.unwrap();

let snw = notifier_host::register_host(&con).await.unwrap();
let name = notifier_host::attach_new_wellknown_name(&con).await.unwrap();

let arc = Arc::new(DBusGlobalState {
con,
name,
});
*dbus_state = Arc::downgrade(&arc);

arc
}
}

pub fn maintain_menubar(menubar: gtk::MenuBar) {
menubar.show_all();
glib::MainContext::default().spawn_local(async move {
let mut host = Host {
menubar,
items: std::collections::HashMap::new(),
};
notifier_host::serve_host_forever_on(&mut host, snw).await.unwrap();
let s = &dbus_state().await;
notifier_host::run_host_forever(&mut host, &s.con, &s.name).await.unwrap();
});
}
42 changes: 17 additions & 25 deletions crates/notifier_host/src/host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,41 +7,33 @@ pub trait Host {
fn remove_item(&mut self, id: &str);
}

/// Register this connection as a StatusNotifierHost.
pub async fn register_host(con: &zbus::Connection) -> zbus::Result<dbus::StatusNotifierWatcherProxy> {
// From <https://www.freedesktop.org/wiki/Specifications/StatusNotifierItem/StatusNotifierHost/>:
//
// Instances of this service are registered on the Dbus session bus, under a name on the
// form org.freedesktop.StatusNotifierHost-id where id is an unique identifier, that keeps
// the names unique on the bus, such as the process-id of the application or another type
// of identifier if more that one StatusNotifierHost is registered by the same process.

// pick a new wellknown_name
/// 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>> {
let pid = std::process::id();
let mut i = 0;
let wellknown_name = loop {
let wellknown_name = format!("org.freedesktop.StatusNotifierHost-{}-{}", pid, i);
let flags = [zbus::fdo::RequestNameFlags::DoNotQueue];

let wellknown = loop {
use zbus::fdo::RequestNameReply::*;
match con.request_name_with_flags(wellknown_name.as_str(), flags.into_iter().collect()).await? {
PrimaryOwner => break wellknown_name,
Exists => {},
AlreadyOwner => {}, // we choose to not use an existing owner, is this correct?
InQueue => panic!("request_name_with_flags returned InQueue even though we specified DoNotQueue"),
};

i += 1;
let wellknown = format!("org.freedesktop.StatusNotifierHost-{}-{}", pid, i);
let wellknown: zbus::names::WellKnownName = wellknown.try_into().expect("generated well-known name is invalid");

let flags = [zbus::fdo::RequestNameFlags::DoNotQueue];
match con.request_name_with_flags(&wellknown, flags.into_iter().collect()).await? {
PrimaryOwner => break wellknown,
Exists => {},
AlreadyOwner => {},
InQueue => unreachable!("request_name_with_flags returned InQueue even though we specified DoNotQueue"),
};
};
Ok(wellknown)
}

pub async fn run_host_forever(host: &mut dyn Host, con: &zbus::Connection, name: &zbus::names::WellKnownName<'_>) -> zbus::Result<()> {
// register ourself to StatusNotifierWatcher
let snw = dbus::StatusNotifierWatcherProxy::new(&con).await?;
snw.register_status_notifier_host(&wellknown_name).await?;

Ok(snw)
}
snw.register_status_notifier_host(&name).await?;

pub async fn serve_host_forever_on(host: &mut dyn Host, snw: dbus::StatusNotifierWatcherProxy<'_>) -> zbus::Result<()> {
enum ItemEvent {
NewItem(dbus::StatusNotifierItemRegistered),
GoneItem(dbus::StatusNotifierItemUnregistered),
Expand Down
5 changes: 3 additions & 2 deletions crates/notifier_host/src/watcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,15 +149,16 @@ impl Watcher {
let service = zbus::names::BusName::Unique(service);

let item = format!("{}{}", service, objpath);
log::info!("new item: {}", item);

{
let mut items = self.items.lock().unwrap();
if !items.insert(item.clone()) {
// we're already tracking them
log::info!("new item: {} (duplicate)", item);
return Ok(())
}
}
log::info!("new item: {}", item);

self.registered_status_notifier_items_changed(&ctxt).await?;
Watcher::status_notifier_item_registered(&ctxt, service.as_ref()).await?;
Expand Down Expand Up @@ -259,7 +260,7 @@ pub async fn watcher_on(con: &zbus::Connection) -> zbus::Result<()> {
RequestNameReply::PrimaryOwner => return Ok(()),
RequestNameReply::Exists => {},
RequestNameReply::AlreadyOwner => {}, // TODO should this return
RequestNameReply::InQueue => panic!("request_name_with_flags returned InQueue even though we specified DoNotQueue"),
RequestNameReply::InQueue => unreachable!("request_name_with_flags returned InQueue even though we specified DoNotQueue"),
}

// TODO should we queue?
Expand Down

0 comments on commit b043801

Please sign in to comment.