From 008e4d980cc1a8ab79b0b19c9a6b7da0b2df2711 Mon Sep 17 00:00:00 2001 From: The_miro Date: Fri, 24 Jul 2026 15:06:55 +0200 Subject: [PATCH] feat(hyprdrive,hyprlua): notification action buttons for timer/screenrec timer-run now offers Dismiss/Snooze 2m/Snooze 5m buttons via notify-send -A (beacon/mnotifd both implement the freedesktop Actions spec) and rings the alarm on a loop until the notification resolves, instead of playing once and going silent. screenrec.sh gets a real "Stop Recording" button in the same vein, replacing a broken anonymous --action= and a `$nid=` assignment bug that never actually captured the notification id; also fixes hyprlua's stale `dunstctl close` call left over from the mnotifd migration (dunst is no longer running). Co-Authored-By: Claude Sonnet 5 --- desktopenvs/hyprdrive/scripts/screenrec.sh | 28 +++++++++---- desktopenvs/hyprdrive/scripts/timer-run | 42 ++++++++++++++----- desktopenvs/hyprlua/scripts/screenrec.sh | 34 ++++++++++----- desktopenvs/hyprlua/scripts/timer-run | 48 ++++++++++++++++------ 4 files changed, 110 insertions(+), 42 deletions(-) diff --git a/desktopenvs/hyprdrive/scripts/screenrec.sh b/desktopenvs/hyprdrive/scripts/screenrec.sh index 34f3fb5..6061b8b 100755 --- a/desktopenvs/hyprdrive/scripts/screenrec.sh +++ b/desktopenvs/hyprdrive/scripts/screenrec.sh @@ -1,5 +1,9 @@ #!/bin/bash - +# screenrec.sh — toggle screen recording via wf-recorder. +# Press the bound key once to start (region picked via slurp): a +# "Stop Recording" button appears on the notification and the script blocks +# on it. Either clicking that button, or pressing the same keybind again, +# ends the recording. endrec () { killall -s SIGINT wf-recorder @@ -14,16 +18,22 @@ endrec () { mkdir -p ~/Videos statecon=$( pidof wf-recorder ) - outfile="$HOME/Videos/$(date +'%Y%m%d%H%M%S').mp4" -#$outfile nid="" -if [ "$statecon" == '' ]; then - wf-recorder -g "$(slurp)" -f $outfile & - $nid=$(notify-send -p -u critical -t 0 "recording started" --action="goto endrecplace" -) + +if [ -z "$statecon" ]; then + wf-recorder -g "$(slurp)" -f "$outfile" & + + # --id-fd keeps the notification id off stdout so -A's chosen action + # (the only thing we read back) isn't mixed in with it. + nidfile=$(mktemp) + action=$(notify-send -u critical -t 0 --id-fd=3 \ + -A "stop=Stop Recording" \ + "recording started" "output: $outfile" 3>"$nidfile") + nid=$(<"$nidfile") + rm -f "$nidfile" + + [ "$action" == "stop" ] && endrec else endrec fi - -pidof wf-recorder && endrec diff --git a/desktopenvs/hyprdrive/scripts/timer-run b/desktopenvs/hyprdrive/scripts/timer-run index e97472a..ccc746b 100755 --- a/desktopenvs/hyprdrive/scripts/timer-run +++ b/desktopenvs/hyprdrive/scripts/timer-run @@ -1,7 +1,8 @@ #!/usr/bin/env bash # timer-run [label] # Runs entirely in background. No terminal needed after launch. -# Sends a dunst notification + audio beep when done. +# Sends a desktop notification (with Dismiss / Snooze 2m / Snooze 5m action +# buttons, via beacon) + audio beep when done. # # Install: ~/.config/scripts/timer-run (companion: timer-pick in same dir) @@ -60,20 +61,41 @@ if [[ -z "${DBUS_SESSION_BUS_ADDRESS:-}" ]]; then done fi +# ── audio alert (rings on a loop until dismissed/snoozed) ───────────────────── +_ALARM_SOUND="/usr/share/sounds/freedesktop/stereo/alarm-clock-elapsed.oga" + +play_alarm() { + if command -v paplay &>/dev/null && [[ -f "$_ALARM_SOUND" ]]; then + paplay "$_ALARM_SOUND" 2>/dev/null + elif command -v canberra-gtk-play &>/dev/null; then + canberra-gtk-play --id=alarm-clock-elapsed 2>/dev/null + fi +} + +( while true; do play_alarm; sleep 1; done ) & +RING_PID=$! + +# ── notification with action buttons ────────────────────────────────────────── +# -A implies --wait: notify-send blocks until the card is closed or an action +# is invoked, printing the chosen action's id to stdout. beacon supports the +# spec's Actions/ActionInvoked, so this works without any custom hint. +ACTION="" if command -v notify-send &>/dev/null; then - notify-send \ + ACTION=$(notify-send \ --urgency=critical \ --expire-time=0 \ --icon=alarm-timer \ + -A "dismiss=Dismiss" \ + -A "snooze2=Snooze 2m" \ + -A "snooze5=Snooze 5m" \ "$NOTIF_SUMMARY" \ - "$NOTIF_BODY" + "$NOTIF_BODY") fi -# ── audio alert ─────────────────────────────────────────────────────────────── -_ALARM_SOUND="/usr/share/sounds/freedesktop/stereo/alarm-clock-elapsed.oga" +kill "$RING_PID" 2>/dev/null +wait "$RING_PID" 2>/dev/null -if command -v paplay &>/dev/null && [[ -f "$_ALARM_SOUND" ]]; then - paplay "$_ALARM_SOUND" 2>/dev/null -elif command -v canberra-gtk-play &>/dev/null; then - canberra-gtk-play --id=alarm-clock-elapsed 2>/dev/null -fi +case "$ACTION" in + snooze2) exec bash "$0" 120 "$LABEL" ;; + snooze5) exec bash "$0" 300 "$LABEL" ;; +esac diff --git a/desktopenvs/hyprlua/scripts/screenrec.sh b/desktopenvs/hyprlua/scripts/screenrec.sh index 9f63495..c3d3e45 100755 --- a/desktopenvs/hyprlua/scripts/screenrec.sh +++ b/desktopenvs/hyprlua/scripts/screenrec.sh @@ -1,25 +1,39 @@ #!/bin/bash - +# screenrec.sh — toggle screen recording via wf-recorder. +# Press the bound key once to start (region picked via slurp): a +# "Stop Recording" button appears on the notification and the script blocks +# on it. Either clicking that button, or pressing the same keybind again, +# ends the recording. endrec () { killall -s SIGINT wf-recorder - dunstctl close $nid + # mnotifd (the notif daemon) has no dunstctl; close the "recording started" + # card via the freedesktop CloseNotification method instead. + [ -n "$nid" ] && gdbus call --session --dest org.freedesktop.Notifications \ + --object-path /org/freedesktop/Notifications \ + --method org.freedesktop.Notifications.CloseNotification "$nid" >/dev/null 2>&1 notify-send "recording ended - output to $outfile" } mkdir -p ~/Videos statecon=$( pidof wf-recorder ) - outfile="$HOME/Videos/$(date +'%Y%m%d%H%M%S').mp4" -#$outfile nid="" -if [ "$statecon" == '' ]; then - wf-recorder -g "$(slurp)" -f $outfile & - $nid=$(notify-send -p -u critical -t 0 "recording started" --action="goto endrecplace" -) + +if [ -z "$statecon" ]; then + wf-recorder -g "$(slurp)" -f "$outfile" & + + # --id-fd keeps the notification id off stdout so -A's chosen action + # (the only thing we read back) isn't mixed in with it. + nidfile=$(mktemp) + action=$(notify-send -u critical -t 0 --id-fd=3 \ + -A "stop=Stop Recording" \ + "recording started" "output: $outfile" 3>"$nidfile") + nid=$(<"$nidfile") + rm -f "$nidfile" + + [ "$action" == "stop" ] && endrec else endrec fi - -pidof wf-recorder && endrec diff --git a/desktopenvs/hyprlua/scripts/timer-run b/desktopenvs/hyprlua/scripts/timer-run index f09ce97..1f8df49 100755 --- a/desktopenvs/hyprlua/scripts/timer-run +++ b/desktopenvs/hyprlua/scripts/timer-run @@ -1,7 +1,8 @@ #!/usr/bin/env bash # timer-run [label] # Runs entirely in background. No terminal needed after launch. -# Sends a dunst notification + audio beep when done. +# Sends a desktop notification (with Dismiss / Snooze 2m / Snooze 5m action +# buttons, via mnotifd) + audio beep when done. # # Install: ~/.config/scripts/timer-run (companion: timer-pick in same dir) @@ -42,13 +43,13 @@ DURATION_STR=$(fmt_dur "$TOTAL") NOTIF_SUMMARY="⏰ Timer done${LABEL:+ — $LABEL}" NOTIF_BODY="Set for ${DURATION_STR}. Finished at $(date '+%H:%M:%S')." -# ── dunst notification ──────────────────────────────────────────────────────── +# ── desktop notification ────────────────────────────────────────────────────── # notify-send needs DBUS_SESSION_BUS_ADDRESS. If not in env, find it via the -# running dunst process (reliable on single-user Wayland/Hyprland setups). +# running mnotifd notif daemon (reliable on single-user Wayland/Hyprland setups). if [[ -z "${DBUS_SESSION_BUS_ADDRESS:-}" ]]; then local_uid=$(id -u) # try to grab it from the environment of any process owned by this user - for pid in $(pgrep -u "$local_uid" dunst 2>/dev/null); do + for pid in $(pgrep -u "$local_uid" -f '[m]notifd/main.py' 2>/dev/null); do addr=$(cat /proc/$pid/environ 2>/dev/null \ | tr '\0' '\n' \ | grep '^DBUS_SESSION_BUS_ADDRESS=' \ @@ -60,20 +61,41 @@ if [[ -z "${DBUS_SESSION_BUS_ADDRESS:-}" ]]; then done fi +# ── audio alert (rings on a loop until dismissed/snoozed) ───────────────────── +_ALARM_SOUND="/usr/share/sounds/freedesktop/stereo/alarm-clock-elapsed.oga" + +play_alarm() { + if command -v paplay &>/dev/null && [[ -f "$_ALARM_SOUND" ]]; then + paplay "$_ALARM_SOUND" 2>/dev/null + elif command -v canberra-gtk-play &>/dev/null; then + canberra-gtk-play --id=alarm-clock-elapsed 2>/dev/null + fi +} + +( while true; do play_alarm; sleep 1; done ) & +RING_PID=$! + +# ── notification with action buttons ────────────────────────────────────────── +# -A implies --wait: notify-send blocks until the card is closed or an action +# is invoked, printing the chosen action's id to stdout. mnotifd supports the +# spec's Actions/ActionInvoked, so this works without any custom hint. +ACTION="" if command -v notify-send &>/dev/null; then - notify-send \ + ACTION=$(notify-send \ --urgency=critical \ --expire-time=0 \ --icon=alarm-timer \ + -A "dismiss=Dismiss" \ + -A "snooze2=Snooze 2m" \ + -A "snooze5=Snooze 5m" \ "$NOTIF_SUMMARY" \ - "$NOTIF_BODY" + "$NOTIF_BODY") fi -# ── audio alert ─────────────────────────────────────────────────────────────── -_ALARM_SOUND="/usr/share/sounds/freedesktop/stereo/alarm-clock-elapsed.oga" +kill "$RING_PID" 2>/dev/null +wait "$RING_PID" 2>/dev/null -if command -v paplay &>/dev/null && [[ -f "$_ALARM_SOUND" ]]; then - paplay "$_ALARM_SOUND" 2>/dev/null -elif command -v canberra-gtk-play &>/dev/null; then - canberra-gtk-play --id=alarm-clock-elapsed 2>/dev/null -fi +case "$ACTION" in + snooze2) exec bash "$0" 120 "$LABEL" ;; + snooze5) exec bash "$0" 300 "$LABEL" ;; +esac