The bare $HOME repo is gone. This is now the rice: install.sh, rice-* scripts in bin/, configs in home/ linked into $HOME, theme templates, st and the particle wallpaper. Clone to ~/rice and run install.sh.
112 lines
4.8 KiB
Bash
Executable file
112 lines
4.8 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# rice-display - monitor state for the eww panel, and the actions it runs.
|
|
#
|
|
# rice-display info JSON: output, modes, refresh rates, DDC brightness
|
|
# rice-display brightness <0-100>
|
|
# rice-display mode <id> switch resolution (reverts in 15s unless you keep it)
|
|
# rice-display rate <id> switch refresh rate at the current resolution
|
|
set -uo pipefail
|
|
|
|
run="${XDG_RUNTIME_DIR:-/tmp}/rice"
|
|
modes_tsv="$run/modes.tsv"
|
|
rates_tsv="$run/rates.tsv"
|
|
bright_cache="$run/brightness"
|
|
mkdir -p "$run"
|
|
|
|
say() { rice-notify send "$1" "${2:-}" -i video-display; }
|
|
|
|
primary() {
|
|
xrandr --query | awk '/ connected primary/ { print $1; exit } / connected/ && !first { first = $1 } END { if (first) print first }' | head -n1
|
|
}
|
|
|
|
# "WxH<TAB>rate<TAB>current(1/0)" for every mode of the output, xrandr order.
|
|
mode_lines() {
|
|
xrandr --query | awk -v out="$1" '
|
|
$1 == out { inblock = 1; next }
|
|
inblock && /^[^ ]/ { exit }
|
|
inblock {
|
|
mode = $1
|
|
for (i = 2; i <= NF; i++) {
|
|
r = $i; cur = (r ~ /\*/) ? 1 : 0; gsub(/[*+]/, "", r)
|
|
if (r != "") printf "%s\t%s\t%d\n", mode, r, cur
|
|
}
|
|
}'
|
|
}
|
|
|
|
# DDC is slow (up to a second per read), so reads are cached for a minute.
|
|
brightness() {
|
|
if [[ -f $bright_cache ]] && (( $(date +%s) - $(stat -c %Y "$bright_cache") < 60 )); then
|
|
cat "$bright_cache"; return
|
|
fi
|
|
local v
|
|
v=$(ddcutil getvcp 10 --brief 2>/dev/null | awk '$1 == "VCP" { print $4; exit }')
|
|
[[ -n $v ]] && echo "$v" | tee "$bright_cache"
|
|
}
|
|
|
|
info() {
|
|
local out cur_mode cur_rate
|
|
out=$(primary)
|
|
mode_lines "$out" > "$run/all-modes.tsv"
|
|
IFS=$'\t' read -r cur_mode cur_rate _ < <(awk -F'\t' '$3 == 1' "$run/all-modes.tsv")
|
|
|
|
awk -F'\t' '!seen[$1]++ { print $1 }' "$run/all-modes.tsv" | head -n 6 |
|
|
awk -v cur="$cur_mode" '{ printf "%d\t%s\t%d\n", NR - 1, $1, ($1 == cur) }' > "$modes_tsv"
|
|
# One entry per rounded rate (monitors list 120.00, 119.98 and 119.88), keeping the active one.
|
|
awk -F'\t' -v cur="$cur_mode" '$1 == cur { printf "%d\t%s\t%d\n", n++, $2, $3 }' "$run/all-modes.tsv" |
|
|
sort -t$'\t' -k3,3nr -k2,2nr | awk -F'\t' '!seen[int($2 + 0.5)]++' |
|
|
sort -t$'\t' -k2,2nr | head -n 5 > "$rates_tsv"
|
|
|
|
local b ddc=false
|
|
if command -v ddcutil >/dev/null; then
|
|
b=$(brightness)
|
|
[[ -n $b ]] && ddc=true
|
|
fi
|
|
|
|
jq -cn --arg output "$out" --arg mode "$cur_mode" --arg rate "$cur_rate" \
|
|
--argjson ddc "$ddc" --argjson brightness "${b:-0}" \
|
|
--argjson bar "$(pgrep -x polybar >/dev/null && echo true || echo false)" \
|
|
--argjson modes "$(jq -R -s -c 'split("\n") | map(select(length > 0) | split("\t") | {id: (.[0] | tonumber), mode: .[1], current: (.[2] == "1")})' "$modes_tsv")" \
|
|
--argjson rates "$(jq -R -s -c 'split("\n") | map(select(length > 0) | split("\t") | {id: (.[0] | tonumber), label: ((.[1] | tonumber | round | tostring) + "Hz"), current: (.[2] == "1")})' "$rates_tsv")" \
|
|
'{output: $output, mode: $mode, rate: $rate, ddc: $ddc, brightness: $brightness, bar: $bar, modes: $modes, rates: $rates,
|
|
ddcHint: "Needs ddcutil and the i2c-dev module"}'
|
|
}
|
|
|
|
# Apply an xrandr change, then revert unless it is confirmed within 15 seconds.
|
|
apply_with_revert() {
|
|
local out="$1" old_mode="$2" old_rate="$3" new_mode="$4" new_rate="$5"
|
|
xrandr --output "$out" --mode "$new_mode" --rate "$new_rate" || { say "Display" "That mode did not apply"; return 1; }
|
|
local keep
|
|
keep=$(printf 'Keep %s @ %sHz\nRevert\n' "$new_mode" "$new_rate" |
|
|
timeout 15 rofi -dmenu -theme "$HOME/.config/rofi/menu.rasi" -p "Display" -mesg "Reverting in 15 seconds") || keep=""
|
|
if [[ $keep != Keep* ]]; then
|
|
xrandr --output "$out" --mode "$old_mode" --rate "$old_rate"
|
|
say "Display" "Reverted to $old_mode @ ${old_rate}Hz"
|
|
fi
|
|
}
|
|
|
|
case "${1:-}" in
|
|
info) info ;;
|
|
brightness)
|
|
[[ ${2:-} =~ ^[0-9]+$ ]] && (( $2 <= 100 )) || { echo "rice-display: brightness 0-100" >&2; exit 2; }
|
|
echo "$2" > "$bright_cache"
|
|
# Sliders fire many values; only the newest one talks to the monitor.
|
|
pkill -u "$UID" -f 'ddcutil setvcp 10' 2>/dev/null
|
|
setsid -f ddcutil setvcp 10 "$2" --noverify >/dev/null 2>&1
|
|
;;
|
|
mode|rate)
|
|
[[ ${2:-} =~ ^[0-9]+$ ]] || { echo "rice-display: $1 takes an id" >&2; exit 2; }
|
|
out=$(primary)
|
|
IFS=$'\t' read -r old_mode old_rate _ < <(mode_lines "$out" | awk -F'\t' '$3 == 1')
|
|
if [[ $1 == mode ]]; then
|
|
new_mode=$(awk -F'\t' -v id="$2" '$1 == id { print $2 }' "$modes_tsv")
|
|
new_rate=$(mode_lines "$out" | awk -F'\t' -v m="$new_mode" '$1 == m { print $2 }' | sort -nr | head -n1)
|
|
else
|
|
new_mode="$old_mode"
|
|
new_rate=$(awk -F'\t' -v id="$2" '$1 == id { print $2 }' "$rates_tsv")
|
|
fi
|
|
[[ -n $new_mode && -n $new_rate ]] || exit 1
|
|
rice-panel close
|
|
apply_with_revert "$out" "$old_mode" "$old_rate" "$new_mode" "$new_rate"
|
|
;;
|
|
*) sed -n '2,8p' "$0" | sed 's/^# \{0,1\}//'; exit 2 ;;
|
|
esac
|