#!/bin/bash # I, Ian Kelling, follow the GNU license recommendations at # https://www.gnu.org/licenses/license-recommendations.en.html. They # recommend that small programs, < 300 lines, be licensed under the # Apache License 2.0. This file contains or is part of one or more small # programs. If a small program grows beyond 300 lines, I plan to switch # its license to GPL. # Copyright 2024 Ian Kelling # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # usage: # # * left click seconds to reset & enable main timer which shows 30 # minutes in blue and half hours in orange. # # * right click seconds to disable minutes & half hours of main timer # # * left click minutes to reset & enable 2nd timer. # # * right click minutes to disable 2nd timer # docs: # # Sections from right to left. # # section #1, labeled day_percent # # 1. Thousandths of a 16 hour day left. 1 = 57.6 seconds. # 2. Bar of one thousand of a day which gets shorter by ninths. One ninth is 6.4 seconds. # 3. Start of the day (only the non-zero numbers). Set this with the ds command. # 4. The time right now. # 5. Ten thousands of the year left. 1 = 52:34 minutes (leap year = 52:42) # 6. Bar of 1/10,000 of a year shrinking by ninths. 1 ninth = 5:50 minutes (leap year = 5:51 mins). # # section #2, labeled seconds # # block characters, ▉, are added every 3 seconds, a total of 20 for 1 # minute. The groups of 5 blocks = 15 seconds each. # # section #3, labeled mins # # Only enabled by clicking on seconds, see usage. # # Block characters are added every minute until 30, then reset. There # are 6 groups of 5 minutes. # # Section #4 # # Half hours in orange, grouped into groups of 2. This will grow until # it fills the screen. # # Section #5 & 6 # # Only enabled by clicking on mins section, see usage. # # These repeat sections 3 and 4 in violet and some other color. # # if [ -z "$BASH_VERSION" ]; then echo "error: shell is not bash" >&2; exit 1; fi source /a/bin/bash-bear-trap/bash-bear shopt -s nullglob shopt -s dotglob tmp_focus1=$(mktemp) tmp_focus2=$(mktemp) mins=0 half_hours=0 start=$EPOCHSECONDS domins=false if date -d 'february 29' &>/dev/null; then days_in_this_year=366 else days_in_this_year=365 fi get_daystart() { if [[ /b/data/daystart -ot /b/data/daystart-generated && $day_start_24h ]]; then return 0 fi day_start_24h=$(cat /b/data/daystart) day_start_sig_digits=${day_start_24h%?} day_start_sig_digits=${day_start_sig_digits%0} day_start_hour=${day_start_24h%??} day_start_min=${day_start_24h: -2} echo $day_start_hour > /b/data/daystart-generated echo $day_start_min >> /b/data/daystart-generated day_start=$(date -d $day_start_hour:$day_start_min +%s) # for after midnight but before the day start. if (( day_start > EPOCHSECONDS )); then day_start=$(date -d "$day_start_hour:$day_start_min yesterday" +%s) fi } main() { get_daystart ## debug # if [[ $line ]]; then # echo "line=$line" >>/tmp/t # fi case $line in ""|"[") : ;; *) json="${line#,}" case $(echo "$json" | jq -r .name) in seconds) case $(echo "$json" | jq -r .button) in # left click 1) start=$EPOCHSECONDS domins=true ;; # right click 3) domins=false ;; esac ;; mins) case $(echo "$json" | jq -r .button) in 1) start2=$EPOCHSECONDS ;; 3) start2= ;; esac ;; esac esac time=$((EPOCHSECONDS - start)) total_mins=$(( time / 60 )) mins=$(( total_mins % 30 )) half_hours=$(( total_mins / 30 )) printf '[' if [[ -e /dev/shm/iank-status && ! -e /tmp/quiet-status ]]; then ps_char= eval "$(< /dev/shm/iank-status)" fi touch --date="10 minutes ago" $tmp_focus1 touch --date="30 minutes ago" $tmp_focus2 f=/tmp/focus-last-input # We output a reminder to do input to the focus app if we haven't done # it less 10 minutes ago (but dont bother if its been more than 30 # minutes, maybe we aren't running it anymore.) if [[ -e $f && $tmp_focus2 -ot $f && $tmp_focus1 -nt $f ]]; then ps_char="=======FOCUS====== $ps_char" fi if [[ -e /tmp/iank-i3-no-auto ]]; then ps_char="$ps_char I" fi if [[ -e /tmp/no-obs-auto-scene-switch ]]; then ps_char="$ps_char O" fi printf '{ "name":"status", "color":"#ED297D", "full_text": "%s' "$ps_char" printf '"},' if [[ $start2 ]]; then time2=$((EPOCHSECONDS - start2)) total_mins2=$(( time2 / 60 )) mins2=$(( total_mins2 % 30 )) half_hours2=$(( total_mins2 / 30 )) # this is duplicate of mins and half hours except for different # colors and looking at vars above. # begin half hours printf '{ "color": "#EFC93E", "full_text": "' for ((i=half_hours2-1; i >= 0 ; i--)); do printf ▉ if (( i > 0 && i % 2 == 0 )); then printf " " fi done printf '"},' # begin minutes printf '{ "name":"mins2", "color":"#ED297D", "full_text": "' for ((i=29; i >= 0 ; i--)); do if (( i < mins2 )); then printf ▉ else printf " " fi if (( i > 0 && i % 5 == 0 )); then printf " " fi done printf '"},' fi if $domins; then # begin half hours printf '{ "color": "#FFB542", "full_text": "' for ((i=half_hours-1; i >= 0 ; i--)); do printf ▉ if (( i > 0 && i % 2 == 0 )); then printf " " fi done printf '"},' # begin minutes printf '{ "name":"mins", "color":"#0D6BDD", "full_text": "' for ((i=29; i >= 0 ; i--)); do if (( i < mins )); then printf ▉ else printf " " fi if (( i > 0 && i % 5 == 0 )); then printf " " fi done printf '"},' fi # begin seconds printf '{ "name": "seconds", "full_text": "' for ((i=0; i < 20; i++)); do # This first condition is to make the transition from full to empty # be less jarring. We are filling a bucket of space with ticks of # time, we would have to choose to show full or empty, but never # both. Or we could have a half/tick to show full then empty real # quick. I decided to try having it work like a snake, empty out the # 1st quarter as we fill up the last quarter. if (( i > 0 && i % 5 == 0 )); then printf " " fi i_end=$(( time % 60 / 3 + 1 )) if (( i_end - i > 15 )); then printf " " elif (( i_end == 1 && i == 0 )); then printf B elif (( i < i_end )); then printf ▉ else printf " " fi done printf '"},' ## begin day percent, in thousandths, plus a spark block for ten_thousandth. printf '{ "name": "day_percent", "full_text": "' # after 24 hours, reset the day start if (( day_start + 24 * 60 * 60 < EPOCHSECONDS )); then day_start=$(date -d $day_start_hour:$day_start_min +%s) fi # there are 9 spark levels, 1/9 = .111.... In order to keep it in bash math, we upscale # the number and divide by 111, 3 digits is good enough accuracy. spark_index="$(( (1000000 -(EPOCHSECONDS - day_start)*1000000 / (16*60*60) ) % 1000 / 111 ))" spark=" ▁▂▃▄▅▆▇█" # note: 960 minutes, so 10 minutes is about 1% day_thousandth=$(( 1000 - (EPOCHSECONDS - day_start)*1000 / (16*60*60) )) printf %s "$day_thousandth${spark:spark_index:1}$day_start_sig_digits $(date "+%l:%M")" # .1% of a waking year is ~5.75 hours, or 365 thousandths of a day. # 1% of a waking year is 3.7 days # A spark line of a thousandth of a waking year is 39 minutes. year_start=$(date +%s -d 'january 1 6am') year_days=$(( (EPOCHSECONDS - year_start) / (24*60*60) )) year_start=$(( year_start + year_days * 8*60*60 )) year_spark_index=$(( ( 1000000 - (EPOCHSECONDS - year_start)*1000000 / (days_in_this_year*16*60*60) ) % 1000 / 111 )) year_tenthousandth=$(( 10000 - (EPOCHSECONDS - year_start)*10000 / (days_in_this_year*16*60*60) )) printf %s " $year_tenthousandth${spark:year_spark_index:1}" echo '"} ], ' } # pass any arg and we just run once. mainly for debugging if (( $# )); then main # debug #echo date -d $day_start_hour:$day_start_min +%s #echo day_start=$day_start now=$EPOCHSECONDS else printf '{ "version": 1, "click_events": true }\n[' while true; do if [[ -e /tmp/noi3bar ]]; then sleep 10 else main line= read -r -t 3 line ||: fi done fi rm -f $tmp_focus1 $tmp_focus2