#!/usr/bin/env bash # Shared by the watcher and recovery helper. Config is trusted user-owned Bash. log() { printf '%s: %s\n' "$LOGTAG" "$*" >&2 } # These settings are consumed by scripts sourcing this library and via ${!name}. # shellcheck disable=SC2034 load_config() { MONITOR=HDMI-A-2 DEBOUNCE_SECONDS=30 HDMI_SETTLE_SECONDS=5 VT_SWITCH_DELAY_SECONDS=1 VT_RETURN_SETTLE_SECONDS=2 local config="${XDG_CONFIG_HOME:-$HOME/.config}/revive-monitor/config.env" if [[ -r "$config" ]]; then # shellcheck disable=SC1090 source "$config" || return 1 fi if [[ ! "$MONITOR" =~ ^[a-zA-Z0-9_-]+$ ]]; then log 'Invalid MONITOR: expected an output name such as HDMI-A-2' return 1 fi local name value limit for name in DEBOUNCE_SECONDS HDMI_SETTLE_SECONDS VT_SWITCH_DELAY_SECONDS VT_RETURN_SETTLE_SECONDS; do value="${!name}" limit=300 [[ "$name" == DEBOUNCE_SECONDS ]] && limit=3600 if [[ ! "$value" =~ ^[0-9]{1,4}$ ]] || (( 10#$value > limit )); then log "Invalid $name: expected an integer between 0 and $limit" return 1 fi printf -v "$name" '%d' "$((10#$value))" done } require_commands() { local command_name for command_name in "$@"; do if ! command -v "$command_name" >/dev/null 2>&1; then log "Required command not found: $command_name" return 1 fi done } prepare_runtime() { # No shared /tmp fallback: locks must live in a private per-user directory. local runtime="${XDG_RUNTIME_DIR:-}" if [[ "$runtime" != /* || ! -d "$runtime" || -L "$runtime" || ! -O "$runtime" ]] || [[ "$(stat -c %a -- "$runtime" 2>/dev/null)" != 700 ]]; then log 'XDG_RUNTIME_DIR must be an existing, user-owned directory with mode 0700' return 1 fi STATE_DIR="$runtime/revive-monitor" if [[ -L "$STATE_DIR" ]]; then log 'Refusing a symlink as runtime state directory' return 1 fi (umask 077; mkdir -p -- "$STATE_DIR") || return 1 if [[ ! -O "$STATE_DIR" || "$(stat -c %a -- "$STATE_DIR")" != 700 ]]; then log 'Runtime state directory must be user-owned with mode 0700' return 1 fi } get_active_wayland_session() { local sessions sid rest properties key value uid type active vt seat desktop sessions="$(timeout 5 loginctl list-sessions --no-legend --no-pager 2>/dev/null)" || return 1 while read -r sid rest; do [[ -n "$sid" ]] || continue properties="$(timeout 5 loginctl show-session "$sid" -p User -p Type -p Active -p VTNr -p Seat -p Desktop 2>/dev/null)" || continue uid='' type='' active='' vt='' seat='' desktop='' while IFS='=' read -r key value; do case "$key" in User) uid="$value" ;; Type) type="$value" ;; Active) active="$value" ;; VTNr) vt="$value" ;; Seat) seat="$value" ;; Desktop) desktop="${value,,}" ;; esac done <<< "$properties" [[ "$uid" == "$UID" && "$type" == wayland && "$active" == yes && "$seat" == seat0 ]] || continue [[ -z "$desktop" || "$desktop" == *hyprland* ]] || continue if [[ "$vt" =~ ^[1-9][0-9]?$ ]] && (( vt <= 63 )); then printf '%s:%s\n' "$sid" "$vt" return 0 fi done <<< "$sessions" return 1 }