diff options
| author | Danilo M. <danix@danix.xyz> | 2026-10-19 09:52:38 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-10-19 09:52:38 +0200 |
| commit | 10868cab37e7dadf247d5da78f5eb5e69bcb6c9d (patch) | |
| tree | 7bcc4b8986ae3a0d3551e38da4ba94d1bcc01c0f /shared | |
| parent | dad46796c841ada831f40e9ef27efd91a9897616 (diff) | |
| download | quickshell-10868cab37e7dadf247d5da78f5eb5e69bcb6c9d.tar.gz quickshell-10868cab37e7dadf247d5da78f5eb5e69bcb6c9d.zip | |
notifications: resolve the sound keys and choose a balloon's sound
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'shared')
| -rw-r--r-- | shared/NotifyConfig.js | 19 | ||||
| -rw-r--r-- | shared/test_notify_config.js | 18 |
2 files changed, 35 insertions, 2 deletions
diff --git a/shared/NotifyConfig.js b/shared/NotifyConfig.js index ff522c4..143168f 100644 --- a/shared/NotifyConfig.js +++ b/shared/NotifyConfig.js @@ -13,7 +13,7 @@ // ~/.config/notifyd/config.json, resolved over its defaults. notifyd reads // the same file for timeouts and caps (its config.go clamps the same way); -// the balloon keys are this side's alone. Tested by test_notify_config.js. +// the balloon keys and `sounds` are this side's alone. Tested by test_notify_config.js. const corners = ["top-left", "top-right", "bottom-left", "bottom-right"]; @@ -25,6 +25,8 @@ function defaults() { balloons: { screen: "DP-1", corner: "bottom-right" }, dndPassCritical: true, muted: [], + sounds: { low: "", normal: "", critical: "" }, + soundTheme: "freedesktop", }; } @@ -38,6 +40,7 @@ function resolve(raw) { if (!raw || typeof raw !== "object") return d; const t = raw.timeouts || {}; const b = raw.balloons || {}; + const s = raw.sounds || {}; return { timeouts: { low: seconds(t.low, d.timeouts.low), @@ -52,6 +55,12 @@ function resolve(raw) { }, dndPassCritical: typeof raw.dndPassCritical === "boolean" ? raw.dndPassCritical : d.dndPassCritical, muted: Array.isArray(raw.muted) ? raw.muted.filter(a => typeof a === "string") : d.muted, + sounds: { + low: typeof s.low === "string" ? s.low : d.sounds.low, + normal: typeof s.normal === "string" ? s.normal : d.sounds.normal, + critical: typeof s.critical === "string" ? s.critical : d.sounds.critical, + }, + soundTheme: typeof raw.soundTheme === "string" && raw.soundTheme !== "" ? raw.soundTheme : d.soundTheme, }; } @@ -61,3 +70,11 @@ function withheld(cfg, n, dnd) { if (cfg.muted.indexOf(n.app) >= 0) return true; return dnd && !(n.urgency === "critical" && cfg.dndPassCritical); } + +// The file a balloon plays, "" for none: the app's suppress-sound wins, then +// the app's own sound (notifyd resolved its hints), then the urgency's. +function soundFor(cfg, n) { + if (n.silent) return ""; + if (n.sound) return n.sound; + return cfg.sounds[n.urgency] || ""; +} diff --git a/shared/test_notify_config.js b/shared/test_notify_config.js index b58fbde..c7bc2cc 100644 --- a/shared/test_notify_config.js +++ b/shared/test_notify_config.js @@ -18,7 +18,7 @@ const assert = require("assert"); const src = fs.readFileSync(__dirname + "/NotifyConfig.js", "utf8") .replace(/^\.pragma library$/m, ""); -const C = new Function(src + "\nreturn { defaults, resolve, withheld };")(); +const C = new Function(src + "\nreturn { defaults, resolve, withheld, soundFor };")(); // Nothing, or garbage, is the defaults. assert.deepStrictEqual(C.resolve(undefined), C.defaults()); @@ -48,4 +48,20 @@ assert.strictEqual(C.withheld(cfg, n("ok", "normal"), true), true); assert.strictEqual(C.withheld(cfg, n("ok", "critical"), true), false); assert.strictEqual(C.withheld(C.resolve({ dndPassCritical: false }), n("ok", "critical"), true), true); +// Sounds: strings only, empty theme falls back. +const s = C.resolve({ sounds: { low: "/a.oga", normal: 3 }, soundTheme: "" }); +assert.deepStrictEqual(s.sounds, { low: "/a.oga", normal: "", critical: "" }); +assert.strictEqual(s.soundTheme, "freedesktop"); +assert.strictEqual(C.resolve({ soundTheme: "mine" }).soundTheme, "mine"); + +// soundFor: silent beats everything, the app's sound beats the urgency's. +const sc = C.resolve({ sounds: { low: "", normal: "/n.oga", critical: "/c.oga" } }); +const sn = (urgency, sound, silent) => ({ app: "x", urgency, sound, silent }); +assert.strictEqual(C.soundFor(sc, sn("normal", "", false)), "/n.oga"); +assert.strictEqual(C.soundFor(sc, sn("low", "", false)), ""); +assert.strictEqual(C.soundFor(sc, sn("low", "/app.oga", false)), "/app.oga"); +assert.strictEqual(C.soundFor(sc, sn("critical", "/app.oga", true)), ""); +// An old daemon publishes neither field. +assert.strictEqual(C.soundFor(sc, { app: "x", urgency: "critical" }), "/c.oga"); + console.log("ok"); |
