added timer
parent
4b8dd4f5d9
commit
c61766960b
|
|
@ -88,7 +88,7 @@ bind = $mainMod CTRL, M, exec, ~/.config/scripts/toggle-layout.sh
|
|||
|
||||
bind = $mainMod, S, exec, [tag +mixer] pavucontrol
|
||||
bind = $mainMod, F1, exec, [tag +centered] kitty ~/.config/scripts/helpmenu.sh
|
||||
bind = $mainMod CTRL, T, exec, [tag +centered] kitty --class timer-popup bash ~/.config/scripts/timer.sh
|
||||
bind = $mainMod CTRL, T, exec, [tag +centered] kitty bash ~/.config/scripts/timer-pick
|
||||
bind = $mainMod, U, exec, [tag +centered-L] kitty btop
|
||||
bind = $mainMod SHIFT, F1, exec, [tag +centered-L] kitty nvim ~/.config/binds.conf
|
||||
bind = $mainMod CTRL, E, exec, ~/.config/scripts/screenrotationwcw.sh
|
||||
|
|
|
|||
|
|
@ -44,3 +44,4 @@ application/x-extension-xht=firefox.desktop;
|
|||
application/xml=nvim.desktop;
|
||||
text/x-python=nvim.desktop;
|
||||
video/mp4=firefox.desktop;vlc.desktop;
|
||||
application/x-shellscript=nvim.desktop;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,22 @@
|
|||
#!/usr/bin/env bash
|
||||
# Background timer: sleeps, notifies, rings thrice in 30s unless dismissed
|
||||
# Usage: timer-notify.sh <seconds> <label>
|
||||
# Usage: timer-notify.sh <seconds> <label> [--verbose]
|
||||
|
||||
DURATION=${1:?missing duration}
|
||||
LABEL=${2:-"00:00:00"}
|
||||
VERBOSE=0
|
||||
[[ "${3:-}" == "--verbose" ]] && VERBOSE=1
|
||||
|
||||
sleep "$DURATION"
|
||||
if [[ $VERBOSE -eq 1 ]]; then
|
||||
printf 'Timer started: %s\n\n' "$LABEL"
|
||||
for (( i=DURATION; i>0; i-- )); do
|
||||
printf '\r %02d:%02d:%02d remaining...' $((i/3600)) $(( (i%3600)/60 )) $((i%60))
|
||||
sleep 1
|
||||
done
|
||||
printf '\r 00:00:00 — timer fired! \n\n'
|
||||
else
|
||||
sleep "$DURATION"
|
||||
fi
|
||||
|
||||
# Find a ring sound (freedesktop stereo sounds)
|
||||
SOUND=""
|
||||
|
|
@ -0,0 +1,201 @@
|
|||
#!/usr/bin/env bash
|
||||
# timer-pick — TUI time picker
|
||||
# Install: ~/.config/scripts/timer-pick (companion: timer-run in same dir)
|
||||
|
||||
SCRIPTS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
ESC=$'\e'; CSI="${ESC}["
|
||||
cup() { printf "${CSI}%d;%dH" "$1" "$2"; }
|
||||
cls() { printf "${CSI}2J${CSI}H"; }
|
||||
hide() { printf "${CSI}?25l"; }
|
||||
show() { printf "${CSI}?25h"; }
|
||||
|
||||
b="${CSI}1m" ; d="${CSI}2m" ; r="${CSI}0m"
|
||||
cy="${CSI}96m"; gn="${CSI}92m"
|
||||
yl="${CSI}93m"; wh="${CSI}97m"; gy="${CSI}90m"
|
||||
rv="${CSI}7m"
|
||||
|
||||
FIELDS=(HH MM SS)
|
||||
MAX=(99 59 59)
|
||||
VALUES=(0 0 0)
|
||||
ACTIVE=1
|
||||
LABEL=""
|
||||
|
||||
TW=34; IW=32; CW=10
|
||||
BR=2; BC=2
|
||||
|
||||
hl() { printf '─%.0s' $(seq 1 "$1"); }
|
||||
|
||||
redraw() {
|
||||
local row=$BR col=$BC
|
||||
|
||||
cup $row $col; printf "${d}╭$(hl $IW)╮${r}"
|
||||
|
||||
(( row++ )); cup $row $col
|
||||
printf "${d}│${r}${b}${cy} ⏱ Set Timer%-*s${r}${d}│${r}" $(( IW - 14 )) ""
|
||||
|
||||
(( row++ )); cup $row $col
|
||||
printf "${d}├$(hl $CW)┬$(hl $CW)┬$(hl $CW)┤${r}"
|
||||
|
||||
# labels
|
||||
(( row++ )); cup $row $col; printf "${d}│${r}"
|
||||
for i in 0 1 2; do
|
||||
local lpad=$(( (CW - 2) / 2 )) rpad=$(( CW - 2 - (CW-2)/2 ))
|
||||
(( i == ACTIVE )) \
|
||||
&& printf "%*s${yl}${b}${FIELDS[$i]}${r}%*s" $lpad "" $rpad "" \
|
||||
|| printf "%*s${gy}${FIELDS[$i]}${r}%*s" $lpad "" $rpad ""
|
||||
(( i < 2 )) && printf "${d}│${r}"
|
||||
done; printf "${d}│${r}"
|
||||
|
||||
# up arrows
|
||||
(( row++ )); cup $row $col; printf "${d}│${r}"
|
||||
for i in 0 1 2; do
|
||||
local lpad=$(( (CW-1) / 2 )) rpad=$(( CW - 1 - (CW-1)/2 ))
|
||||
(( i == ACTIVE )) \
|
||||
&& printf "%*s${yl}${b}▲${r}%*s" $lpad "" $rpad "" \
|
||||
|| printf "%*s${gy}▲${r}%*s" $lpad "" $rpad ""
|
||||
(( i < 2 )) && printf "${d}│${r}"
|
||||
done; printf "${d}│${r}"
|
||||
|
||||
# values
|
||||
(( row++ )); cup $row $col; printf "${d}│${r}"
|
||||
for i in 0 1 2; do
|
||||
local vstr; printf -v vstr '%02d' "${VALUES[$i]}"
|
||||
local lpad=$(( (CW-4) / 2 )) rpad=$(( CW - 4 - (CW-4)/2 ))
|
||||
(( i == ACTIVE )) \
|
||||
&& printf "%*s${rv}${wh}${b} %s ${r}%*s" $lpad "" "$vstr" $rpad "" \
|
||||
|| printf "%*s${d} %s ${r}%*s" $lpad "" "$vstr" $rpad ""
|
||||
(( i < 2 )) && printf "${d}│${r}"
|
||||
done; printf "${d}│${r}"
|
||||
|
||||
# down arrows
|
||||
(( row++ )); cup $row $col; printf "${d}│${r}"
|
||||
for i in 0 1 2; do
|
||||
local lpad=$(( (CW-1) / 2 )) rpad=$(( CW - 1 - (CW-1)/2 ))
|
||||
(( i == ACTIVE )) \
|
||||
&& printf "%*s${yl}${b}▼${r}%*s" $lpad "" $rpad "" \
|
||||
|| printf "%*s${gy}▼${r}%*s" $lpad "" $rpad ""
|
||||
(( i < 2 )) && printf "${d}│${r}"
|
||||
done; printf "${d}│${r}"
|
||||
|
||||
(( row++ )); cup $row $col; printf "${d}├$(hl $IW)┤${r}"
|
||||
|
||||
(( row++ )); cup $row $col
|
||||
local ld="${LABEL:-(optional)}"
|
||||
printf "${d}│${r} ${gy}label:${r} ${cy}%-*s${r}${d}│${r}" $(( IW - 8 )) "$ld"
|
||||
|
||||
(( row++ )); cup $row $col; printf "${d}├$(hl $IW)┤${r}"
|
||||
|
||||
(( row++ )); cup $row $col
|
||||
printf "${d}│${r} ${gy}h/l${r} field ${gy}j/k${r} value ${gy}[N]j/k${r} ${d}│${r}"
|
||||
(( row++ )); cup $row $col
|
||||
printf "${d}│${r} ${gy}i${r} label ${gy}0/G${r} min/max ${gn}↵${r} start ${d}│${r}"
|
||||
|
||||
(( row++ )); cup $row $col; printf "${d}╰$(hl $IW)╯${r}"
|
||||
|
||||
cup $(( row + 1 )) 1
|
||||
}
|
||||
|
||||
read_label() {
|
||||
show
|
||||
local lrow=$(( BR + 9 )) lcol=$(( BC + 9 ))
|
||||
cup $lrow $lcol; printf "${CSI}K"
|
||||
local buf="" ch
|
||||
while IFS= read -rsn1 ch; do
|
||||
case "$ch" in
|
||||
$'\n'|$'\r'|$'\e') break ;;
|
||||
$'\x7f'|$'\b')
|
||||
[[ -n $buf ]] && buf="${buf%?}"
|
||||
cup $lrow $lcol; printf "${CSI}K%s" "$buf" ;;
|
||||
*) [[ ${#buf} -lt $(( IW - 10 )) ]] && buf+="$ch" && printf "%s" "$ch" ;;
|
||||
esac
|
||||
done
|
||||
LABEL="$buf"; hide
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
# only reset tty if we actually changed it
|
||||
[[ "${TTY_SAVED:-0}" == "1" ]] && stty sane 2>/dev/null
|
||||
show
|
||||
cls
|
||||
tput cup 0 0 2>/dev/null
|
||||
}
|
||||
|
||||
main() {
|
||||
# Wait for kitty to fully attach the pty — critical when launched via Hyprland
|
||||
# keybind where the window and pty aren't synchronised at script start.
|
||||
local tries=0
|
||||
until [[ -t 0 && -t 1 ]]; do
|
||||
sleep 0.05
|
||||
(( tries++ ))
|
||||
(( tries > 40 )) && exit 1 # give up after 2s
|
||||
done
|
||||
|
||||
hide; cls
|
||||
stty -echo -icanon min 1 time 0
|
||||
TTY_SAVED=1
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
redraw
|
||||
|
||||
local ch seq count_buf=""
|
||||
|
||||
while true; do
|
||||
IFS= read -rsn1 ch || continue
|
||||
if [[ $ch == $'\e' ]]; then
|
||||
IFS= read -rsn2 -t 0.05 seq || seq=""
|
||||
ch="${ch}${seq}"
|
||||
fi
|
||||
|
||||
if [[ $ch =~ ^[1-9]$ ]]; then count_buf+="$ch"; redraw; continue; fi
|
||||
local N=1
|
||||
[[ -n $count_buf && $count_buf =~ ^[0-9]+$ ]] && N=$count_buf
|
||||
|
||||
case "$ch" in
|
||||
$'\t'|$'\e[C'|l) ACTIVE=$(( (ACTIVE + 1) % 3 )); count_buf="" ;;
|
||||
$'\e[Z'|$'\e[D'|h) ACTIVE=$(( (ACTIVE + 2) % 3 )); count_buf="" ;;
|
||||
'^') ACTIVE=0; count_buf="" ;;
|
||||
'$') ACTIVE=2; count_buf="" ;;
|
||||
|
||||
$'\e[A'|k)
|
||||
local mx=${MAX[$ACTIVE]} v=${VALUES[$ACTIVE]}
|
||||
local nv=$(( v + N )); (( nv > mx )) && nv=$mx
|
||||
VALUES[$ACTIVE]=$nv; count_buf="" ;;
|
||||
|
||||
$'\e[B'|j)
|
||||
local v=${VALUES[$ACTIVE]}
|
||||
local nv=$(( v - N )); (( nv < 0 )) && nv=0
|
||||
VALUES[$ACTIVE]=$nv; count_buf="" ;;
|
||||
|
||||
0)
|
||||
if [[ -n $count_buf ]]; then count_buf+="0"; redraw; continue; fi
|
||||
VALUES[$ACTIVE]=0; count_buf="" ;;
|
||||
G) VALUES[$ACTIVE]=${MAX[$ACTIVE]}; count_buf="" ;;
|
||||
|
||||
i) count_buf=""; read_label ;;
|
||||
|
||||
''|$'\n')
|
||||
count_buf=""
|
||||
local total=$(( VALUES[0]*3600 + VALUES[1]*60 + VALUES[2] ))
|
||||
if (( total == 0 )); then redraw; continue; fi
|
||||
TTY_SAVED=0
|
||||
stty sane 2>/dev/null; show; cls; tput cup 0 0 2>/dev/null
|
||||
# SIG_IGN for SIGHUP is inherited through exec, eliminating the race
|
||||
# between fork() and setsid() creating its own session. Without this,
|
||||
# kitty closing the pty on exit sends SIGHUP to the foreground process
|
||||
# group (which includes the child in non-interactive bash) before setsid
|
||||
# has a chance to move it to a new session.
|
||||
( trap '' SIGHUP; exec setsid bash "$SCRIPTS_DIR/timer-run" "$total" "$LABEL" </dev/null >/dev/null 2>&1 ) &
|
||||
exit 0 ;;
|
||||
|
||||
q|Q) exit 0 ;;
|
||||
*) count_buf="" ;;
|
||||
esac
|
||||
|
||||
redraw
|
||||
done
|
||||
}
|
||||
|
||||
main
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
#!/usr/bin/env bash
|
||||
# timer-run <total_seconds> [label]
|
||||
# Runs entirely in background. No terminal needed after launch.
|
||||
# Sends a dunst notification + audio beep when done.
|
||||
#
|
||||
# Install: ~/.config/scripts/timer-run (companion: timer-pick in same dir)
|
||||
|
||||
TOTAL="${1:?missing seconds}"
|
||||
LABEL="${2:-}"
|
||||
|
||||
# ── source the user environment if running without a login shell ───────────────
|
||||
# When launched from Hyprland keybind → kitty → exec, the process inherits
|
||||
# kitty's env which already has DBUS_SESSION_BUS_ADDRESS, XDG_RUNTIME_DIR etc.
|
||||
# But after setsid detach those are preserved since we don't re-login.
|
||||
# We do need to make sure XDG_RUNTIME_DIR is set for pipewire/pulse socket paths.
|
||||
if [[ -z "${XDG_RUNTIME_DIR:-}" ]]; then
|
||||
export XDG_RUNTIME_DIR="/run/user/$(id -u)"
|
||||
fi
|
||||
|
||||
# ── detach from terminal immediately ──────────────────────────────────────────
|
||||
# Preserve the full environment across the setsid re-exec (no login shell).
|
||||
if [[ -t 0 || -t 1 || -t 2 ]]; then
|
||||
( trap '' SIGHUP; exec setsid bash "$0" "$TOTAL" "$LABEL" </dev/null >/dev/null 2>&1 ) &
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ── sleep until done ──────────────────────────────────────────────────────────
|
||||
sleep "$TOTAL"
|
||||
|
||||
# ── format a human-readable duration ─────────────────────────────────────────
|
||||
fmt_dur() {
|
||||
local s=$1 h=0 m=0 out=""
|
||||
h=$(( s / 3600 )); s=$(( s % 3600 ))
|
||||
m=$(( s / 60 )); s=$(( s % 60 ))
|
||||
(( h > 0 )) && out+="${h}h "
|
||||
(( m > 0 )) && out+="${m}m "
|
||||
(( s > 0 || (h == 0 && m == 0) )) && out+="${s}s"
|
||||
echo "${out% }"
|
||||
}
|
||||
|
||||
DURATION_STR=$(fmt_dur "$TOTAL")
|
||||
NOTIF_SUMMARY="⏰ Timer done${LABEL:+ — $LABEL}"
|
||||
NOTIF_BODY="Set for ${DURATION_STR}. Finished at $(date '+%H:%M:%S')."
|
||||
|
||||
# ── dunst notification ────────────────────────────────────────────────────────
|
||||
# notify-send needs DBUS_SESSION_BUS_ADDRESS. If not in env, find it via the
|
||||
# running dunst process (reliable on single-user Wayland/Hyprland setups).
|
||||
if [[ -z "${DBUS_SESSION_BUS_ADDRESS:-}" ]]; then
|
||||
local_uid=$(id -u)
|
||||
# try to grab it from the environment of any process owned by this user
|
||||
for pid in $(pgrep -u "$local_uid" dunst 2>/dev/null); do
|
||||
addr=$(cat /proc/$pid/environ 2>/dev/null \
|
||||
| tr '\0' '\n' \
|
||||
| grep '^DBUS_SESSION_BUS_ADDRESS=' \
|
||||
| cut -d= -f2-)
|
||||
if [[ -n $addr ]]; then
|
||||
export DBUS_SESSION_BUS_ADDRESS="$addr"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if command -v notify-send &>/dev/null; then
|
||||
notify-send \
|
||||
--urgency=critical \
|
||||
--expire-time=0 \
|
||||
--icon=alarm-timer \
|
||||
"$NOTIF_SUMMARY" \
|
||||
"$NOTIF_BODY"
|
||||
fi
|
||||
|
||||
# ── audio alert ───────────────────────────────────────────────────────────────
|
||||
# pw-play / paplay need PIPEWIRE_RUNTIME_DIR or PULSE_RUNTIME_PATH which live
|
||||
# under XDG_RUNTIME_DIR — already ensured above.
|
||||
|
||||
_beep_pcm() {
|
||||
python3 - <<'PYEOF'
|
||||
import struct, math, sys
|
||||
|
||||
RATE = 44100
|
||||
FREQ = 880.0
|
||||
VOL = 0.65
|
||||
BEEPS = 3
|
||||
DUR = 0.55
|
||||
GAP = 0.18
|
||||
|
||||
def sine(freq, secs):
|
||||
n = int(RATE * secs)
|
||||
return [int(VOL * 32767 * math.sin(2 * math.pi * freq * i / RATE)) for i in range(n)]
|
||||
|
||||
def silence(secs):
|
||||
return [0] * int(RATE * secs)
|
||||
|
||||
samples = []
|
||||
for i in range(BEEPS):
|
||||
samples += sine(FREQ, DUR)
|
||||
if i < BEEPS - 1:
|
||||
samples += silence(GAP)
|
||||
|
||||
sys.stdout.buffer.write(struct.pack(f'<{len(samples)}h', *samples))
|
||||
PYEOF
|
||||
}
|
||||
|
||||
if command -v pw-play &>/dev/null; then
|
||||
_beep_pcm | pw-play --rate=44100 --channels=1 --format=s16 - 2>/dev/null
|
||||
elif command -v paplay &>/dev/null; then
|
||||
_beep_pcm | paplay --raw --rate=44100 --channels=1 --format=s16le - 2>/dev/null
|
||||
elif command -v aplay &>/dev/null; then
|
||||
_beep_pcm | aplay -q -r 44100 -c 1 -f S16_LE - 2>/dev/null
|
||||
fi
|
||||
|
|
@ -1,162 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
# TUI timer setter — vim keys, big-digit hh:mm:ss
|
||||
|
||||
# ── Setup ──────────────────────────────────────────────────────────────────────
|
||||
cleanup() { tput cnorm; tput rmcup; }
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
tput smcup
|
||||
tput civis
|
||||
|
||||
# ── Colors ─────────────────────────────────────────────────────────────────────
|
||||
R="$(tput sgr0)"
|
||||
NORMAL="$(tput bold)$(tput setaf 7)"
|
||||
SELECT="$(tput bold)$(tput setaf 6)" # cyan — active field
|
||||
TITLE="$(tput bold)$(tput setaf 5)" # magenta
|
||||
DIM="$(tput dim)$(tput setaf 7)"
|
||||
ERR="$(tput bold)$(tput setaf 1)" # red
|
||||
|
||||
# ── Block digits — DIGITS[digit*5 + row] ───────────────────────────────────────
|
||||
DIGITS=(
|
||||
"█████" "█ █" "█ █" "█ █" "█████" # 0
|
||||
" █ " " █ " " █ " " █ " " █ " # 1
|
||||
"█████" " █" "█████" "█ " "█████" # 2
|
||||
"█████" " █" "█████" " █" "█████" # 3
|
||||
"█ █" "█ █" "█████" " █" " █" # 4
|
||||
"█████" "█ " "█████" " █" "█████" # 5
|
||||
"█████" "█ " "█████" "█ █" "█████" # 6
|
||||
"█████" " █" " █" " █" " █" # 7
|
||||
"█████" "█ █" "█████" "█ █" "█████" # 8
|
||||
"█████" "█ █" "█████" " █" "█████" # 9
|
||||
)
|
||||
COLON=(" " "█" " " "█" " ")
|
||||
|
||||
# ── State ──────────────────────────────────────────────────────────────────────
|
||||
VALUES=(0 0 0)
|
||||
LIMITS=(23 59 59)
|
||||
LABELS=("HOURS" "MINUTES" "SECONDS")
|
||||
FIELD=0
|
||||
SHOW_ERR=0
|
||||
|
||||
# ── Draw helpers ───────────────────────────────────────────────────────────────
|
||||
draw_field() { # draw_field row col val active
|
||||
local y=$1 x=$2 val=$3 active=$4
|
||||
local tens=$((val / 10)) units=$((val % 10))
|
||||
local color; [[ $active -eq 1 ]] && color=$SELECT || color=$NORMAL
|
||||
for row in 0 1 2 3 4; do
|
||||
tput cup $((y + row)) $x
|
||||
printf '%s%s %s%s' \
|
||||
"$color" \
|
||||
"${DIGITS[$((tens * 5 + row))]}" \
|
||||
"${DIGITS[$((units * 5 + row))]}" \
|
||||
"$R"
|
||||
done
|
||||
}
|
||||
|
||||
draw_colon() { # draw_colon row col
|
||||
local y=$1 x=$2
|
||||
for row in 0 1 2 3 4; do
|
||||
tput cup $((y + row)) $((x + 1))
|
||||
printf '%s%s%s' "$DIM" "${COLON[$row]}" "$R"
|
||||
done
|
||||
}
|
||||
|
||||
draw() {
|
||||
local W H
|
||||
W=$(tput cols); H=$(tput lines)
|
||||
|
||||
printf '\033[2J\033[H' # clear + home (no flicker vs tput clear)
|
||||
|
||||
# Title
|
||||
local title="─── T I M E R ───"
|
||||
tput cup 2 $(( (W - ${#title}) / 2 ))
|
||||
printf '%s%s%s' "$TITLE" "$title" "$R"
|
||||
|
||||
# Layout: field(11) + colon(3) + field(11) + colon(3) + field(11) = 39 wide
|
||||
local sx=$(( (W - 39) / 2 ))
|
||||
local dy=$(( (H - 5) / 2 - 1 ))
|
||||
(( dy < 4 )) && dy=4
|
||||
|
||||
local -a fx=( $sx $((sx + 14)) $((sx + 28)) )
|
||||
local -a cx=( $((sx + 11)) $((sx + 25)) )
|
||||
|
||||
for i in 0 1 2; do
|
||||
local active=0; [[ $i -eq $FIELD ]] && active=1
|
||||
draw_field $dy "${fx[$i]}" "${VALUES[$i]}" $active
|
||||
done
|
||||
for x in "${cx[@]}"; do
|
||||
draw_colon $dy $x
|
||||
done
|
||||
|
||||
# Labels
|
||||
local ly=$((dy + 6))
|
||||
for i in 0 1 2; do
|
||||
local label="${LABELS[$i]}"
|
||||
local lx=$(( fx[$i] + (11 - ${#label}) / 2 ))
|
||||
tput cup $ly $lx
|
||||
if [[ $i -eq $FIELD ]]; then
|
||||
printf '%s%s%s' "$SELECT" "$label" "$R"
|
||||
else
|
||||
printf '%s%s%s' "$DIM" "$label" "$R"
|
||||
fi
|
||||
done
|
||||
|
||||
# Error
|
||||
if [[ $SHOW_ERR -eq 1 ]]; then
|
||||
local msg="set a non-zero time first"
|
||||
tput cup $((ly + 2)) $(( (W - ${#msg}) / 2 ))
|
||||
printf '%s%s%s' "$ERR" "$msg" "$R"
|
||||
fi
|
||||
|
||||
# Hint
|
||||
local hint="h/l field j/k adjust Enter start q cancel"
|
||||
tput cup $((H - 2)) $(( (W - ${#hint}) / 2 ))
|
||||
printf '%s%s%s' "$DIM" "$hint" "$R"
|
||||
}
|
||||
|
||||
# ── Key reader — handles ESC sequences (arrow keys) ───────────────────────────
|
||||
read_key() {
|
||||
local key seq
|
||||
IFS= read -rsn1 key
|
||||
if [[ "$key" == $'\033' ]]; then
|
||||
IFS= read -rsn2 -t 0.05 seq 2>/dev/null || true
|
||||
key="$key$seq"
|
||||
fi
|
||||
printf '%s' "$key"
|
||||
}
|
||||
|
||||
# ── Main loop ──────────────────────────────────────────────────────────────────
|
||||
while true; do
|
||||
draw
|
||||
SHOW_ERR=0
|
||||
key=$(read_key)
|
||||
|
||||
case "$key" in
|
||||
q|$'\033')
|
||||
exit 0
|
||||
;;
|
||||
h|$'\033[D')
|
||||
FIELD=$(( (FIELD + 2) % 3 ))
|
||||
;;
|
||||
l|$'\033[C')
|
||||
FIELD=$(( (FIELD + 1) % 3 ))
|
||||
;;
|
||||
k|$'\033[A')
|
||||
VALUES[$FIELD]=$(( (VALUES[$FIELD] + 1) % (LIMITS[$FIELD] + 1) ))
|
||||
;;
|
||||
j|$'\033[B')
|
||||
VALUES[$FIELD]=$(( (VALUES[$FIELD] - 1 + LIMITS[$FIELD] + 1) % (LIMITS[$FIELD] + 1) ))
|
||||
;;
|
||||
$'\n'|$'\r')
|
||||
total=$(( VALUES[0] * 3600 + VALUES[1] * 60 + VALUES[2] ))
|
||||
if (( total == 0 )); then
|
||||
SHOW_ERR=1
|
||||
else
|
||||
label=$(printf '%02d:%02d:%02d' "${VALUES[0]}" "${VALUES[1]}" "${VALUES[2]}")
|
||||
setsid bash "$HOME/.config/scripts/timer-notify.sh" "$total" "$label" &>/dev/null &
|
||||
disown
|
||||
exit 0
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
#!/usr/bin/env bash
|
||||
# TUI timer setter — vim keys, unicode frames, labeled fields
|
||||
# Usage: timer.sh [-v|--verbose]
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$(realpath "$0")")" && pwd)"
|
||||
|
||||
VERBOSE=0
|
||||
for arg in "$@"; do
|
||||
[[ "$arg" == "-v" || "$arg" == "--verbose" ]] && VERBOSE=1
|
||||
done
|
||||
|
||||
cleanup() { tput cnorm; tput rmcup; }
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
tput smcup
|
||||
tput civis
|
||||
|
||||
R="$(tput sgr0)"
|
||||
NORMAL="$(tput setaf 7)"
|
||||
SELECT="$(tput bold)$(tput setaf 6)"
|
||||
TITLE="$(tput bold)$(tput setaf 5)"
|
||||
DIM="$(tput dim)$(tput setaf 7)"
|
||||
ERR="$(tput bold)$(tput setaf 1)"
|
||||
|
||||
VALUES=(0 0 0)
|
||||
LIMITS=(23 59 59)
|
||||
LABELS=("HOURS" "MINUTES" "SECONDS")
|
||||
FIELD=0
|
||||
SHOW_ERR=0
|
||||
KEY=""
|
||||
|
||||
# draw_box row col val label active
|
||||
# Box inner width = 10, total = 12, height = 6
|
||||
draw_box() {
|
||||
local y=$1 x=$2 val=$3 label=$4 active=$5
|
||||
local num; printf -v num '%02d' "$val"
|
||||
local inner=10
|
||||
|
||||
local tl tr bl br bh bv color
|
||||
if [[ $active -eq 1 ]]; then
|
||||
tl='╔' tr='╗' bl='╚' br='╝' bh='═' bv='║' color=$SELECT
|
||||
else
|
||||
tl='┌' tr='┐' bl='└' br='┘' bh='─' bv='│' color=$NORMAL
|
||||
fi
|
||||
|
||||
local hline; printf -v hline '%*s' $inner ''; hline="${hline// /$bh}"
|
||||
|
||||
local lpad=$(( (inner - ${#label}) / 2 ))
|
||||
local lrpad=$(( inner - lpad - ${#label} ))
|
||||
local npad=$(( (inner - ${#num}) / 2 ))
|
||||
local nrpad=$(( inner - npad - ${#num} ))
|
||||
|
||||
tput cup $y $x; printf '%s%s%s%s%s' "$color" "$tl" "$hline" "$tr" "$R"
|
||||
tput cup $((y+1)) $x; printf '%s%s%*s%s%*s%s%s' "$color" "$bv" $lpad '' "$label" $lrpad '' "$bv" "$R"
|
||||
tput cup $((y+2)) $x; printf '%s%s%*s%s%s' "$color" "$bv" $inner '' "$bv" "$R"
|
||||
tput cup $((y+3)) $x; printf '%s%s%*s%s%*s%s%s' "$color" "$bv" $npad '' "$num" $nrpad '' "$bv" "$R"
|
||||
tput cup $((y+4)) $x; printf '%s%s%*s%s%s' "$color" "$bv" $inner '' "$bv" "$R"
|
||||
tput cup $((y+5)) $x; printf '%s%s%s%s%s' "$color" "$bl" "$hline" "$br" "$R"
|
||||
}
|
||||
|
||||
draw_colon() {
|
||||
local y=$1 x=$2
|
||||
tput cup $((y+3)) $x
|
||||
printf '%s ∶ %s' "$DIM" "$R"
|
||||
}
|
||||
|
||||
draw() {
|
||||
local W H
|
||||
W=$(tput cols); H=$(tput lines)
|
||||
printf '\033[2J\033[H'
|
||||
|
||||
local title="─── T I M E R ───"
|
||||
tput cup 2 $(( (W - ${#title}) / 2 ))
|
||||
printf '%s%s%s' "$TITLE" "$title" "$R"
|
||||
|
||||
# 3 boxes × 12 wide + 2 colons × 3 wide = 42 total
|
||||
local total=42
|
||||
local sx=$(( (W - total) / 2 ))
|
||||
local dy=$(( (H - 6) / 2 ))
|
||||
(( dy < 4 )) && dy=4
|
||||
|
||||
local -a bx=( $sx $((sx + 15)) $((sx + 30)) )
|
||||
|
||||
for i in 0 1 2; do
|
||||
local active=0; [[ $i -eq $FIELD ]] && active=1
|
||||
draw_box $dy "${bx[$i]}" "${VALUES[$i]}" "${LABELS[$i]}" $active
|
||||
done
|
||||
|
||||
draw_colon $dy $((sx + 12))
|
||||
draw_colon $dy $((sx + 27))
|
||||
|
||||
if [[ $SHOW_ERR -eq 1 ]]; then
|
||||
local msg="set a non-zero time first"
|
||||
tput cup $((dy + 7)) $(( (W - ${#msg}) / 2 ))
|
||||
printf '%s%s%s' "$ERR" "$msg" "$R"
|
||||
fi
|
||||
|
||||
local verbose_tag=""; [[ $VERBOSE -eq 1 ]] && verbose_tag=" [verbose]"
|
||||
local hint="h/l field j/k adjust Enter start q cancel${verbose_tag}"
|
||||
tput cup $((H - 2)) $(( (W - ${#hint}) / 2 ))
|
||||
printf '%s%s%s' "$DIM" "$hint" "$R"
|
||||
}
|
||||
|
||||
read_key() {
|
||||
local seq
|
||||
IFS= read -rsn1 KEY
|
||||
if [[ "$KEY" == $'\033' ]]; then
|
||||
IFS= read -rsn2 -t 0.05 seq 2>/dev/null || true
|
||||
KEY="$KEY$seq"
|
||||
fi
|
||||
}
|
||||
|
||||
while true; do
|
||||
draw
|
||||
SHOW_ERR=0
|
||||
read_key
|
||||
|
||||
case "$KEY" in
|
||||
q|$'\033')
|
||||
exit 0
|
||||
;;
|
||||
h|$'\033[D')
|
||||
FIELD=$(( (FIELD + 2) % 3 ))
|
||||
;;
|
||||
l|$'\033[C')
|
||||
FIELD=$(( (FIELD + 1) % 3 ))
|
||||
;;
|
||||
k|$'\033[A')
|
||||
VALUES[$FIELD]=$(( (VALUES[$FIELD] + 1) % (LIMITS[$FIELD] + 1) ))
|
||||
;;
|
||||
j|$'\033[B')
|
||||
VALUES[$FIELD]=$(( (VALUES[$FIELD] - 1 + LIMITS[$FIELD] + 1) % (LIMITS[$FIELD] + 1) ))
|
||||
;;
|
||||
$'\n'|$'\r')
|
||||
total=$(( VALUES[0] * 3600 + VALUES[1] * 60 + VALUES[2] ))
|
||||
if (( total == 0 )); then
|
||||
SHOW_ERR=1
|
||||
else
|
||||
label=$(printf '%02d:%02d:%02d' "${VALUES[0]}" "${VALUES[1]}" "${VALUES[2]}")
|
||||
if [[ $VERBOSE -eq 1 ]]; then
|
||||
kitty --title "Timer: $label" bash "$SCRIPT_DIR/timer-notify.sh" "$total" "$label" --verbose &
|
||||
disown
|
||||
else
|
||||
setsid bash "$SCRIPT_DIR/timer-notify.sh" "$total" "$label" &>/dev/null &
|
||||
disown
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
<property name="last-separator-position" type="int" value="139"/>
|
||||
<property name="misc-single-click" type="bool" value="false"/>
|
||||
<property name="last-details-view-zoom-level" type="string" value="THUNAR_ZOOM_LEVEL_50_PERCENT"/>
|
||||
<property name="last-details-view-column-widths" type="string" value="50,50,159,50,50,186,87,150,620,50,50,87,159,177"/>
|
||||
<property name="last-details-view-column-widths" type="string" value="50,50,159,50,50,186,87,645,580,50,50,96,159,231"/>
|
||||
<property name="last-details-view-visible-columns" type="string" value="THUNAR_COLUMN_DATE_MODIFIED,THUNAR_COLUMN_GROUP,THUNAR_COLUMN_MIME_TYPE,THUNAR_COLUMN_NAME,THUNAR_COLUMN_SIZE,THUNAR_COLUMN_SIZE_IN_BYTES,THUNAR_COLUMN_TYPE"/>
|
||||
<property name="last-side-pane" type="string" value="THUNAR_SIDEPANE_TYPE_TREE"/>
|
||||
<property name="last-show-hidden" type="bool" value="true"/>
|
||||
|
|
|
|||
Loading…
Reference in New Issue