aboutsummaryrefslogtreecommitdiffstats
path: root/internal/notify/service.go
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-09-15 18:03:29 +0200
committerDanilo M. <danix@danix.xyz>2026-09-15 18:03:29 +0200
commit03784b2024436827de25515b1d85d9ffdde40745 (patch)
treebbd1d9264a83a5ea6ad359fe699b622ec15386bb /internal/notify/service.go
parent7a6b1c1ada3ba6ca2ca4ed70cf51f2f5f30d7b1e (diff)
downloadnotifyd-03784b2024436827de25515b1d85d9ffdde40745.tar.gz
notifyd-03784b2024436827de25515b1d85d9ffdde40745.zip
feat(notify): materialise notification images
Notify resolves app_icon and image-path theme names, decodes image-data to a PNG under the image directory, and re-publishes the entry with its image path. The daemon advertises body-images, and removeImage refuses to touch a path outside its own directory so a client's screenshot file is never deleted.
Diffstat (limited to 'internal/notify/service.go')
-rw-r--r--internal/notify/service.go27
1 files changed, 24 insertions, 3 deletions
diff --git a/internal/notify/service.go b/internal/notify/service.go
index 2442127..7bfad6e 100644
--- a/internal/notify/service.go
+++ b/internal/notify/service.go
@@ -73,6 +73,9 @@ func (s *Service) Start() error {
if err := s.conn.Export(&control{s}, dbus.ObjectPath(ctrlPath), ctrlIface); err != nil {
return err
}
+ if err := os.RemoveAll(ImagesDir(s.dir)); err != nil {
+ log.Printf("notifyd: clear images: %v", err)
+ }
s.store.Reset()
return nil
}
@@ -83,9 +86,10 @@ func (s *Service) emitClosed(id, reason uint32) {
// GetCapabilities tells clients what the daemon understands. actions and
// body-markup are load-bearing: mail-notify sends actions and escapes its body
-// because the running dunst advertises markup.
+// because the running dunst advertises markup. body-images says the daemon
+// decodes image-data and image-path, not just a static app icon.
func (s *Service) GetCapabilities() ([]string, *dbus.Error) {
- return []string{"actions", "body-markup", "icon-static", "persistence"}, nil
+ return []string{"actions", "body-markup", "body-images", "icon-static", "persistence"}, nil
}
func (s *Service) GetServerInformation() (string, string, string, string, *dbus.Error) {
@@ -99,19 +103,36 @@ func (s *Service) Notify(appName string, replacesID uint32, appIcon, summary, bo
tag := StackTagFromHints(hints)
now := time.Now().UnixMilli()
ms := EffectiveTimeoutMS(expireTimeout, u)
+ // image-data wins over image-path, and its blob cannot be written until
+ // Add has assigned the id the filename is derived from.
+ var pendingImage []byte
n := &Popup{
App: appName,
Summary: summary,
Body: body,
Urgency: u,
- Icon: appIcon,
+ Icon: ResolveIcon(appIcon),
Actions: ParseActions(actions),
Created: now,
}
+ if raw, ok := ImageDataFromHints(hints); ok {
+ if data, err := raw.PNG(); err == nil {
+ pendingImage = data
+ }
+ } else if path, ok := ImagePathFromHints(hints); ok {
+ n.Image = ResolveIcon(path)
+ }
if ms > 0 {
n.Expires = now + ms
}
id, _ := s.store.Add(n, tag, replacesID)
+ if pendingImage != nil {
+ if path, err := WriteImage(s.dir, id, pendingImage); err == nil {
+ s.store.SetImage(id, path)
+ } else {
+ log.Printf("notifyd: write image: %v", err)
+ }
+ }
s.arm(id, ms)
return id, nil
}