#!/bin/zsh # vim: set ft=sh: #shellcheck shell=sh # This software is released into the public domain. # It is provided "as is", without warranties or conditions of any kind. # Anyone is free to use, modify, redistribute and do anything with this software. # backlight brightness configuration utility # # Requirements: POSIX sh or zsh, backlight in /sys/class/backlight/*, # write permission for $BACKLIGHT/brightness, # one of zsh, bc or qalc (only for -c) set -eu sys_backlight="/sys/class/backlight" sys_leds="/sys/class/leds" find_backlight() { for f in "$sys_backlight"/*; do if [ "$f" = "$sys_backlight/*" ]; then printf "cannot find a backlight in %s\n" "$sys_backlight" >&2 exit 1 elif [ -n "$bldir" ]; then printf "multiple backlights found, choose one with \$BACKLIGHT\n" >&2 exit 1 fi bldir="$f" done } is_backlight() { [ -d "$1" ] && [ -f "$1/brightness" ] && [ -f "$1/max_brightness" ] } list_backlights() { for f in "$sys_backlight"/* "$sys_leds"/*; do if is_backlight "$f"; then printf "%s\n" "${f##*/}" fi done } get_bldir() { bldir= case "${BACKLIGHT:-}" in /*) bldir="$BACKLIGHT" ;; "") find_backlight ;; *) if is_backlight "$sys_backlight/$BACKLIGHT"; then bldir="$sys_backlight/$BACKLIGHT" elif is_backlight "$sys_leds/$BACKLIGHT"; then bldir="$sys_leds/$BACKLIGHT" else printf "no such backlight found: '%s'\n" "$BACKLIGHT" >&2 exit 1 fi ;; esac blname="${bldir##*/}" } read_num() { read -r num <"$1" if ! is_num "$num"; then printf "expected a number reading '%s', but got '%s'\n" "$1" "$num" >&2 exit 1 fi } is_num() { case "$1" in [0-9]) return 0 ;; [0-9][0-9]) return 0 ;; [0-9][0-9][0-9]) return 0 ;; [0-9][0-9][0-9][0-9]) return 0 ;; [0-9][0-9][0-9][0-9][0-9]) return 0 ;; [0-9][0-9][0-9][0-9][0-9][0-9][0-9]) return 0 ;; [0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]) return 0 ;; [0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]) return 0 ;; # hopefully it's not a >2^32 number esac return 1 } raw_to_num() { case "$math" in "") : $(( num = (raw + (raw_max / 2 / max)) * max / raw_max )) ;; zsh) : $(( num = int(0.5 + (100 - ((1.0 * raw * max / raw_max - 100) / 10) ** 2)) )) ;; zshcmd) num="$(zsh -c "zmodload zsh/mathfunc && echo \$(( int(0.5 + (100 - ((1.0 * $raw * $max / $raw_max - 100) / 10) ** 2)) )) || :")" ;; bc) num="$(printf "%s\n" "100.5 - ((($raw * $max / $raw_max) - 100) / 10) ^ 2" | bc -l)" num="${num%.*}" ;; qalc) num="$(qalc -t "round((100 - ((($raw * $max / $raw_max) - 100) / 10) ** 2))")" ;; esac } num_to_raw() { case "$math" in "") : $(( raw = num * raw_max / max )) ;; zsh) : $(( raw = int(0.5 + ((100 - 10 * sqrt(100 - 1.0 * num)) * raw_max / max)) )) ;; zshcmd) raw="$(zsh -c "zmodload zsh/mathfunc && echo \$(( int(0.5 + ((100 - 10 * sqrt(100 - 1.0 * $num)) * $raw_max / $max)) )) || :")" ;; bc) raw="$(printf "%s\n" "0.5 + (100 - 10 * sqrt(100 - $num)) * $raw_max / $max" | bc -l)" raw="${raw%.*}" ;; qalc) raw="$(qalc -t "round((100 - 10 * sqrt(100 - $num)) * $raw_max / $max)")" ;; esac } get_brightness() { read_num "$bldir/max_brightness" raw_max="$num" read_num "$bldir/brightness" raw_curr="$num" max=100 raw="$raw_curr" raw_to_num curr="$num" # brightness 0 tends to completely turn off the backlight raw_min=1 case "$blname" in nvidia_[0-9]*) raw_min=0 ;; # nvidia_0 at 0 is still not off esac } get_raw_new() { op="$1" val="$2" raw_new= new= if ! is_num "$val"; then printf "expected a number for brightness, but got '%s'\n" "$val" >&2 exit 2 fi while :; do case "$val" in 0*) val="${val#0}" ;; *) break ;; esac done case "$op" in "+") : $(( new = curr + val )) ;; "-") : $(( new = curr - val )) ;; "") : $(( new = val )) ;; "=+") : $(( raw_new = raw_curr + val )) ;; "=-") : $(( raw_new = raw_curr - val )) ;; "=") : $(( raw_new = val )) ;; *) printf "invalid operator '%s'\n" "$op" >&2 exit 2 ;; esac if [ -n "$new" ]; then num="$new" num_to_raw raw_new="$raw" if [ "$new" -lt "$curr" ] && [ "$raw_new" -ge "$raw_curr" ]; then : $(( raw_new = raw_curr - 1 )) elif [ "$new" -gt "$curr" ] && [ "$raw_new" -le "$raw_curr" ]; then : $(( raw_new = raw_curr + 1 )) fi fi if [ -n "$raw_new" ]; then if [ "$raw_new" -gt "$raw_max" ]; then raw_new="$raw_max" fi if [ "$raw_new" -lt "$raw_min" ]; then raw_new="$raw_min" fi fi } set_brightness() { get_raw_new "$1" "$2" printf %d "$raw_new" > "$bldir/brightness" } usage() { prog="$(basename "$0")" printf "%s: usage: %s [OPTION]... OPERATOR\n" "$prog" "$prog" printf "\n" printf "Operators:\n" printf " (none) print current brightness in percentages\n" printf " PERC set brightness to PERC percentages of max\n" printf " +PERC increase brightness by PERC percentage points\n" printf " -PERC decrease brightness by PERC percentage points\n" printf " = print current brightness raw value\n" printf " VAL set brightness to VAL raw value\n" printf " =+VAL increase brightness by VAL raw value\n" printf " =-VAL decrease brightness by VAL raw value\n" printf " -L, --list list all found backlights\n" printf "\n" printf "Options:\n" printf " -c modify percentage curve to emphasize low raw values\n" printf "\n" printf "Environment variables:\n" printf " BACKLIGHT the backlight to use (absolute path or a folder\n" printf " in %s or %s)\n" "$sys_backlight" "$sys_leds" } main() { math= if [ "${1:-}" = "-c" ]; then if [ -n "${ZSH_VERSION:-}" ]; then zmodload zsh/mathfunc math="zsh" elif command -v zsh >/dev/null; then math="zshcmd" elif command -v bc >/dev/null; then math="bc" elif command -v qalc >/dev/null; then math="qalc" else printf "no supported math option (zsh, bc, qalc) found\n" >&2 return 1 fi shift fi if [ $# -gt 1 ]; then usage >&2 exit 2 fi if [ "${1:-}" = "-h" ] || [ "${1:-}" = "--help" ]; then usage exit fi if [ "${1:-}" = "-L" ] || [ "${1:-}" = "--list" ]; then list_backlights exit fi get_bldir get_brightness op="${1:-}" case "$op" in "") printf "%d\n" "$curr" ;; "+"[0-9]*) set_brightness + "${op#+}" ;; "-"[0-9]*) set_brightness - "${op#-}" ;; [0-9]*) set_brightness "" "$op" ;; "=") printf "%d\n" "$raw_curr" ;; "="+[0-9]*) set_brightness "=+" "${op#=+}" ;; #"+="[0-9]*) set_brightness "=+" "${op#+=}" ;; "="-[0-9]*) set_brightness "=-" "${op#=-}" ;; #"-="[0-9]*) set_brightness "=-" "${op#-=}" ;; "="[0-9]*) set_brightness "=" "${op#=}" ;; *) printf "invalid operator: '%s'\n" "$op" >&2 exit 2 esac } main "$@"