Skip to content
AtlasOSFramework API
Browse the docs

notify

Desktop notifications over org.freedesktop.Notifications, sent the way KNotification sends them so Plasma groups them under the app and honours the user's per-event choice, with the notify feature.

The notify module is the one way an Atlas app sends a desktop notification. It needs the notify feature of atlas-framework-system. It sends KDE's hints (desktop-entry, x-kde-appname, x-kde-eventId), so Plasma groups the notifications under the app and honours the user's choice per event in System Settings.

Example

use atlas_framework_system::notify::{DEFAULT_ACTION, Note, Notifier, escape};

let app = atlas_framework_core::app_info! {
    name: "Atlas Updater",
    id: "net.eterneon.atlas.updater",
    repo: "atlasos-updater",
};
let notifier = Notifier::new(&app);
let mut note = Note::new("updateStaged", "Update ready", escape("44.20261001"));
note.actions.push((DEFAULT_ACTION, "Open Atlas Updater".into()));
notifier.send_blocking(&note)?; // from a worker thread, when nobody acts on it

In async code, use notifier.send(&conn, &note).await on a session-bus zbus::Connection.

The notifyrc file

The names the app gives KNotification come from its AppInfo: the component (the notifyrc's name) is AppInfo::short_name(), the desktop entry and the icon are the app ID, and the app name is the display name. Each app ships <short name>.notifyrc (atlas- and the last part of the app ID) in /usr/share/knotifications6/ with DesktopEntry=<app id> and one camelCase [Event/<eventId>] per kind of notification, whose Action= says Popup. The app template has one. Plasma writes the user's choice to ~/.config/<component>.notifyrc ([Event/<id>] Action=, a |-separated list); without a user file, an [Event/<id>] group or an Action key there, the event pops up.

AtlasOS rules for notifications

  • Notify only when the user can act on it, or must know: not for progress or success they did not wait for.
  • Popups only, no sounds. Leave urgency as None or Normal (High at most, never Critical); persistent only when ignoring the notification has consequences (a restart is due).
  • Actions are short verbs that open the right page ("Restart now", "Open Atlas Updater"), never "OK" or "Dismiss". DEFAULT_ACTION is the click on the notification itself.
  • Never from root to a user's session. A system service records the event, and the user-session app notices it and notifies.
  • Do Not Disturb is Plasma's: do not second-guess it.
  • Everything that came from outside (an app name, an error, a version) goes through escape before it goes into Note::text.

Errors and limits

  • Calls to the server give up after 10 seconds. The server may still show the notification after that, so do not retry blindly.
  • With no notification service (or no session bus), the error reads "No notification service is running".
  • send and close need a Tokio runtime with the time driver (#[tokio::main] and enable_all() have it); without one the timeout panics.
  • send_blocking runs on a thread of its own with a runtime of its own, so it is safe to call inside an existing Tokio runtime's spawn_blocking, but not from the UI thread or from async code.
  • An event ID that is not valid is refused with an error. An icon that is not valid is replaced by the app icon and logged.

Note

#[derive(Debug, Clone)] pub struct Note

Field Type Description
event &'static str The notifyrc event (updateStaged, restartSoon, ...): camelCase, letters and digits only
title String
text String Body markup: escape anything that came from outside
icon String Empty (the app's own icon), an icon name (letters, digits and ._+-) or an absolute path without ... Anything else is replaced by the app's icon
actions Vec<(&'static str, String)> (key, label) pairs. DEFAULT_ACTION is the click on the notification itself
urgency Option<Urgency> None leaves the server's default
persistent bool Stays until the user acts (no timeout)

Note::new(event: &'static str, title: impl Into<String>, text: impl Into<String>) -> Note is a plain notification: the app's icon, no actions, the server's default urgency and timeout.

#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Urgency { Normal, High, Critical }. High is KNotification's HighUrgency: the spec has no such level, so it goes out as normal. Critical goes out as level 2.

Notifier

#[derive(Debug, Clone, PartialEq, Eq)] pub struct Notifier

Method Description
fn new(app: &AppInfo) -> Notifier Takes the names from the app's AppInfo
fn component(&self) -> &str The notifyrc's name: the app's short_name()
fn desktop_entry(&self) -> &str The desktop file's name, which is the app ID
fn app_name(&self) -> &str The display name
fn app_icon(&self) -> &str The app's own icon: its ID
fn popup_enabled(&self, event: &str) -> bool Whether event pops up, from the user's notifyrc. An event ID that is not valid never pops up
async fn send(&self, conn: &zbus::Connection, n: &Note) -> zbus::Result<Option<Sent>> Sends n. Ok(None) means the user turned this event's popup off
fn send_blocking(&self, n: &Note) -> Result<(), String> send over a new session-bus connection, blocking, for a notification nobody acts on (its actions are not followed)

#[derive(Debug, Clone, PartialEq, Eq)] pub struct Sent { pub id: u32, pub server: Option<String> }: id is the server's notification ID, and server is the server's unique bus name (only its signals are about id).

Functions and constants

Name Signature or value Description
SERVICE, PATH, INTERFACE "org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications" The notification server's bus name, object path and interface
DEFAULT_ACTION "default" The key the server sends when the notification itself is clicked
escape pub fn escape(s: &str) -> String Escapes text for the body, which the server reads as markup: &, <, >, " and ' become entities, and control characters other than line feed and tab become spaces
event_id_ok pub fn event_id_ok(event: &str) -> bool 1 to 64 ASCII letters and digits, not starting with a digit
icon_ok pub fn icon_ok(icon: &str) -> bool Empty, an icon name, or an absolute path without .. and control characters. A URL or relative path is not
close pub async fn close(conn: &zbus::Connection, id: u32) -> zbus::Result<()> Closes notification id. Gives up after 10 seconds