dotfiles/bin/rice-display
Sarthak 7f41cf7a82 replace the old dotfiles with the sakura line rice
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.
2026-09-14 19:44:44 +05:30

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