diff --git a/profile.d/rain.sh b/profile.d/rain.sh index 046c5ca..1833afc 100644 --- a/profile.d/rain.sh +++ b/profile.d/rain.sh @@ -35,23 +35,201 @@ # ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------ -# Let the rain fall +# Generic rain-like engine and presets + +_rain_build_colors() +{ + local base_color="$1" + RAIN_ENGINE_COLORS=() + + case $base_color in + green) + for i in {22..28} {34..40} {46..48}; do RAIN_ENGINE_COLORS+=("\e[38;5;${i}m"); done ;; + blue) + for i in {17..21} {27..33} {39..45}; do RAIN_ENGINE_COLORS+=("\e[38;5;${i}m"); done ;; + red) + for i in {52..52} {88..88} {124..124} {160..160} {196..201}; do RAIN_ENGINE_COLORS+=("\e[38;5;${i}m"); done ;; + yellow) + for i in {58..58} {100..100} {142..142} {184..184} {226..229}; do RAIN_ENGINE_COLORS+=("\e[38;5;${i}m"); done ;; + cyan) + for i in {30..31} {37..38} {44..45} {50..51}; do RAIN_ENGINE_COLORS+=("\e[38;5;${i}m"); done ;; + *) + RAIN_ENGINE_COLORS=("\e[37m" "\e[37;1m") + for i in {244..255}; do RAIN_ENGINE_COLORS+=("\e[38;5;${i}m"); done ;; + esac +} + +_rain_build_chars() +{ + local mode="$1" + local charset="$2" + RAIN_ENGINE_CHARS=() + + case "$mode" in + matrix) + case "$charset" in + ""|binary) + RAIN_ENGINE_CHARS=("0" "1") + ;; + kana|kanji) + # Half-width katakana set, generally rendered as single-cell glyphs. + RAIN_ENGINE_CHARS=("ア" "イ" "ウ" "エ" "オ" "カ" "キ" "ク" "ケ" "コ" "サ" "シ" "ス" "セ" "ソ" "タ" "チ" "ツ" "テ" "ト" "ナ" "ニ" "ヌ" "ネ" "ノ" "ハ" "ヒ" "フ" "ヘ" "ホ" "マ" "ミ" "ム" "メ" "モ" "ヤ" "ユ" "ヨ" "ラ" "リ" "ル" "レ" "ロ" "ワ" "ン") + ;; + ascii) + RAIN_ENGINE_CHARS=("0" "1" "2" "3" "4" "5" "6" "7" "8" "9" "A" "B" "C" "D" "E" "F") + ;; + *) + disp E "Unknown charset: ${charset} (supported: binary, kana, ascii)." + return 1 + ;; + esac + ;; + *) + RAIN_ENGINE_CHARS=("|" "│" "┃" "┆" "┇" "┊" "┋" "╽" "╿") + ;; + esac + + return 0 +} + +_rain_engine() +{ + local step_duration="$1" + local base_color="$2" + local mode="$3" + local charset="$4" + + command -v tput >/dev/null 2>&1 || { + disp E "The program 'tput' is required but not installed." + return 1 + } + + _rain_build_colors "$base_color" + _rain_build_chars "$mode" "$charset" || return 1 + + local rain_colors=("${RAIN_ENGINE_COLORS[@]}") + local rain_chars=("${RAIN_ENGINE_CHARS[@]}") + local rain_color_tab=${#rain_colors[@]} + local rain_tab=${#rain_chars[@]} + + local exit_st=0 + local num_rain_metadata=5 + local term_height=0 term_width=0 + local X=0 Y=0 drop_length=0 rain_drop=0 + local max_rain_width=0 max_rain_height=0 + local new_rain_odd=0 falling_odd=0 + + sigwinch() + { + term_width=$(tput cols) + term_height=$(tput lines) + + case "$mode" in + matrix) + ((max_rain_width = term_width * term_height / 3)) + ((max_rain_height = term_height < 8 ? 1 : term_height / 6)) + ((new_rain_odd = term_height > 50 ? 100 : term_height * 2)) + ((new_rain_odd = new_rain_odd * 85 / 100)) + ((falling_odd = 100)) + ;; + *) + ((max_rain_width = term_width * term_height / 4)) + ((max_rain_height = term_height < 10 ? 1 : term_height / 10)) + ((new_rain_odd = term_height > 50 ? 100 : term_height * 2)) + ((new_rain_odd = new_rain_odd * 75 / 100)) + ((falling_odd = term_height > 25 ? 100 : term_height * 4)) + ((falling_odd = falling_odd * 90 / 100)) + ;; + esac + } + + do_exit() + { + exit_st=1 + } + + do_render() + { + local idx=0 y=0 drop_color="" + + for ((idx = 0; idx < num_rains * num_rain_metadata; idx += num_rain_metadata)); do + X=${rains[idx]} + Y=${rains[idx + 1]} + drop_length=${rains[idx + 4]} + for ((y = Y; y < Y + drop_length; y++)); do + ((y < 1 || y > term_height)) && continue + printf "\e[%d;%dH " "$y" "$X" + done + done + + for ((idx = 0; idx < num_rains * num_rain_metadata; idx += num_rain_metadata)); do + if ((100 * RANDOM / 32768 < falling_odd)); then + if ((++rains[idx + 1] > term_height)); then + rains=("${rains[@]:0:idx}" "${rains[@]:idx+num_rain_metadata:num_rains*num_rain_metadata}") + ((num_rains--)) + continue + fi + fi + + X=${rains[idx]} + Y=${rains[idx + 1]} + rain_drop=${rains[idx + 2]} + drop_color=${rains[idx + 3]} + drop_length=${rains[idx + 4]} + + for ((y = Y; y < Y + drop_length; y++)); do + ((y < 1 || y > term_height)) && continue + printf "\e[%d;%dH%s%s" "$y" "$X" "$drop_color" "$rain_drop" + done + done + } + + trap do_exit TERM INT + trap sigwinch WINCH + stty -echo + printf "\e[?25l" + printf "\e[2J" + + local rains=() + local num_rains=0 + local ch="" + + sigwinch + while ((exit_st <= 0)); do + read -r -n 1 -t "$step_duration" ch + case "$ch" in + q|Q) + do_exit + ;; + esac + + if ((num_rains < max_rain_width)) && ((100 * RANDOM / 32768 < new_rain_odd)); then + rain_drop="${rain_chars[rain_tab * RANDOM / 32768]}" + drop_color="${rain_colors[rain_color_tab * RANDOM / 32768]}" + drop_length=$((max_rain_height * RANDOM / 32768 + 1)) + X=$((term_width * RANDOM / 32768 + 1)) + Y=$((1 - drop_length)) + rains=("${rains[@]}" "$X" "$Y" "$rain_drop" "$drop_color" "$drop_length") + ((num_rains++)) + fi + + do_render + done + + printf "\e[%d;1H\e[0K" "$term_height" + printf "\e[?25h" + stty echo + trap - TERM INT + trap - WINCH +} + +# ------------------------------------------------------------------------------ +# Let the rain fall (current style) # Usage: rain [OPTIONS] -# Options: -# -s, --speed NUM Set the drop delay in seconds (default: 0.050). -# Lower values = faster rain. -# -c, --color COLOR Set the color theme (default: white). -# -h, --help Display this help message and exit. -# Available Colors: -# green : The classic Matrix digital rain -# blue : Deep ocean blue gradients -# red : Crimson/Blood rain -# yellow : Amber and gold tones -# cyan : Electric cyan/turquoise -# white : Greyscale and white (original style) rain() { - show_usage() { + _rain_show_usage() + { printf "Usage: rain [OPTIONS]\n" printf "Options:\n" printf "\t-s, --speed NUM Set the drop delay in seconds (default: 0.050).\n" @@ -59,7 +237,7 @@ rain() printf "\t-c, --color COLOR Set the color theme (default: white).\n" printf "\t-h, --help Display this help message and exit.\n\n" printf "Available Colors:\n" - printf "\t\e[32mgreen\e[0m\t: The classic Matrix digital rain\n" + printf "\t\e[32mgreen\e[0m\t: Matrix-like green shades\n" printf "\t\e[34mblue\e[0m\t: Deep ocean blue gradients\n" printf "\t\e[31mred\e[0m\t: Crimson/Blood rain\n" printf "\t\e[33myellow\e[0m\t: Amber and gold tones\n" @@ -69,163 +247,124 @@ rain() } local step_duration=0.050 - local base_color="white" # default color scheme, can be overridden by --color + local base_color="white" - # Analyse arguments while [[ "$#" -gt 0 ]]; do case $1 in -s|--speed) if [[ -n "$2" && ! "$2" =~ ^- ]]; then - step_duration="$2"; shift + step_duration="$2" + shift else disp E "--speed requires a numeric value." - show_usage && return 1 + _rain_show_usage + return 1 fi ;; -c|--color) if [[ -n "$2" && ! "$2" =~ ^- ]]; then - base_color="$2"; shift + base_color="$2" + shift else disp E "--color requires a color name." - show_usage && return 1 + _rain_show_usage + return 1 fi ;; -h|--help) - show_usage && return 0 + _rain_show_usage + return 0 + ;; + --) + shift + break ;; *) disp E "Unknown option: $1" - show_usage && return 1 + _rain_show_usage + return 1 ;; esac shift done - # Define colors (256-colors gradients) - local rain_colors=() - case $base_color in - green) # Matrix style green - for i in {22..28} {34..40} {46..48}; do rain_colors+=("\e[38;5;${i}m"); done ;; - blue) # Deep ocean blues - for i in {17..21} {27..33} {39..45}; do rain_colors+=("\e[38;5;${i}m"); done ;; - red) # Crimson / blood red - for i in {52..52} {88..88} {124..124} {160..160} {196..201}; do rain_colors+=("\e[38;5;${i}m"); done ;; - yellow) # Amber / gold - for i in {58..58} {100..100} {142..142} {184..184} {226..229}; do rain_colors+=("\e[38;5;${i}m"); done ;; - cyan) # Electric cyan / turquoise - for i in {30..31} {37..38} {44..45} {50..51}; do rain_colors+=("\e[38;5;${i}m"); done ;; - *) # Greyscale / white (original style) - rain_colors=("\e[37m" "\e[37;1m") - for i in {244..255}; do rain_colors+=("\e[38;5;${i}m"); done ;; - esac - - local exit_st=0 - local rain_cars=("|" "│" "┃" "┆" "┇" "┊" "┋" "╽" "╿") - local rain_tab=${#rain_cars[@]} - local rain_color_tab=${#rain_colors[@]} - local num_rain_metadata=5 - local term_height=$(tput lines) - local term_width=$(tput cols) - local X=0 Y=0 drop_length=0 rain_drop=0 - local max_rain_width=0 new_rain_odd=0 falling_odd=0 - - sigwinch() { - term_width=$(tput cols) - term_height=$(tput lines) - #step_duration=0.025 - ((max_rain_width = term_width * term_height / 4)) - ((max_rain_height = term_height < 10 ? 1 : term_height / 10)) - # In percentage - ((new_rain_odd = term_height > 50 ? 100 : term_height * 2)) - ((new_rain_odd = new_rain_odd * 75 / 100)) - ((falling_odd = term_height > 25 ? 100 : term_height * 4)) - ((falling_odd = falling_odd * 90 / 100)) - } - - do_exit() { - exit_st=1 - } - - do_render() { - # Clean screen first - local idx=0 - for ((idx = 0; idx < num_rains * num_rain_metadata; idx += num_rain_metadata)); do - X=${rains[idx]} - Y=${rains[idx + 1]} - drop_length=${rains[idx + 4]} - for ((y = Y; y < Y + drop_length; y++)); do - ((y < 1 || y > term_height)) && continue - echo -ne "\e[${y};${X}H " - done - done - - for ((idx = 0; idx < num_rains * num_rain_metadata; idx += num_rain_metadata)); do - if ((100 * RANDOM / 32768 < falling_odd)); then - # Falling - if ((++rains[idx + 1] > term_height)); then - # Out of screen, bye sweet <3 - rains=("${rains[@]:0:idx}" - "${rains[@]:idx+num_rain_metadata:num_rains*num_rain_metadata}") - ((num_rains--)) - continue - fi - fi - X=${rains[idx]} - Y=${rains[idx + 1]} - rain_drop=${rains[idx + 2]} - drop_color=${rains[idx + 3]} - drop_length=${rains[idx + 4]} - for ((y = Y; y < Y + drop_length; y++)); do - ((y < 1 || y > term_height)) && continue - printf "\e[${y};${X}H${drop_color}${rain_drop}" - done - done - } - - trap do_exit TERM INT - trap sigwinch WINCH - # No echo stdin and hide the cursor - stty -echo - printf "\e[?25l" - printf "\e[2J" - - local rains=() - local num_rains=0 - sigwinch - while ((exit_st <= 0)); do - if (($exit_st <= 0)); then - read -n 1 -t $step_duration ch - case "$ch" in - q | Q) - do_exit - ;; - esac - - if ((num_rains < max_rain_width)) && ((100 * RANDOM / 32768 < new_rain_odd)); then - # Need new |, 1-based - rain_drop="${rain_cars[rain_tab * RANDOM / 32768]}" - drop_color="${rain_colors[rain_color_tab * RANDOM / 32768]}" - drop_length=$((max_rain_height * RANDOM / 32768 + 1)) - X=$((term_width * RANDOM / 32768 + 1)) - Y=$((1 - drop_length)) - rains=("${rains[@]}" "$X" "$Y" "$rain_drop" "$drop_color" "$drop_length") - ((num_rains++)) - fi - - # Let rain fall! - do_render - fi - done - echo -ne "\e[${term_height};1H\e[0K" - - # Show cursor and echo stdin - echo -ne "\e[?25h" - stty echo - unset exit_st - trap - TERM INT - trap - WINCH + _rain_engine "$step_duration" "$base_color" "rain" "" } export -f rain +# ------------------------------------------------------------------------------ +# Matrix style digital rain +# Usage: matrix [OPTIONS] +matrix() +{ + _matrix_show_usage() + { + printf "Usage: matrix [OPTIONS]\n" + printf "Options:\n" + printf "\t-s, --speed NUM Set the drop delay in seconds (default: 0.035).\n" + printf "\t Lower values = faster stream.\n" + printf "\t-c, --color COLOR Set color theme (default: green).\n" + printf "\t-t, --charset SET Character set: binary, kana, ascii (default: binary).\n" + printf "\t-h, --help Display this help message and exit.\n\n" + printf "Example: matrix --charset kana --speed 0.02\n" + } + + local step_duration=0.035 + local base_color="green" + local charset="binary" + + while [[ "$#" -gt 0 ]]; do + case $1 in + -s|--speed) + if [[ -n "$2" && ! "$2" =~ ^- ]]; then + step_duration="$2" + shift + else + disp E "--speed requires a numeric value." + _matrix_show_usage + return 1 + fi + ;; + -c|--color) + if [[ -n "$2" && ! "$2" =~ ^- ]]; then + base_color="$2" + shift + else + disp E "--color requires a color name." + _matrix_show_usage + return 1 + fi + ;; + -t|--charset) + if [[ -n "$2" && ! "$2" =~ ^- ]]; then + charset="${2,,}" + shift + else + disp E "--charset requires a value." + _matrix_show_usage + return 1 + fi + ;; + -h|--help) + _matrix_show_usage + return 0 + ;; + --) + shift + break + ;; + *) + disp E "Unknown option: $1" + _matrix_show_usage + return 1 + ;; + esac + shift + done + + _rain_engine "$step_duration" "$base_color" "matrix" "$charset" +} +export -f matrix + # ------------------------------------------------------------------------------ # EOF