#!/usr/bin/env bash # Monitors D-Bus for org.freedesktop.Notifications.Notify method calls. # For each notification, captures the sender process PID and outputs: # : # Quickshell reads this to know which process originated each notification, # allowing it to walk the /proc tree and focus the exact window that sent it. get_pid() { busctl call org.freedesktop.DBus /org/freedesktop/DBus \ org.freedesktop.DBus GetConnectionUnixProcessID s "$1" 2>/dev/null \ | awk '{print $2}' } # Maps serial → pid so concurrent notifications don't clobber each other. declare -A pending_pids awaiting_nid=false pending_pid="" dbus-monitor --session "dest='org.freedesktop.Notifications'" 2>/dev/null | \ while IFS= read -r line; do line="${line#"${line%%[![:space:]]*}"}" # trim leading whitespace if [[ "$line" == *"method call"* && "$line" == *"member=Notify"* ]]; then if [[ "$line" =~ sender=([^[:space:],;]+) ]]; then sender="${BASH_REMATCH[1]}"; fi if [[ "$line" =~ serial=([0-9]+) ]]; then serial="${BASH_REMATCH[1]}"; fi if [[ "$sender" == :* && -n "$serial" ]]; then pid=$(get_pid "$sender") if [[ -n "$pid" && "$pid" != "0" ]]; then pending_pids["$serial"]="$pid" fi fi elif [[ "$line" == *"method return"* ]]; then if [[ "$line" =~ reply_serial=([0-9]+) ]]; then reply="${BASH_REMATCH[1]}" if [[ -n "${pending_pids[$reply]}" ]]; then awaiting_nid=true pending_pid="${pending_pids[$reply]}" unset "pending_pids[$reply]" fi fi elif [[ "$awaiting_nid" == true ]]; then if [[ "$line" =~ ^uint32[[:space:]]+([0-9]+) ]]; then echo "${BASH_REMATCH[1]}:${pending_pid}" awaiting_nid=false pending_pid="" elif [[ -n "$line" ]]; then awaiting_nid=false fi fi done