From 96b359b8f9c7dbfea7ac4f2ff367a0385d72622c Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Mon, 19 Oct 2026 09:52:58 +0200 Subject: notifications: play a sound for a new balloon pw-play, once per new or replaced notification, only when the balloon would show: the drawer, snooze, mute and DND that withhold the balloon silence it too. A restart records the queue without playing it. Co-Authored-By: Claude Opus 5.5 --- notifications/Balloons.qml | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'notifications') diff --git a/notifications/Balloons.qml b/notifications/Balloons.qml index b02099c..1b3cce8 100644 --- a/notifications/Balloons.qml +++ b/notifications/Balloons.qml @@ -13,6 +13,7 @@ import Quickshell import Quickshell.Io import Quickshell.Wayland import QtQuick +import "NotifyConfig.js" as NC // The balloon stack, in the corner and on the screen notifyd's config.json // names (bottom-right of DP-1 by default), over conky. @@ -37,6 +38,39 @@ Scope { // (reloading icons, resetting hover) four times a second. readonly property var live: Notify.queue || [] + // A sound per new or replaced notification (replace keeps the id and + // changes created), played only when its balloon would show now: the + // same drawer, snooze, mute and DND checks as NotificationBalloon. The + // first read after a start only records what is there, so a restart is + // silent. seen is rebuilt from the queue each time, so it never grows. + property var seen: ({}) + property bool primed: false + Connections { + target: Notify + function onQueueChanged() { + const next = {}; + let file = ""; + for (const n of Notify.queue || []) { + const key = n.id + ":" + n.created; + next[key] = true; + if (!root.primed || root.seen[key] || file !== "") continue; + if (Notify.drawerOpen || Notify.snoozeUntil > Date.now()) continue; + if (NC.withheld(Notify.config, n, Notify.dnd)) continue; + file = NC.soundFor(Notify.config, n); + } + root.seen = next; + root.primed = true; + if (file !== "") { + // ponytail: one player, a new sound cuts the last; a queue of + // sounds if bursts ever need every one heard. + player.command = ["pw-play", file]; + player.running = false; + player.running = true; + } + } + } + Process { id: player } + // The window border gradient, from the same file Hyprland reads, so the // balloons wear the windows' border. udt-accent writes it as Hyprland // colours, `rgb(rrggbb)` or `rgba(rrggbbaa)`; Qt wants `#aarrggbb`. The -- cgit v1.2.3