#!/usr/bin/env bash # printbar-watch — refresh Waybar's printbar module(s) instantly on CUPS print events. # # It subscribes (via the CUPS `dbus` notifier) to job/printer events for every local # queue, then listens on the system bus and sends SIGRTMIN+N to Waybar on each event so # the printbar module re-runs immediately (no waiting for its poll interval). # # N defaults to 15; override with PRINTBAR_WAYBAR_SIGNAL to match your module's "signal". set -uo pipefail SIGNAL="${PRINTBAR_WAYBAR_SIGNAL:-15}" DEBOUNCE_NS=700000000 # 0.7s subtest="$(mktemp "${TMPDIR:-/tmp}/printbar-sub.XXXXXX")" || exit 1 trap 'rm -f "$subtest"' EXIT cat > "$subtest" <<'IPPT' { OPERATION Create-Printer-Subscriptions GROUP operation-attributes-tag ATTR charset attributes-charset utf-8 ATTR naturalLanguage attributes-natural-language en ATTR uri printer-uri $uri ATTR name requesting-user-name $user GROUP subscription-attributes-tag ATTR uri notify-recipient-uri dbus:// ATTR keyword notify-events job-created job-completed job-state-changed printer-state-changed printer-stopped ATTR integer notify-lease-duration 0 } IPPT subscribe() { # `lpstat -e` prints one queue name per line. Read them line by line instead # of via `$(...)` word splitting: a name with a space or a glob character # would otherwise split or expand, and the name is interpolated into a URL # below. cups-browsed can create queue names from mDNS, so this is not fully # local. `%` is percent-encoded so the name cannot alter the URL path. local p enc while IFS= read -r p; do [ -n "$p" ] || continue enc=${p//%/%25} ipptool -tv -d user="$USER" "ipp://localhost/printers/$enc" "$subtest" >/dev/null 2>&1 done < <(lpstat -e 2>/dev/null) } # Re-subscribe periodically so a cupsd restart (which drops subscriptions) is tolerated. ( while true; do subscribe; sleep 300; done ) & trap 'rm -f "$subtest"; kill %1 2>/dev/null' EXIT # Refresh on each CUPS notifier signal, debounced. last=0 dbus-monitor --system "type='signal',interface='org.cups.cupsd.Notifier'" 2>/dev/null | while read -r line; do case "$line" in *"member=Job"* | *"member=Printer"*) now=$(date +%s%N) if ((now - last > DEBOUNCE_NS)); then pkill -RTMIN+"$SIGNAL" waybar 2>/dev/null printf 'printbar-watch: CUPS event → Waybar refreshed (RTMIN+%s)\n' "$SIGNAL" >&2 last=$now fi ;; esac done