From 08a0586b1518285eaf7eccac18d40d3d53b4134e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20La=C3=ADn?= Date: Mon, 11 Dec 2023 21:04:47 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat(system):=20replaced=20pacman?= =?UTF-8?q?=20installed=20packages=20with=20just=20their=20binary=20or=20s?= =?UTF-8?q?cript?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit removing bloat pacman packages --- .config/pacman/aur-packages.txt | 3 - .local/bin/cbonsai | Bin 0 -> 36880 bytes .local/bin/pipes | 385 ++++++++++++++++ .local/bin/tty-clock | Bin 0 -> 44760 bytes .local/bin/unimatrix | 766 ++++++++++++++++++++++++++++++++ 5 files changed, 1151 insertions(+), 3 deletions(-) create mode 100755 .local/bin/cbonsai create mode 100755 .local/bin/pipes create mode 100755 .local/bin/tty-clock create mode 100755 .local/bin/unimatrix diff --git a/.config/pacman/aur-packages.txt b/.config/pacman/aur-packages.txt index a41fda83..cec13c48 100644 --- a/.config/pacman/aur-packages.txt +++ b/.config/pacman/aur-packages.txt @@ -31,7 +31,6 @@ pacseek 1.8.2-1 pcsx2-latest-bin 1.7.5201-1 pika-backup 1:0.6.2-1 pinterest-downloader-git 7d540b1.20221130.157-1 -pipes.sh 1.3.0-1 proton-ge-custom-bin 1:GE_Proton8_25-1 protontricks 1.10.5-1 pyprland 1.6.8-1 @@ -58,8 +57,6 @@ tgpt-bin 2.2.1-1 topgrade 13.0.0-1 ttf-ms-win11-auto 10.0.22621.525-1 ttf-symbola 14.00-1 -tty-clock-git 1:2.3.r40.f2f847c-2 -unimatrix-git r66.75f7402-1 universal-android-debloater 0.6.1-1 upscayl-bin 2.9.5-1 ventoy-bin 1.0.96-1 diff --git a/.local/bin/cbonsai b/.local/bin/cbonsai new file mode 100755 index 0000000000000000000000000000000000000000..177c329325c7867b6903cc4dcac8a26863a382c9 GIT binary patch literal 36880 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 AbN~PV literal 0 HcmV?d00001 diff --git a/.local/bin/pipes b/.local/bin/pipes new file mode 100755 index 00000000..73346ef2 --- /dev/null +++ b/.local/bin/pipes @@ -0,0 +1,385 @@ +#!/usr/bin/env bash +# pipes.sh: Animated pipes terminal screensaver. +# https://github.com/pipeseroni/pipes.sh +# +# Copyright (c) 2015-2018 Pipeseroni/pipes.sh contributors +# Copyright (c) 2013-2015 Yu-Jie Lin +# Copyright (c) 2010 Matthew Simpson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +VERSION=1.3.0 + +M=32768 # Bash RANDOM maximum + 1 +p=1 # number of pipes +f=75 # frame rate +s=13 # probability of straight fitting +r=2000 # characters limit +t=0 # iteration counter for -r character limit +w=80 # terminal size +h=24 + +# ab -> sets[][idx] = a*4 + b +# 0: up, 1: right, 2: down, 3: left +# 00 means going up , then going up -> ┃ +# 12 means going right, then going down -> ┓ +sets=( + "┃┏ ┓┛━┓ ┗┃┛┗ ┏━" + "│╭ ╮╯─╮ ╰│╯╰ ╭─" + "│┌ ┐┘─┐ └│┘└ ┌─" + "║╔ ╗╝═╗ ╚║╝╚ ╔═" + "|+ ++-+ +|++ +-" + "|/ \/-\ \|/\ /-" + ".. .... .... .." + ".o oo.o o.oo o." + "-\ /\|/ /-\/ \|" # railway + "╿┍ ┑┚╼┒ ┕╽┙┖ ┎╾" # knobby pipe +) +SETS=() # rearranged all pipe chars into individul elements for easier access + +# pipes' +x=() # current position +y=() +l=() # current directions + # 0: up, 1: right, 2: down, 3: left +n=() # new directions +v=() # current types +c=() # current escape codes + +# selected pipes' +V=() # types (indexes to sets[]) +C=() # color indices for tput setaf +VN=0 # number of selected types +CN=0 # number of selected colors +E=() # pre-generated escape codes from BOLD, NOCOLOR, and C + +# switches +RNDSTART=0 # randomize starting position and direction +BOLD=1 +NOCOLOR=0 +KEEPCT=0 # keep pipe color and type + + +# print help message in 72-char width +print_help() { + local cgap + printf -v cgap '%*s' $((15 - ${#COLORS})) '' + cat <= 0 + is_N() { + [[ -n $1 && -z ${1//[0-9]} ]] + } + + + # test if $1 is a hexadecimal string + is_hex() { + [[ -n $1 && -z ${1//[0-9A-Fa-f]} ]] + } + + + # print error message for invalid argument to standard error, this + # - mimics getopts error message + # - use all positional parameters as error message + # - has a newline appended + # $arg and $OPTARG are the option name and argument set by getopts. + pearg() { + printf "%s: -$arg invalid argument -- $OPTARG; %s\n" "$0" "$*" >&2 + } + + + OPTIND=1 + while getopts "p:t:c:f:s:r:RBCKhv" arg; do + case $arg in + p) + if is_N "$OPTARG" && ((OPTARG > 0)); then + p=$OPTARG + else + pearg 'must be an integer and greater than 0' + return 1 + fi + ;; + t) + if [[ "$OPTARG" = c???????????????? ]]; then + V+=(${#sets[@]}) + sets+=("${OPTARG:1}") + elif is_N "$OPTARG" && ((OPTARG < ${#sets[@]})); then + V+=($OPTARG) + else + pearg 'must be an integer and from 0 to' \ + "$((${#sets[@]} - 1)); or a custom type" + return 1 + fi + ;; + c) + if [[ $OPTARG == '#'* ]]; then + if ! is_hex "${OPTARG:1}"; then + pearg 'unrecognized hexadecimal string' + return 1 + fi + if ((16$OPTARG >= COLORS)); then + pearg 'hexadecimal must be from #0 to' \ + "#$(printf '%X' $((COLORS - 1)))" + return 1 + fi + C+=($((16$OPTARG))) + elif is_N "$OPTARG" && ((OPTARG < COLORS)); then + C+=($OPTARG) + else + pearg "must be an integer and from 0 to $((COLORS - 1));" \ + 'or a hexadecimal string with # prefix' + return 1 + fi + ;; + f) + if is_N "$OPTARG" && ((OPTARG >= 20 && OPTARG <= 100)); then + f=$OPTARG + else + pearg 'must be an integer and from 20 to 100' + return 1 + fi + ;; + s) + if is_N "$OPTARG" && ((OPTARG >= 5 && OPTARG <= 15)); then + s=$OPTARG + else + pearg 'must be an integer and from 5 to 15' + return 1 + fi + ;; + r) + if is_N "$OPTARG"; then + r=$OPTARG + else + pearg 'must be a non-negative integer' + return 1 + fi + ;; + R) RNDSTART=1;; + B) BOLD=0;; + C) NOCOLOR=1;; + K) KEEPCT=1;; + h) + print_help + exit 0 + ;; + v) echo "$(basename -- "$0") $VERSION" + exit 0 + ;; + *) + return 1 + esac + done + + shift $((OPTIND - 1)) + if (($#)); then + printf "$0: illegal arguments -- $*; no arguments allowed\n" >&2 + return 1 + fi +} + + +cleanup() { + # clear out standard input + read -t 0.001 && cat /dev/null + + tput reset # fix for konsole, see pipeseroni/pipes.sh#43 + tput rmcup + tput cnorm + stty echo + printf "$SGR0" + exit 0 +} + + +resize() { + w=$(tput cols) h=$(tput lines) +} + + +init_pipes() { + # +_CP_init_pipes + local i + + ci=$((KEEPCT ? 0 : CN * RANDOM / M)) + vi=$((KEEPCT ? 0 : VN * RANDOM / M)) + for ((i = 0; i < p; i++)); do + (( + n[i] = 0, + l[i] = RNDSTART ? RANDOM % 4 : 0, + x[i] = RNDSTART ? w * RANDOM / M : w / 2, + y[i] = RNDSTART ? h * RANDOM / M : h / 2, + v[i] = V[vi] + )) + c[i]=${E[ci]} + ((ci = (ci + 1) % CN, vi = (vi + 1) % VN)) + done + # -_CP_init_pipes +} + + +init_screen() { + stty -echo + tput smcup + tput civis + tput clear + trap cleanup HUP TERM + + resize + trap resize SIGWINCH +} + + +main() { + # simple pre-check of TERM, tput's error message should be enough + tput -T "$TERM" sgr0 >/dev/null || return $? + + # +_CP_init_termcap_vars + COLORS=$(tput colors) # COLORS - 1 == maximum color index for -c argument + SGR0=$(tput sgr0) + SGR_BOLD=$(tput bold) + # -_CP_init_termcap_vars + + parse "$@" || return $? + + # +_CP_init_VC + # set default values if not by options + ((${#V[@]})) || V=(0) + VN=${#V[@]} + ((${#C[@]})) || C=(1 2 3 4 5 6 7 0) + CN=${#C[@]} + # -_CP_init_VC + + # +_CP_init_E + # generate E[] based on BOLD (SGR_BOLD), NOCOLOR, and C for each element in + # C, a corresponding element in E[] = + # SGR0 + # + SGR_BOLD, if BOLD + # + tput setaf C, if !NOCOLOR + local i + for ((i = 0; i < CN; i++)) { + E[i]=$SGR0 + ((BOLD)) && E[i]+=$SGR_BOLD + ((NOCOLOR)) || E[i]+=$(tput setaf ${C[i]}) + } + # -_CP_init_E + + # +_CP_init_SETS + local i j + for ((i = 0; i < ${#sets[@]}; i++)) { + for ((j = 0; j < 16; j++)) { + SETS+=("${sets[i]:j:1}") + } + } + unset i j + # -_CP_init_SETS + + init_screen + init_pipes + + # any key press exits the loop and this script + trap 'break 2' INT + + local i + while REPLY=; do + read -t 0.0$((1000 / f)) -n 1 2>/dev/null + case "$REPLY" in + P) ((s = s < 15 ? s + 1 : s));; + O) ((s = s > 3 ? s - 1 : s));; + F) ((f = f < 100 ? f + 1 : f));; + D) ((f = f > 20 ? f - 1 : f));; + B) ((BOLD = (BOLD + 1) % 2));; + C) ((NOCOLOR = (NOCOLOR + 1) % 2));; + K) ((KEEPCT = (KEEPCT + 1) % 2));; + ?) break;; + esac + for ((i = 0; i < p; i++)); do + # New position: + # l[] direction = 0: up, 1: right, 2: down, 3: left + # +_CP_newpos + ((l[i] % 2)) && ((x[i] += -l[i] + 2, 1)) || ((y[i] += l[i] - 1)) + # -_CP_newpos + + # Loop on edges (change color on loop): + # +_CP_warp + ((!KEEPCT && (x[i] >= w || x[i] < 0 || y[i] >= h || y[i] < 0))) \ + && { c[i]=${E[CN * RANDOM / M]}; ((v[i] = V[VN * RANDOM / M])); } + ((x[i] = (x[i] + w) % w, + y[i] = (y[i] + h) % h)) + # -_CP_warp + + # new turning direction: + # $((s - 1)) in $s, going straight, therefore n[i] == l[i]; + # and 1 in $s that pipe makes a right or left turn + # + # s * RANDOM / M - 1 == 0 + # n[i] == -1 + # => n[i] == l[i] + 1 or l[i] - 1 + # +_CP_newdir + (( + n[i] = s * RANDOM / M - 1, + n[i] = n[i] >= 0 ? l[i] : l[i] + (2 * (RANDOM % 2) - 1), + n[i] = (n[i] + 4) % 4 + )) + # -_CP_newdir + + # Print: + # +_CP_print + printf '\e[%d;%dH%s%s' \ + $((y[i] + 1)) $((x[i] + 1)) ${c[i]} \ + "${SETS[v[i] * 16 + l[i] * 4 + n[i]]}" + # -_CP_print + l[i]=${n[i]} + done + ((r > 0 && t * p >= r)) && tput reset && tput civis && t=0 || ((t++)) + done + + cleanup +} + + +# when being sourced, $0 == bash, only invoke main when they are the same +[[ "$0" != "$BASH_SOURCE" ]] || main "$@" diff --git a/.local/bin/tty-clock b/.local/bin/tty-clock new file mode 100755 index 0000000000000000000000000000000000000000..135b6a6fe350f2c094ac0f4f889d735762e9af16 GIT binary patch literal 44760 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 B&vyU- literal 0 HcmV?d00001 diff --git a/.local/bin/unimatrix b/.local/bin/unimatrix new file mode 100755 index 00000000..098f9289 --- /dev/null +++ b/.local/bin/unimatrix @@ -0,0 +1,766 @@ +#!/usr/bin/env python3 +# +# unimatrix.py +# +# +# Python script to simulate the display from "The Matrix" in terminal. Uses +# half-width katakana unicode characters by default, but can use custom +# character sets. Accepts keyboard controls while running. +# +# Based on CMatrix by Chris Allegretta and Abishek V. Ashok. The following +# option should produce virtually the same output as CMatrix: +# $ unimatrix -n -s 96 -l o +# +# Unimatrix is free software: you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation, either version 3 of the License, or (at your option) any later +# version. +# +# Unimatrix is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License at +# for more details. +# +# Created by William Mannard +# 2018/01/19 + +import argparse +import curses +import time +from random import choice, randint + +help_msg = ''' +USAGE + unimatrix [-a] [-b] [-c COLOR] [-f] [-g COLOR] [-h] [-l CHARACTER_LIST] [-n] + [-o] [-s SPEED] [-u CUSTOM_CHARACTERS] + +OPTIONAL ARGUMENTS + -a Asynchronous scroll. Lines will move at varied speeds. + + -b Use only bold characters + + -c COLOR One of: green (default), red, blue, white, yellow, cyan, + magenta, black + + -f Enable "flashers," characters that continuously change. + + -g COLOR Background color (See -c). Defaults to keeping + terminal's current background. + + -h Show this help message and exit + + -i Ignore keyboard + + -l CHARACTER_LIST Select character set(s) using a string over letter + codes (see CHARACTER SETS below.) + + -n Do not use bold characters (overrides -b) + + -o Disable on-screen status + + -s SPEED Integer up to 100. 0 uses a one-second delay before + refreshing, 100 uses none. Use negative numbers for + even lower speeds. Default=85 + + -t TIME Exit the process after TIME seconds + + -u CUSTOM_CHARACTERS Your own string of characters to display. Enclose in + single quotes ('') to escape special characters. For + example: -u '#$(' + + -w Single-wave mode: Does a single burst of green rain, + exits. You can put in a .bashrc file to run when your + terminal launches. Works well with speed at 95. + +LONG ARGUMENTS + -a --asynchronous + -b --all-bold + -c --color=COLOR + -f --flashers + -g --bg-color=COLOR + -h --help + -i --ignore-keyboard + -l --character-list=CHARACTER_LIST + -s --speed=SPEED + -n --no-bold + -o --status-off + -t --time + -u --custom-characters=CUSTOM_CHARACTERS + -w --single-wave + +CHARACTER SETS + When using '-l' or '--character-list=' option, follow it with one or more of + the following letters: + + a Lowercase alphabet + A Uppercase alphabet + c Lowercase Russian Cyrillic alphabet + C Uppercase Russian Cyrillic alphabet + e A few common emoji ( ☺☻✌♡♥❤⚘❀❃❁✼☀✌♫♪☃❄❅❆☕☂★ ) + g Lowercase Greek alphabet + G Uppercase Greek alphabet + k Japanese katakana (half-width) + m Default 'Matrix' set, equal to 'knnssss' + n Numbers 0-9 + o 'Old' style non-unicode set, like cmatrix. Equal to 'AaSn' + p Klingon pIqaD (requires 'Horta' family font)* + P Klingon pIqaD (requires 'Klingon-pIqaD' or 'Code2000' family font)* + r Lowercase Roman numerals ( mcclllxxxxvvvvviiiiii ) + R Uppercase Roman numerals ( MCCLLLXXXXVVVVVIIIIII ) + s A subset of symbols actually used in the Matrix films ( -=*_+|:<>" ) + S All common keyboard symbols ( `-=~!z#$%^&*()_+[]{}|\;':",./<>?" ) + u Custom characters selected using -u switch + + For example: '-l naAS' or '--character-list=naAS' will give something similar + to the output of the original cmatrix program in its default mode. + '-l ACG' will use all the upper-case character sets. Use the same + letter multiple times to increase the frequency of the character set. For + example, the default setting is equal to '-l knnssss'. + + * With most modern Linux terminals (gnome-terminal, konsole, lxterminal, + xfce4-terminal, mate-terminal) simply having the font installed system-wide + is enough. The terminal will fall back to it for the Klingon, meaning that + you don't have to select the font in your terminal settings. 'Horta' seems + not to work in Konsole. Fonts may need to be set manually as fallbacks in + .Xresources for older terminals, such as urxvt and xterm. + +KEYBOARD CONTROL + SPACE, CTRL-c or q exit + - or LEFT decrease speed by 1 + + or RIGHT increase speed by 1 + [ or DOWN decrease speed by 10 + ] or UP increase speed by 10 + a toggle asynchronous scrolling + b cycle through bold character options + (bold off-->bold on-->all bold) + f toggle flashing characters + o toggle on-screen status + 1 to 9 set color: (1) Green (2) Red (3) Blue (4) White + (5) Yellow (6) Cyan (7) Magenta (8) Black + (9) Terminal default + ! to ( set background color (same colors as above, but pressing + shift + number) + +EXAMPLES + Mimic default output of cmatrix (no unicode characters, works in TTY): + $ unimatrix -n -s 96 -l o + + Use the letters from the name of your favorite operating system in bold blue: + $ unimatrix -B -u Linux -c blue + + Use default character set, plus dollar symbol (note single quotes around + special character): + $ unimatrix -l knnssssu -u '$' + + No bold characters, slowly, using emojis, numbers and a few symbols: + $ unimatrix -n -l ens -s 50 +''' + +### Set up parser and apply arguments settings + +parser = argparse.ArgumentParser(add_help=False) + +parser.add_argument('-a', '--asynchronous', + action='store_true', + help='use asynchronous scrolling') +parser.add_argument('-b', '--all-bold', + action='store_true', + help='use all bold characters') +parser.add_argument('-c', '--color', + default='green', + help='one of: green (default), red, blue, white, yellow, \ + cyan, magenta, black', + type=str) +parser.add_argument('-f', '--flashers', + action='store_true', + help='some characters will continuously change in place') +parser.add_argument('-g', '--bg-color', + default='default', + help='background color (see -c)', + type=str) +parser.add_argument('-h', '--help', + help='display extended usage information and exit.', + action='store_true') +parser.add_argument('-i', '--ignore-keyboard', + help='ignore all keyboard input.', + action='store_true') +parser.add_argument('-l', '--character-list', + help='character set. See details below', + type=str) +parser.add_argument('-n', '--no-bold', + action='store_true', + help='do not use bold characters') +parser.add_argument('-o', '--status-off', + action='store_true', + help='Disable on-screen status') +parser.add_argument('-s', '--speed', + help='speed, integer up to 100. Default=85', + default=85, + type=int) +parser.add_argument('-t', '--time', + help='time. See details below', + type=int) +parser.add_argument('-u', '--custom-characters', + help='your own string of characters to display', + default='', + type=str) +parser.add_argument('-w', '--single-wave', + help='runs a single "wave" of green rain then exits', + action='store_true') + +args = parser.parse_args() + +if args.help: + print(help_msg) + exit() + +char_set = { + + 'a': 'qwertyuiopasdfghjklzxcvbnm', + 'A': 'QWERTYUIOPASDFGHJKLZXCVBNM', + 'c': 'абвгдежзиклмнопрстуфхцчшщъыьэюя', + 'C': 'АБВГДЕЖЗИКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ', + 'e': '☺☻✌♡♥❤⚘❀❃❁✼☀✌♫♪☃❄❅❆☕☂★', + 'g': 'αβγδεζηθικλμνξοπρστυφχψως', + 'G': 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ', + 'k': 'ヲァィゥェォャュョッーアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン', + 'm': 'ヲァィゥェォャュョッーアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン1234567890' + '1234567890-=*_+|:<>"-=*_+|:<>"-=*_+|:<>"-=*_+|:<>"', + 'n': '1234567890', + 'o': 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890' + '`-=~!@#$%^&*()_+[]{}|\;\':",./<>?"', + 'p': '', + 'P': '', + 'r': 'mcclllxxxxvvvvviiiiii', + 'R': 'MCCLLLXXXXVVVVVIIIIII', + 's': '-=*_+|:<>"', + 'S': '`-=~!@#$%^&*()_+[]{}|\;\':",./<>?"', + 'u': args.custom_characters} + +colors_str = { + 'green': curses.COLOR_GREEN, + 'red': curses.COLOR_RED, + 'blue': curses.COLOR_BLUE, + 'white': curses.COLOR_WHITE, + 'yellow': curses.COLOR_YELLOW, + 'cyan': curses.COLOR_CYAN, + 'magenta': curses.COLOR_MAGENTA, + 'black': curses.COLOR_BLACK, + 'default': -1} + +start_color = colors_str[args.color] +start_bg = colors_str[args.bg_color] + +speed = args.speed +start_delay = (100 - speed) * 10 + +runtime = None + +if args.time: + runtime = args.time + +# "-l" option has been used +if args.character_list: + chars = '' + for letter in args.character_list: + try: + chars += char_set[letter] + except KeyError: + print("Letter '%s' does not represent a valid character list." + % letter) + exit() + +# "-l" not used, but "-u" is set +elif args.custom_characters: + chars = args.custom_characters + +# Neither "-l" nor "-u" has been set, use default characters +else: + chars = char_set['m'] + +if args.no_bold: + args.all_bold = False + +chars_len = len(chars) - 1 + + +### Classes + +class Canvas: + """ + Represents the whole screen and stores its height and width. Gets + overwritten whenever the screen resizes. Serves as a container for columns. + """ + + def __init__(self, screen): + screen.clear() + rows, cols = screen.getmaxyx() + self.col_count = cols + self.row_count = rows + self.size_changed = False + self.columns = [] + for col in range(0, cols, 2): + self.columns.append(Column(col, self.row_count)) + self.nodes = [] + self.flashers = set() + + # Draw a background + for x in range(self.row_count): + try: + screen.addstr(x, 0, ' ' * self.col_count, curses.color_pair(1)) + except curses.error: + pass + + +class Status: + """ + Displays a status message at top left when a setting is changed. + """ + + def __init__(self, screen): + curses.init_pair(3, curses.COLOR_BLACK, curses.COLOR_WHITE) + self.screen = screen + self.countdown = 0 + self.last_message = '' + + def update(self, message, delay): + """ + Writes new message to the status area + """ + if not args.status_off: + message_str = message.ljust(11) + self.screen.addstr(0, 0, message_str, curses.color_pair(3)) + self.last_message = message_str + # More frames for faster speeds: + self.countdown = (100 // (delay // 10 + 1)) + 2 + + def refresh(self): + """ + Used to keep refreshing status message until countdown runs out + """ + message_str = self.last_message + self.screen.addstr(0, 0, message_str, curses.color_pair(3)) + + def clear(self): + """ + Erases message with spaces when the countdown runs out + """ + self.screen.addstr(0, 0, ' ' * 11, curses.color_pair(1)) + + +class Column: + """ + Creates nodes (points that move down the screen) that are then stored in + canvas.nodes. Countdown timer determines time to spawn new node. + """ + + def __init__(self, x_coord, row_count): + self.drawing = None # None means not yet. Later will be True or False + self.x_coord = x_coord + self.timer = randint(1, row_count) + self.async_speed = randint(1, 3) + if args.single_wave: + # Speeds it up a bit + self.timer = int(0.6 * self.timer) + + def spawn_node(self, canvas): + """ + Creates nodes: points that move down the screen either writing or + erasing characters as they go down + """ + if args.single_wave and self.drawing is False: + return + + self.drawing = not self.drawing + + # Multiplier (mult) is for spawning slow-moving asynchronous nodes + # less frequently in order to maintain their length + if args.asynchronous: + mult = self.async_speed + else: + mult = 1 + + if self.drawing: + # "max_range" prevents crash with very small terminal height + max_range = max((3 * mult), ((canvas.row_count - 3) * mult)) + self.timer = randint(3 * mult, max_range) + if args.single_wave: + # A bit faster for single wave mode + self.timer = int(0.8 * self.timer) + else: + self.timer = randint(1 * mult, canvas.row_count * mult) + + x = self.x_coord + n_type = 'eraser' + async_speed = self.async_speed + white = False + if self.drawing: + n_type = 'writer' + if randint(0, 2) == 0: + white = True + + canvas.nodes.append(Node(x, n_type, async_speed, white)) + + +class Node: + """ + A point that runs down the screen drawing or erasing characters. + n_type -> 'writer' or 'eraser' + white -> Bool. If True, a white char is written before the green one. + last_char -> Stores last character, since white characters have to be + overwritten with the same one in green one. + expired -> Bool. If True, node is marked for deletion + """ + + def __init__(self, x_coord, n_type, async_speed, white=False): + self.x_coord = x_coord + self.y_coord = 0 + self.n_type = n_type + self.white = white + self.last_char = None + self.expired = False + self.async_speed = async_speed + + +class KeyHandler: + """ + Handles keyboard input. + """ + + def __init__(self, screen, stat): + self.screen = screen + self.stat = stat + self.screen.nodelay(True) + self.delay = start_delay + self.fg = start_color + self.bg = start_bg + + def cycle_bold(self): + """ + Called on 'b' press. Cycles though Bold options: + off -> on -> all bold + """ + if args.all_bold: + args.no_bold = True + args.all_bold = False + self.stat.update('Bold: off', self.delay) + elif args.no_bold: + args.no_bold = False + args.all_bold = False + self.stat.update('Bold: on', self.delay) + else: + args.no_bold = False + args.all_bold = True + self.stat.update('Bold: all', self.delay) + + def get(self): + """ + Handles key presses. Returns True if a key was found, False otherwise. + """ + if args.ignore_keyboard: + return False; + + kp = self.screen.getch() + + if kp == -1: + return False + elif kp == ord(" ") or kp == ord("q") or kp == 27: # 27 = ESC + exit() + elif kp == ord('a'): + args.asynchronous = not args.asynchronous + on_off = 'on' if args.asynchronous else 'off' + self.stat.update('Async: %s' % on_off, self.delay) + elif kp == ord('b'): + self.cycle_bold() + elif kp == ord('f'): + args.flashers = not args.flashers + on_off = 'on' if args.flashers else 'off' + self.stat.update('Flash: %s' % on_off, self.delay) + elif kp == ord('o'): + self.toggle_status() + + # Speed control + elif kp == ord('-') or kp == ord('_') or kp == curses.KEY_LEFT: + self.delay = min(self.delay + 10, 10990) + self.show_speed() + elif kp == ord('=') or kp == ord('+') or kp == curses.KEY_RIGHT: + self.delay = max(self.delay - 10, 0) + self.show_speed() + elif kp == ord('[') or kp == curses.KEY_DOWN: + self.delay = min(self.delay + 100, 10990) + self.show_speed() + elif kp == ord(']') or kp == curses.KEY_UP: + self.delay = max(self.delay - 100, 0) + self.show_speed() + + # Foreground color control + elif kp == ord('1'): + self.set_fg_color('Green') + elif kp == ord('2'): + self.set_fg_color('Red') + elif kp == ord('3'): + self.set_fg_color('Blue') + elif kp == ord('4'): + self.set_fg_color('White') + elif kp == ord('5'): + self.set_fg_color('Yellow') + elif kp == ord('6'): + self.set_fg_color('Cyan') + elif kp == ord('7'): + self.set_fg_color('Magenta') + elif kp == ord('8'): + self.set_fg_color('Black') + elif kp == ord('9'): + self.set_fg_color('default') + + # Background color control + elif kp == ord('!'): + self.set_bg_color('Green') + elif kp == ord('@'): + self.set_bg_color('Red') + elif kp == ord('#'): + self.set_bg_color('Blue') + elif kp == ord('$'): + self.set_bg_color('White') + elif kp == ord('%'): + self.set_bg_color('Yellow') + elif kp == ord('^'): + self.set_bg_color('Cyan') + elif kp == ord('&'): + self.set_bg_color('Magenta') + elif kp == ord('*'): + self.set_bg_color('Black') + elif kp == ord('('): + self.set_bg_color('default') + + return True + + def set_fg_color(self, name): + """ + Set foreground color + """ + self.fg = colors_str[name.lower()] + curses.init_pair(1, self.fg, self.bg) + if name == 'default': + name = "Def't color" + self.stat.update(name, self.delay) + + def set_bg_color(self, name): + """ + Set background color + """ + self.bg = colors_str[name.lower()] + curses.init_pair(1, self.fg, self.bg) + curses.init_pair(2, curses.COLOR_WHITE, self.bg) + self.stat.update('BG: %s' % name, self.delay) + + def show_speed(self): + """ + Display current speed (-999 to 100) when it is changed by keypress + """ + self.stat.update('Speed: %d' % (100 - self.delay // 10), self.delay) + + def toggle_status(self): + """ + On 'o' keypress, turn status display on or off + """ + args.status_off = not args.status_off + on_off = 'off' if args.status_off else 'on' + self.stat.update('Status: %s' % on_off, self.delay) + + +class Writer: + """ + Initializes character writing options and contains methods for writing and + erasing characters from the screen. + """ + + def __init__(self, screen): + self.screen = screen + self.screen.scrollok(0) + curses.curs_set(0) + curses.use_default_colors() + curses.init_pair(1, start_color, start_bg) + curses.init_pair(2, curses.COLOR_WHITE, start_bg) + curses.init_pair(3, curses.COLOR_BLACK, curses.COLOR_WHITE) + self.white = curses.color_pair(2) + + @staticmethod + def get_char(): + """ + Returns a random character from the active character set + """ + return chars[randint(0, chars_len)] + + @staticmethod + def get_attr(node, above=False): + """ + Returns either A_BOLD attribute or A_NORMAL based on Bold setting + "above=True" means it an extra green character used to overwrite the + while head character. + """ + if args.no_bold: + return curses.A_NORMAL + elif args.all_bold: + return curses.A_BOLD + else: + if node.white and not above: + return curses.A_BOLD + else: + return choice([curses.A_BOLD, curses.A_NORMAL]) + + def draw(self, node): + """ + Draws characters, included spaces to overwrite/erase characters. + """ + y = node.y_coord + x = node.x_coord + character = ' ' + attr = self.get_attr(node) + color = curses.color_pair(1) + if node.n_type == 'writer': + if not node.white and node.last_char: + # Special green character for overwriting last white one + # at bottom of column that was not being overwritten. + character = node.last_char + else: + character = self.get_char() + if node.white: + color = curses.color_pair(2) + + try: + # Draw the character + self.screen.addstr(y, x, character, color | attr) + if node.white: + if node.last_char: + # If it's a white node, also write a green character above + # to overwrite last white character + attr = self.get_attr(node, above=True) + self.screen.addstr(y - 1, x, node.last_char, + curses.color_pair(1) | attr) + node.last_char = character + except curses.error: + # Override scrolling error if characters pushed off the screen. + pass + + def draw_flasher(self, flasher): + """ + Draws characters, included spaces to overwrite/erase characters. + """ + color = curses.color_pair(1) + attr = choice([curses.A_BOLD, curses.A_NORMAL]) + y = flasher[0] + x = flasher[1] + try: + self.screen.addstr(y, x, self.get_char(), color | attr) + except curses.error: + pass + + +### Main loop + +def _main(screen): + writer = Writer(screen) + stat = Status(screen) + key = KeyHandler(screen, stat) + # Prevent single_wave mode from shutting down too early: + if args.single_wave: + wave_delay = 10 + else: + wave_delay = 0 + + starttime = time.time() + + # Keep restarting however many times the screen resizes + while True: + canvas = Canvas(screen) + # Set a rhythm for asynchronous movement + async_clock = 5 + # Loop to draw the green rain + while not canvas.size_changed: + if runtime and time.time() - starttime > runtime: + exit() + # Catch keypress + if key.get(): + continue + # Spawn new nodes + for col in canvas.columns: + if col.timer == 0: + col.spawn_node(canvas) + col.timer -= 1 + + for node in canvas.nodes: + + if args.flashers: + if node.n_type == 'writer' and not randint(0, 9): + canvas.flashers.add((node.y_coord, node.x_coord)) + elif node.n_type == 'eraser': + try: + canvas.flashers.remove((node.y_coord, node.x_coord)) + except KeyError: + pass + + if args.asynchronous: + if async_clock % node.async_speed == 0: + writer.draw(node) + node.y_coord += 1 + else: + writer.draw(node) + node.y_coord += 1 + + # Mark old nodes for deletion + if node.y_coord >= canvas.row_count: + if node.white: + # Stop white nodes from staying 'stuck' on last row. + # Creates a special green node with a last_char + # attribute to overwrite last white node. + node.white = False + node.y_coord -= 1 + else: + node.expired = True + + if args.flashers and (not async_clock % 3): + for f in canvas.flashers: + writer.draw_flasher(f) + + # Rewrite nodes list without expired nodes + canvas.nodes = [node for node in canvas.nodes if not node.expired] + + if args.single_wave: + if len(canvas.nodes) == 0 and wave_delay < 0: + exit() + wave_delay -= 1 + + # End of loop, refresh screen + if stat.countdown > 0: + if stat.countdown == 1: + stat.clear() + else: + stat.refresh() + stat.countdown -= 1 + screen.refresh() + + # Check for screen resize + if screen.getmaxyx() != (canvas.row_count, canvas.col_count): + canvas.size_changed = True + + # Add delay before next loop + curses.napms(key.delay) + + # update async clock + if async_clock: + async_clock -= 1 + else: + async_clock = 5 + + +def main(): + # Wrapper to allow CTRL-C to exit smoothly: + try: + curses.wrapper(_main) + except KeyboardInterrupt: + pass + + +if __name__ == '__main__': + main()