aboutsummaryrefslogtreecommitdiffstats
path: root/internal/notify
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-10-19 09:15:53 +0200
committerDanilo M. <danix@danix.xyz>2026-10-19 09:15:53 +0200
commit1e70e552db6790f2fd07fdd1721bf6cf76e0c6b3 (patch)
tree29175225bf2297b07ef9652790978dcb337604b3 /internal/notify
parent75acc11af55b4efe0ea095a52a3916b13639c2cd (diff)
downloadnotifyd-1e70e552db6790f2fd07fdd1721bf6cf76e0c6b3.tar.gz
notifyd-1e70e552db6790f2fd07fdd1721bf6cf76e0c6b3.zip
feat(notify): take the per-urgency default timeout from config
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'internal/notify')
-rw-r--r--internal/notify/policy.go26
-rw-r--r--internal/notify/policy_test.go19
-rw-r--r--internal/notify/service.go8
3 files changed, 32 insertions, 21 deletions
diff --git a/internal/notify/policy.go b/internal/notify/policy.go
index 5c70390..8161ea3 100644
--- a/internal/notify/policy.go
+++ b/internal/notify/policy.go
@@ -23,26 +23,28 @@ const (
)
// DefaultTimeoutMS is the balloon lifetime for an urgency when the client
-// leaves the choice to the server (expire_timeout -1). Critical never expires,
-// which is 0 here.
-func DefaultTimeoutMS(u Urgency) int64 {
+// leaves the choice to the server (expire_timeout -1), from the config's
+// seconds. 0 is never.
+func DefaultTimeoutMS(u Urgency, t Timeouts) int64 {
switch u {
- case Low, Normal:
- return 10_000
+ case Low:
+ return int64(t.Low) * 1000
+ case Critical:
+ return int64(t.Critical) * 1000
default:
- return 0
+ return int64(t.Normal) * 1000
}
}
// EffectiveTimeoutMS resolves the client's expire_timeout per the freedesktop
-// spec: -1 means the server decides (the urgency default), 0 means never, and
-// anything positive is milliseconds and wins. The spec is precise about the
-// direction and libnotify's default is -1, so getting it backwards would make
-// every plain notify-send immortal.
-func EffectiveTimeoutMS(expire int32, u Urgency) int64 {
+// spec: -1 means the server decides (the configured default), 0 means never,
+// and anything positive is milliseconds and wins. The spec is precise about
+// the direction and libnotify's default is -1, so getting it backwards would
+// make every plain notify-send immortal.
+func EffectiveTimeoutMS(expire int32, u Urgency, t Timeouts) int64 {
switch {
case expire == -1:
- return DefaultTimeoutMS(u)
+ return DefaultTimeoutMS(u, t)
case expire == 0:
return 0
default:
diff --git a/internal/notify/policy_test.go b/internal/notify/policy_test.go
index 378c573..a6ca5a5 100644
--- a/internal/notify/policy_test.go
+++ b/internal/notify/policy_test.go
@@ -40,22 +40,27 @@ func TestUrgencyFromHints(t *testing.T) {
}
func TestEffectiveTimeoutMS(t *testing.T) {
+ def := DefaultConfig().Timeouts
+ custom := Timeouts{Low: 3, Normal: 7, Critical: 60}
cases := []struct {
name string
expire int32
u Urgency
+ t Timeouts
want int64
}{
- {"minus one uses low default", -1, Low, 10_000},
- {"minus one uses normal default", -1, Normal, 10_000},
- {"minus one critical never", -1, Critical, 0},
- {"zero never", 0, Normal, 0},
- {"explicit wins", 3_000, Normal, 3_000},
- {"sub second exact", 1, Normal, 1},
+ {"minus one uses low default", -1, Low, def, 10_000},
+ {"minus one uses normal default", -1, Normal, def, 10_000},
+ {"minus one critical never", -1, Critical, def, 0},
+ {"minus one uses configured low", -1, Low, custom, 3_000},
+ {"minus one uses configured critical", -1, Critical, custom, 60_000},
+ {"zero never", 0, Normal, custom, 0},
+ {"explicit wins", 3_000, Normal, custom, 3_000},
+ {"sub second exact", 1, Normal, def, 1},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
- if got := EffectiveTimeoutMS(c.expire, c.u); got != c.want {
+ if got := EffectiveTimeoutMS(c.expire, c.u, c.t); got != c.want {
t.Errorf("EffectiveTimeoutMS(%d, %q) = %d, want %d", c.expire, c.u, got, c.want)
}
})
diff --git a/internal/notify/service.go b/internal/notify/service.go
index a2bf103..b29a783 100644
--- a/internal/notify/service.go
+++ b/internal/notify/service.go
@@ -39,10 +39,11 @@ type Service struct {
mu sync.Mutex
timers map[uint32]*time.Timer
+ cfg Config
}
func NewService(conn *dbus.Conn, dir string) *Service {
- s := &Service{conn: conn, dir: dir, timers: map[uint32]*time.Timer{}}
+ s := &Service{conn: conn, dir: dir, timers: map[uint32]*time.Timer{}, cfg: DefaultConfig()}
s.store = NewStore(s.emitClosed, func(live, history []Popup) {
if err := Publish(s.dir, live, history); err != nil {
log.Printf("notifyd: publish: %v", err)
@@ -102,7 +103,10 @@ func (s *Service) Notify(appName string, replacesID uint32, appIcon, summary, bo
u := UrgencyFromHints(hints)
tag := StackTagFromHints(hints)
now := time.Now().UnixMilli()
- ms := EffectiveTimeoutMS(expireTimeout, u)
+ s.mu.Lock()
+ timeouts := s.cfg.Timeouts
+ s.mu.Unlock()
+ ms := EffectiveTimeoutMS(expireTimeout, u, timeouts)
// Spec priority is image-data > image-path > the deprecated icon_data. A
// raw blob cannot be written until Add has assigned the id the filename is
// derived from, so any raw tier is held in pendingImage.