608c5cee55110fb0c611875738c0dbecacdac725
[distro-setup] / myi3status
1 #!/bin/bash
2 # I, Ian Kelling, follow the GNU license recommendations at
3 # https://www.gnu.org/licenses/license-recommendations.en.html. They
4 # recommend that small programs, < 300 lines, be licensed under the
5 # Apache License 2.0. This file contains or is part of one or more small
6 # programs. If a small program grows beyond 300 lines, I plan to switch
7 # its license to GPL.
8
9 # Copyright 2024 Ian Kelling
10
11 # Licensed under the Apache License, Version 2.0 (the "License");
12 # you may not use this file except in compliance with the License.
13 # You may obtain a copy of the License at
14
15 # http://www.apache.org/licenses/LICENSE-2.0
16
17 # Unless required by applicable law or agreed to in writing, software
18 # distributed under the License is distributed on an "AS IS" BASIS,
19 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 # See the License for the specific language governing permissions and
21 # limitations under the License.
22
23
24 # usage:
25 #
26 # * left click seconds to reset & enable main timer which shows 30
27 # minutes in blue and half hours in orange.
28 #
29 # * right click seconds to disable minutes & half hours of main timer
30 #
31 # * left click minutes to reset & enable 2nd timer.
32 #
33 # * right click minutes to disable 2nd timer
34
35 # docs:
36 #
37 # Sections from right to left.
38 #
39 # section #1, labeled day_percent
40 #
41 # 1. Thousandths of a 16 hour day left. 1 = 57.6 seconds.
42 # 2. Bar of one thousand of a day which gets shorter by ninths. One ninth is 6.4 seconds.
43 # 3. Start of the day (only the non-zero numbers). Set this with the ds command.
44 # 4. The time right now.
45 # 5. Ten thousands of the year left. 1 = 52:34 minutes (leap year = 52:42)
46 # 6. Bar of 1/10,000 of a year shrinking by ninths. 1 ninth = 5:50 minutes (leap year = 5:51 mins).
47 #
48 # section #2, labeled seconds
49 #
50 # block characters, ▉, are added every 3 seconds, a total of 20 for 1
51 # minute. The groups of 5 blocks = 15 seconds each.
52 #
53 # section #3, labeled mins
54 #
55 # Only enabled by clicking on seconds, see usage.
56 #
57 # Block characters are added every minute until 30, then reset. There
58 # are 6 groups of 5 minutes.
59 #
60 # Section #4
61 #
62 # Half hours in orange, grouped into groups of 2. This will grow until
63 # it fills the screen.
64 #
65 # Section #5 & 6
66 #
67 # Only enabled by clicking on mins section, see usage.
68 #
69 # These repeat sections 3 and 4 in violet and some other color.
70 #
71 #
72 if [ -z "$BASH_VERSION" ]; then echo "error: shell is not bash" >&2; exit 1; fi
73
74 source /a/bin/bash-bear-trap/bash-bear
75
76 shopt -s nullglob
77 shopt -s dotglob
78
79 tmp_focus1=$(mktemp)
80 tmp_focus2=$(mktemp)
81
82 mins=0
83 half_hours=0
84 start=$EPOCHSECONDS
85 domins=false
86
87 if date -d 'february 29' &>/dev/null; then
88 days_in_this_year=366
89 else
90 days_in_this_year=365
91 fi
92
93
94 get_daystart() {
95 if [[ /b/data/daystart -ot /b/data/daystart-generated && $day_start_24h ]]; then
96 return 0
97 fi
98 day_start_24h=$(cat /b/data/daystart)
99 day_start_sig_digits=${day_start_24h%?}
100 day_start_sig_digits=${day_start_sig_digits%0}
101 day_start_hour=${day_start_24h%??}
102 day_start_min=${day_start_24h: -2}
103 echo $day_start_hour > /b/data/daystart-generated
104 echo $day_start_min >> /b/data/daystart-generated
105 day_start=$(date -d $day_start_hour:$day_start_min +%s)
106 # for after midnight but before the day start.
107 if (( day_start > EPOCHSECONDS )); then
108 day_start=$(date -d "$day_start_hour:$day_start_min yesterday" +%s)
109 fi
110 }
111
112
113 main() {
114 get_daystart
115
116 ## debug
117 # if [[ $line ]]; then
118 # echo "line=$line" >>/tmp/t
119 # fi
120 case $line in
121 ""|"[") : ;;
122 *)
123 json="${line#,}"
124 case $(echo "$json" | jq -r .name) in
125 seconds)
126 case $(echo "$json" | jq -r .button) in
127 # left click
128 1)
129 start=$EPOCHSECONDS
130 domins=true
131 ;;
132 # right click
133 3)
134 domins=false
135 ;;
136 esac
137 ;;
138 mins)
139 case $(echo "$json" | jq -r .button) in
140 1)
141 start2=$EPOCHSECONDS
142 ;;
143 3)
144 start2=
145 ;;
146 esac
147
148 ;;
149 esac
150 esac
151
152 time=$((EPOCHSECONDS - start))
153 total_mins=$(( time / 60 ))
154 mins=$(( total_mins % 30 ))
155 half_hours=$(( total_mins / 30 ))
156
157
158
159 printf '['
160
161 if [[ -e /dev/shm/iank-status && ! -e /tmp/quiet-status ]]; then
162 ps_char=
163 eval "$(< /dev/shm/iank-status)"
164 fi
165
166 touch --date="10 minutes ago" $tmp_focus1
167 touch --date="30 minutes ago" $tmp_focus2
168 f=/tmp/focus-last-input
169
170 # We output a reminder to do input to the focus app if we haven't done
171 # it less 10 minutes ago (but dont bother if its been more than 30
172 # minutes, maybe we aren't running it anymore.)
173 if [[ -e $f && $tmp_focus2 -ot $f && $tmp_focus1 -nt $f ]]; then
174 ps_char="=======FOCUS====== $ps_char"
175 fi
176
177 if [[ -e /tmp/iank-i3-no-auto ]]; then
178 ps_char="$ps_char I"
179 fi
180
181 # not using currently
182 # if [[ -e /tmp/no-obs-auto-scene-switch ]]; then
183 # ps_char="$ps_char O"
184 # fi
185
186 if pgrep -fc '^ffmpeg.*icecast://source.*/fsf' &>/dev/null; then
187 if [[ -e /tmp/iank-ffmpeg-interlude-toggle ]]; then
188 ps_char="= BRB = $ps_char"
189 else
190 ps_char="=|=|= STREAMING =|=|= $ps_char"
191 fi
192
193 muted=$(cat $HOME/.iank-stream-muted) ||:
194 case $muted in
195 0) : ;;
196 1)
197 if find $HOME/.iank-stream-muted -mmin +2 | grep . &>/dev/null && \
198 ! pgrep -fc '^ffmpeg.*icecast://source.*/fsf-test'; then
199 toggle-mute mute notify
200 else
201 ps_char="# UNMUTED # $ps_char"
202 fi
203 ;;
204 *)
205 ps_char="!!!!! FAILED GETTING MUTE STATUS !!!!! $ps_char"
206 ;;
207 esac
208 fi
209
210 # fyi: to check system mute:
211 # if pactl get-source-mute @DEFAULT_SOURCE@ 2>/dev/null | awk '{print $2}' | grep no &>/dev/null; then
212
213
214
215 printf '{ "name":"status", "color":"#ED297D", "full_text": "%s' "$ps_char"
216 printf '"},'
217
218
219 if [[ $start2 ]]; then
220 time2=$((EPOCHSECONDS - start2))
221 total_mins2=$(( time2 / 60 ))
222 mins2=$(( total_mins2 % 30 ))
223 half_hours2=$(( total_mins2 / 30 ))
224 # this is duplicate of mins and half hours except for different
225 # colors and looking at vars above.
226
227 # begin half hours
228 printf '{ "color": "#EFC93E", "full_text": "'
229 for ((i=half_hours2-1; i >= 0 ; i--)); do
230 printf
231 if (( i > 0 && i % 2 == 0 )); then
232 printf " "
233 fi
234 done
235 printf '"},'
236
237
238 # begin minutes
239 printf '{ "name":"mins2", "color":"#ED297D", "full_text": "'
240 for ((i=29; i >= 0 ; i--)); do
241 if (( i < mins2 )); then
242 printf
243 else
244 printf " "
245 fi
246 if (( i > 0 && i % 5 == 0 )); then
247 printf " "
248 fi
249 done
250 printf '"},'
251
252 fi
253
254 if $domins; then
255 # begin half hours
256 printf '{ "color": "#FFB542", "full_text": "'
257 for ((i=half_hours-1; i >= 0 ; i--)); do
258 printf
259 if (( i > 0 && i % 2 == 0 )); then
260 printf " "
261 fi
262 done
263 printf '"},'
264
265
266 # begin minutes
267 printf '{ "name":"mins", "color":"#0D6BDD", "full_text": "'
268 for ((i=29; i >= 0 ; i--)); do
269 if (( i < mins )); then
270 printf
271 else
272 printf " "
273 fi
274 if (( i > 0 && i % 5 == 0 )); then
275 printf " "
276 fi
277 done
278 printf '"},'
279 fi
280
281 # begin seconds
282 printf '{ "name": "seconds", "full_text": "'
283
284 for ((i=0; i < 20; i++)); do
285 # This first condition is to make the transition from full to empty
286 # be less jarring. We are filling a bucket of space with ticks of
287 # time, we would have to choose to show full or empty, but never
288 # both. Or we could have a half/tick to show full then empty real
289 # quick. I decided to try having it work like a snake, empty out the
290 # 1st quarter as we fill up the last quarter.
291
292 if (( i > 0 && i % 5 == 0 )); then
293 printf " "
294 fi
295 i_end=$(( time % 60 / 3 + 1 ))
296 if (( i_end - i > 15 )); then
297 printf " "
298 elif (( i_end == 1 && i == 0 )); then
299 printf B
300 elif (( i < i_end )); then
301 printf
302 else
303 printf " "
304 fi
305 done
306 printf '"},'
307
308 ## begin day percent, in thousandths, plus a spark block for ten_thousandth.
309 printf '{ "name": "day_percent", "full_text": "'
310
311
312 # after 24 hours, reset the day start
313 if (( day_start + 24 * 60 * 60 < EPOCHSECONDS )); then
314 day_start=$(date -d $day_start_hour:$day_start_min +%s)
315 fi
316
317 # there are 9 spark levels, 1/9 = .111.... In order to keep it in bash math, we upscale
318 # the number and divide by 111, 3 digits is good enough accuracy.
319 spark_index="$(( (1000000 -(EPOCHSECONDS - day_start)*1000000 / (16*60*60) ) % 1000 / 111 ))"
320 spark=" ▁▂▃▄▅▆▇█"
321 # note: 960 minutes, so 10 minutes is about 1%
322 day_thousandth=$(( 1000 - (EPOCHSECONDS - day_start)*1000 / (16*60*60) ))
323 printf %s "$day_thousandth${spark:spark_index:1}$day_start_sig_digits $(date "+%l:%M")"
324
325 # .1% of a waking year is ~5.75 hours, or 365 thousandths of a day.
326 # 1% of a waking year is 3.7 days
327 # A spark line of a thousandth of a waking year is 39 minutes.
328 year_start=$(date +%s -d 'january 1 6am')
329 year_days=$(( (EPOCHSECONDS - year_start) / (24*60*60) ))
330 year_start=$(( year_start + year_days * 8*60*60 ))
331 year_spark_index=$(( ( 1000000 - (EPOCHSECONDS - year_start)*1000000 / (days_in_this_year*16*60*60) ) % 1000 / 111 ))
332 year_tenthousandth=$(( 10000 - (EPOCHSECONDS - year_start)*10000 / (days_in_this_year*16*60*60) ))
333 printf %s " $year_tenthousandth${spark:year_spark_index:1}"
334
335
336 echo '"}
337 ],
338 '
339 }
340
341
342 # pass any arg and we just run once. mainly for debugging
343 if (( $# )); then
344 main
345
346 # debug
347 #echo date -d $day_start_hour:$day_start_min +%s
348 #echo day_start=$day_start now=$EPOCHSECONDS
349
350 else
351 printf '{ "version": 1, "click_events": true }\n['
352 while true; do
353 if [[ -e /tmp/noi3bar ]]; then
354 sleep 10
355 else
356 main
357 line=
358 read -r -t 3 line ||:
359 fi
360 done
361 fi
362
363 rm -f $tmp_focus1 $tmp_focus2