#!/bin/bash
# buffKeysInterval - Bufferize keyboard interaction and return at fixed interval
# (C) 2026 F-Hauri.ch - http://www.f-hauri.ch
# Licensed under terms of GPL v3. www.gnu.org
# Version: 0.0.1 -- Last update: Fri Jul  3 19:12:50 2026
# Usage:
#     getKeyInterval.sh <float interval in seconds>
# Interaction:
#     Use `q` (or `Q`) to quit!

buffKeysInterval() {		# Bufferize user interactions in a fixed period
    local crtslp key subkey nextStep nsec=${1:-150000}
    nextStep=$(((${EPOCHREALTIME/.} / nsec + 1)* nsec))	# sleep $nsec, while
    while ((${EPOCHREALTIME/.}<nextStep)); do		# reading keyboard
	crtslp=${EPOCHREALTIME/.}			# entries...
	# Doing ``pre-sleep'' in order to reduce *deviation*.
	crtslp=00000$(( nextStep - crtslp > 8000 ?
			nextStep - crtslp - 8000 : nextStep - crtslp ))
	printf -v crtslp %.6f ${crtslp::-6}.${crtslp: -6}
	if IFS= read -d '' -rsn 1 -t "$crtslp" key; then
	    [[ $key == $'\e' ]] &&			# trap special keys
		while IFS= read -d '' -rsn 1 -t .002 subkey; do
		    [[ $subkey == $'\e' ]] && keys+=("$key") key=
		    key+=$subkey
		done
	    keys+=("$key")
	fi
    done
}

printf -v sec %.6f "${1:-.15}"	# convert argument from second to nanosecond
set -- $((10#${sec/.}))

tput civis			# Hide cursor
oTTy=$(stty -g)			# Stop ECHO on terminal and trap all keys
stty -icanon -echo intr undef susp undef stop undef start undef
trap 'tput cnorm;stty "$oTTy"' 0 1 3 6 15
declare -a spinnerShapes=( ⢈⡱  ⢄⡱  ⢆⡰  ⢎⡠  ⢎⡁  ⢎⠑  ⠎⠱  ⠊⡱ )

while :;do
    keys=()
    buffKeysInterval "$1"
    IFS=. read -r sec mus <<<$EPOCHREALTIME
    # Compute deviations (relative: dev1 and absolute: dev2)
    dev1=$(( old >0 ? old + ${1:-150000} - sec*1000000 - 10#$mus : 0 ))
    dev2=$((  ( sec*1000000 + 10#$mus ) % ${1:-150000} ))
    old="$sec$mus"
    # Prepare list of hitted keys, comma separated.
    printf -v buff '%s, ' "${keys[@]@Q}"
    printf '%(%F %T)T.%s (dev. %6s, %6s): %2d: %s  %s\e[K\r' \
	   "$sec" "$mus" "$dev1" "$dev2" "${#keys[@]}" "${buff%, }" \
	   "${spinnerShapes[pnt++%${#spinnerShapes[@]}]}"
    # Add newline (and clean spinner shape) if any key was hitted.
    ((${#keys[@]})) && printf '%(%F %T)T.%s (dev. %6s, %6s): %2d: %s.\e[K\n' \
	   "$sec" "$mus" "$dev1" "$dev2" "${#keys[@]}" "${buff%, }"

    [[ ${keys[*],} == *q* ]] && break
done
