#!/bin/sh argv0=$(basename "${0}") # @FUNCTION: err # @USAGE: [-x] ... # @DESCRIPTION: # Print given messages to stderr line by line and exit with status 1. # # If "-x" is specified, suppress the program prefix (`program: `) # from the output. Otherwise, the first line is prefixed with "program: ". # # This function is intended for fatal errors; it always exits the script. # @EXAMPLE: # err "Invalid usage" "Try '${argv0} -h' for help." err() { if [ "${1}" != "-x" ]; then printf "%s: " "${argv0}" else shift fi for line in "${@}"; do echo "${line}" >&2 done exit 1 } # @FUNCTION: invalid_use # USAGE: [-h] # @DESCRIPTION: # Output a usage error message. If `-h` is not specified output: # ": Invalid usage " # "Try 'program -h' for help." # else output only: # "Try 'program -h' for help." invalid_use() { [ "${1}" = "-h" ] && err -x "Try '${argv0} -h' for help." err "Invalid usage" "Try '${argv0} -h' for help." } # @FUNCTION: check_program # USAGE: [error-msg] # @DESCRIPTION: # Check if command exists on the system. # If not, print an error message to stderr and exit with status 1. # The default error message is "`command` must be installed" # but can optionally be overwritten. # # @EXAMPLE: # Check if pulseaudio is installed. # # check_program "pactl" "pulseaudio must be installed" check_program() { command -v "${1}" > /dev/null 2>&1 && return 0 [ -n "${2}" ] && err "${2}" err "${1} must be installed" } # @FUNCTION: send_notification # @USAGE: send_notification ... # @DESCRIPTION: # Write a notification message to a temporary file and trigger a dwm notification signal. # The message is written to /tmp/noti.txt. If writing fails, a warning is printed to stderr. # After writing, a dwm signal is sent using `xsetroot -name "fsignal:1"`. # # @EXAMPLE: # Send a desktop notification with the message "Backup complete". # # send_notification "Backup complete" send_notification() { echo "$@" > /tmp/noti.txt || echo "Warning: failed to write to notification file" >&2 xsetroot -name "fsignal:1" } # @FUNCTION: run # @USAGE: [--reload-status] [--reload-compositor] [--success-notify ] [--failure-notify ] [success-msg] [failure-msg] # @DESCRIPTION: # Safely execute a simple shell command, print optional success/failure messages, # and optionally reload the status bar or restart the compositor. # # Success messages are printed to stdout; failure messages are printed to stderr. # Optional desktop notifications can be sent using --success-notify and --failure-notify. # Command output is not suppressed. # # Options: # --reload-status Reload the status bar (via `slreload`). # --reload-compositor Restart the compositor (kills and restarts `picom`). # --success-notify Send a desktop notification on success. # --failure-notify Send a desktop notification on failure. # # Restrictions: # - Does NOT use `eval`; only simple commands and arguments are supported. # - Shell operators like `&&`, `||`, `|`, `>`, `>>`, etc. will NOT work. # - This design prevents unintended execution and makes the function safe in scripts. # # Return value: # Exits with 0 if the command succeeds, 1 otherwise. # # @EXAMPLES: # # Run xwallpaper and print the image path on success # run "xwallpaper --zoom ${image}" "${image}" # # # Restart compositor and show a success message # run --reload-compositor "xwallpaper --zoom ${image}" "Wallpaper updated" "Wallpaper failed" # # # With notification hooks # run --success-notify "Wallpaper set" \ # --failure-notify "Wallpaper failed" \ # "xwallpaper --zoom ${image}" "Wallpaper updated" "Wallpaper failed" run() { relstat=0 compstat=0 exitval=0 while [ $# -gt 0 ]; do case "$1" in --reload-status) relstat=1 ;; --reload-compositor) compstat=1 ;; --success-notify) success_msg="${2}"; shift ;; --failure-notify) failure_msg="${2}"; shift ;; *) break; esac shift done if [ "${compstat}" -eq 1 ]; then pgrep -x picom > /dev/null && killall picom fi if ${1}; then [ -n "${2}" ] && echo "${2}" [ -n "${success_msg}" ] && send_notification "${argv0}:" "${success_msg}" else [ -n "${3}" ] && err "${3}" [ -n "${failure_msg}" ] && send_notification "${argv0}:" "${failure_msg}" exitval=1 fi if [ "${relstat}" -eq 1 ]; then slreload || echo "Warning: Failed to reload slstatus" >&2 fi if [ "${compstat}" -eq 1 ]; then picom -b || echo "Warning: Failed to start picom" >&2 fi exit "${exitval}" }