1#!/bin/zsh
2
3STATE_FILE="$HOME/.local/state/wraith/brightness.state"
4LOCK_FILE="/tmp/brightness.lock"
5DEFAULT_STEP=5
6DEFAULT_FALLBACK=10
7
8set -e #saying that exit script immediately if something goes wrong
9
10show_usage() {
11 echo "Usage: $0 <command> [value]"
12 echo ""
13 echo "Commands:"
14 echo " get Get current brightness (JSON format)"
15 echo " set <value> Set brightness to specific value (0-100)"
16 echo " set +<value> Increase brightness by value"
17 echo " set -<value> Decrease brightness by value"
18 echo " set + Increase by $DEFAULT_STEP% (default step)"
19 echo " set - Decrease by $DEFAULT_STEP% (default step)"
20 echo ""
21 echo "Examples:"
22 echo " $0 get"
23 echo " $0 set 75"
24 echo " $0 set +10"
25 echo " $0 set -5"
26 echo " $0 set +"
27 echo " $0 set -"
28}
29
30cleanup() {
31 rm -f "$LOCK_FILE"
32}
33
34trap cleanup EXIT
35
36acquire_lock() {
37 local operation="$1"
38
39 if [[ -f "$LOCK_FILE" ]]; then
40 # If lock file is older than 10 seconds, remove it (stale lock)
41 if [[ $(($(date +%s) - $(stat -c %Y "$LOCK_FILE" 2>/dev/null || echo 0))) -gt 10 ]]; then
42 rm -f "$LOCK_FILE"
43 else
44 if [[ "$operation" == "set" ]]; then
45 echo "Another brightness operation is in progress..." >&2
46 exit 1
47 else
48 # For get operations, wait briefly then continue
49 sleep 1
50 [[ -f "$LOCK_FILE" ]] && return 1
51 fi
52 fi
53 fi
54
55 touch "$LOCK_FILE"
56 return 0
57}
58
59init_brightness() {
60 echo "Initializing brightness from monitor..." >&2
61
62 brightness=$(ddcutil getvcp 10 --brief 2>/dev/null | awk '{print $4}'| sed 's/[^0-9]*//g')
63 if [[ -z "$brightness" ]] || [[ ! "$brightness" =~ ^[0-9]+$ ]]; then
64 echo "Error: ddcutil failed. Install ddcutil" >&2;
65 exit 1;
66 else
67 echo "Initial brightness detected: $brightness%" >&2
68 fi
69
70 mkdir -p "$(dirname "$STATE_FILE")"
71 echo "$brightness" > "$STATE_FILE"
72 echo "$brightness"
73}
74
75validate_brightness() {
76 local value="$1"
77
78 if [[ ! "$value" =~ ^[0-9]+$ ]]; then
79 return 1
80 fi
81
82 if [[ $value -lt 0 || $value -gt 100 ]]; then
83 return 1
84 fi
85
86 return 0
87}
88
89get_brightness() {
90 if [[ ! -f "$STATE_FILE" ]]; then
91 if ! acquire_lock "get"; then
92 sleep 1
93 if [[ -f "$STATE_FILE" ]]; then
94 brightness=$(cat "$STATE_FILE")
95 else
96 brightness=$(init_brightness)
97 fi
98 else
99 brightness=$(init_brightness)
100 fi
101 else
102
103 brightness=$(cat "$STATE_FILE" 2>/dev/null)
104
105 if ! validate_brightness "$brightness"; then
106 if acquire_lock "get"; then
107 brightness=$(init_brightness)
108 else
109 echo "Error occured: brightness file state corrupted";
110 exit 1;
111 fi
112 fi
113 fi
114 echo "{\"percentage\": $brightness}"
115}
116
117set_brightness() {
118 local change="$1"
119
120 if [[ -z "$change" ]]; then
121 echo "Error: No value provided for set command" >&2
122 show_usage
123 exit 1
124 fi
125
126 if ! acquire_lock "set"; then
127 exit 1
128 fi
129
130 if [[ ! -f "$STATE_FILE" ]]; then
131 echo "State file doesn't exist, initializing..." >&2
132 init_brightness >/dev/null
133 fi
134
135 current=$(cat "$STATE_FILE" 2>/dev/null)
136 if ! validate_brightness "$current"; then
137 current=$DEFAULT_FALLBACK
138 echo "Warning: Invalid state file, using fallback value: $DEFAULT_FALLBACK%" >&2
139 fi
140
141 local new_brightness
142 if [[ "$change" == "+" ]]; then
143 new_brightness=$((current + DEFAULT_STEP))
144 elif [[ "$change" == "-" ]]; then
145 new_brightness=$((current - DEFAULT_STEP))
146 elif [[ "$change" == +* ]]; then
147 local step="${change:1}"
148 if [[ "$step" =~ ^[0-9]+$ ]]; then
149 new_brightness=$((current + step))
150 else
151 echo "Error: Invalid increment value: $change" >&2
152 exit 1
153 fi
154 elif [[ "$change" == -* ]]; then
155 local step="${change:1}"
156 if [[ "$step" =~ ^[0-9]+$ ]]; then
157 new_brightness=$((current - step))
158 else
159 echo "Error: Invalid decrement value: $change" >&2
160 exit 1
161 fi
162 elif [[ "$change" =~ ^[0-9]+$ ]]; then
163 new_brightness="$change"
164 else
165 echo "Error: Invalid argument: $change" >&2
166 show_usage
167 exit 1
168 fi
169
170 if [[ $new_brightness -lt 0 ]]; then
171 new_brightness=0
172 elif [[ $new_brightness -gt 100 ]]; then
173 new_brightness=100
174 fi
175
176 if [[ $new_brightness -eq $current ]]; then
177 echo "Brightness already at $new_brightness%, no change needed" >&2
178 echo "{\"percentage\": $new_brightness}"
179 return 0
180 fi
181
182 echo "Setting brightness from $current% to $new_brightness%..." >&2
183
184 if ddcutil setvcp -b 5 10 $new_brightness --brief >/dev/null 2>&1; then
185 echo "$new_brightness" > "$STATE_FILE"
186 echo "✓ Brightness set to $new_brightness%" >&2
187
188 # if command -v notify-send >/dev/null 2>&1; then
189 # notify-send "Brightness" "$new_brightness%" -t 3000 -h int:value:$new_brightness -h string:synchronous:brightness 2>/dev/null
190 # fi
191
192 echo "{\"percentage\": $new_brightness}"
193 else
194 echo "✗ Failed to set brightness with ddcutil" >&2
195 exit 1
196 fi
197}
198
199# Main script logic
200case "$1" in
201 "get")
202 if [[ $# -ne 1 ]]; then
203 echo "Error: 'get' command takes no arguments" >&2
204 show_usage
205 exit 1
206 fi
207 get_brightness
208 ;;
209 "set")
210 if [[ $# -ne 2 ]]; then
211 echo "Error: 'set' command requires exactly one argument" >&2
212 show_usage
213 exit 1
214 fi
215 set_brightness "$2"
216 ;;
217 "reset")
218 set_brightness "$DEFAULT_FALLBACK"
219 ;;
220 "help"|"-h"|"--help")
221 show_usage
222 ;;
223 "")
224 show_usage
225 exit 1
226 ;;
227 *)
228 echo "Error: Unknown command: $1" >&2
229 exit 1
230 ;;
231esac