2df974662eca2923488c6c8eeb2484e0aad928d8
[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 if [[ -e /tmp/no-obs-auto-scene-switch ]]; then
181 ps_char="$ps_char O"
182 fi
183
184 if pgrep -fc '^ffmpeg.*icecast://source.*/fsf' &>/dev/null; then
185 if [[ -e /tmp/iank-ffmpeg-interlude-toggle ]]; then
186 ps_char="= BRB = $ps_char"
187 else
188 ps_char="=|=|= STREAMING =|=|= $ps_char"
189 if pactl get-source-mute @DEFAULT_SOURCE@ 2>/dev/null | awk '{print $2}' | grep no &>/dev/null; then
190 ps_char="! UNMUTED ! $ps_char"
191 fi
192 fi
193 fi
194
195
196 printf '{ "name":"status", "color":"#ED297D", "full_text": "%s' "$ps_char"
197 printf '"},'
198
199
200 if [[ $start2 ]]; then
201 time2=$((EPOCHSECONDS - start2))
202 total_mins2=$(( time2 / 60 ))
203 mins2=$(( total_mins2 % 30 ))
204 half_hours2=$(( total_mins2 / 30 ))
205 # this is duplicate of mins and half hours except for different
206 # colors and looking at vars above.
207
208 # begin half hours
209 printf '{ "color": "#EFC93E", "full_text": "'
210 for ((i=half_hours2-1; i >= 0 ; i--)); do
211 printf
212 if (( i > 0 && i % 2 == 0 )); then
213 printf " "
214 fi
215 done
216 printf '"},'
217
218
219 # begin minutes
220 printf '{ "name":"mins2", "color":"#ED297D", "full_text": "'
221 for ((i=29; i >= 0 ; i--)); do
222 if (( i < mins2 )); then
223 printf
224 else
225 printf " "
226 fi
227 if (( i > 0 && i % 5 == 0 )); then
228 printf " "
229 fi
230 done
231 printf '"},'
232
233 fi
234
235 if $domins; then
236 # begin half hours
237 printf '{ "color": "#FFB542", "full_text": "'
238 for ((i=half_hours-1; i >= 0 ; i--)); do
239 printf
240 if (( i > 0 && i % 2 == 0 )); then
241 printf " "
242 fi
243 done
244 printf '"},'
245
246
247 # begin minutes
248 printf '{ "name":"mins", "color":"#0D6BDD", "full_text": "'
249 for ((i=29; i >= 0 ; i--)); do
250 if (( i < mins )); then
251 printf
252 else
253 printf " "
254 fi
255 if (( i > 0 && i % 5 == 0 )); then
256 printf " "
257 fi
258 done
259 printf '"},'
260 fi
261
262 # begin seconds
263 printf '{ "name": "seconds", "full_text": "'
264
265 for ((i=0; i < 20; i++)); do
266 # This first condition is to make the transition from full to empty
267 # be less jarring. We are filling a bucket of space with ticks of
268 # time, we would have to choose to show full or empty, but never
269 # both. Or we could have a half/tick to show full then empty real
270 # quick. I decided to try having it work like a snake, empty out the
271 # 1st quarter as we fill up the last quarter.
272
273 if (( i > 0 && i % 5 == 0 )); then
274 printf " "
275 fi
276 i_end=$(( time % 60 / 3 + 1 ))
277 if (( i_end - i > 15 )); then
278 printf " "
279 elif (( i_end == 1 && i == 0 )); then
280 printf B
281 elif (( i < i_end )); then
282 printf
283 else
284 printf " "
285 fi
286 done
287 printf '"},'
288
289 ## begin day percent, in thousandths, plus a spark block for ten_thousandth.
290 printf '{ "name": "day_percent", "full_text": "'
291
292
293 # after 24 hours, reset the day start
294 if (( day_start + 24 * 60 * 60 < EPOCHSECONDS )); then
295 day_start=$(date -d $day_start_hour:$day_start_min +%s)
296 fi
297
298 # there are 9 spark levels, 1/9 = .111.... In order to keep it in bash math, we upscale
299 # the number and divide by 111, 3 digits is good enough accuracy.
300 spark_index="$(( (1000000 -(EPOCHSECONDS - day_start)*1000000 / (16*60*60) ) % 1000 / 111 ))"
301 spark=" ▁▂▃▄▅▆▇█"
302 # note: 960 minutes, so 10 minutes is about 1%
303 day_thousandth=$(( 1000 - (EPOCHSECONDS - day_start)*1000 / (16*60*60) ))
304 printf %s "$day_thousandth${spark:spark_index:1}$day_start_sig_digits $(date "+%l:%M")"
305
306 # .1% of a waking year is ~5.75 hours, or 365 thousandths of a day.
307 # 1% of a waking year is 3.7 days
308 # A spark line of a thousandth of a waking year is 39 minutes.
309 year_start=$(date +%s -d 'january 1 6am')
310 year_days=$(( (EPOCHSECONDS - year_start) / (24*60*60) ))
311 year_start=$(( year_start + year_days * 8*60*60 ))
312 year_spark_index=$(( ( 1000000 - (EPOCHSECONDS - year_start)*1000000 / (days_in_this_year*16*60*60) ) % 1000 / 111 ))
313 year_tenthousandth=$(( 10000 - (EPOCHSECONDS - year_start)*10000 / (days_in_this_year*16*60*60) ))
314 printf %s " $year_tenthousandth${spark:year_spark_index:1}"
315
316
317 echo '"}
318 ],
319 '
320 }
321
322
323 # pass any arg and we just run once. mainly for debugging
324 if (( $# )); then
325 main
326
327 # debug
328 #echo date -d $day_start_hour:$day_start_min +%s
329 #echo day_start=$day_start now=$EPOCHSECONDS
330
331 else
332 printf '{ "version": 1, "click_events": true }\n['
333 while true; do
334 if [[ -e /tmp/noi3bar ]]; then
335 sleep 10
336 else
337 main
338 line=
339 read -r -t 3 line ||:
340 fi
341 done
342 fi
343
344 rm -f $tmp_focus1 $tmp_focus2