notifications

package module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 7, 2026 License: BSD-3-Clause Imports: 10 Imported by: 0

README

notifications — go-freedesktop

ci Go Reference License Go Coverage

A pure-Go (CGO_ENABLED=0) implementation of the freedesktop.org Desktop Notifications Specification: the D-Bus service org.freedesktop.Notifications that desktop applications call — through notify-send, libnotify, GLib.Notification, … — to post notification bubbles.

It is the daemon side. Point notify-send at it and it decodes the Notify call, renders the notification as a go-widgets Toast, and emits the NotificationClosed / ActionInvoked signals a client waits on.

What it does

  • Core (notifications) — a platform-neutral model of a Notification, a defensive decoder for the hint dictionary (urgency, category, resident, transient, desktop-entry, image-path) and the inline image payload (image-data (iiibiiay)*image.RGBA, 3- and 4-channel with rowstride), and the spec constants + truthful capability list.
  • Server (server_linux.go) — exports the four methods (Notify, CloseNotification, GetCapabilities, GetServerInformation) and emits the two signals over github.com/godbus/dbus/v5, forwarding decoded requests to a Handler. A non-Linux stub keeps the package cross-compiling.
  • Toast bridge (notifications/toast) — a pure ToToast mapping a Notification onto a go-widgets Toast (summary + body → lines, urgency → kind, timeout / resident → life, actions → buttons that emit ActionInvoked, inline image / image-path / app_icon → icon), plus a small platform-neutral Daemon that stacks toasts, ticks their lifetimes and drives the close signals with the right reason (expiry / dismissal / close-call).
  • cmd/notifyd — a reference go-widgets daemon wiring it all together.

Advertised capabilities

GetCapabilities reports only what the bridge actually renders:

body   actions   body-markup   icon-static   persistence

GetServerInformation reports ("go-widgets-notifyd", "go-freedesktop", <version>, "1.2").

Quickstart

conn, _ := dbus.ConnectSessionBus()
daemon := toast.NewDaemon(nil, toolkit.DefaultDark(), myIconLookup)
server := notifications.NewServer(conn, daemon)
daemon.SetEmitter(server)
if err := server.Export(); err != nil { // claims org.freedesktop.Notifications
    log.Fatal(err)
}
// ... call daemon.Tick() from your render loop and draw daemon.Toasts().

Or just run the reference daemon:

go run ./cmd/notifyd
# elsewhere:
notify-send -u critical "Build failed" "3 tests red" -A reply=Reply

Transport

Built on github.com/godbus/dbus/v5 (pinned to v5.2.2, CGO-free on linux/darwin). No D-Bus wire codec is reimplemented and nothing shells out to a CLI. The server test suite exercises real susssasa{sv}i marshalling over an in-memory net.Pipe peer-to-peer connection, so it needs no running dbus-daemon.

Scope

In scope: the four org.freedesktop.Notifications methods, both signals, the hint and image-data decoding the bridge renders, and corner-anchored stacking. Out of scope: sound (sound-file / sound-name), action icons, animated (icon-multi) images, and SVG rasterisation of image-path values (a .svg path is reported as a vector icon for the caller to resolve through an icon-theme raster instead) — none of which the Toast surface renders, so none are advertised.

Relationship to wasmdesk / wasmbox

This library is the native-desktop counterpart of the in-browser notification surface in wasmdesk's wasmbox compositor (whose compositor/*_notifications.rb modules render the same notification model in a WASM desktop). The two share the freedesktop notification vocabulary — summary, body, urgency, actions, resident/transient — so a notification means the same thing whether it lands on a go-widgets desktop or in the browser compositor.

Tests & coverage

CGO_ENABLED=0 go test ./...100% statement coverage, including every error branch. The server suite exercises real susssasa{sv}i marshalling over an in-memory net.Pipe peer, so it needs no running dbus-daemon. CI additionally cross-builds on the six supported 64-bit targets (amd64/arm64 natively, riscv64/loong64/ppc64le/s390x under qemu-user).

License

BSD-3-Clause. See LICENSE. Copyright the go-freedesktop/notifications authors.

Documentation

Overview

Package notifications is a pure-Go (CGO_ENABLED=0) implementation of the freedesktop.org Desktop Notifications Specification -- the D-Bus service org.freedesktop.Notifications that desktop applications call (via notify-send, libnotify, GLib.Notification, ...) to post transient notification bubbles.

The package is split into a platform-neutral core (this file plus notification.go, hints.go and image.go) that models a Notification and decodes the wire-level hint dictionary and image payloads, a Linux D-Bus server (server_linux.go) that exports the four spec methods and emits the two spec signals over the owned pure-Go github.com/go-freedesktop/dbus, and a pure bridge (./toast) that turns a decoded Notification into a github.com/go-widgets Toast widget so a go-widgets desktop can act as the notification daemon.

Specification: https://specifications.freedesktop.org/notification-spec/latest/

Index

Constants

View Source
const (
	// BusName is the well-known bus name a notification daemon owns.
	BusName = "org.freedesktop.Notifications"
	// ObjectPath is the object path the service is exported at.
	ObjectPath = "/org/freedesktop/Notifications"
	// Interface is the D-Bus interface the four methods and two signals
	// belong to.
	Interface = "org.freedesktop.Notifications"
)

Well-known D-Bus name, object path and interface of the notification service, as mandated by the specification.

View Source
const (
	MethodNotify             = "Notify"
	MethodCloseNotification  = "CloseNotification"
	MethodGetCapabilities    = "GetCapabilities"
	MethodGetServerInfo      = "GetServerInformation"
	SignalNotificationClosed = "NotificationClosed"
	SignalActionInvoked      = "ActionInvoked"
)

The four method member names of the interface.

View Source
const (
	ServerName  = "go-widgets-notifyd"
	VendorName  = "go-freedesktop"
	Version     = "0.2.0"
	SpecVersion = "1.2"
)

Server-information values reported by GetServerInformation. SpecVersion is the version of the notification specification the server implements.

Variables

View Source
var (
	// ErrBadImageData is returned by decodeImageData when the inline
	// image-data hint is not a well-formed (iiibiiay) payload.
	ErrBadImageData = errors.New("notifications: malformed image-data hint")
	// ErrVectorIcon is returned by LoadImagePath for a scalable (SVG) path:
	// this pure-raster decoder does not rasterise vector art, so the caller
	// should fall back to an icon-theme raster lookup or drop the image.
	ErrVectorIcon = errors.New("notifications: vector (svg) icon path not rasterised")
)

Errors returned by the image decoders.

View Source
var ErrNameTaken = errors.New("notifications: another daemon already owns " + BusName)

ErrNameTaken is returned by Export when another process already owns the org.freedesktop.Notifications name (a notification daemon is already running).

Functions

func Capabilities

func Capabilities() []string

Capabilities returns the notification capabilities this implementation advertises through GetCapabilities. The list is deliberately TRUTHFUL: it names only what the ./toast bridge actually renders --

  • "body" : the notification carries a body text distinct from the summary (rendered as a second Toast line);
  • "actions" : action buttons are rendered and ActionInvoked is emitted when one is clicked;
  • "body-markup" : the body may contain the small hypertext-subset markup the spec defines (the bridge strips the tags to plain text);
  • "icon-static" : a static (non-animated) icon or image is rendered beside the text;
  • "persistence" : a notification with an infinite / resident lifetime stays on screen until dismissed (a sticky Toast).

Capabilities we do NOT render (sound, action-icons, icon-multi, ...) are omitted so a client is never misled about what the daemon can do.

func DecodeHints

func DecodeHints(n *Notification, hints map[string]dbus.Variant)

DecodeHints folds the Notify hint dictionary into n. Every field is decoded defensively: a missing key leaves the corresponding field at its zero value, and a value of the wrong D-Bus type is ignored rather than propagated as an error, so a malformed client can never break decoding. A nil map is a no-op.

func LoadImagePath

func LoadImagePath(path string) (*image.RGBA, error)

LoadImagePath decodes an on-disk image referenced by a notification "image-path" hint (or a resolved app_icon path) into an *image.RGBA. A "file://" URI prefix is accepted and stripped. Raster formats (PNG, JPEG, GIF) are decoded through the standard library; a ".svg" path returns ErrVectorIcon because this decoder is deliberately pure-raster and drags in no SVG rasteriser. A missing or undecodable file returns the underlying error.

Types

type Action

type Action struct {
	Key   string
	Label string
}

Action is one entry of the Notify "actions" array: a machine Key the client keys ActionInvoked on, plus the human-readable Label rendered on the button. The reserved key "default" marks the action taken on a plain activation (a click on the notification body rather than a specific button).

func (Action) IsDefault

func (a Action) IsDefault() bool

IsDefault reports whether this is the reserved "default" action.

type CloseReason

type CloseReason uint32

CloseReason is the reason code carried by the NotificationClosed signal, as enumerated by the specification.

const (
	// ReasonExpired: the notification's timeout elapsed.
	ReasonExpired CloseReason = 1
	// ReasonDismissed: the user dismissed the notification.
	ReasonDismissed CloseReason = 2
	// ReasonClosed: the notification was closed by a CloseNotification call.
	ReasonClosed CloseReason = 3
	// ReasonUndefined: closed for some other/undefined reason.
	ReasonUndefined CloseReason = 4
)

func (CloseReason) String

func (r CloseReason) String() string

String names the close reason for diagnostics.

type Handler

type Handler interface {
	OnNotify(n *Notification) uint32
	OnClose(id uint32, reason CloseReason)
}

Handler is the application-side callback set a Server drives. OnNotify is invoked for each incoming Notify (the Notification already carries its server-assigned, non-zero ID); it renders the notification and returns the id the caller should see (normally n.ID). OnClose is invoked when a notification is closed -- by a CloseNotification call, an expiry or a dismissal -- so the handler can retire the on-screen surface.

type Notification

type Notification struct {
	// ID is the notification id assigned by the server (0 until assigned).
	ID uint32
	// AppName is the optional application name (first Notify argument).
	AppName string
	// ReplacesID is the id of a notification this one replaces (0 = none).
	ReplacesID uint32
	// AppIcon is the app_icon argument: an icon name (to resolve through an
	// icon theme) or a file/URI path.
	AppIcon string
	// Summary is the single-line title (required by the spec).
	Summary string
	// Body is the optional multi-line body; it may carry the spec's
	// hypertext-subset markup.
	Body string
	// Actions are the parsed action pairs, in order.
	Actions []Action
	// ExpireMS is the requested timeout in milliseconds: -1 = server
	// default, 0 = never expire (persist until dismissed / closed).
	ExpireMS int32

	// Urgency is the decoded "urgency" hint (defaults to UrgencyNormal).
	Urgency Urgency
	// Category is the decoded "category" hint (may be empty).
	Category string
	// DesktopEntry is the decoded "desktop-entry" hint (may be empty).
	DesktopEntry string
	// Resident is the decoded "resident" hint: the notification is not
	// removed after an action is invoked.
	Resident bool
	// Transient is the decoded "transient" hint: bypass any persistence.
	Transient bool

	// Image is the inline image supplied through an "image-data" /
	// "image_data" / "icon_data" hint (nil when none was supplied).
	Image *image.RGBA
	// ImagePath is the decoded "image-path" / "image_path" hint: a path or
	// icon name pointing at an image on disk (may be empty).
	ImagePath string
}

Notification is a decoded org.freedesktop.Notifications.Notify request: the spec's positional arguments plus everything unpacked from its hint dictionary. It is the platform-neutral value the server hands to a Handler and the ./toast bridge turns into a Toast.

func Decode

func Decode(appName string, replacesID uint32, appIcon, summary, body string,
	actions []string, hints map[string]dbus.Variant, expireMS int32) *Notification

Decode assembles a Notification from the raw positional arguments of a Notify call and its hint dictionary. It never fails: malformed hints are ignored field by field (see DecodeHints) so a hostile or buggy client can never break the daemon.

func (*Notification) Sticky

func (n *Notification) Sticky() bool

Sticky reports whether the notification should persist on screen until the user acts, rather than auto-expiring: an explicit zero timeout, a resident hint, or critical urgency all make it sticky (unless it is transient).

type Server

type Server struct {
	// contains filtered or unexported fields
}

Server exports the org.freedesktop.Notifications service on a D-Bus connection and forwards decoded requests to a Handler. It is safe for concurrent use: the id allocator is mutex-guarded.

The three fallible D-Bus operations the server performs -- exporting the method object, claiming the well-known name, and emitting a signal -- are indirected through function fields (exportFn, requestNameFn, emitFn) so the method handlers can be unit-tested as plain Go calls, with injected fakes covering every branch, without ever bringing up a bus or a connection.

func NewServer

func NewServer(conn *dbus.Conn, h Handler) *Server

NewServer returns a Server that will export the notification service on conn and forward requests to h.

func (*Server) EmitActionInvoked

func (s *Server) EmitActionInvoked(id uint32, key string) error

EmitActionInvoked emits the ActionInvoked signal binding action key to id.

func (*Server) EmitClosed

func (s *Server) EmitClosed(id uint32, reason CloseReason) error

EmitClosed emits the NotificationClosed signal for id with the given reason.

func (*Server) Export

func (s *Server) Export() error

Export publishes the four notification methods on conn at ObjectPath and claims the well-known BusName. It returns ErrNameTaken if the name is already owned, or the underlying error if exporting or the name request fails.

The org.freedesktop.DBus.Introspectable interface is served automatically by the connection (it reflects the exported methods into an introspection document), so no introspection node is registered here.

type Urgency

type Urgency byte

Urgency is the freedesktop "urgency" hint: a byte ranking how much the notification demands attention. The spec defines exactly three levels.

const (
	// UrgencyLow is background information the user need not act on.
	UrgencyLow Urgency = 0
	// UrgencyNormal is the default level for ordinary notifications.
	UrgencyNormal Urgency = 1
	// UrgencyCritical must stay visible until the user acts (it maps to a
	// resident, error-coloured Toast).
	UrgencyCritical Urgency = 2
)

func (Urgency) String

func (u Urgency) String() string

String names the urgency level for diagnostics.

Directories

Path Synopsis
cmd
notifyd command
Command notifyd is the reference go-widgets notification daemon: it owns the org.freedesktop.Notifications name on the session bus and renders each incoming notification as a go-widgets Toast, driven by the github.com/go-freedesktop/notifications server and its ./toast bridge.
Command notifyd is the reference go-widgets notification daemon: it owns the org.freedesktop.Notifications name on the session bus and renders each incoming notification as a go-widgets Toast, driven by the github.com/go-freedesktop/notifications server and its ./toast bridge.
Package toast bridges a decoded freedesktop github.com/go-freedesktop/notifications.Notification onto a github.com/go-widgets/toolkit.Toast widget, so a go-widgets desktop can render notification bubbles and act as the notification daemon.
Package toast bridges a decoded freedesktop github.com/go-freedesktop/notifications.Notification onto a github.com/go-widgets/toolkit.Toast widget, so a go-widgets desktop can render notification bubbles and act as the notification daemon.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL