cosmic-applets/cosmic-applet-status-area/src/unique_names.rs
Ian Douglas Scott aa438821b9 status-area: Seperate daemon for status notifier daemon
This allows the applet to be restarted on panel configuration changes
without replacing the daemon, or having races between different applet
instances trying to run the watcher.

Otherwise, this should behave similarly to the existing version.

Should fix https://github.com/pop-os/cosmic-panel/issues/284.
2026-02-02 10:36:16 -08:00

107 lines
3.2 KiB
Rust

// Copyright 2026 System76 <info@system76.com>
// SPDX-License-Identifier: GPL-3.0-only
// Based on https://github.com/pop-os/cosmic-comp/blob/master/src/dbus/name_owners.rs,
// but only tracking unique names, and using tokio executor.
use futures::StreamExt;
use std::{
collections::HashSet,
future::{Future, poll_fn},
sync::{Arc, Mutex, Weak},
task::{Context, Poll, Waker},
};
use zbus::{
fdo,
names::{BusName, UniqueName},
};
#[derive(Debug)]
struct Inner {
unique_names: HashSet<UniqueName<'static>>,
stream: fdo::NameOwnerChangedStream,
// Waker from `update_task` is stored, so that task will still be woken after
// polling elsewhere.
waker: Waker,
}
impl Drop for Inner {
fn drop(&mut self) {
// Wake `update_task` so it can terminate
self.waker.wake_by_ref();
}
}
impl Inner {
/// Process all events so far on `stream`, and update `unique_names`.
fn update_if_needed(&mut self) {
let mut context = Context::from_waker(&self.waker);
while let Poll::Ready(val) = self.stream.poll_next_unpin(&mut context) {
let val = val.unwrap();
let args = val.args().unwrap();
match args.name {
BusName::Unique(name) => {
if args.new_owner.is_some() {
self.unique_names.insert(name.to_owned());
} else {
self.unique_names.remove(&name.to_owned());
}
}
BusName::WellKnown(_) => {}
}
}
}
}
/// This task polls the steam regularly, to make sure events on the stream aren't just
/// buffered indefinitely.
fn update_task(inner: Weak<Mutex<Inner>>) -> impl Future<Output = ()> {
poll_fn(move |context| {
if let Some(inner) = inner.upgrade() {
let mut inner = inner.lock().unwrap();
inner.waker = context.waker().clone();
inner.update_if_needed();
// Nothing to do now until waker is invoked
Poll::Pending
} else {
// All strong references have been dropped, so task has nothing left to do.
Poll::Ready(())
}
})
}
#[derive(Clone, Debug)]
pub struct UniqueNames(Arc<Mutex<Inner>>);
impl UniqueNames {
pub async fn new(connection: &zbus::Connection) -> zbus::Result<Self> {
let dbus = fdo::DBusProxy::new(connection).await?;
let stream = dbus.receive_name_owner_changed().await?;
let names = dbus.list_names().await?;
let unique_names = names
.iter()
.filter_map(|n| match n.inner() {
BusName::Unique(name) => Some(name.to_owned()),
BusName::WellKnown(_) => None,
})
.collect();
let inner = Arc::new(Mutex::new(Inner {
unique_names,
stream,
waker: Waker::noop().clone(),
}));
tokio::spawn(update_task(Arc::downgrade(&inner)));
Ok(UniqueNames(inner))
}
#[allow(dead_code)]
pub fn has_unique_name(&self, name: &UniqueName<'_>) -> bool {
let mut inner = self.0.lock().unwrap();
inner.update_if_needed();
inner.unique_names.contains(name)
}
}