"Fossies" - the Fresh Open Source Software Archive

Member "bashtop-0.9.25/bashtop" (20 Jul 2020, 206484 Bytes) of package /linux/misc/bashtop-0.9.25.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Bash source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "bashtop": 0.9.24_vs_0.9.25.

    1 #!/usr/bin/env bash
    2 # indent type=tab
    3 # tab size=4
    4 # shellcheck disable=SC2034 #Unused variables
    5 # shellcheck disable=SC2068 #Double quote array warning
    6 # shellcheck disable=SC2086 # Double quote warning
    7 # shellcheck disable=SC2140 # Word form warning
    8 # shellcheck disable=SC2162 #Read without -r
    9 # shellcheck disable=SC2206 #Word split warning
   10 # shellcheck disable=SC2178 #Array to string warning
   11 # shellcheck disable=SC2102 #Ranges only match single
   12 # shellcheck disable=SC2004 #arithmetic brackets warning
   13 # shellcheck disable=SC2017 #arithmetic precision warning
   14 # shellcheck disable=SC2207 #split array warning
   15 # shellcheck disable=SC2154 #variable referenced but not assigned
   16 # shellcheck disable=SC1003 #info: single quote escape
   17 # shellcheck disable=SC2179 # array append warning
   18 # shellcheck disable=SC2128 # expanding array without index warning
   19 
   20 
   21 # Copyright 2020 Aristocratos (jakob@qvantnet.com)
   22 
   23 #    Licensed under the Apache License, Version 2.0 (the "License");
   24 #    you may not use this file except in compliance with the License.
   25 #    You may obtain a copy of the License at
   26 
   27 #        http://www.apache.org/licenses/LICENSE-2.0
   28 
   29 #    Unless required by applicable law or agreed to in writing, software
   30 #    distributed under the License is distributed on an "AS IS" BASIS,
   31 #    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   32 #    See the License for the specific language governing permissions and
   33 #    limitations under the License.
   34 
   35 declare -x LC_MESSAGES="C" LC_NUMERIC="C" LC_ALL=""
   36 
   37 #* Fail if running on unsupported OS
   38 case "$(uname -s)" in
   39     Linux*)  system=Linux;;
   40     *BSD)    system=BSD;;
   41     Darwin*) system=MacOS;;
   42     CYGWIN*) system=Cygwin;;
   43     MINGW*)  system=MinGw;;
   44     *)       system="Other"
   45 esac
   46 if [[ ! $system =~ Linux|MacOS|BSD ]]; then
   47     echo "This version of bashtop does not support $system platform."
   48     exit 1
   49 fi
   50 
   51 #* Fail if Bash version is below 4.4
   52 bash_version_major=${BASH_VERSINFO[0]}
   53 bash_version_minor=${BASH_VERSINFO[1]}
   54 if [[ "$bash_version_major" -lt 4 ]] || [[ "$bash_version_major" == 4 && "$bash_version_minor" -lt 4 ]]; then
   55     echo "ERROR: Bash 4.4 or later is required (you are using Bash $bash_version_major.$bash_version_minor)."
   56     exit 1
   57 fi
   58 
   59 shopt -qu failglob nullglob
   60 shopt -qs extglob globasciiranges globstar
   61 
   62 #* Check for UTF-8 locale and set LANG variable if not set
   63 if [[ ! $LANG =~ UTF-8 ]]; then
   64     if [[ -n $LANG && ${LANG::1} != "C" ]]; then old_lang="${LANG%.*}"; fi
   65     for set_lang in $(locale -a); do
   66         if [[ $set_lang =~ utf8|UTF-8 ]]; then
   67             if [[ -n $old_lang && $set_lang =~ ${old_lang} ]]; then
   68                 declare -x LANG="${set_lang/utf8/UTF-8}"
   69                 set_lang_search="found"
   70                 break
   71             elif [[ -z $first_lang ]]; then
   72                 first_lang="${set_lang/utf8/UTF-8}"
   73                 set_lang_first="found"
   74             fi
   75             if [[ -z $old_lang ]]; then break; fi
   76         fi
   77     done
   78     if [[ $set_lang_search != "found" && $set_lang_first != "found" ]]; then
   79         echo "ERROR: No UTF-8 locale found!"
   80         exit 1
   81     elif [[ $set_lang_search != "found" ]]; then
   82             declare -x LANG="${first_lang/utf8/UTF-8}"
   83     fi
   84     unset old_lang set_lang first_lang set_lang_search set_lang_first
   85 fi
   86 
   87 declare -a banner banner_colors
   88 
   89 banner=(
   90 "██████╗  █████╗ ███████╗██╗  ██╗████████╗ ██████╗ ██████╗ "
   91 "██╔══██╗██╔══██╗██╔════╝██║  ██║╚══██╔══╝██╔═══██╗██╔══██╗"
   92 "██████╔╝███████║███████╗███████║   ██║   ██║   ██║██████╔╝"
   93 "██╔══██╗██╔══██║╚════██║██╔══██║   ██║   ██║   ██║██╔═══╝ "
   94 "██████╔╝██║  ██║███████║██║  ██║   ██║   ╚██████╔╝██║     "
   95 "╚═════╝ ╚═╝  ╚═╝╚══════╝╚═╝  ╚═╝   ╚═╝    ╚═════╝ ╚═╝     ")
   96 declare version="0.9.25"
   97 
   98 #* Get latest version of BashTOP from https://github.com/aristocratos/bashtop
   99 
  100 declare banner_width=${#banner[0]}
  101 banner_colors=("#E62525" "#CD2121" "#B31D1D" "#9A1919" "#801414")
  102 
  103 #* Set correct names for GNU tools depending on OS
  104 if [[ $system != "Linux" ]]; then tool_prefix="g"; fi
  105 for tool in "dd" "df" "stty" "tail" "realpath" "wc" "rm" "mv" "sleep" "stdbuf" "mkfifo" "date" "kill" "sed"; do
  106     declare -n set_tool="${tool}"
  107     set_tool="${tool_prefix}${tool}"
  108 done
  109 
  110 if ! command -v ${dd} >/dev/null 2>&1; then
  111     echo "ERROR: Missing GNU coreutils!"
  112     exit 1
  113 elif ! command -v ${sed} >/dev/null 2>&1; then
  114     echo "ERROR: Missing GNU sed!"
  115     exit 1
  116 fi
  117 
  118 read tty_height tty_width < <(${stty} size)
  119 
  120 #? Start default variables------------------------------------------------------------------------------>
  121 #? These values are used to create "$HOME/.config/bashtop/bashtop.cfg"
  122 #? Any changes made here will be ignored if config file exists
  123 aaa_config() { : ; } #! Do not remove this line!
  124 
  125 #* Color theme, looks for a .theme file in "$HOME/.config/bashtop/themes" and "$HOME/.config/bashtop/user_themes"
  126 #* Should be prefixed with either "themes/" or "user_themes/" depending on location, "Default" for builtin default theme
  127 color_theme="Default"
  128 
  129 #* Update time in milliseconds, increases automatically if set below internal loops processing time, recommended 2000 ms or above for better sample times for graphs
  130 update_ms="2500"
  131 
  132 #* Processes sorting, "pid" "program" "arguments" "threads" "user" "memory" "cpu lazy" "cpu responsive"
  133 #* "cpu lazy" updates sorting over time, "cpu responsive" updates sorting directly
  134 proc_sorting="cpu lazy"
  135 
  136 #* Reverse sorting order, "true" or "false"
  137 proc_reversed="false"
  138 
  139 #* Show processes as a tree
  140 proc_tree="false"
  141 
  142 #* Check cpu temperature, only works if "sensors", "vcgencmd" or "osx-cpu-temp" commands is available
  143 check_temp="true"
  144 
  145 #* Draw a clock at top of screen, formatting according to strftime, empty string to disable
  146 draw_clock="%X"
  147 
  148 #* Update main ui when menus are showing, set this to false if the menus is flickering too much for comfort
  149 background_update="true"
  150 
  151 #* Custom cpu model name, empty string to disable
  152 custom_cpu_name=""
  153 
  154 #* Enable error logging to "$HOME/.config/bashtop/error.log", "true" or "false"
  155 error_logging="true"
  156 
  157 #* Show color gradient in process list, "true" or "false"
  158 proc_gradient="true"
  159 
  160 #* If process cpu usage should be of the core it's running on or usage of the total available cpu power
  161 proc_per_core="false"
  162 
  163 #* Optional filter for shown disks, should be names of mountpoints, "root" replaces "/", separate multiple values with space
  164 disks_filter=""
  165 
  166 #* Enable check for new version from github.com/aristocratos/bashtop at start
  167 update_check="true"
  168 
  169 #* Enable graphs with double the horizontal resolution, increases cpu usage
  170 hires_graphs="false"
  171 
  172 #* Enable the use of psutil python3 module for data collection, default on OSX
  173 use_psutil="true"
  174 
  175 aaz_config() { : ; } #! Do not remove this line!
  176 #? End default variables-------------------------------------------------------------------------------->
  177 
  178 declare -a menu_options menu_help menu_quit
  179 
  180 menu_options=(
  181 "┌─┐┌─┐┌┬┐┬┌─┐┌┐┌┌─┐"
  182 "│ │├─┘ │ ││ ││││└─┐"
  183 "└─┘┴   ┴ ┴└─┘┘└┘└─┘")
  184 menu_help=(
  185 "┬ ┬┌─┐┬  ┌─┐"
  186 "├─┤├┤ │  ├─┘"
  187 "┴ ┴└─┘┴─┘┴  ")
  188 menu_quit=(
  189 "┌─┐ ┬ ┬ ┬┌┬┐"
  190 "│─┼┐│ │ │ │ "
  191 "└─┘└└─┘ ┴ ┴ ")
  192 
  193 menu_options_selected=(
  194 "╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗"
  195 "║ ║╠═╝ ║ ║║ ║║║║╚═╗"
  196 "╚═╝╩   ╩ ╩╚═╝╝╚╝╚═╝")
  197 menu_help_selected=(
  198 "╦ ╦╔═╗╦  ╔═╗"
  199 "╠═╣║╣ ║  ╠═╝"
  200 "╩ ╩╚═╝╩═╝╩  ")
  201 menu_quit_selected=(
  202 "╔═╗ ╦ ╦ ╦╔╦╗ "
  203 "║═╬╗║ ║ ║ ║  "
  204 "╚═╝╚╚═╝ ╩ ╩  ")
  205 
  206 declare -A cpu mem swap proc net box theme disks
  207 declare -a cpu_usage cpu_graph_a cpu_graph_b color_meter color_temp_graph color_cpu color_cpu_graph cpu_history color_mem_graph color_swap_graph
  208 declare -a mem_history swap_history net_history_download net_history_upload mem_graph swap_graph proc_array download_graph upload_graph trace_array
  209 declare resized=1 size_error clock tty_width tty_height hex="16#" cpu_p_box swap_on=1 draw_out esc_character boxes_out last_screen clock_out update_string
  210 declare -a options_array=("color_theme" "update_ms" "use_psutil" "proc_sorting" "proc_tree" "check_temp" "draw_clock" "background_update" "custom_cpu_name"
  211     "proc_per_core" "proc_reversed" "proc_gradient" "disks_filter" "hires_graphs" "net_totals_reset" "update_check" "error_logging")
  212 declare -a save_array=(${options_array[*]/net_totals_reset/})
  213 declare -a sorting=( "pid" "program" "arguments" "threads" "user" "memory" "cpu lazy" "cpu responsive")
  214 declare -a detail_graph detail_history detail_mem_history disks_io
  215 declare -A pid_history
  216 declare time_left timestamp_start timestamp_end timestamp_input_start timestamp_input_end time_string mem_out proc_misc prev_screen pause_screen filter input_to_filter
  217 declare no_epoch proc_det proc_misc2 sleeping=0 detail_mem_graph proc_det2 proc_out curled git_version has_iostat sensor_comm failed_pipes=0 py_error
  218 declare esc_character tab backspace sleepy late_update skip_process_draw winches quitting theme_int notifier saved_stty nic_int net_misc skip_net_draw
  219 declare psutil_disk_fail
  220 declare -a disks_free disks_total disks_name disks_free_percent saved_key themes nic_list old_procs
  221 printf -v esc_character "\u1b"
  222 printf -v tab "\u09"
  223 printf -v backspace "\u7F" #? Backspace set to DELETE
  224 printf -v backspace_real "\u08" #? Real backspace
  225 #printf -v enter_key "\uA"
  226 printf -v enter_key "\uD"
  227 printf -v ctrl_c "\u03"
  228 printf -v ctrl_z "\u1A"
  229 
  230 hide_cursor='\033[?25l'     #* Hide terminal cursor
  231 show_cursor='\033[?25h'     #* Show terminal cursor
  232 alt_screen='\033[?1049h'    #* Switch to alternate screen
  233 normal_screen='\033[?1049l' #* Switch to normal screen
  234 clear_screen='\033[2J'      #* Clear screen
  235 
  236 #* Symbols for graphs
  237 declare -a graph_symbol
  238 graph_symbol=(" " "⡀" "⣀" "⣄" "⣤" "⣦" "⣴" "⣶" "⣷" "⣾" "⣿")
  239 graph_symbol+=( " " "⣿" "⢿" "⡿" "⠿" "⠻" "⠟"  "⠛" "⠙" "⠉" "⠈")
  240 declare -A graph_symbol_up='(
  241     [0_0]=⠀ [0_1]=⢀ [0_2]=⢠ [0_3]=⢰ [0_4]=⢸
  242     [1_0]=⡀ [1_1]=⣀ [1_2]=⣠ [1_3]=⣰ [1_4]=⣸
  243     [2_0]=⡄ [2_1]=⣄ [2_2]=⣤ [2_3]=⣴ [2_4]=⣼
  244     [3_0]=⡆ [3_1]=⣆ [3_2]=⣦ [3_3]=⣶ [3_4]=⣾
  245     [4_0]=⡇ [4_1]=⣇ [4_2]=⣧ [4_3]=⣷ [4_4]=⣿
  246 )'
  247 declare -A graph_symbol_down='(
  248     [0_0]=⠀ [0_1]=⠈ [0_2]=⠘ [0_3]=⠸ [0_4]=⢸
  249     [1_0]=⠁ [1_1]=⠉ [1_2]=⠙ [1_3]=⠹ [1_4]=⢹
  250     [2_0]=⠃ [2_1]=⠋ [2_2]=⠛ [2_3]=⠻ [2_4]=⢻
  251     [3_0]=⠇ [3_1]=⠏ [3_2]=⠟ [3_3]=⠿ [3_4]=⢿
  252     [4_0]=⡇ [4_1]=⡏ [4_2]=⡟ [4_3]=⡿ [4_4]=⣿
  253 )'
  254 declare -A graph
  255 box[boxes]="cpu mem net processes"
  256 
  257 cpu[threads]=0
  258 
  259 #* Symbols for subscript function
  260 subscript=("₀" "₁" "₂" "₃" "₄" "₅" "₆" "₇" "₈" "₉")
  261 
  262 #* Symbols for create_box function
  263 box[single_hor_line]="─"
  264 box[single_vert_line]="│"
  265 box[single_left_corner_up]="┌"
  266 box[single_right_corner_up]="┐"
  267 box[single_left_corner_down]="└"
  268 box[single_right_corner_down]="┘"
  269 box[single_title_left]="├"
  270 box[single_title_right]="┤"
  271 
  272 box[double_hor_line]="═"
  273 box[double_vert_line]="║"
  274 box[double_left_corner_up]="╔"
  275 box[double_right_corner_up]="╗"
  276 box[double_left_corner_down]="╚"
  277 box[double_right_corner_down]="╝"
  278 box[double_title_left]="╟"
  279 box[double_title_right]="╢"
  280 
  281 init_() { #? Collect needed information and set options before startig main loop
  282     if [[ -z $1 ]]; then
  283         local i stx=0
  284         #* Set terminal options, save and clear screen
  285         saved_stty="$(${stty} -g)"
  286         echo -en "${alt_screen}${hide_cursor}${clear_screen}"
  287         echo -en "\033]0;${TERMINAL_TITLE} BashTOP\a"
  288         ${stty} -echo
  289 
  290         #* Wait for resize if terminal size is smaller then 80x24
  291         if (($tty_width<80 | $tty_height<24)); then resized; echo -en "${clear_screen}"; fi
  292 
  293         #* Draw banner to banner array
  294         local letter b_color banner_line y=0
  295         local -a banner_out
  296         #print -v banner_out[0] -t "\e[0m"
  297         for banner_line in "${banner[@]}"; do
  298             #* Read banner array letter by letter to set correct color for filled vs outline characters
  299             while read -rN1 letter; do
  300                 if [[ $letter == "█" ]]; then b_color="${banner_colors[$y]}"
  301                 else b_color="#$((80-y*6))"; fi
  302                 if [[ $letter == " " ]]; then
  303                     print -v banner_out[y] -r 1
  304                 else
  305                     print -v banner_out[y] -fg ${b_color} "${letter}"
  306                 fi
  307             done <<<"$banner_line"
  308             ((++y))
  309         done
  310         banner=("${banner_out[@]}")
  311 
  312         #* Draw banner to screen and show status while running init
  313         draw_banner $((tty_height/2-10))
  314 
  315         #* Start psutil coprocess if enabled
  316         if [[ $use_psutil == true ]]; then
  317             print -m $(( (tty_height/2-3)+stx++ )) 0 -bg "#00" -fg "#cc" -b -c "Creating psutil coprocess..."
  318             return
  319         fi
  320     fi
  321 
  322     if [[ -n $1 ]]; then local i stx=1; print -bg "#00" -fg "#30ff50" -r 1 -t "√"; fi
  323 
  324     #* Check if "sensors", "osx-cpu-temp" or "vcgencmd" commands is available, if not, disable temperature collection
  325     print -m $(( (tty_height/2-3)+stx++ )) 0 -bg "#00" -fg "#cc" -b -c "Checking available tools..."
  326     if [[ $check_temp == true ]]; then
  327         local has_temp
  328         sensor_comm=""
  329         if [[ $use_psutil == true ]]; then
  330             py_command -v has_temp "get_sensors_check()"
  331             if [[ $has_temp == true ]]; then sensor_comm="psutil"; fi
  332         fi
  333         if [[ -z $sensor_comm ]]; then
  334             local checker
  335             for checker in "vcgencmd" "sensors" "osx-cpu-temp"; do
  336                 if command -v "${checker}" >/dev/null 2>&1; then sensor_comm="${checker}"; break; fi
  337             done
  338         fi
  339         if [[ -z $sensor_comm ]]; then check_temp="false"; fi
  340     fi
  341 
  342     #* Check if "curl" command is available, if not, disable update check and theme downloads
  343     if command -v curl >/dev/null 2>&1; then curled=1; else unset curled; fi
  344 
  345     #* Check if "notify-send" command is available, if not, disable update notifier
  346     if [[ -n $curled ]] && command -v notify-send >/dev/null 2>&1; then notifier=1; else unset notifier; fi
  347 
  348     #* Check if "iostat" command is available, if not, disable disk io stat collection
  349     if command -v iostat >/dev/null 2>&1; then has_iostat=1; else unset has_iostat; fi
  350 
  351     #* Get number of cores and cpu threads
  352     print -bg "#00" -fg "#30ff50" -r 1 -t "√"
  353     print -m $(( (tty_height/2-3)+stx++ )) 0 -bg "#00" -fg "#cc" -c -b "Checking cpu..."
  354     get_cpu_info
  355 
  356     #* Set graph resolution
  357     graph[hires]="${hires_graphs}"
  358 
  359     #* Get processor BCLK
  360     local param_var
  361     if [[ $use_psutil == false ]] && [[ -e /usr/include/asm-generic/param.h ]]; then
  362         param_var="$(</usr/include/asm-generic/param.h)"
  363         get_value -v 'cpu[hz]' -sv "param_var" -k "define HZ" -i
  364     else
  365         cpu[hz]="100"
  366     fi
  367 
  368     #* Get max pid value and length
  369     if [[ $use_psutil == false ]];  then
  370         proc[pid_max]="$(</proc/sys/kernel/pid_max)"
  371         proc[pid_len]=${#proc[pid_max]}
  372         if [[ ${proc[pid_len]} -lt 5 ]]; then proc[pid_len]=5; fi
  373     else
  374         proc[pid_len]="7"
  375     fi
  376 
  377     #* Calculate sizes of boxes
  378     print -bg "#00" -fg "#30ff50" -r 1 -t "√"
  379     print -m $(( (tty_height/2-3)+stx++ )) 0 -bg "#00" -fg "#cc" -c -b "Calculating sizes..."
  380     calc_sizes
  381 
  382     #* Call init for cpu data collection
  383     print -bg "#00" -fg "#30ff50" -r 1 -t "√"
  384     print -m $(( (tty_height/2-3)+stx++ )) 0 -bg "#00" -fg "#cc" -c -b "Running cpu collection init..."
  385     collect_cpu init
  386 
  387     #* Call init for memory data collection and check if swap is available
  388     print -bg "#00" -fg "#30ff50" -r 1 -t "√"
  389     print -m $(( (tty_height/2-3)+stx++ )) 0 -bg "#00" -fg "#cc" -c -b "Running mem collection init..."
  390     mem[counter]=10
  391     collect_mem init
  392 
  393     #* Get default network device from "ip route" command and call init for net collection if device is found
  394     print -bg "#00" -fg "#30ff50" -r 1 -t "√"
  395     print -m $(( (tty_height/2-3)+stx++ )) 0 -bg "#00" -fg "#cc" -c -b "Checking network devices..."
  396     get_net_device
  397 
  398     #* Check if newer version of bashtop is available from https://github.com/aristocratos/bashtop
  399     if [[ -n $curled && $update_check == "true" ]]; then
  400         print -bg "#00" -fg "#30ff50" -r 1 -t "√"
  401         print -m $(( (tty_height/2-3)+stx++ )) 0 -bg "#00" -fg "#cc" -c -b "Checking for updates..."
  402         if ! get_value -v git_version -ss "$(curl -m 4 --raw -r 0-5000 https://raw.githubusercontent.com/aristocratos/bashtop/master/bashtop 2>/dev/null)" -k "version=" -r "[^0-9.]"; then unset git_version; fi
  403     fi
  404 
  405     #* Add update notification to banner if new version is available
  406     local banner_out_up
  407     print -v banner_out_up -rs -fg "#cc" -b "← esc"
  408     if [[ -n $git_version && $git_version != "$version" ]]; then
  409         print -v banner_out_up -rs -fg "#80cc80" -r 15 "[${git_version} available!]" -r $((9-${#git_version}))
  410         if [[ -n $notifier ]]; then
  411             notify-send -u normal\
  412             "Bashtop Update!" "New version of Bashtop available\!\nCurrent version: ${version}\n\New version: ${git_version}\nDownload at github.com/aristocratos/bashtop"\
  413             -i face-glasses -t 10000
  414         fi
  415     else
  416         print -v banner_out_up -r 37
  417     fi
  418     print -v banner_out_up -fg "#cc" -i -b "Version: ${version}" -rs
  419     banner+=("${banner_out_up}")
  420 
  421     #* Get theme and set colors
  422     print -bg "#00" -fg "#30ff50" -r 1 -t "√"
  423     print -m $(( (tty_height/2-3)+stx++ )) 0 -bg "#00" -fg "#cc" -c -b "Generating colors for theme..."
  424     color_init_
  425 
  426     #* Set up internals for quick processes sorting switching
  427     for((i=0;i<${#sorting[@]};i++)); do
  428         if [[ ${sorting[i]} == "${proc_sorting}" ]]; then
  429             proc[sorting_int]=$i
  430             break
  431         fi
  432     done
  433     if [[ -z ${proc[sorting_int]} ]]; then
  434         proc[sorting_int]=0
  435         proc_sorting="${sorting[0]}"
  436     fi
  437 
  438     if [[ ${proc_reversed} == true ]]; then
  439         proc[reverse]="+"
  440     else
  441         unset 'proc[reverse]'
  442     fi
  443 
  444     if [[ ${proc_tree} == true ]]; then
  445         proc[tree]="+"
  446     else
  447         unset 'proc[tree]'
  448     fi
  449 
  450     #* Call init for processes data collection
  451     print -bg "#00" -fg "#30ff50" -r 1 -t "√"
  452     print -m $(( (tty_height/2-3)+stx++ )) 0 -bg "#00" -fg "#cc" -c -b "Running process collection init..."
  453     proc[selected]=0
  454     proc[start]=1
  455     collect_processes init
  456 
  457     #* Draw first screen
  458     print -bg "#00" -fg "#30ff50" -r 1 -t "√"
  459     print -m $(( (tty_height/2-3)+stx++ )) 0 -bg "#00" -fg "#cc" -c -b "Drawing screen..."
  460 
  461     draw_bg quiet
  462     get_ms timestamp_start
  463 
  464     for task in processes cpu mem net; do
  465         collect_${task}
  466         draw_${task}
  467     done
  468     last_screen="${draw_out}"
  469 
  470     print -bg "#00" -fg "#30ff50" -r 1 -t "√" -rs
  471     sleep 0.5
  472 
  473     draw_clock
  474     echo -en "${clear_screen}${draw_out}${proc_out}${clock_out}"
  475     resized=0
  476     unset draw_out
  477 }
  478 
  479 color_init_() { #? Check for theme file and set colors
  480     local main_bg="" main_fg="#cc" title="#ee" hi_fg="#90" inactive_fg="#40" cpu_box="#3d7b46" mem_box="#8a882e" net_box="#423ba5" proc_box="#923535" proc_misc="#0de756" selected_bg="#7e2626" selected_fg="#ee"
  481     local temp_start="#4897d4" temp_mid="#5474e8" temp_end="#ff40b6" cpu_start="#50f095" cpu_mid="#f2e266" cpu_end="#fa1e1e" div_line="#30"
  482     local free_start="#223014" free_mid="#b5e685" free_end="#dcff85" cached_start="#0b1a29" cached_mid="#74e6fc" cached_end="#26c5ff" available_start="#292107" available_mid="#ffd77a" available_end="#ffb814"
  483     local used_start="#3b1f1c" used_mid="#d9626d" used_end="#ff4769" download_start="#231a63" download_mid="#4f43a3" download_end="#b0a9de" upload_start="#510554" upload_mid="#7d4180" upload_end="#dcafde"
  484     local hex2rgb color_name array_name this_color main_fg_dec sourced theme_unset
  485     local -i i y
  486     local -A rgb
  487     local -a dec_test
  488     local -a convert_color=("main_bg" "temp_start" "temp_mid" "temp_end" "cpu_start" "cpu_mid" "cpu_end" "upload_start" "upload_mid" "upload_end" "download_start" "download_mid" "download_end" "used_start" "used_mid" "used_end" "available_start" "available_mid" "available_end" "cached_start" "cached_mid" "cached_end" "free_start" "free_mid" "free_end" "proc_misc" "main_fg_dec")
  489     local -a set_color=("main_fg" "title" "hi_fg" "div_line" "inactive_fg" "selected_fg" "selected_bg" "cpu_box" "mem_box" "net_box" "proc_box")
  490 
  491     for theme_unset in ${!theme[@]}; do
  492         unset 'theme[${theme_unset}]'
  493     done
  494 
  495     #* Check if theme set in config exists and source it if it does
  496     if [[ -n ${color_theme} && ${color_theme} != "Default" && ${color_theme} =~ (themes/)|(user_themes/) && -e "${config_dir}/${color_theme%.theme}.theme" ]]; then
  497         # shellcheck source=/dev/null
  498         source "${config_dir}/${color_theme%.theme}.theme"
  499         sourced=1
  500     else
  501         color_theme="Default"
  502     fi
  503 
  504     main_fg_dec="${theme[main_fg]:-$main_fg}"
  505     theme[main_fg_dec]="${main_fg_dec}"
  506 
  507     #* Convert colors for graphs and meters from rgb hexadecimal to rgb decimal if needed
  508     for color_name in ${convert_color[@]}; do
  509         if [[ -n $sourced ]]; then hex2rgb="${theme[${color_name}]}"
  510         else hex2rgb="${!color_name}"; fi
  511 
  512         hex2rgb=${hex2rgb//#/}
  513 
  514         if [[ ${#hex2rgb} == 6 ]] && is_hex "$hex2rgb"; then hex2rgb="$((${hex}${hex2rgb:0:2})) $((${hex}${hex2rgb:2:2})) $((${hex}${hex2rgb:4:2}))"
  515         elif [[ ${#hex2rgb} == 2 ]] && is_hex "$hex2rgb"; then hex2rgb="$((${hex}${hex2rgb:0:2})) $((${hex}${hex2rgb:0:2})) $((${hex}${hex2rgb:0:2}))"
  516         else
  517             dec_test=(${hex2rgb})
  518             if [[ ${#dec_test[@]} -eq 3 ]] && is_int "${dec_test[@]}"; then hex2rgb="${dec_test[*]}"
  519             else unset hex2rgb; fi
  520         fi
  521 
  522         theme[${color_name}]="${hex2rgb}"
  523     done
  524 
  525     #* Set background color if set, otherwise use terminal default
  526     if [[ -n ${theme[main_bg]} ]]; then theme[main_bg_dec]="${theme[main_bg]}"; theme[main_bg]=";48;2;${theme[main_bg]// /;}"; fi
  527 
  528     #* Set colors from theme file if found and valid hexadecimal or integers, otherwise use default values
  529     for color_name in "${set_color[@]}"; do
  530         if [[ -z ${theme[$color_name]} ]] || ! is_hex "${theme[$color_name]}" && ! is_int "${theme[$color_name]}"; then theme[${color_name}]="${!color_name}"; fi
  531     done
  532 
  533     box[cpu_color]="${theme[cpu_box]}"
  534     box[mem_color]="${theme[mem_box]}"
  535     box[net_color]="${theme[net_box]}"
  536     box[processes_color]="${theme[proc_box]}"
  537 
  538     #* Create color arrays from one, two or three color gradient, 100 values in each
  539     for array_name in "temp" "cpu" "upload" "download" "used" "available" "cached" "free"; do
  540         local -n color_array="color_${array_name}_graph"
  541         local -a rgb_start=(${theme[${array_name}_start]}) rgb_mid=(${theme[${array_name}_mid]}) rgb_end=(${theme[${array_name}_end]})
  542         local pf_calc middle=1
  543 
  544         rgb[red]=${rgb_start[0]}; rgb[green]=${rgb_start[1]}; rgb[blue]=${rgb_start[2]}
  545 
  546         if [[ -z ${rgb_mid[*]} ]] && ((rgb_end[0]+rgb_end[1]+rgb_end[2]>rgb_start[0]+rgb_start[1]+rgb_start[2])); then
  547             rgb_mid=( $(( rgb_start[0]+( (rgb_end[0]-rgb_start[0])/2) )) $((rgb_start[1]+( (rgb_end[1]-rgb_start[1])/2) )) $((rgb_start[2]+( (rgb_end[2]-rgb_start[2])/2) )) )
  548         elif [[ -z ${rgb_mid[*]} ]]; then
  549             rgb_mid=( $(( rgb_end[0]+( (rgb_start[0]-rgb_end[0])/2) )) $(( rgb_end[1]+( (rgb_start[1]-rgb_end[1])/2) )) $(( rgb_end[2]+( (rgb_start[2]-rgb_end[2])/2) )) )
  550         fi
  551 
  552         for((i=0;i<=100;i++,y=0)); do
  553 
  554             if [[ -n ${rgb_end[*]} ]]; then
  555                 for this_color in "red" "green" "blue"; do
  556                     if ((i==50)); then rgb_start[y]=${rgb[$this_color]}; fi
  557 
  558                     if ((middle==1 & rgb[$this_color]<rgb_mid[y])); then
  559                         printf -v pf_calc "%.0f" "$(( i*( (rgb_mid[y]-rgb_start[y])*100/50*100) ))e-4"
  560 
  561                     elif ((middle==1 & rgb[$this_color]>rgb_mid[y])); then
  562                         printf -v pf_calc "%.0f" "-$(( i*( (rgb_start[y]-rgb_mid[y])*100/50*100) ))e-4"
  563 
  564                     elif ((middle==0 & rgb[$this_color]<rgb_end[y])); then
  565                         printf -v pf_calc "%.0f" "$(( (i-50)*( (rgb_end[y]-rgb_start[y])*100/50*100) ))e-4"
  566 
  567                     elif ((middle==0 & rgb[$this_color]>rgb_end[y])); then
  568                         printf -v pf_calc "%.0f" "-$(( (i-50)*( (rgb_start[y]-rgb_end[y])*100/50*100) ))e-4"
  569 
  570                     else
  571                         pf_calc=0
  572                     fi
  573 
  574                     rgb[$this_color]=$((rgb_start[y]+pf_calc))
  575                     if ((rgb[$this_color]<0)); then rgb[$this_color]=0
  576                     elif ((rgb[$this_color]>255)); then rgb[$this_color]=255; fi
  577 
  578                     y+=1
  579                     if ((i==49 & y==3 & middle==1)); then middle=0; fi
  580                 done
  581             fi
  582             color_array[i]="${rgb[red]} ${rgb[green]} ${rgb[blue]}"
  583         done
  584 
  585     done
  586 }
  587 
  588 quit_() { #? Clean exit
  589     #* Restore terminal options and screen
  590     if [[ $use_psutil == true && $2 != "psutil" ]]; then
  591         py_command quit
  592         sleep 0.1
  593         rm -rf "${pytmpdir}"
  594     fi
  595     echo -en "${clear_screen}${normal_screen}${show_cursor}"
  596     ${stty} "${saved_stty}"
  597     echo -en "\033]0;\a"
  598 
  599     #* Save any changed values to config file
  600     if [[ $config_file != "/dev/null" ]]; then
  601         save_config "${save_array[@]}"
  602     fi
  603 
  604     if [[ $1 == "restart" ]]; then exec "$(${realpath} "$0")"; fi
  605 
  606     exit ${1:-0}
  607 }
  608 
  609 sleep_() { #? Restore terminal options, stop and send to background if caught SIGTSTP (ctrl+z)
  610     echo -en "${clear_screen}${normal_screen}${show_cursor}"
  611     ${stty} "${saved_stty}"
  612     echo -en "\033]0;\a"
  613 
  614     if [[ $use_psutil == true ]]; then
  615         if ((failed_pipes>1)); then ((failed_pipes--)); fi
  616         py_command quit
  617         failed_pipe=1
  618         wait ${pycoproc_PID}
  619     fi
  620 
  621     ${kill} -s SIGSTOP $$
  622 }
  623 
  624 resume_() { #? Set terminal options and resume if caught SIGCONT ('fg' from terminal)
  625     sleepy=0
  626     echo -en "${alt_screen}${hide_cursor}${clear_screen}"
  627     echo -en "\033]0;${TERMINAL_TITLE} BashTOP\a"
  628     ${stty} -echo
  629 
  630     if [[ -n $pause_screen ]]; then
  631         echo -en "$pause_screen"
  632     else
  633         echo -en "${boxes_out}${proc_det}${last_screen}${mem_out}${proc_misc}${proc_misc2}${update_string}${clock_out}"
  634     fi
  635 }
  636 
  637 traperr() { #? Function for reporting error line numbers
  638     local match len trap_muted err="${BASH_LINENO[0]}"
  639 
  640     len=$((${#trace_array[@]}))
  641     if ((len-->=1)); then
  642         while ((len>=${#trace_array[@]}-2)); do
  643             if [[ $err == "${trace_array[$((len--))]}" ]]; then ((++match)) ; fi
  644         done
  645         if ((match==2 & len != -2)); then return
  646         elif ((match>=1)); then trap_muted="(MUTED!)"
  647         fi
  648     fi
  649     if ((len>100)); then unset 'trace_array[@]'; fi
  650     trace_array+=("$err")
  651     printf "%(%X)T ERROR: On line %s %s\n" -1 "$err" "$trap_muted" >> "${config_dir}/error.log"
  652 
  653 }
  654 
  655 resized() { #? Get new terminal size if terminal is resized
  656     resized=1
  657     unset winches
  658     while ((++winches<5)); do
  659         read tty_height tty_width < <(${stty} size)
  660         if (($tty_width<80 | $tty_height<24)); then
  661             size_error_msg
  662             winches=0
  663         else
  664             echo -en "${clear_screen}"
  665             create_box -w 30 -h 3 -c 1 -l 1 -lc "#EE2020" -title "resizing"
  666             print -jc 28 -fg ${theme[title]} "New size: ${tty_width}x${tty_height}"
  667             ${sleep} 0.2
  668             if [[ $(${stty} size) != "$tty_height $tty_width" ]]; then winches=0; fi
  669         fi
  670     done
  671 }
  672 
  673 size_error_msg() { #? Shows error message if terminal size is below 80x25
  674     local width=$tty_width
  675     local height=$tty_height
  676     echo -en "${clear_screen}"
  677     create_box -full -lc "#EE2020" -title "resize window"
  678     print -rs -m $((tty_height/2-1)) 2 -fg ${theme[title]} -c -l 11 "Current size: " -bg "#00" -fg "#dd2020" -d 1 -c "${tty_width}x${tty_height}" -rs
  679     print -d 1 -fg ${theme[title]} -c -l 15 "Need to be atleast:" -bg "#00" -fg "#30dd50" -d 1 -c "80x24" -rs
  680     while [[ $(${stty} size) == "$tty_height $tty_width" ]]; do ${sleep} 0.2; if [[ -n $quitting ]]; then quit_; fi ; done
  681 }
  682 
  683 draw_banner() { #? Draw banner, usage: draw_banner <line> [output variable]
  684     local y letter b_color x_color xpos ypos=$1 banner_out
  685     if [[ -n $2 ]]; then local -n banner_out=$2; fi
  686     xpos=$(( (tty_width/2)-(banner_width/2) ))
  687 
  688     for banner_line in "${banner[@]}"; do
  689         print -v banner_out -rs -move $((ypos+++y)) $xpos -t "${banner_line}"
  690     done
  691 
  692     if [[ -z $2 ]]; then echo -en "${banner_out}"; fi
  693 }
  694 
  695 create_config() { #? Creates a new config file with default values from above
  696     local c_line c_read this_file
  697     this_file="$(${realpath} "$0")"
  698     echo "#? Config file for bashtop v. ${version}" > "$config_file"
  699     while IFS= read -r c_line; do
  700         if [[ $c_line =~ aaz_config() ]]; then break
  701         elif [[ $c_read == "1" ]]; then echo "$c_line" >> "$config_file"
  702         elif [[ $c_line =~ aaa_config() ]]; then c_read=1; fi
  703     done < "$this_file"
  704 }
  705 
  706 save_config() { #? Saves variables to config file if not same, usage: save_config "var1" ["var2"] ["var3"]...
  707     if [[ -z $1 || $config_file == "/dev/null" ]]; then return; fi
  708     local var tmp_conf tmp_value quote original new
  709     tmp_conf="$(<"$config_file")"
  710     for var in "$@"; do
  711         if [[ ${tmp_conf} =~ ${var} ]]; then
  712             get_value -v "tmp_value" -sv "tmp_conf" -k "${var}="
  713             if [[ ${tmp_value//\"/} != "${!var}" ]]; then
  714                 original="${var}=${tmp_value}"
  715                 new="${var}=\"${!var}\""
  716                 original="${original//'/'/'\/'}"
  717                 new="${new//'/'/'\/'}"
  718                 ${sed} -i "s/${original}/${new}/" "${config_file}"
  719             fi
  720         else
  721             echo "${var}=\"${!var}\"" >> "$config_file"
  722         fi
  723     done
  724 }
  725 
  726 set_font() { #? Take a string and generate a string of unicode characters of given font, usage; set_font "font-name [bold] [italic]" "string"
  727     local i letter letter_hex new_hex add_hex start font="$1" string_in="$2" string_out hex="16#"
  728     if [[ -z $font || -z $string_in ]]; then return; fi
  729     case "$font" in
  730         "sans-serif") lower_start="1D5BA"; upper_start="1D5A0"; digit_start="1D7E2";;
  731         "sans-serif bold") lower_start="1D5EE"; upper_start="1D5D4"; digit_start="1D7EC";;
  732         "sans-serif italic") lower_start="1D622"; upper_start="1D608"; digit_start="1D7E2";;
  733         #"sans-serif bold italic") start="1D656"; upper_start="1D63C"; digit_start="1D7EC";;
  734         "script") lower_start="1D4B6"; upper_start="1D49C"; digit_start="1D7E2";;
  735         "script bold") lower_start="1D4EA"; upper_start="1D4D0"; digit_start="1D7EC";;
  736         "fraktur") lower_start="1D51E"; upper_start="1D504"; digit_start="1D7E2";;
  737         "fraktur bold") lower_start="1D586"; upper_start="1D56C"; digit_start="1D7EC";;
  738         "monospace") lower_start="1D68A"; upper_start="1D670"; digit_start="1D7F6";;
  739         "double-struck") lower_start="1D552"; upper_start="1D538"; digit_start="1D7D8";;
  740         *) echo -n "${string_in}"; return;;
  741     esac
  742 
  743     for((i=0;i<${#string_in};i++)); do
  744         letter=${string_in:i:1}
  745         if [[ $letter =~ [a-z] ]]; then #61
  746             printf -v letter_hex '%X\n' "'$letter"
  747             printf -v add_hex '%X' "$((${hex}${letter_hex}-${hex}61))"
  748             printf -v new_hex '%X' "$((${hex}${lower_start}+${hex}${add_hex}))"
  749             string_out="${string_out}\U${new_hex}"
  750             #if [[ $font =~ sans-serif && $letter =~ m|w ]]; then string_out="${string_out} "; fi
  751             #\U205F
  752         elif [[ $letter =~ [A-Z] ]]; then #41
  753             printf -v letter_hex '%X\n' "'$letter"
  754             printf -v add_hex '%X' "$((${hex}${letter_hex}-${hex}41))"
  755             printf -v new_hex '%X' "$((${hex}${upper_start}+${hex}${add_hex}))"
  756             string_out="${string_out}\U${new_hex}"
  757             #if [[ $font =~ sans-serif && $letter =~ M|W ]]; then string_out="${string_out} "; fi
  758         elif [[ $letter =~ [0-9] ]]; then #30
  759             printf -v letter_hex '%X\n' "'$letter"
  760             printf -v add_hex '%X' "$((${hex}${letter_hex}-${hex}30))"
  761             printf -v new_hex '%X' "$((${hex}${digit_start}+${hex}${add_hex}))"
  762             string_out="${string_out}\U${new_hex}"
  763         else
  764             string_out="${string_out} \e[1D${letter}"
  765         fi
  766     done
  767 
  768     echo -en "${string_out}"
  769 }
  770 
  771 sort_array_int() {  #? Copy and sort an array of integers from largest to smallest value, usage: sort_array_int "input array" "output array"
  772     #* Return if given array has no values
  773     if [[ -z ${!1} ]]; then return; fi
  774     local start_n search_n tmp_array
  775 
  776     #* Create pointers to arrays
  777     local -n in_arr="$1"
  778     local -n out_arr="$2"
  779 
  780     #* Create local copy of array
  781     local array=("${in_arr[@]}")
  782 
  783     #* Start sorting
  784     for ((start_n=0;start_n<=${#array[@]}-1;++start_n)); do
  785         for ((search_n=start_n+1;search_n<=${#array[@]}-1;++search_n)); do
  786             if ((array[start_n]<array[search_n])); then
  787                 tmp_array=${array[start_n]}
  788                 array[start_n]=${array[search_n]}
  789                 array[search_n]=$tmp_array
  790             fi
  791         done
  792     done
  793 
  794     #* Write the sorted array to output array
  795     out_arr=("${array[@]}")
  796 }
  797 
  798 subscript() { #? Convert an integer to a string of subscript numbers
  799     local i out int=$1
  800     for((i=0;i<${#int};i++)); do
  801         out="${out}${subscript[${int:$i:1}]}"
  802     done
  803     echo -n "${out}"
  804 }
  805 
  806 spaces() { #? Prints back spaces, usage: spaces "number of spaces"
  807     printf "%${1}s" ""
  808 }
  809 
  810 is_int() { #? Check if value(s) is integer
  811     local param
  812     for param; do
  813         if [[ ! $param =~ ^[\-]?[0-9]+$ ]]; then return 1; fi
  814     done
  815 }
  816 
  817 is_float() { #? Check if value(s) is floating point
  818     local param
  819     for param; do
  820         if [[ ! $param =~ ^[\-]?[0-9]*[,.][0-9]+$ ]]; then return 1; fi
  821     done
  822 }
  823 
  824 is_hex() { #? Check if value(s) is hexadecimal
  825     local param
  826     for param; do
  827         if [[ ! ${param//#/} =~ ^[0-9a-fA-F]*$ ]]; then return 1; fi
  828     done
  829 }
  830 
  831 floating_humanizer() {  #? Convert integer to floating point and scale up in steps of 1024 to highest positive unit
  832                         #? Usage: floating_humanizer <-b,-bit|-B,-Byte> [-ps,-per-second] [-s,-start "1024 multiplier start"] [-v,-variable-output] <input>
  833     local value selector per_second unit_mult decimals out_var ext_var short sep=" "
  834     local -a unit
  835     until (($#==0)); do
  836         case "$1" in
  837             -b|-bit) unit=(bit Kib Mib Gib Tib Pib); unit_mult=8;;
  838             -B|-Byte) unit=(Byte KiB MiB GiB TiB PiB); unit_mult=1;;
  839             -ps|-per-second) per_second=1;;
  840             -short) short=1; sep="";;
  841             -s|-start) selector="$2"; shift;;
  842             -v|-variable-output) local -n out_var="$2"; ext_var=1; shift;;
  843             *) if is_int "$1"; then value=$1; break; fi;;
  844         esac
  845         shift
  846     done
  847 
  848     if [[ -z $value || $value -lt 0 || -z $unit_mult ]]; then return; fi
  849 
  850     if ((per_second==1 & unit_mult==1)); then per_second="/s"
  851     elif ((per_second==1)); then per_second="ps"; fi
  852 
  853     if ((value>0)); then
  854         value=$((value*100*unit_mult))
  855 
  856         until ((${#value}<6)); do
  857             value=$((value>>10))
  858             if ((value<100)); then value=100; fi
  859             ((++selector))
  860         done
  861 
  862         if ((${#value}<5 & ${#value}>=2 & selector>0)); then
  863             decimals=$((5-${#value}))
  864             value="${value::-2}.${value:(-${decimals})}"
  865         elif ((${#value}>=2)); then
  866             value="${value::-2}"
  867         fi
  868     fi
  869 
  870     if [[ -n $short ]]; then value="${value%.*}"; fi
  871 
  872     out_var="${value}${sep}${unit[$selector]::${short:-${#unit[$selector]}}}${per_second}"
  873     if [[ -z $ext_var ]]; then echo -n "${out_var}"; fi
  874 }
  875 
  876 get_cpu_info() {
  877     local lscpu_var pyin
  878     if [[ $use_psutil == true ]]; then
  879         if [[ -z ${cpu[threads]} || -z ${cpu[cores]} ]]; then
  880             py_command -v pyin "get_cpu_cores()"
  881             read cpu[cores] cpu[threads] <<<"${pyin}"
  882         fi
  883 
  884     else
  885         if command -v lscpu >/dev/null 2>&1; then lscpu_var="$(lscpu)"; fi
  886         if [[ -z ${cpu[threads]} || -z ${cpu[cores]} ]]; then
  887             if ! get_value -v 'cpu[threads]' -sv "lscpu_var" -k "CPU(s):" -i || [[ ${cpu[threads]} == "0" ]]; then
  888                 cpu[threads]="$(nproc 2>/dev/null ||true)"
  889                 if [[ -z ${cpu[threads]} ]]; then cpu[threads]="1"; fi
  890                 cpu[cores]=${cpu[threads]}
  891             else
  892             get_value -v 'cpu[cores]' -sv "lscpu_var" -k "Core(s)" -i
  893             fi
  894         fi
  895     fi
  896 
  897     if [[ $use_psutil == false && -z $custom_cpu_name ]]; then
  898         if ! get_value -v 'cpu[model]' -sv "lscpu_var" -k "Model name:" -a -b -k "CPU" -mk -1; then
  899             if ! get_value -v 'cpu[model]' -sv "lscpu_var" -k "Model name:" -r "  "; then
  900                 cpu[model]="cpu"
  901             fi
  902         fi
  903     elif [[ $use_psutil == true && -z $custom_cpu_name ]]; then
  904         py_command -v cpu[model] "get_cpu_name()"
  905     else
  906         cpu[model]="${custom_cpu_name}"
  907     fi
  908 }
  909 
  910 get_value() { #? Get a value from a file, variable or array by searching for a non spaced "key name" on the same line
  911     local match line_pos=1 int reg key all tmp_array input found input_line line_array line_val ext_var line_nr current_line match_key math removing ext_arr
  912     local -a remove
  913     until (($#==0)); do
  914         until (($#==0)); do
  915             case "$1" in
  916                 -k|-key) key="$2"; shift;;                                                      #? Key "string" on the same line as target value
  917                 -m|-match) match="$2"; shift;;                                                  #? If multiple matches on a line, match occurrence "x"
  918                 -mk|-match-key) match_key=$2; line_pos=0; shift;;                               #? Match in relation to key position, -1 for previous value, 1 for next value
  919                 -b|-break) shift; break;;                                                       #? Break up arguments for multiple searches
  920                 -a|-all) all=1;;                                                                #? Prints back found line including key
  921                 -l|-line) line_nr="$2"; shift;;                                                 #? Set target line if no key is available
  922                 -ss|-source-string) input="$2"; shift;;                                         #? Argument string as source
  923                 -sf|-source-file) input="$(<"$2")"; shift;;                                     #? File as source
  924                 -sv|-source-var) input="${!2}"; shift;;                                         #? Variable as source
  925                 -sa|-source-array) local -n tmp_array=$2; input="${tmp_array[*]}"; shift;;      #? Array as source
  926                 -fp|-floating-point) reg="[\-]?[0-9]*[.,][0-9]+"; match=1;;                     #? Match floating point value
  927                 -math) math="$2"; shift;;                                                       #? Perform math on a integer value, "x" represents value, only works if "integer" argument is given
  928                 -i|-integer) reg="[\-]?[0-9]+[.,]?[0-9]*"; int=1; match=1;;                     #? Match integer value or float and convert to int
  929                 -r|-remove) remove+=("$2"); shift;;                                             #? Format output by removing entered regex, can be used multiple times
  930                 -v|-variable-out) local -n found="$2"; ext_var=1; shift;;                       #? Output to variable
  931                 -map|-map-array) local -n array_out="$2"; ext_var=1; ext_arr=1; shift;;         #? Map output to array
  932             esac
  933             shift
  934         done
  935 
  936         found=""
  937 
  938         if [[ -z $input ]]; then return 1; fi
  939         if [[ -z $line_nr && -z $key ]]; then line_nr=1; fi
  940 
  941         while IFS='' read -r input_line; do
  942             ((++current_line))
  943             if [[ -n $line_nr && $current_line -eq $line_nr || -z $line_nr && -n $key && ${input_line/${key}/} != "$input_line" ]]; then
  944                 if [[ -n $all ]]; then
  945                     found="${input_line}"
  946                     break
  947 
  948                 elif [[ -z $match && -z $match_key && -z $reg ]]; then
  949                     found="${input_line/${key}/}"
  950                     break
  951 
  952                 else
  953                     line_array=(${input_line/${key}/${key// /}})
  954 
  955                 fi
  956 
  957                 for line_val in "${line_array[@]}"; do
  958                     if [[ -n $match_key && $line_val == "${key// /}" ]]; then
  959                         if ((match_key<0 & line_pos+match_key>=0)) || ((match_key>=0 & line_pos+match_key<${#line_array[@]})); then
  960                             found="${line_array[$((line_pos+match_key))]}"
  961                             break 2
  962                         else
  963                             return 1
  964                         fi
  965 
  966                     elif [[ -n $match_key ]]; then
  967                         ((++line_pos))
  968 
  969                     elif [[ -n $reg && $line_val =~ ^${reg}$ || -z $reg && -n $match ]]; then
  970                         if ((line_pos==match)); then
  971                             found=${line_val}
  972                             break 2
  973                         fi
  974                         ((++line_pos))
  975                     fi
  976                 done
  977             fi
  978         done <<<"${input}"
  979 
  980         if [[ -z $found ]]; then return 1; fi
  981 
  982         if [[ -n ${remove[*]} ]]; then
  983             for removing in "${remove[@]}"; do
  984                 found="${found//${removing}/}"
  985             done
  986         fi
  987 
  988         if [[ -n $int && $found =~ [.,] ]]; then
  989             found="${found/,/.}"
  990             printf -v found "%.0f" "${found}"
  991         fi
  992 
  993         if [[ -n $math && -n $int ]]; then
  994             math="${math//x/$found}"
  995             found=$((${math}))
  996         fi
  997 
  998         if (($#>0)); then
  999             input="${found}"
 1000             unset key match match_key all reg found int 'remove[@]' current_line
 1001             line_pos=1
 1002         fi
 1003 
 1004     done
 1005 
 1006     if [[ -z $ext_var ]]; then echo "${found}"; fi
 1007     if [[ -n $ext_arr ]]; then array_out=(${found}); fi
 1008 }
 1009 
 1010 get_themes() {
 1011     local file
 1012     theme_int=0
 1013     themes=("Default")
 1014     for file in "${config_dir}/themes"/*.theme; do
 1015         file="${file##*/}"
 1016         if [[ ${file} != "*.theme" ]]; then themes+=("themes/${file%.theme}"); fi
 1017         if [[ ${themes[-1]} == "${color_theme}" ]]; then theme_int=${#themes[@]}-1; fi
 1018     done
 1019     for file in "${config_dir}/user_themes"/*.theme; do
 1020         file="${file##*/}"
 1021         if [[ ${file} != "*.theme" ]]; then themes+=("user_themes/${file%.theme}"); fi
 1022         if [[ ${themes[-1]} == "${color_theme}" ]]; then theme_int=${#themes[@]}-1; fi
 1023     done
 1024 }
 1025 
 1026 get_net_device() { #? Check for internet connection, name of default network device and create list of all devices
 1027     if [[ $use_psutil == true ]]; then get_net_device_psutil; return; fi
 1028     local -a netdev
 1029     local ndline
 1030     if ! get_value -v 'net[device]' -ss "$(ip route get 1.1.1.1 2>/dev/null)" -k "dev" -mk 1; then
 1031         net[no_device]=1
 1032     else
 1033         unset 'net[no_device]' 'nic_list[@]' nic_int
 1034         readarray -t netdev </proc/net/dev
 1035         for ndline in "${netdev[@]:2}"; do
 1036             ndline="${ndline%:*}"; ndline="${ndline// /}"
 1037             nic_list+=("${ndline}")
 1038             if [[ ${ndline} == "${net[device]}" ]]; then
 1039                 nic_int=$((${#nic_list[@]}-1))
 1040             fi
 1041         done
 1042         collect_net init
 1043     fi
 1044 }
 1045 
 1046 get_net_device_psutil() {
 1047     unset 'nic_list[@]'
 1048     py_command -a nic_list "get_nics()"
 1049     net[device]="${nic_list[0]}"
 1050     nic_int=0
 1051     if [[ -z ${net[device]} ]]; then
 1052         net[no_device]=1
 1053     else
 1054         unset 'net[no_device]'
 1055         collect_net init
 1056     fi
 1057 }
 1058 
 1059 cur_pos() { #? Get cursor postion, argument "line" prints current line, argument "col" prints current column, no argument prints both in format "line column"
 1060     local line col
 1061     IFS=';' read -sdR -p $'\E[6n' line col
 1062     if [[ -z $1 || $1 == "line" ]]; then echo -n "${line#*[}${1:-" "}"; fi
 1063     if [[ -z $1 || $1 == "col" ]]; then echo -n "$col"; fi
 1064 }
 1065 
 1066 create_box() { #? Draw a box with an optional title at given location
 1067     local width height col line title ltype hpos vpos i hlines vlines color line_color c_rev=0 box_out ext_var fill
 1068     until (($#==0)); do
 1069         case $1 in
 1070             -f|-full) col=1; line=1; width=$((tty_width)); height=$((tty_height));;                         #? Use full terminal size for box
 1071             -c|-col) if is_int "$2"; then col=$2; shift; fi;;                                               #? Column position to start box
 1072             -l|-line) if is_int "$2"; then line=$2; shift; fi;;                                             #? Line position to start box
 1073             -w|-width) if is_int "$2"; then width=$2; shift; fi;;                                           #? Width of box
 1074             -h|-height) if is_int "$2"; then height=$2; shift; fi;;                                         #? Height of box
 1075             -t|-title) if [[ -n $2 ]]; then title="$2"; shift; fi;;                                         #? Draw title without titlebar
 1076             -s|-single) ltype="single";;                                                                    #? Use single lines
 1077             -d|-double) ltype="double";;                                                                    #? Use double lines
 1078             -lc|-line-color) line_color="$2"; shift;;                                                       #? Color of the lines
 1079             -fill) fill=1;;                                                                                 #? Fill background of box
 1080             -v|-variable) local -n box_out=$2; ext_var=1; shift;;                                           #? Output box to a variable
 1081         esac
 1082         shift
 1083     done
 1084     if [[ -z $col || -z $line || -z $width || -z $height ]]; then return; fi
 1085 
 1086     ltype=${ltype:-"single"}
 1087     vlines+=("$col" "$((col+width-1))")
 1088     hlines+=("$line" "$((line+height-1))")
 1089 
 1090     print -v box_out -rs
 1091 
 1092     #* Fill box if enabled
 1093     if [[ -n $fill ]]; then
 1094         for((i=line+1;i<line+height-1;i++)); do
 1095             print -v box_out -m $i $((col+1)) -rp $((width-2)) -t " "
 1096         done
 1097     fi
 1098 
 1099     #* Draw all horizontal lines
 1100     print -v box_out -fg ${line_color:-${theme[div_line]}}
 1101     for hpos in "${hlines[@]}"; do
 1102         print -v box_out -m $hpos $col -rp $((width-1)) -t "${box[${ltype}_hor_line]}"
 1103     done
 1104 
 1105     #* Draw all vertical lines
 1106     for vpos in "${vlines[@]}"; do
 1107         print -v box_out -m $line $vpos
 1108         for((hpos=line;hpos<=line+height-1;hpos++)); do
 1109             print -v box_out -m $hpos $vpos -t "${box[${ltype}_vert_line]}"
 1110         done
 1111     done
 1112 
 1113     #* Draw corners
 1114     print -v box_out -m $line $col -t "${box[${ltype}_left_corner_up]}"
 1115     print -v box_out -m $line $((col+width-1)) -t "${box[${ltype}_right_corner_up]}"
 1116     print -v box_out -m $((line+height-1)) $col -t "${box[${ltype}_left_corner_down]}"
 1117     print -v box_out -m $((line+height-1)) $((col+width-1)) -t "${box[${ltype}_right_corner_down]}"
 1118 
 1119     #* Draw small title without titlebar
 1120     if [[ -n $title ]]; then
 1121         print -v box_out -m $line $((col+2)) -t "┤" -fg ${theme[title]} -b -t "$title" -rs -fg ${line_color:-${theme[div_line]}} -t "├"
 1122     fi
 1123 
 1124     print -v box_out -rs -m $((line+1)) $((col+1))
 1125 
 1126     if [[ -z $ext_var ]]; then echo -en "${box_out}"; fi
 1127 
 1128 
 1129 }
 1130 
 1131 create_meter() {    #? Create a horizontal percentage meter, usage; create_meter <value 0-100>
 1132                     #? Optional arguments: [-p, -place <line> <col>] [-w, -width <columns>] [-f, -fill-empty]
 1133                     #? [-c, -color "array-name"] [-i, -invert-color] [-v, -variable "variable-name"]
 1134     if [[ -z $1 ]]; then return; fi
 1135     local val width colors color block="■" i fill_empty col line var ext_var out meter_var print_var invert bg_color=${theme[inactive_fg]}
 1136 
 1137     #* Argument parsing
 1138     until (($#==0)); do
 1139         case $1 in
 1140             -p|-place) if is_int "${@:2:2}"; then line=$2; col=$3; shift 2; fi;;                                #? Placement for meter
 1141             -w|-width) width=$2; shift;;                                                                        #? Width of meter in columns
 1142             -c|-color) local -n colors=$2; shift;;                                                              #? Name of an array containing colors from index 0-100
 1143             -i|-invert) invert=1;;                                                                              #? Invert meter
 1144             -f|-fill-empty) fill_empty=1;;                                                                      #? Fill unused space with dark blocks
 1145             -v|-variable) local -n meter_var=$2; ext_var=1; shift;;                                             #? Output meter to a variable
 1146             *) if is_int "$1"; then val=$1; fi;;
 1147         esac
 1148         shift
 1149     done
 1150 
 1151     if [[ -z $val ]]; then return; fi
 1152 
 1153     #* Set default width if not given
 1154     width=${width:-10}
 1155 
 1156     #* If no color array was given, create a simple greyscale array
 1157     if [[ -z $colors ]]; then
 1158         for ((i=0,ic=50;i<=100;i++,ic=ic+2)); do
 1159             colors[i]="${ic} ${ic} ${ic}"
 1160         done
 1161     fi
 1162 
 1163     #* Create the meter
 1164     meter_var=""
 1165     if [[ -n $line && -n $col ]]; then print -v meter_var -rs -m $line $col
 1166     else print -v meter_var -rs; fi
 1167 
 1168     if [[ -n $invert ]]; then print -v meter_var -r $((width+1)); fi
 1169     for((i=1;i<=width;i++)); do
 1170         if [[ -n $invert ]]; then print -v meter_var -l 2; fi
 1171 
 1172         if ((val>=i*100/width)); then
 1173             print -v meter_var -fg ${colors[$((i*100/width))]} -t "${block}"
 1174         elif ((fill_empty==1)); then
 1175             if [[ -n $invert ]]; then print -v meter_var -l $((width-i)); fi
 1176             print -v meter_var -fg $bg_color -rp $((1+width-i)) -t "${block}"; break
 1177         else
 1178             if [[ -n $invert ]]; then break; print -v meter_var -l $((1+width-i))
 1179             else print -v meter_var -r $((1+width-i)); break; fi
 1180         fi
 1181     done
 1182     if [[ -z $ext_var ]]; then echo -en "${meter_var}"; fi
 1183 }
 1184 
 1185 create_graph() {    #? Create a graph from an array of percentage values, usage;    create_graph <options> <value-array>
 1186                     #? Create a graph from an array of non percentage values:       create_graph <options> <-max "max value"> <value-array>
 1187                     #? Add a value to existing graph;                               create_graph [-i, -invert] [-max "max value"] -add-value "graph_array" <value>
 1188                     #? Add last value from an array to existing graph;              create_graph [-i, -invert] [-max "max value"] -add-last "graph_array" "value-array"
 1189                     #? Options: < -d, -dimensions <line> <col> <height> <width> > [-i, -invert] [-n, -no-guide] [-c, -color "array-name"] [-o, -output-array "variable-name"]
 1190     if [[ -z $1 ]]; then return; fi
 1191     if [[ ${graph[hires]} == true ]]; then create_graph_hires "$@"; return; fi
 1192 
 1193     local val col s_col line s_line height s_height width s_width colors color i var ext_var out side_num side_nums=1 add add_array invert no_guide max
 1194     local -a graph_array input_array
 1195 
 1196     #* Argument parsing
 1197     until (($#==0)); do
 1198         case $1 in
 1199             -d|-dimensions) if is_int "${@:2:4}"; then line=$2; col=$3; height=$4; width=$5; shift 4; fi;;                      #? Graph dimensions
 1200             -c|-color) local -n colors=$2; shift;;                                                                              #? Name of an array containing colors from index 0-100
 1201             -o|-output-array) local -n output_array=$2; ext_var=1; shift;;                                                      #? Output meter to an array
 1202             -add-value) if is_int "$3"; then local -n output_array=$2; add=$3; break; else return; fi;;                         #? Add a value to existing graph
 1203             -add-last) local -n output_array=$2; local -n add_array=$3; add=${add_array[-1]}; break;;                           #? Add last value from array to existing graph
 1204             -i|-invert) invert=1;;                                                                                              #? Invert graph, drawing from top to bottom
 1205             -n|-no-guide) no_guide=1;;                                                                                          #? Don't print side and bottom guide lines
 1206             -max) if is_int "$2"; then max=$2; shift; fi;;                                                                      #? Needed max value for non percentage arrays
 1207             *) local -n tmp_in_array=$1; input_array=("${tmp_in_array[@]}");;
 1208         esac
 1209         shift
 1210     done
 1211 
 1212     if [[ -z $no_guide ]]; then
 1213         ((--height))
 1214     else
 1215         if [[ -n $invert ]]; then ((line--)); fi
 1216     fi
 1217 
 1218 
 1219     if ((width<3)); then width=3; fi
 1220     if ((height<1)); then height=1; fi
 1221 
 1222 
 1223     #* If argument "add" was passed check for existing graph and make room for new value(s)
 1224     local add_start add_end
 1225     if [[ -n $add ]]; then
 1226         local cut_left search
 1227         if [[ -n ${input_array[0]} ]]; then return; fi
 1228         if [[ -n $output_array ]]; then
 1229             graph_array=("${output_array[@]}")
 1230             if [[ -z ${graph_array[0]} ]]; then return; fi
 1231         else
 1232             return
 1233         fi
 1234         height=$((${#graph_array[@]}-1))
 1235         input_array[0]=${add}
 1236 
 1237         #* Remove last value in current graph
 1238 
 1239         for ((i=0;i<height;i++)); do
 1240             cut_left="${graph_array[i]%m*}"
 1241             search=$((${#cut_left}+1))
 1242             graph_array[i]="${graph_array[i]::$search}${graph_array[i]:$((search+1))}"
 1243         done
 1244 
 1245     fi
 1246 
 1247     #* Initialize graph if no "add" argument was given
 1248     if [[ -z $add ]]; then
 1249         #* Scale down graph one line if height is even
 1250         local inv_offset h_inv normal_vals=1
 1251         local -a side_num=(100 0) g_char=(" ⡇" " ⠓" "⠒") g_index
 1252 
 1253         if [[ -n $invert ]]; then
 1254             for((i=height;i>=0;i--)); do
 1255                 g_index+=($i)
 1256             done
 1257 
 1258         else
 1259             for((i=0;i<=height;i++)); do
 1260                 g_index+=($i)
 1261             done
 1262         fi
 1263 
 1264         if [[ -n $no_guide ]]; then unset normal_vals
 1265         elif [[ -n $invert ]]; then g_char=(" ⡇" " ⡤" "⠤")
 1266         fi
 1267 
 1268         #* Set up graph array print side numbers and lines
 1269         print -v graph_array[0] -rs
 1270         print -v graph_array[0] -m $((line+g_index[0])) ${col} ${normal_vals:+-jr 3 -fg "#ee" -b -t "${side_num[0]}" -rs -fg ${theme[main_fg]} -t "${g_char[0]}"} -fg ${colors[100]}
 1271         for((i=1;i<height;i++)); do
 1272             print -v graph_array[i] -m $((line+g_index[i])) ${col} ${normal_vals:+-r 3 -fg ${theme[main_fg]} -t "${g_char[0]}"} -fg ${colors[$((100-i*100/height))]}
 1273         done
 1274 
 1275         if [[ -z $no_guide ]]; then width=$((width-5)); fi
 1276 
 1277         graph_array[height]=""
 1278         if [[ -z $no_guide ]]; then
 1279             print -v graph_array[$height] -m $((line+g_index[(-1)])) ${col} -jr 3 -fg "#ee" -b -t "${side_num[1]}" -rs -fg ${theme[main_fg]} -t "${g_char[1]}" -rp ${width} -t "${g_char[2]}"
 1280         fi
 1281 
 1282         #* If no color array was given, create a simple greyscale array
 1283         if [[ -z $colors ]]; then
 1284             for ((i=0,ic=50;i<=100;i++,ic=ic+2)); do
 1285                 colors[i]="${ic} ${ic} ${ic}"
 1286             done
 1287         fi
 1288     fi
 1289 
 1290     #* Create the graph
 1291     local value_width x y a cur_value prev_value=100 symbol tmp_out compare found count virt_height=$((height*10))
 1292     if [[ -n $add ]]; then
 1293         value_width=1
 1294     elif ((${#input_array[@]}<=width)); then
 1295         value_width=${#input_array[@]};
 1296     else
 1297         value_width=${width}
 1298         input_array=("${input_array[@]:(-$width)}")
 1299     fi
 1300 
 1301     if [[ -n $invert ]]; then
 1302         y=$((height-1))
 1303         done_val="-1"
 1304     else
 1305         y=0
 1306         done_val=$height
 1307     fi
 1308 
 1309     #* Convert input array to percentage values of max if a max value was given
 1310     if [[ -n $max ]]; then
 1311         for((i=0;i<${#input_array[@]};i++)); do
 1312             if ((input_array[i]>=max)); then
 1313                 input_array[i]=100
 1314             else
 1315                 input_array[i]=$((input_array[i]*100/max))
 1316             fi
 1317         done
 1318     fi
 1319 
 1320     until ((y==done_val)); do
 1321 
 1322         #* Print spaces to right-justify graph if number of values is less than graph width
 1323         if [[ -z $add ]] && ((value_width<width)); then print -v graph_array[y] -rp $((width-value_width)) -t " "; fi
 1324 
 1325         cur_value=$(( virt_height-(y*10) ))
 1326         next_value=$(( virt_height-((y+1)*10) ))
 1327 
 1328         count=0
 1329         x=0
 1330 
 1331         #* Create graph by walking through all values for each line, speed up by counting similar values and print once, when difference is met
 1332         while ((x<value_width)); do
 1333 
 1334             if [[ -z ${input_array[x]} ]] || ((input_array[x]<1)) || ((${#input_array[x]}>3)); then input_array[x]=0; fi
 1335 
 1336             #* Print empty space if current value is less than percentage for current line
 1337             while ((x<value_width & input_array[x]*virt_height/100<next_value)); do
 1338                 ((++count))
 1339                 ((++x))
 1340             done
 1341             if ((count>0)); then
 1342                 print -v graph_array[y] -rp ${count} -t " "
 1343                 count=0
 1344             fi
 1345 
 1346             #* Print current value in percent relative to graph size if current value is less than line percentage but greater than next line percentage
 1347             while ((x<value_width & input_array[x]*virt_height/100<cur_value & input_array[x]*virt_height/100>=next_value)); do
 1348                 print -v graph_array[y] -t "${graph_symbol[${invert:+-}$(( (input_array[x]*virt_height/100)-next_value ))]}"
 1349                 ((++x))
 1350             done
 1351 
 1352             #* Print full block if current value is greater than percentage for current line
 1353             while ((x<value_width & input_array[x]*virt_height/100>=cur_value)); do
 1354                 ((++count))
 1355                 ((++x))
 1356             done
 1357             if ((count>0)); then
 1358                 print -v graph_array[y] -rp ${count} -t "${graph_symbol[10]}"
 1359                 count=0
 1360             fi
 1361         done
 1362 
 1363     if [[ -n $invert ]]; then
 1364         ((y--)) || true
 1365     else
 1366         ((++y))
 1367     fi
 1368     done
 1369 
 1370     #* Echo out graph if no argument for a output array was given
 1371     if [[ -z $ext_var && -z $add ]]; then echo -en "${graph_array[*]}"
 1372     else output_array=("${graph_array[@]}"); fi
 1373 }
 1374 
 1375 create_mini_graph() {   #? Create a one line high graph from an array of percentage values, usage;  create_mini_graph <options> <value-array>
 1376                         #? Add a value to existing graph;                       create_mini_graph [-i, -invert] [-nc, -no-color] [-c, -color "array-name"] -add-value "graph_variable" <value>
 1377                         #? Add last value from an array to existing graph;      create_mini_graph [-i, -invert] [-nc, -no-color] [-c, -color "array-name"] -add-last "graph_variable" "value-array"
 1378                         #? Options: [-w, -width <width>] [-i, -invert] [-nc, -no-color] [-c, -color "array-name"] [-o, -output-variable "variable-name"]
 1379     if [[ -z $1 ]]; then return; fi
 1380 
 1381     if [[ ${graph[hires]} == true ]]; then create_mini_graph_hires "$@"; return; fi
 1382 
 1383     local val col s_col line s_line height s_height width s_width colors color i var ext_var out side_num side_nums=1 add invert no_guide graph_var no_color color_value
 1384 
 1385     #* Argument parsing
 1386     until (($#==0)); do
 1387         case $1 in
 1388             -w|-width) if is_int "$2"; then width=$2; shift; fi;;                                                           #? Graph width
 1389             -c|-color) local -n colors=$2; shift;;                                                                          #? Name of an array containing colors from index 0-100
 1390             -nc|-no-color) no_color=1;;                                                                                     #? Set no color
 1391             -o|-output-variable) local -n output_var=$2; ext_var=1; shift;;                                                 #? Output graph to a variable
 1392             -add-value) if is_int "$3"; then local -n output_var=$2; add=$3; break; else return; fi;;                       #? Add a value to existing graph
 1393             -add-last) local -n output_var=$2 add_array=$3; add="${add_array[-1]}"; break;;                                 #? Add last value from array to existing graph
 1394             -i|-invert) invert=1;;                                                                                          #? Invert graph, drawing from top to bottom
 1395             *) local -n input_array=$1;;
 1396         esac
 1397         shift
 1398     done
 1399 
 1400     if ((width<1)); then width=1; fi
 1401 
 1402     #* If argument "add" was passed check for existing graph and make room for new value(s)
 1403     local add_start add_end
 1404     if [[ -n $add ]]; then
 1405         local cut_left search
 1406         #if [[ -n ${input_array[0]} ]]; then return; fi
 1407         if [[ -n $output_var ]]; then
 1408             graph_var="${output_var}"
 1409             if [[ -z ${graph_var} ]]; then return; fi
 1410         else
 1411             return
 1412         fi
 1413 
 1414         declare -a input_array
 1415         input_array[0]=${add}
 1416 
 1417         #* Remove last value in current graph
 1418         if [[ -n ${graph_var} && -z $no_color ]]; then
 1419             if [[ ${graph_var::5} == "\e[1C" ]]; then
 1420                 graph_var="${graph_var#'\e[1C'}"
 1421             else
 1422                 cut_left="${graph_var%%m*}"
 1423                 search=$((${#cut_left}+1))
 1424                 graph_var="${graph_var:$((search+1))}"
 1425             fi
 1426         elif [[ -n ${graph_var} && -n $no_color ]]; then
 1427             if [[ ${graph_var::5} == "\e[1C" ]]; then
 1428                 #cut_left="${graph_var%%C*}"
 1429                 #search=$((${#cut_left}+1))
 1430                 #graph_var="${graph_var:$((search))}"
 1431                 graph_var="${graph_var#'\e[1C'}"
 1432             else
 1433                 graph_var="${graph_var:1}"
 1434             fi
 1435         fi
 1436     fi
 1437 
 1438 
 1439     #* If no color array was given, create a simple greyscale array
 1440     if [[ -z $colors && -z $no_color ]]; then
 1441         for ((i=0,ic=50;i<=100;i++,ic=ic+2)); do
 1442             colors[i]="${ic} ${ic} ${ic}"
 1443         done
 1444     fi
 1445 
 1446 
 1447     #* Create the graph
 1448     local value_width x=0 y a cur_value virt_height=$((height*10)) offset=0 org_value
 1449     if [[ -n $add ]]; then
 1450         value_width=1
 1451     elif ((${#input_array[@]}<=width)); then
 1452         value_width=${#input_array[@]};
 1453     else
 1454         value_width=${width}
 1455         offset=$((${#input_array[@]}-width))
 1456     fi
 1457 
 1458     #* Print spaces to right-justify graph if number of values is less than graph width
 1459         if [[ -z $add && -z $no_color ]] && ((value_width<width)); then print -v graph_var -rp $((width-value_width)) -t "\e[1C"
 1460         elif [[ -z $add && -n $no_color ]] && ((value_width<width)); then print -v graph_var -rp $((width-value_width)) -t "\e[1C"; fi
 1461         #* Create graph
 1462         while ((x<value_width)); do
 1463             #* Round current input_array value divided by 10 to closest whole number
 1464             org_value=${input_array[offset+x]}
 1465             if ((org_value<=0)); then org_value=0; fi
 1466             if ((org_value>=100)); then cur_value=10; org_value=100
 1467             elif [[ ${#org_value} -gt 1 && ${org_value:(-1)} -ge 5 ]]; then cur_value=$((${org_value::1}+1))
 1468             elif [[ ${#org_value} -gt 1 && ${org_value:(-1)} -lt 5 ]]; then cur_value=$((${org_value::1}))
 1469             elif [[ ${org_value:(-1)} -ge 5 ]]; then cur_value=1
 1470             else cur_value=0
 1471             fi
 1472             if [[ -z $no_color ]]; then
 1473                 color="-fg ${colors[$org_value]} "
 1474             else
 1475                 color=""
 1476             fi
 1477 
 1478             if [[ $cur_value == 0 ]]; then
 1479                 print -v graph_var -t "\e[1C"
 1480             else
 1481                 print -v graph_var ${color}-t "${graph_symbol[${invert:+-}$cur_value]}"
 1482             fi
 1483             ((++x))
 1484         done
 1485 
 1486     #* Echo out graph if no argument for a output array was given
 1487     if [[ -z $ext_var && -z $add ]]; then echo -en "${graph_var}"
 1488     else output_var="${graph_var}"; fi
 1489 }
 1490 
 1491 create_graph_hires() {  #? Create a graph from an array of percentage values, usage;    create_graph <options> <value-array>
 1492                     #? Create a graph from an array of non percentage values:       create_graph <options> <-max "max value"> <value-array>
 1493                     #? Add a value to existing graph;                               create_graph [-i, -invert] [-max "max value"] -add-value "graph_array" <value>
 1494                     #? Add last value from an array to existing graph;              create_graph [-i, -invert] [-max "max value"] -add-last "graph_array" "value-array"
 1495                     #? Options: < -d, -dimensions <line> <col> <height> <width> > [-i, -invert] [-n, -no-guide] [-c, -color "array-name"] [-o, -output-array "variable-name"]
 1496     if [[ -z $1 ]]; then return; fi
 1497     local val col s_col line s_line height s_height width s_width colors color var ext_var out side_num side_nums=1 add add_array invert no_guide max graph_name offset=0 last_val
 1498     local -a input_array
 1499     local -i i
 1500 
 1501     #* Argument parsing
 1502     until (($#==0)); do
 1503         case $1 in
 1504             -d|-dimensions) if is_int "${@:2:4}"; then line=$2; col=$3; height=$4; width=$5; shift 4; fi;;                      #? Graph dimensions
 1505             -c|-color) local -n colors=$2; shift;;                                                                              #? Name of an array containing colors from index 0-100
 1506             -o|-output-array) local -n output_array=$2; graph_name=$2; ext_var=1; shift;;                                       #? Output meter to an array
 1507             -add-value) if is_int "$3"; then local -n output_array=$2; graph_name=$2; add=$3; break; else return; fi;;          #? Add a value to existing graph
 1508             -add-last) local -n output_array=$2; graph_name=$2; local -n add_array=$3; add=${add_array[-1]}; break;;            #? Add last value from array to existing graph
 1509             -i|-invert) invert=1;;                                                                                              #? Invert graph, drawing from top to bottom
 1510             -n|-no-guide) no_guide=1;;                                                                                          #? Don't print side and bottom guide lines
 1511             -max) if is_int "$2"; then max=$2; shift; fi;;                                                                      #? Needed max value for non percentage arrays
 1512             *) local -n tmp_in_array="$1"; input_array=("${tmp_in_array[@]}");;
 1513         esac
 1514         shift
 1515     done
 1516 
 1517     local -n last_val="graph[${graph_name}_last_val]"
 1518     local -n last_type="graph[${graph_name}_last_type]"
 1519 
 1520 
 1521     if [[ -z $add ]]; then
 1522         last_type="even"
 1523         last_val=0
 1524         local -n graph_array="${graph_name}_odd"
 1525         local -n graph_even="${graph_name}_even"
 1526         graph_even=("")
 1527         graph_array=("")
 1528     elif [[ ${last_type} == "even" ]]; then
 1529         local -n graph_array="${graph_name}_odd"
 1530         last_type="odd"
 1531     elif [[ ${last_type} == "odd" ]]; then
 1532         local -n graph_array="${graph_name}_even"
 1533         last_type="even"
 1534     fi
 1535 
 1536     if [[ -z $no_guide ]]; then ((--height))
 1537     elif [[ -n $invert ]]; then ((line--))
 1538     fi
 1539 
 1540     if ((width<3)); then width=3; fi
 1541     if ((height<1)); then height=1; fi
 1542 
 1543 
 1544     #* If argument "add" was passed check for existing graph and make room for new value(s)
 1545     local add_start add_end
 1546     if [[ -n $add ]]; then
 1547         local cut_left search
 1548         if [[ -n ${input_array[*]} || -z ${graph_array[0]} ]]; then return; fi
 1549 
 1550         height=$((${#graph_array[@]}-1))
 1551         input_array=("${add}")
 1552 
 1553         #* Remove last value in current graph
 1554 
 1555         for ((i=0;i<height;i++)); do
 1556             cut_left="${graph_array[i]%m*}"
 1557             search=$((${#cut_left}+1))
 1558             graph_array[i]="${graph_array[i]::$search}${graph_array[i]:$((search+1))}"
 1559         done
 1560 
 1561     fi
 1562 
 1563     #* Initialize graph if no "add" argument was given
 1564     if [[ -z $add ]]; then
 1565         #* Scale down graph one line if height is even
 1566         local inv_offset h_inv normal_vals=1
 1567         local -a side_num=(100 0) g_char=(" ⡇" " ⠓" "⠒") g_index
 1568 
 1569         if [[ -n $invert ]]; then
 1570             for((i=height;i>=0;i--)); do
 1571                 g_index+=($i)
 1572             done
 1573 
 1574         else
 1575             for((i=0;i<=height;i++)); do
 1576                 g_index+=($i)
 1577             done
 1578         fi
 1579 
 1580         if [[ -n $no_guide ]]; then unset normal_vals
 1581         elif [[ -n $invert ]]; then g_char=(" ⡇" " ⡤" "⠤")
 1582         fi
 1583 
 1584         #* Set up graph array print side numbers and lines
 1585         print -v graph_array[0] -rs -m $((line+g_index[0])) ${col} ${normal_vals:+-jr 3 -fg "#ee" -b -t "${side_num[0]}" -rs -fg ${theme[main_fg]} -t "${g_char[0]}"} -fg ${colors[100]}
 1586         for((i=1;i<height;i++)); do
 1587             print -v graph_array[i] -m $((line+g_index[i])) ${col} ${normal_vals:+-r 3 -fg ${theme[main_fg]} -t "${g_char[0]}"} -fg ${colors[$((100-i*100/height))]}
 1588         done
 1589 
 1590         if [[ -z $no_guide ]]; then width=$((width-5)); fi
 1591 
 1592         graph_array[$height]=""
 1593         if [[ -z $no_guide ]]; then
 1594             print -v graph_array[$height] -m $((line+g_index[(-1)])) ${col} -jr 3 -fg "#ee" -b -t "${side_num[1]}" -rs -fg ${theme[main_fg]} -t "${g_char[1]}" -rp ${width} -t "${g_char[2]}"
 1595         fi
 1596 
 1597         graph_even=("${graph_array[@]}")
 1598 
 1599         #* If no color array was given, create a simple greyscale array
 1600         if [[ -z $colors ]]; then
 1601             for ((i=0,ic=50;i<=100;i++,ic=ic+2)); do
 1602                 colors[i]="${ic} ${ic} ${ic}"
 1603             done
 1604         fi
 1605     fi
 1606 
 1607     #* Create the graph
 1608     local value_width next_line prev_value cur_value virt_height=$((height*4)) converted
 1609     local -i x y c_val p_val l_val
 1610     if [[ -n $add ]]; then
 1611         value_width=1
 1612     elif ((${#input_array[@]}<=width*2)); then
 1613         value_width=$((${#input_array[@]}*2))
 1614     else
 1615         value_width=$((width*2))
 1616         input_array=("${input_array[@]:(-${value_width})}")
 1617     fi
 1618 
 1619     if [[ -z $add ]] && ! ((${#input_array[@]}%2)); then last_val=${input_array[0]}; input_array=("${input_array[@]:1}"); converted=1; fi
 1620 
 1621     #* Print spaces to right-justify graph if number of values is less than graph width
 1622     if [[ -z $add ]] && ((${#input_array[@]}/2<width)); then
 1623         for((i=0;i<height;i++)); do
 1624             print -v graph_array[i] -rp $((width-1-${#input_array[@]}/2)) -t " "
 1625         done
 1626         graph_even=("${graph_array[@]}")
 1627     fi
 1628 
 1629     if [[ -n $invert ]]; then
 1630         y=$((height-1))
 1631         done_val="-1"
 1632     else
 1633         y=0
 1634         done_val=$height
 1635     fi
 1636 
 1637     #* Convert input array to percentage values of max if a max value was given
 1638     if [[ -n $max ]]; then
 1639         for((i=0;i<${#input_array[@]};i++)); do
 1640             if ((input_array[i]>=max)); then
 1641                 input_array[i]=100
 1642             else
 1643                 input_array[i]=$((input_array[i]*100/max))
 1644             fi
 1645         done
 1646         if [[ -n $converted ]]; then
 1647             last_val=$((${last_val}*100/max))
 1648             if ((${last_val}>100)); then last_val=100; fi
 1649         fi
 1650     fi
 1651 
 1652     if [[ -n $invert ]]; then local -n symbols=graph_symbol_down
 1653     else local -n symbols=graph_symbol_up
 1654     fi
 1655 
 1656     until ((y==done_val)); do
 1657 
 1658         next_line=$(( virt_height-((y+1)*4) ))
 1659         unset p_val
 1660 
 1661         #* Create graph by walking through all values for each line
 1662         for ((x=0;x<${#input_array[@]};x++)); do
 1663             c_val=${input_array[x]}
 1664             p_val=${p_val:-${last_val}}
 1665             cur_value="$((c_val*virt_height/100-next_line))"
 1666             prev_value=$((p_val*virt_height/100-next_line))
 1667 
 1668             if ((cur_value<0)); then cur_value=0
 1669             elif ((cur_value>4)); then cur_value=4; fi
 1670             if ((prev_value<0)); then prev_value=0
 1671             elif ((prev_value>4)); then prev_value=4; fi
 1672 
 1673             if [[ -z $add ]] && ((x==0)); then
 1674                 print -v graph_even[y] -t "${symbols[${prev_value}_${cur_value}]}"
 1675                 print -v graph_array[y] -t "${symbols[0_${prev_value}]}"
 1676             elif [[ -z $add ]] && ! ((x%2)); then
 1677                 print -v graph_even[y] -t "${symbols[${prev_value}_${cur_value}]}"
 1678             else
 1679                 print -v graph_array[y] -t "${symbols[${prev_value}_${cur_value}]}"
 1680             fi
 1681 
 1682             if [[ -z $add ]]; then p_val=${input_array[x]}; else unset p_val; fi
 1683 
 1684         done
 1685 
 1686         if [[ -n $invert ]]; then
 1687             ((y--)) || true
 1688         else
 1689             ((++y))
 1690         fi
 1691 
 1692     done
 1693 
 1694     if [[ -z $add && ${last_type} == "even" ]]; then
 1695         declare -n graph_array="${graph_name}_even"
 1696     fi
 1697 
 1698     last_val=$c_val
 1699 
 1700     output_array=("${graph_array[@]}")
 1701 }
 1702 
 1703 
 1704 create_mini_graph_hires() {     #? Create a one line high graph from an array of percentage values, usage;  create_mini_graph <options> <value-array>
 1705                         #? Add a value to existing graph;                       create_mini_graph [-i, -invert] [-nc, -no-color] [-c, -color "array-name"] -add-value "graph_variable" <value>
 1706                         #? Add last value from an array to existing graph;      create_mini_graph [-i, -invert] [-nc, -no-color] [-c, -color "array-name"] -add-last "graph_variable" "value-array"
 1707                         #? Options: [-w, -width <width>] [-i, -invert] [-nc, -no-color] [-c, -color "array-name"] [-o, -output-variable "variable-name"]
 1708     if [[ -z $1 ]]; then return; fi
 1709     local val col s_col line s_line height s_height width s_width colors color var ext_var out side_num side_nums=1 add invert no_guide graph_var no_color color_value graph_name
 1710     local -a input_array
 1711     local -i i
 1712 
 1713     #* Argument parsing
 1714     until (($#==0)); do
 1715         case $1 in
 1716             -w|-width) if is_int "$2"; then width=$2; shift; fi;;                                                           #? Graph width
 1717             -c|-color) local -n colors=$2; shift;;                                                                          #? Name of an array containing colors from index 0-100
 1718             -nc|-no-color) no_color=1;;                                                                                     #? Set no color
 1719             -o|-output-variable) local -n output_var=$2; graph_name=$2; ext_var=1; shift;;                                  #? Output graph to a variable
 1720             -add-value) if is_int "$3"; then local -n output_var=$2; graph_name=$2; add=$3; break; else return; fi;;        #? Add a value to existing graph
 1721             -add-last) local -n output_var=$2; local -n add_array=$3; graph_name=$2; add="${add_array[-1]:-0}"; break;;         #? Add last value from array to existing graph
 1722             -i|-invert) invert=1;;                                                                                          #? Invert graph, drawing from top to bottom
 1723             *) local -n tmp_in_arr=$1; input_array=("${tmp_in_arr[@]}");;
 1724         esac
 1725         shift
 1726     done
 1727 
 1728     local -n last_val="${graph_name}_last_val"
 1729     local -n last_type="${graph_name}_last_type"
 1730 
 1731     if [[ -z $add ]]; then
 1732         last_type="even"
 1733         last_val=0
 1734         local -n graph_var="${graph_name}_odd"
 1735         local -n graph_other="${graph_name}_even"
 1736         graph_var=""; graph_other=""
 1737     elif [[ ${last_type} == "even" ]]; then
 1738         local -n graph_var="${graph_name}_odd"
 1739         last_type="odd"
 1740     elif [[ ${last_type} == "odd" ]]; then
 1741         local -n graph_var="${graph_name}_even"
 1742         last_type="even"
 1743     fi
 1744 
 1745     if ((width<1)); then width=1; fi
 1746 
 1747     #* If argument "add" was passed check for existing graph and make room for new value(s)
 1748     local add_start add_end
 1749     if [[ -n $add ]]; then
 1750         local cut_left search
 1751         input_array[0]=${add}
 1752 
 1753         #* Remove last value in current graph
 1754         if [[ -n ${graph_var} && -z $no_color ]]; then
 1755             if [[ ${graph_var::5} == '\e[1C' ]]; then
 1756                 graph_var="${graph_var#'\e[1C'}"
 1757             else
 1758                 cut_left="${graph_var%m*}"
 1759                 search=$((${#cut_left}+1))
 1760                 graph_var="${graph_var::$search}${graph_var:$((search+1))}"
 1761             fi
 1762         elif [[ -n ${graph_var} && -n $no_color ]]; then
 1763             if [[ ${graph_var::5} == "\e[1C" ]]; then
 1764                 #cut_left="${graph_var%%C*}"
 1765                 #search=$((${#cut_left}+1))
 1766                 #graph_var="${graph_var:$((search))}"
 1767                 graph_var="${graph_var#'\e[1C'}"
 1768             else
 1769                 graph_var="${graph_var:1}"
 1770             fi
 1771         fi
 1772     fi
 1773 
 1774 
 1775     #* If no color array was given, create a simple greyscale array
 1776     if [[ -z $colors && -z $no_color ]]; then
 1777         for ((i=0,ic=50;i<=100;i++,ic=ic+2)); do
 1778             colors[i]="${ic} ${ic} ${ic}"
 1779         done
 1780     fi
 1781 
 1782 
 1783     #* Create the graph
 1784     local value_width x=0 y a cur_value prev_value p_val c_val acolor jump odd offset=0
 1785     if [[ -n $add ]]; then
 1786         value_width=1
 1787     elif ((${#input_array[@]}<=width*2)); then
 1788         value_width=$((${#input_array[@]}*2))
 1789     else
 1790         value_width=$((width*2))
 1791         input_array=("${input_array[@]:(-${value_width})}")
 1792     fi
 1793 
 1794     if [[ -z $add ]] && ! ((${#input_array[@]}%2)); then last_val=${input_array[0]}; input_array=("${input_array[@]:1}"); fi
 1795 
 1796     #* Print spaces to right-justify graph if number of values is less than graph width
 1797     if [[ -z $add ]] && ((${#input_array[@]}/2<width)); then print -v graph_var -rp $((width-1-${#input_array[@]}/2)) -t "\e[1C"; graph_other="${graph_var}"; fi
 1798 
 1799     if [[ -n $invert ]]; then local -n symbols=graph_symbol_down
 1800     else local -n symbols=graph_symbol_up
 1801     fi
 1802 
 1803     unset p_val
 1804 
 1805     #* Create graph
 1806     for((i=0;i<${#input_array[@]};i++)); do
 1807 
 1808         c_val=${input_array[i]}
 1809         p_val=${p_val:-${last_val}}
 1810 
 1811         if ((c_val>=85)); then cur_value=4
 1812         elif ((c_val>=60)); then cur_value=3
 1813         elif ((c_val>=30)); then cur_value=2
 1814         elif ((c_val>=10)); then cur_value=1
 1815         elif ((c_val<10)); then cur_value=0; fi
 1816 
 1817         if ((p_val>=85)); then prev_value=4
 1818         elif ((p_val>=60)); then prev_value=3
 1819         elif ((p_val>=30)); then prev_value=2
 1820         elif ((p_val>=10)); then prev_value=1
 1821         elif ((p_val<10)); then prev_value=0; fi
 1822 
 1823         if [[ -z $no_color ]]; then
 1824             if ((c_val>p_val)); then acolor=$((c_val-p_val))
 1825             else acolor=$((p_val-c_val)); fi
 1826             if ((acolor>100)); then acolor=100; elif ((acolor<0)); then acolor=0; fi
 1827             color="-fg ${colors[${acolor:-0}]} "
 1828         else
 1829             unset color
 1830         fi
 1831 
 1832         if ((cur_value==0 & prev_value==0)); then jump="\e[1C"; else unset jump; fi
 1833 
 1834         if [[ -z $add ]] && ((i==0)); then
 1835             print -v graph_other ${color}-t "${jump:-${symbols[${prev_value}_${cur_value}]}}"
 1836             print -v graph_var ${color}-t "${jump:-${symbols[0_${prev_value}]}}"
 1837         elif [[ -z $add ]] && ((i%2)); then
 1838             print -v graph_other ${color}-t "${jump:-${symbols[${prev_value}_${cur_value}]}}"
 1839         else
 1840             print -v graph_var ${color}-t "${jump:-${symbols[${prev_value}_${cur_value}]}}"
 1841         fi
 1842 
 1843         if [[ -z $add ]]; then p_val=$c_val; else unset p_val; fi
 1844     done
 1845 
 1846     #if [[ -z $add ]]; then
 1847     #   declare -n graph_var="${graph_name}_even"
 1848     #   #echo "yup" >&2
 1849     #fi
 1850 
 1851     last_val=$c_val
 1852 
 1853     output_var="${graph_var}"
 1854 }
 1855 
 1856 print() {   #? Print text, set true-color foreground/background color, add effects, center text, move cursor, save cursor position and restore cursor postion
 1857             #? Effects: [-fg, -foreground <RGB Hex>|<R Dec> <G Dec> <B Dec>] [-bg, -background <RGB Hex>|<R Dec> <G Dec> <B Dec>] [-rs, -reset] [-/+b, -/+bold] [-/+da, -/+dark]
 1858             #? [-/+ul, -/+underline] [-/+i, -/+italic] [-/+bl, -/+blink] [-f, -font "sans-serif|script|fraktur|monospace|double-struck"]
 1859             #? Manipulation: [-m, -move <line> <column>] [-l, -left <x>] [-r, -right <x>] [-u, -up <x>] [-d, -down <x>] [-c, -center] [-sc, -save] [-rc, -restore]
 1860             #? [-jl, -justify-left <width>] [-jr, -justify-right <width>] [-jc, -justify-center <width>] [-rp, -repeat <x>]
 1861             #? Text: [-v, -variable "variable-name"] [-stdin] [-t, -text "string"] ["string"]
 1862 
 1863     #* Return if no arguments is given
 1864     if [[ -z $1 ]]; then return; fi
 1865 
 1866     #* Just echo and return if only one argument and not a valid option
 1867     if [[ $# -eq 1 && ${1::1} != "-"  ]]; then echo -en "$1"; return; fi
 1868 
 1869     local effect color add_command text text2 esc center clear fgc bgc fg_bg_div tmp tmp_len bold italic custom_font val var out ext_var hex="16#"
 1870     local justify_left justify_right justify_center repeat r_tmp trans
 1871 
 1872 
 1873     #* Loop function until we are out of arguments
 1874     until (($#==0)); do
 1875 
 1876         #* Argument parsing
 1877         until (($#==0)); do
 1878             case $1 in
 1879                 -t|-text) text="$2"; shift 2; break;;                                                               #? String to print
 1880                 -stdin) text="$(</dev/stdin)"; shift; break;;                                                                               #? Print from stdin
 1881                 -fg|-foreground)    #? Set text foreground color, accepts either 6 digit hexadecimal "#RRGGBB", 2 digit hex (greyscale) or decimal RGB "<0-255> <0-255> <0-255>"
 1882                     if [[ ${2::1} == "#" ]]; then
 1883                         val=${2//#/}
 1884                         if [[ ${#val} == 6 ]]; then fgc="\e[38;2;$((${hex}${val:0:2}));$((${hex}${val:2:2}));$((${hex}${val:4:2}))m"; shift
 1885                         elif [[ ${#val} == 2 ]]; then fgc="\e[38;2;$((${hex}${val:0:2}));$((${hex}${val:0:2}));$((${hex}${val:0:2}))m"; shift
 1886                         fi
 1887                     elif is_int "${@:2:3}"; then fgc="\e[38;2;$2;$3;$4m"; shift 3
 1888                     fi
 1889                     ;;
 1890                 -bg|-background)    #? Set text background color, accepts either 6 digit hexadecimal "#RRGGBB", 2 digit hex (greyscale) or decimal RGB "<0-255> <0-255> <0-255>"
 1891                     if [[ ${2::1} == "#" ]]; then
 1892                         val=${2//#/}
 1893                         if [[ ${#val} == 6 ]]; then bgc="\e[48;2;$((${hex}${val:0:2}));$((${hex}${val:2:2}));$((${hex}${val:4:2}))m"; shift
 1894                         elif [[ ${#val} == 2 ]]; then bgc="\e[48;2;$((${hex}${val:0:2}));$((${hex}${val:0:2}));$((${hex}${val:0:2}))m"; shift
 1895                         fi
 1896                     elif is_int "${@:2:3}"; then bgc="\e[48;2;$2;$3;$4m"; shift 3
 1897                     fi
 1898                     ;;
 1899                 -c|-center) center=1;;                                                                                                      #? Center text horizontally on screen
 1900                 -rs|-reset) effect="0${effect}${theme[main_bg]}";;                                                                          #? Reset text colors and effects
 1901                 -b|-bold) effect="${effect}${effect:+;}1"; bold=1;;                                                                         #? Enable bold text
 1902                 +b|+bold) effect="${effect}${effect:+;}21"; bold=0;;                                                                        #? Disable bold text
 1903                 -da|-dark) effect="${effect}${effect:+;}2";;                                                                                #? Enable dark text
 1904                 +da|+dark) effect="${effect}${effect:+;}22";;                                                                               #? Disable dark text
 1905                 -i|-italic) effect="${effect}${effect:+;}3"; italic=1;;                                                                     #? Enable italic text
 1906                 +i|+italic) effect="${effect}${effect:+;}23"; italic=0;;                                                                    #? Disable italic text
 1907                 -ul|-underline) effect="${effect}${effect:+;}4";;                                                                           #? Enable underlined text
 1908                 +ul|+underline) effect="${effect}${effect:+;}24";;                                                                          #? Disable underlined text
 1909                 -bl|-blink) effect="${effect}${effect:+;}5";;                                                                               #? Enable blinking text
 1910                 +bl|+blink) effect="${effect}${effect:+;}25";;                                                                              #? Disable blinking text
 1911                 -f|-font) if [[ $2 =~ ^(sans-serif|script|fraktur|monospace|double-struck)$ ]]; then custom_font="$2"; shift; fi;;          #? Set custom font
 1912                 -m|-move) add_command="${add_command}\e[${2};${3}f"; shift 2;;                                                              #? Move to postion "LINE" "COLUMN"
 1913                 -l|-left) add_command="${add_command}\e[${2}D"; shift;;                                                                     #? Move left x columns
 1914                 -r|-right) add_command="${add_command}\e[${2}C"; shift;;                                                                    #? Move right x columns
 1915                 -u|-up) add_command="${add_command}\e[${2}A"; shift;;                                                                       #? Move up x lines
 1916                 -d|-down) add_command="${add_command}\e[${2}B"; shift;;                                                                     #? Move down x lines
 1917                 -jl|-justify-left) justify_left="${2}"; shift;;                                                                             #? Justify string left within given width
 1918                 -jr|-justify-right) justify_right="${2}"; shift;;                                                                           #? Justify string right within given width
 1919                 -jc|-justify-center) justify_center="${2}"; shift;;                                                                         #? Justify string center within given width
 1920                 -rp|-repeat) repeat=${2}; shift;;                                                                                           #? Repeat next string x number of times
 1921                 -sc|-save) add_command="\e[s${add_command}";;                                                                               #? Save cursor position
 1922                 -rc|-restore) add_command="${add_command}\e[u";;                                                                            #? Restore cursor position
 1923                 -trans) trans=1;;                                                                                                           #? Make whitespace transparent
 1924                 -v|-variable) local -n var=$2; ext_var=1; shift;;                                                                           #? Send output to a variable, appending if not unset
 1925                 *) text="$1"; shift; break;;                                                                                                #? Assumes text string if no argument is found
 1926             esac
 1927             shift
 1928         done
 1929 
 1930         #* Repeat string if repeat is enabled
 1931         if [[ -n $repeat ]]; then
 1932             printf -v r_tmp "%${repeat}s" ""
 1933             text="${r_tmp// /$text}"
 1934         fi
 1935 
 1936         #* Set correct placement for screen centered text
 1937         if ((center==1 & ${#text}>0 & ${#text}<tty_width-4)); then
 1938             add_command="${add_command}\e[${tty_width}D\e[$(( (tty_width/2)-(${#text}/2) ))C"
 1939         fi
 1940 
 1941         #* Convert text string to custom font if set and remove non working effects
 1942         if [[ -n $custom_font ]]; then
 1943             unset effect
 1944             text=$(set_font "${custom_font}${bold:+" bold"}${italic:+" italic"}" "${text}")
 1945         fi
 1946 
 1947         #* Set text justification if set
 1948         if [[ -n $justify_left ]] && ((${#text}<justify_left)); then
 1949             printf -v text "%s%$((justify_left-${#text}))s" "${text}" ""
 1950         elif [[ -n $justify_right ]] && ((${#text}<justify_right)); then
 1951             printf -v text "%$((justify_right-${#text}))s%s" "" "${text}"
 1952         elif [[ -n $justify_center ]] && ((${#text}<justify_center)); then
 1953             printf -v text "%$(( (justify_center/2)-(${#text}/2) ))s%s" "" "${text}"
 1954             printf -v text "%s%-$((justify_center-${#text}))s" "${text}" ""
 1955         fi
 1956 
 1957         if [[ -n $trans ]]; then
 1958             text="${text// /'\e[1C'}"
 1959         fi
 1960 
 1961         #* Create text string
 1962         if [[ -n $effect ]]; then effect="\e[${effect}m"; fi
 1963         out="${out}${add_command}${effect}${bgc}${fgc}${text}"
 1964         unset add_command effect fgc bgc center justify_left justify_right justify_center custom_font text repeat trans justify
 1965     done
 1966 
 1967     #* Print the string to stdout if variable out hasn't been set
 1968     if [[ -z $ext_var ]]; then echo -en "$out"
 1969     else var="${var}${out}"; fi
 1970 
 1971 }
 1972 
 1973 collect_cpu() { #? Collects cpu stats from /proc/stat and compares with previously collected sample to get cpu usage
 1974                 #? Returns cpu usage in array "cpu_usage", index 0 is usage for all threads, following indices corresponds to thread usage in multicore/hyperthreading cpus
 1975     local freq thread i threads=${cpu[threads]}
 1976     local -a stat_array stat_input
 1977 
 1978     #* Get values from /proc/stat or psutil, compare to get cpu usage
 1979     if [[ $use_psutil == true ]]; then
 1980         local -a usage_arr
 1981         local x=1
 1982         py_command -a usage_arr "get_cpu_usage()"
 1983         cpu_usage[0]=${usage_arr[0]}
 1984         for thread in ${usage_arr[@]:1}; do
 1985             cpu_usage[$((x++))]=$thread
 1986         done
 1987         py_command -v cpu[freq] "get_cpu_freq()"
 1988         py_command -v cpu[uptime] "get_uptime()"
 1989         py_command -v cpu[load_avg] "get_load_avg()"
 1990     else
 1991         readarray -t stat_input </proc/stat
 1992         thread=0
 1993         while ((thread<threads+1)); do
 1994             stat_array=(${stat_input[thread]})
 1995             cpu[new_${thread}]=$((stat_array[1]+stat_array[2]+stat_array[3]+stat_array[4]))
 1996             cpu[idle_new_${thread}]=${stat_array[4]}
 1997             if [[ -n ${cpu[old_${thread}]} && -n ${cpu[idle_new_${thread}]} && ${cpu[old_${thread}]} -ne ${cpu[new_${thread}]} ]]; then
 1998                 cpu_usage[${thread}]="$(( ( 100*(${cpu[old_${thread}]}-${cpu[new_${thread}]}-${cpu[idle_old_${thread}]}+${cpu[idle_new_${thread}]}) ) / (${cpu[old_${thread}]}-${cpu[new_${thread}]}) ))"
 1999             fi
 2000             cpu[old_${thread}]=${cpu[new_${thread}]}
 2001             cpu[idle_old_${thread}]=${cpu[idle_new_${thread}]}
 2002             ((++thread))
 2003         done
 2004     fi
 2005 
 2006     #* Copy cpu usage for cpu package and cores to cpu history arrays and trim earlier entries
 2007     if ((${#cpu_history[@]}>tty_width*4)); then
 2008         cpu_history=( "${cpu_history[@]:$((tty_width*2))}" "${cpu_usage[0]}")
 2009     else
 2010         cpu_history+=("${cpu_usage[0]}")
 2011     fi
 2012 
 2013     for((i=1;i<=threads;i++)); do
 2014         local -n cpu_core_history="cpu_core_history_$i"
 2015         if ((${#cpu_core_history[@]}>40)); then
 2016             cpu_core_history=( "${cpu_core_history[@]:20}" "${cpu_usage[$i]}")
 2017         else
 2018             cpu_core_history+=("${cpu_usage[$i]}")
 2019         fi
 2020     done
 2021 
 2022     #* Get current cpu frequency from "/proc/cpuinfo" and convert to appropriate unit
 2023     if [[ $use_psutil == false && -z ${cpu[no_cpu_info]} ]] && ! get_value -v 'cpu[freq]' -sf "/proc/cpuinfo" -k "cpu MHz" -i; then
 2024         cpu[no_cpu_info]=1
 2025     fi
 2026 
 2027     #* If getting cpu frequency from "proc/cpuinfo" was unsuccessfull try "/sys/devices/../../scaling_cur_freq"
 2028     if [[ $use_psutil == false && -n ${cpu[no_cpu_info]} && -e "/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq" ]]; then
 2029         get_value -v 'cpu[freq]' -sf "/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq" -i
 2030         printf -v 'cpu[freq]' "%.0f0" "${cpu[freq]}e-4"
 2031     fi
 2032 
 2033     if ((${#cpu[freq]}>3)); then cpu[freq_string]="${cpu[freq]::-3}.${cpu[freq]:(-3):1} GHz"
 2034     elif ((${#cpu[freq]}>1)); then cpu[freq_string]="${cpu[freq]} MHz"
 2035     else cpu[freq_string]=""; fi
 2036 
 2037     #* Get load average and uptime from uptime command
 2038     if [[ $use_psutil == false ]]; then
 2039         local uptime_var
 2040         read -r uptime_var < <(uptime 2>/dev/null || true)
 2041         cpu[load_avg]="${uptime_var#*average: }"
 2042         cpu[load_avg]="${cpu[load_avg]//,/}"
 2043         cpu[uptime]="${uptime_var#*up }"
 2044         cpu[uptime]="${cpu[uptime]%%,  *}"
 2045     fi
 2046 
 2047     #* Collect cpu temps if enabled
 2048     if [[ $check_temp == true ]]; then collect_cpu_temps; fi
 2049 }
 2050 
 2051 collect_cpu_temps() { #? Collect cpu temperatures
 2052     local unit c div threads=${cpu[threads]} sens_var i it ccd_value breaking core_value misc_var
 2053     local -a ccd_array core_array
 2054 
 2055     #* Fetch output from "sensors" command or psutil to a variable
 2056     if [[ $sensor_comm == "psutil" ]]; then
 2057         if ! py_command -vn sens_var "get_sensors()"; then
 2058             if command -v sensors >/dev/null 2>&1; then sensor_comm="sensors"
 2059             else sensor_comm=""; check_temp="false"; resized=1; return; fi
 2060         fi
 2061     fi
 2062     if [[ $sensor_comm == "sensors" ]]; then
 2063         if [[ $use_psutil == true ]]; then
 2064             py_command -vn sens_var "get_cmd_out('sensors 2>/dev/null')"
 2065         else
 2066             read -rd '' sens_var < <(sensors 2>/dev/null || true) || true
 2067         fi
 2068     elif [[ $sensor_comm != "sensors" && $sensor_comm != "psutil" ]]; then
 2069         if [[ $use_psutil == true ]]; then
 2070             py_command -v misc_var "get_cmd_out('${sensor_comm} measure_temp 2>/dev/null')"
 2071         else
 2072             read -r misc_var < <(${sensor_comm} measure_temp 2>/dev/null ||true)
 2073         fi
 2074     fi
 2075 
 2076     #* Get CPU package temp for intel cpus
 2077     if get_value -v 'cpu[temp_0]' -sv "sens_var" -k "Package*:" -mk 1 || get_value -v 'cpu[temp_0]' -sv "sens_var" -k "Core 0:" -mk 1; then
 2078         #* If successful get temperature unit, convert temp to integer and get high and crit
 2079         cpu[temp_unit]="${cpu[temp_0]:(-2)}"; cpu[temp_0]="${cpu[temp_0]%.*}"; if [[ ${cpu[temp_0]::1} == "+" ]]; then cpu[temp_0]="${cpu[temp_0]#+}"; fi
 2080         if [[ -z ${cpu[temp_high]} ]]; then
 2081             if ! get_value -v 'cpu[temp_high]' -sv "sens_var" -k "Package*high =" -m 2 -r "[^0-9.]" -b -i; then cpu[temp_high]="85"; cpu[temp_crit]=$((cpu[temp_high]+10))
 2082             else get_value -v 'cpu[temp_crit]' -sv "sens_var" -k "Package*crit =" -m 2 -r "[^0-9.]" -b -i; fi
 2083         fi
 2084 
 2085         #* Get core temps
 2086         i=0
 2087         while get_value -v "core_value" -sv "sens_var" -k "Core ${i}:" -mk 1 -r "[^0-9.]" -b -i && ((i<=threads)); do core_array+=("$core_value"); ((++i)) ; done
 2088 
 2089         if [[ -z ${core_array[0]} ]]; then core_array=("${cpu[temp_0]}"); fi
 2090 
 2091         if ((${#core_array[@]}<threads/2)); then
 2092             for((i=${#core_array[@]};i<threads/2;i++)); do
 2093                 core_array+=("${cpu[temp_0]}")
 2094             done
 2095         fi
 2096 
 2097         #* Copy core values to hyperthreading cores
 2098         i=1
 2099         for core_value in "${core_array[@]}"; do
 2100             cpu[temp_$((i))]="${core_value}"
 2101             cpu[temp_$((threads/2+i))]="${core_value}"
 2102             ((i++))
 2103         done
 2104 
 2105     #* Get CPU package temp for amd ryzen cpus
 2106     elif get_value -v 'cpu[temp_0]' -sv "sens_var" -k "Tdie:" -mk 1; then
 2107         #* If successful get temperature unit, convert temp to integer and get high
 2108         cpu[temp_unit]="${cpu[temp_0]:(-2)}"; cpu[temp_0]="${cpu[temp_0]%.*}"; if [[ ${cpu[temp_0]::1} == "+" ]]; then cpu[temp_0]="${cpu[temp_0]#+}"; fi
 2109         if [[ -z ${cpu[temp_high]} ]]; then
 2110             if ! get_value -v 'cpu[temp_high]' -sv "sens_var" -k "Tdie*high =" -m 2 -r "[^0-9.]" -b -i; then cpu[temp_high]="85"; fi
 2111             cpu[temp_crit]=$((cpu[temp_high]+10))
 2112         fi
 2113 
 2114         #* Get ccd module temps
 2115         i=1
 2116         while get_value -v "ccd_value" -sv "sens_var" -k "Tccd${i}:" -mk 1 -r "[^0-9.]" -b -i && ((i<=threads)); do ccd_array+=("$ccd_value"); ((i++)) ; done
 2117 
 2118         if [[ -z ${ccd_array[0]} ]]; then ccd_array=("${cpu[temp_0]}"); fi
 2119 
 2120         #* Copy ccd values to cores in each ccd
 2121         z=1
 2122         for ccd_value in "${ccd_array[@]}"; do
 2123             for((i=0;i<threads/${#ccd_array[@]};i++)); do
 2124                 cpu[temp_$((z+i))]="${ccd_value}"
 2125             done
 2126             z=$((z+i))
 2127         done
 2128 
 2129     #* Get CPU package temp for Rapberry Pi cpus and for OSX
 2130     elif [[ $sensor_comm != "sensors" && -n ${misc_var} ]]; then
 2131         cpu[temp_0]="${misc_var#temp=}"
 2132         cpu[temp_unit]=${cpu[temp_0]:(-1)}"; cpu[temp_0]=${cpu[temp_0]%%.*}; if [[ ${cpu[temp_0]::1} == "+" ]]; then cpu[temp_0]=${cpu[temp_0]#+}; fi
 2133         if [[ -z ${cpu[temp_high]} ]]; then
 2134             cpu[temp_high]="75"; cpu[temp_crit]=$((cpu[temp_high]+10))
 2135         fi
 2136 
 2137         #* Copy cpu temp to cores
 2138         for((i=1;i<=threads;i++)); do
 2139             cpu[temp_${i}]="${cpu[temp_0]}"
 2140         done
 2141 
 2142     #* If unsuccessful turn off temperature checking
 2143     else
 2144         check_temp="false"
 2145         resized=1
 2146     fi
 2147 
 2148     if [[ $check_temp == true ]]; then
 2149         local tmp_temp
 2150         for((i=0;i<=threads;i++)); do
 2151             tmp_temp="$(( (${cpu[temp_${i}]}-20)*100/(cpu[temp_high]-20) ))"
 2152             if ((tmp_temp>100)); then tmp_temp=100; elif ((tmp_temp<0)); then tmp_temp=0; fi
 2153             local -n cpu_temp_history="cpu_temp_history_$i"
 2154             if ((${#cpu_temp_history[@]}>20)); then
 2155                 cpu_temp_history=( "${cpu_temp_history[@]:10}" "${tmp_temp}")
 2156             else
 2157                 cpu_temp_history+=("${tmp_temp}")
 2158             fi
 2159         done
 2160     fi
 2161 }
 2162 
 2163 collect_mem() { #? Collect memory information from "/proc/meminfo"
 2164     ((++mem[counter]))
 2165 
 2166     #if [[ $use_psutil == false ]] && ((mem[counter]<4)); then return; fi
 2167     if ((mem[counter]<4)); then return; fi
 2168     mem[counter]=0
 2169 
 2170     local i tmp value array mem_info height=$((box[mem_height]-2)) skip filter_value
 2171     local -a mem_array swap_array available=("mem")
 2172     unset 'mem[total]'
 2173 
 2174     #* Get memory and swap information from "/proc/meminfo" or psutil and calculate percentages
 2175     if [[ $use_psutil == true ]]; then
 2176         local pymemout
 2177 
 2178         py_command -v pymemout "get_mem()" || return
 2179         read mem[total] mem[free] mem[available] mem[cached] swap[total] swap[free] <<<"$pymemout"
 2180 
 2181         if [[ -z ${mem[total]} ]]; then return; fi
 2182         if [[ -n ${swap[total]} ]] && ((swap[total]>0)); then
 2183             swap[free_percent]=$((swap[free]*100/swap[total]))
 2184             swap[used]=$((swap[total]-swap[free]))
 2185             swap[used_percent]=$((swap[used]*100/swap[total]))
 2186             available+=("swap")
 2187         else
 2188             unset swap_on
 2189         fi
 2190     else
 2191         read -rd '' mem_info </proc/meminfo ||true
 2192         get_value -v 'mem[total]' -sv "mem_info" -k "MemTotal:" -i
 2193         get_value -v 'mem[free]' -sv "mem_info" -k "MemFree:" -i
 2194         if ! get_value -v 'mem[available]' -sv "mem_info" -k "MemAvailable:" -i; then
 2195             get_value -v 'mem[available]' -sv "mem_info" -k "Inactive:" -i
 2196             mem[available]=$((mem[available]+mem[free]))
 2197         fi
 2198         get_value -v 'mem[cached]' -sv "mem_info" -k "Cached:" -i
 2199     fi
 2200 
 2201     mem[available_percent]=$((mem[available]*100/mem[total]))
 2202     mem[used]=$((mem[total]-mem[available]))
 2203     mem[used_percent]=$((mem[used]*100/mem[total]))
 2204     mem[free_percent]=$((mem[free]*100/mem[total]))
 2205     mem[cached_percent]=$((mem[cached]*100/mem[total]))
 2206 
 2207     if [[ $use_psutil == false ]] && get_value -v swap[total] -sv "mem_info" -k "SwapTotal:" -i && ((swap[total]>0)); then
 2208         get_value -v 'swap[free]' -sv "mem_info" -k "SwapFree:" -i
 2209         swap[free_percent]=$((swap[free]*100/swap[total]))
 2210 
 2211         swap[used]=$((swap[total]-swap[free]))
 2212         swap[used_percent]=$((swap[used]*100/swap[total]))
 2213 
 2214         available+=("swap")
 2215     elif [[ $use_psutil == false ]]; then
 2216         unset swap_on
 2217     fi
 2218 
 2219     #* Convert values to floating point and humanize
 2220     for array in ${available[@]}; do
 2221         for value in total used free available cached; do
 2222             if [[ $array == "swap" && $value == "available" ]]; then break 2; fi
 2223             local -n this_value="${array}[${value}]" this_string="${array}[${value}_string]"
 2224             floating_humanizer -v this_string -s 1 -B "${this_value}"
 2225         done
 2226     done
 2227 
 2228     #* Get disk information
 2229     local df_line line_array dev_path dev_name iostat_var disk_read disk_write disk_io_string df_count=0 filtering psutil_on
 2230     local -a device_array iostat_array df_array
 2231     unset 'disks_free[@]' 'disks_used[@]' 'disks_used_percent[@]' 'disks_total[@]' 'disks_name[@]' 'disks_free_percent[@]' 'disks_io[@]'
 2232     if [[ -n $psutil_disk_fail ]]; then psutil_on="false"; else psutil_on="$use_psutil"; fi
 2233     if [[ $psutil_on == true ]]; then
 2234         if [[ -n $disks_filter ]]; then filtering=", filtering='${disks_filter}'"; fi
 2235         if ! py_command -a df_array "get_disks(exclude='squashfs'${filtering})"; then psutil_disk_fail=1; psutil_on="false"; fi
 2236     fi
 2237     if [[ $psutil_on == false ]]; then
 2238         readarray -t df_array < <(${df} -x squashfs -x tmpfs -x devtmpfs -x overlay -x 9p 2>/dev/null || true)
 2239     fi
 2240     for df_line in "${df_array[@]:1}"; do
 2241         line_array=(${df_line})
 2242         if ! is_int "${line_array[1]}" || ((line_array[1]<=0)); then continue; fi
 2243 
 2244         if [[ $psutil_on == false && ${line_array[5]} == "/" ]]; then disks_name+=("root")
 2245         elif [[ $psutil_on == false ]]; then disks_name+=("${line_array[5]##*/}")
 2246         elif [[ $psutil_on == true ]]; then disks_name+=("${line_array[*]:7}"); fi
 2247 
 2248         #* Filter disks showed if $disks_filter is set
 2249         if [[ $psutil_on == false && -n $disks_filter ]]; then
 2250             unset found
 2251             for filter_value in ${disks_filter}; do
 2252                 if [[ $filter_value == "${disks_name[-1]}" ]]; then found=1; fi
 2253             done
 2254         fi
 2255 
 2256         if [[ $psutil_on == true || -z $disks_filter || -n $found ]]; then
 2257             disks_total+=("$(floating_humanizer -s 1 -B ${line_array[1]})")
 2258             disks_used+=("$(floating_humanizer -s 1 -B ${line_array[2]})")
 2259             disks_used_percent+=("${line_array[4]%'%'}")
 2260             disks_free+=("$(floating_humanizer -s 1 -B ${line_array[3]})")
 2261             disks_free_percent+=("$((100-${line_array[4]%'%'}))")
 2262 
 2263             #* Get read/write stats for disk from iostat or psutil if available
 2264             if [[ $psutil_on == true || -n $has_iostat ]]; then
 2265                 unset disk_io_string
 2266                 dev_name="${line_array[0]##*/}"
 2267                 if [[ $psutil_on == false && ${dev_name::2} == "md" ]]; then dev_name="${dev_name::3}"; fi
 2268                 if [[ $psutil_on == false ]]; then
 2269                     unset iostat_var 'iostat_array[@]'
 2270                     dev_path="${line_array[0]%${dev_name}}"
 2271                     read -r iostat_var < <(iostat -dkz "${dev_path}${dev_name}" | tail -n +4)
 2272                     iostat_array=(${iostat_var})
 2273                 fi
 2274                 if [[ $psutil_on == true || -n ${iostat_var} ]]; then
 2275 
 2276                     if [[ $psutil_on == true ]]; then
 2277                         disk_read=${line_array[5]}
 2278                         disk_write=${line_array[6]}
 2279                     else
 2280                         disk_read=$((iostat_array[-2]-${disks[${dev_name}_read]:-${iostat_array[-2]}}))
 2281                         disk_write=$((iostat_array[-1]-${disks[${dev_name}_write]:-${iostat_array[-1]}}))
 2282                     fi
 2283 
 2284                     if ((box[m_width2]>25)); then
 2285                         if ((disk_read>0)); then disk_io_string="▲$(floating_humanizer -s 1 -short -B ${disk_read}) "; fi
 2286                         if ((disk_write>0)); then disk_io_string+="▼$(floating_humanizer -s 1 -short -B ${disk_write})"; fi
 2287                     elif ((disk_read+disk_write>0)); then
 2288                         disk_io_string+="▼▲$(floating_humanizer -s 1 -short -B $((disk_read+disk_write)))"
 2289                     fi
 2290 
 2291                     if [[ $psutil_on == false ]]; then
 2292                         disks[${dev_name}_read]="${iostat_array[-2]}"
 2293                         disks[${dev_name}_write]="${iostat_array[-1]}"
 2294                     fi
 2295                 fi
 2296                 disks_io+=("${disk_io_string:-0}")
 2297             fi
 2298         else
 2299             unset 'disks_name[-1]'
 2300             disks_name=("${disks_name[@]}")
 2301         fi
 2302 
 2303         if ((${#disks_name[@]}>=height/2)); then break; fi
 2304 
 2305     done
 2306 
 2307 
 2308 }
 2309 
 2310 collect_processes() { #? Collect process information and calculate accurate cpu usage
 2311     if [[ $use_psutil == true ]]; then collect_processes_psutil $1; return; fi
 2312     local argument="$1"
 2313     if [[ -n $skip_process_draw && $argument != "now" ]]; then return; fi
 2314     local width=${box[processes_width]} height=${box[processes_height]} format_args format_cmd readline sort symbol="▼" cpu_title options pid_string tmp selected
 2315     local tree tree_compare1 tree_compare2 tree_compare3 no_core_divide pids
 2316     local -a grep_array saved_proc_array
 2317 
 2318     if [[ $argument == "now" ]]; then skip_process_draw=1; fi
 2319 
 2320     if [[ -n ${proc[reverse]} ]]; then symbol="▲"; fi
 2321     case ${proc_sorting} in
 2322         "pid") selected="Pid:"; sort="pid";;
 2323         "program") selected="Program:"; sort="comm";;
 2324         "arguments") selected="Arguments:"; sort="args";;
 2325         "threads") selected="Threads:"; sort="nlwp";;
 2326         "user") selected="User:"; sort="euser";;
 2327         "memory") selected="Mem%"; sort="pmem";;
 2328         "cpu lazy"|"cpu responsive") sort="pcpu"; selected="Cpu%";;
 2329     esac
 2330 
 2331     if [[ $proc_tree == true ]]; then tree="Tree:"; fi
 2332     if [[ $proc_per_core == true ]]; then no_core_divide="1"; fi
 2333 
 2334     #* Collect output from ps command to array
 2335     if ((width>60)) && [[ $proc_tree != true ]] ; then format_args=",args:$(( width-(47+proc[pid_len]) ))=Arguments:"; format_cmd=15
 2336     else format_cmd=$(( width-(31+proc[pid_len]) )); fi
 2337     saved_proc_array=("${proc_array[@]}")
 2338     unset 'proc_array[@]' 'pid_array[@]'
 2339 
 2340     if ((proc[detailed]==0)) && [[ -n ${proc[detailed_name]} ]]; then
 2341         unset 'proc[detailed_name]' 'proc[detailed_killed]' 'proc[detailed_cpu_int]' 'proc[detailed_cmd]'
 2342         unset 'proc[detailed_mem]' 'proc[detailed_mem_int]' 'proc[detailed_user]' 'proc[detailed_threads]'
 2343         unset 'detail_graph[@]' 'detail_mem_graph' 'detail_history[@]' 'detail_mem_history[@]'
 2344         unset 'proc[detailed_runtime]' 'proc[detailed_mem_string]' 'proc[detailed_parent_pid]' 'proc[detailed_parent_name]'
 2345     fi
 2346 
 2347     unset 'proc[detailed_cpu]'
 2348 
 2349     if [[ -z $filter ]]; then
 2350         options="-t"
 2351     fi
 2352 
 2353     readarray ${options} proc_array < <(ps ax${tree:+f} -o pid:${proc[pid_len]}=Pid:,comm:${format_cmd}=${tree:-Program:}${format_args},nlwp:3=Tr:,euser:6=User:,pmem=Mem%,pcpu:10=Cpu% --sort ${proc[reverse]:--}${sort})
 2354 
 2355     proc_array[0]="${proc_array[0]/      Tr:/ Threads:}"
 2356     proc_array[0]="${proc_array[0]/ ${selected}/${symbol}${selected}}"
 2357 
 2358     if [[ -n $filter ]]; then
 2359         grep_array[0]="${proc_array[0]}"
 2360         readarray -O 1 -t grep_array < <(echo -e " ${proc_array[*]:1}" | grep -e "${filter}" ${proc[detailed_pid]:+-e ${proc[detailed_pid]}} | cut -c 2- || true)
 2361         proc_array=("${grep_array[@]}")
 2362     fi
 2363 
 2364 
 2365     #* Get accurate cpu usage by fetching and comparing values in /proc/"pid"/stat
 2366     local operations operation utime stime count time_elapsed cpu_percent_string rgb=231 step add proc_out tmp_value_array i pcpu_usage cpu_int tmp_percent breaking
 2367     local -a cpu_percent statfile work_array
 2368 
 2369     #* Timestamp the values in milliseconds to accurately calculate cpu usage
 2370     get_ms proc[new_timestamp]
 2371 
 2372     for readline in "${proc_array[@]:1}"; do
 2373         ((++count))
 2374 
 2375         if ((count==height-3 & breaking==0)); then
 2376             if [[ -n $filter || $proc_sorting != "cpu lazy" || ${proc[selected]} -gt 0 || ${proc[start]} -gt 1 || ${proc_reversed} == true ]]; then :
 2377             else breaking=1; fi
 2378         fi
 2379 
 2380         #if get_key -save && [[ ${#saved_key[@]} -gt 0 ]]; then proc_array=("${saved_proc_array[@]}"); return; fi
 2381 
 2382         if ((breaking==2)); then
 2383             work_array=(${proc_array[-1]})
 2384         else
 2385             work_array=(${readline})
 2386         fi
 2387 
 2388         pid="${work_array[0]}"
 2389         pcpu_usage="${work_array[-1]}"
 2390 
 2391         #* If showing tree structure replace slashes and pipes with actual lines and terminate them at the correct places
 2392         if [[ $proc_tree == true ]]; then
 2393             tree_compare1="${proc_array[$((count+1))]%'\_'*}"
 2394             tree_compare2="${proc_array[count]%'\_'*}"
 2395             tree_compare3="${proc_array[$((count+1))]%'|'*}"
 2396             proc_array[count]="${proc_array[count]//'|'/│}"
 2397             proc_array[count]="${proc_array[count]//'\_'/└─}"
 2398             if ((count<${#proc_array[@]}-1)) && [[ ${#tree_compare1} -eq ${#tree_compare2} || ${#tree_compare2} -eq ${#tree_compare3} ]]; then
 2399                 proc_array[count]="${proc_array[count]//'└'/├}"
 2400             fi
 2401         fi
 2402 
 2403         pid_history[${pid}]="1"
 2404 
 2405         if [[ -n $filter || $proc_sorting == "cpu responsive" ]] && [[ ${proc_array[count]:${proc[pid_len]}:1} != " " ]]; then
 2406             unset pid_string
 2407             printf -v pid_string "%${proc[pid_len]}s" "${pid}"
 2408             proc_array[count]="${pid_string}${proc_array[count]#*${pid}}"
 2409         fi
 2410 
 2411         if [[ -r "/proc/${pid}/stat" ]] && read -ra statfile </proc/${pid}/stat 2>/dev/null; then
 2412 
 2413             utime=${statfile[13]}
 2414             stime=${statfile[14]}
 2415 
 2416             proc[new_${pid}_ticks]=$((utime+stime))
 2417 
 2418 
 2419             if [[ -n ${proc[old_${pid}_ticks]} ]]; then
 2420 
 2421                 time_elapsed=$((proc[new_timestamp]-proc[old_timestamp]))
 2422 
 2423                 #* Calculate current cpu usage for process, * 1000 (for conversion from ms to seconds) * 1000 (for conversion to floating point)
 2424                 cpu_percent[count]=$(( ( ( ${proc[new_${pid}_ticks]}-${proc[old_${pid}_ticks]} ) * 1000 * 1000 ) / ( cpu[hz]*time_elapsed*${no_core_divide:-${cpu[threads]}} ) ))
 2425 
 2426                 if ((cpu_percent[count]<0)); then cpu_percent[count]=0
 2427                 elif [[ -z $no_core_divide ]] && ((cpu_percent[count]>1000)); then cpu_percent[count]=1000; fi
 2428 
 2429                 if ((${#cpu_percent[count]}<=3)); then
 2430                     printf -v cpu_percent_string "%01d%s" "${cpu_percent[count]::-1}" ".${cpu_percent[count]:(-1)}"
 2431                 else
 2432                     cpu_percent_string=${cpu_percent[count]::-1}
 2433                 fi
 2434 
 2435                 printf -v cpu_percent_string "%5s" "${cpu_percent_string::4}"
 2436 
 2437                 proc_array[count]="${proc_array[count]::-5}${cpu_percent_string}"
 2438 
 2439 
 2440                 pid_graph="pid_${pid}_graph"
 2441                 local -n pid_count="pid_${pid}_count"
 2442 
 2443                 printf -v cpu_int "%01d" "${cpu_percent[count]::-1}"
 2444 
 2445                 #* Get info for detailed box if enabled
 2446                 if [[ ${pid} == "${proc[detailed_pid]}" ]]; then
 2447                     if [[ -z ${proc[detailed_name]} ]]; then
 2448                         local get_mem mem_string cmdline=""
 2449                         local -a det_array
 2450                         read -r proc[detailed_name] </proc/${pid}/comm ||true
 2451                         mapfile -d $'\0' -n 0 cmdline </proc/${pid}/cmdline ||true
 2452                         proc[detailed_cmd]="${cmdline[*]}"
 2453                         proc[detailed_name]="${proc[detailed_name]::15}"
 2454                         read -ra det_array < <(ps -o ppid:4,euser:15 --no-headers -p $pid || true)
 2455                         proc[detailed_parent_pid]="${det_array[0]}"
 2456                         proc[detailed_user]="${det_array[*]:1}"
 2457                         read -r proc[detailed_parent_name] < <(ps -o comm --no-headers -p ${det_array[0]} || true)
 2458                         get_mem=1
 2459                     fi
 2460                     proc[detailed_cpu]="${cpu_percent_string// /}"
 2461                     proc[detailed_cpu_int]="${cpu_int}"
 2462                     proc[detailed_threads]="${work_array[-4]}"
 2463                     read -r proc[detailed_runtime] < <(ps -o etime:4 --no-headers -p $pid || true)
 2464 
 2465                     if [[ ${proc[detailed_mem]} != "${work_array[-2]}" || -n $get_mem ]] || ((++proc[detailed_mem_count]>5)); then
 2466                         proc[detailed_mem_count]=0
 2467                         proc[detailed_mem]="${work_array[-2]}"
 2468                         proc[detailed_mem_int]="${proc[detailed_mem]/./}"
 2469                         if [[ ${proc[detailed_mem_int]::1} == "0" ]]; then proc[detailed_mem_int]="${proc[detailed_mem_int]:1}0"; fi
 2470                         #* Scale up low mem values to see any changes on mini graph
 2471                         if ((proc[detailed_mem_int]>900)); then proc[detailed_mem_int]=$((proc[detailed_mem_int]/10))
 2472                         elif ((proc[detailed_mem_int]>600)); then proc[detailed_mem_int]=$((proc[detailed_mem_int]/8))
 2473                         elif ((proc[detailed_mem_int]>300)); then proc[detailed_mem_int]=$((proc[detailed_mem_int]/5))
 2474                         elif ((proc[detailed_mem_int]>100)); then proc[detailed_mem_int]=$((proc[detailed_mem_int]/2))
 2475                         elif ((proc[detailed_mem_int]<50)); then proc[detailed_mem_int]=$((proc[detailed_mem_int]*2)); fi
 2476                         unset 'proc[detailed_mem_string]'
 2477                         read -r mem_string < <(ps -o rss:1 --no-headers -p ${pid} || true)
 2478                         floating_humanizer -v proc[detailed_mem_string] -B -s 1 $mem_string
 2479                         if [[ -z ${proc[detailed_mem_string]} ]]; then proc[detailed_mem_string]="? Byte"; fi
 2480                     fi
 2481 
 2482                     #* Copy process cpu usage to history array and trim earlier entries
 2483                     if ((${#detail_history[@]}>box[details_width]*2)); then
 2484                         detail_history=( "${detail_history[@]:${box[details_width]}}" "$((cpu_int+4))")
 2485                     else
 2486                         detail_history+=("$((cpu_int+4))")
 2487                     fi
 2488 
 2489                     #* Copy process mem usage to history array and trim earlier entries
 2490                     if ((${#detail_mem_history[@]}>box[details_width])); then
 2491                         detail_mem_history=( "${detail_mem_history[@]:$((box[details_width]/2))}" "${proc[detailed_mem_int]}")
 2492                     else
 2493                         detail_mem_history+=("${proc[detailed_mem_int]}")
 2494                     fi
 2495 
 2496                     #* Remove selected process from array if process is excluded by filtering or not on first page
 2497                     if [[ -n $filter && ! ${proc[detailed_name]} =~ $filter ]]; then
 2498                         unset 'proc_array[count]'
 2499                         cpu_int=0; pid_count=0
 2500                     fi
 2501                 fi
 2502 
 2503                 #* Create small graphs for all visible processes using more than 1% cpu time
 2504                 if [[ ${cpu_int} -gt 0 ]]; then pid_count=5; fi
 2505 
 2506                 if [[ -z ${!pid_graph} && ${cpu_int} -gt 0 ]]; then
 2507                     tmp_value_array=("$((cpu_int+4))")
 2508                     create_mini_graph -o "pid_${pid}_graph" -nc -w 5 "tmp_value_array"
 2509                 elif [[ ${pid_count} -gt 0 ]]; then
 2510                     if [[ ${cpu_int} -gt 9 ]]; then
 2511                         create_mini_graph -nc -add-value "pid_${pid}_graph" "$((cpu_int+15))"
 2512                     else
 2513                         create_mini_graph -nc -add-value "pid_${pid}_graph" "$((cpu_int+9))"
 2514                     fi
 2515 
 2516                     pid_count=$((${pid_count}-1))
 2517                 elif [[ ${pid_count} == "0" ]]; then
 2518                     unset "pid_${pid}_graph" "pid_${pid}_graph_even" "pid_${pid}_graph_odd" "pid_${pid}_graph_last_type" "pid_${pid}_graph_last_val"
 2519                     unset "pid_${pid}_count"
 2520                 fi
 2521             else
 2522                 tmp_percent="${proc_array[count]:(-5)}"; tmp_percent="${tmp_percent// /}"; if [[ ${tmp_percent//./} != "$tmp_percent" ]]; then tmp_percent="${tmp_percent::-2}"; fi
 2523                 if ((tmp_percent>100)); then
 2524                     proc_array[count]="${proc_array[count]::-5}  100"
 2525                 fi
 2526             fi
 2527 
 2528             proc[old_${pid}_ticks]=${proc[new_${pid}_ticks]}
 2529 
 2530         fi
 2531 
 2532         if ((breaking==1)); then
 2533             if [[ ${proc[detailed]} == "1" && -z ${proc[detailed_cpu]} ]] && ps ${proc[detailed_pid]} >/dev/null 2>&1; then
 2534                 readarray ${options} -O ${#proc_array[@]} proc_array < <(ps -o pid:${proc[pid_len]}=Pid:,comm:${format_cmd}=${tree:-Program:}${format_args},nlwp:3=Tr:,euser:6=User:,pmem=Mem%,pcpu:10=Cpu% --no-headers -p ${proc[detailed_pid]} || true)
 2535                 ((++breaking))
 2536             else
 2537                 break
 2538             fi
 2539         elif ((breaking==2)); then
 2540             unset 'proc_array[-1]'
 2541             break
 2542         fi
 2543 
 2544     done
 2545 
 2546 
 2547     proc[old_timestamp]=${proc[new_timestamp]}
 2548 
 2549     if ((proc[detailed]==1)) && [[ -z ${proc[detailed_cpu]} && -z ${proc[detailed_killed]} ]]; then proc[detailed_killed]=1; proc[detailed_change]=1
 2550     elif [[ -n ${proc[detailed_cpu]} ]]; then unset 'proc[detailed_killed]'; fi
 2551 
 2552     #* Sort output array based on cpu usage if "cpu responsive" is selected
 2553     if [[ ${proc_sorting} == "cpu responsive" && ${proc_tree} != true ]]; then
 2554         local -a sort_array
 2555         if [[ -z ${proc[reverse]} ]]; then local sort_rev="-r"; fi
 2556         sort_array[0]="${proc_array[0]}"
 2557         readarray -O 1 -t sort_array < <(printf "%s\n" "${proc_array[@]:1}" | awk '{ print $NF, $0 }' | sort -n -k1 ${sort_rev}| sed 's/^[0-9\.]* //')
 2558         proc_array=("${sort_array[@]}")
 2559     fi
 2560 
 2561     #* Clear up memory by removing variables and graphs of no longer running processes
 2562     ((++proc[general_counter]))
 2563     if ((proc[general_counter]>100)); then
 2564         proc[general_counter]=0
 2565         for pids in ${!pid_history[@]}; do
 2566             if [[ ! -e /proc/${pids} ]]; then
 2567                 unset "pid_${pids}_graph" "pid_${pids}_graph_even" "pid_${pids}_graph_odd" "pid_${pids}_graph_last_type" "pid_${pids}_graph_last_val"
 2568                 unset "pid_${pids}_count"
 2569                 unset "proc[new_${pids}_ticks]"
 2570                 unset "proc[old_${pids}_ticks]"
 2571                 unset "pid_history[${pids}]"
 2572             fi
 2573         done
 2574     fi
 2575 
 2576 }
 2577 
 2578 collect_processes_psutil() {
 2579     local argument=$1
 2580     if [[ -n $skip_process_draw && $argument != "now" ]]; then return; fi
 2581     if [[ $argument == "now" ]]; then skip_process_draw=1; fi
 2582     local prog_len arg_len symbol="▼" selected width=${box[processes_width]} height=${box[processes_height]}
 2583     local pcpu_usage pids p_count cpu_int pids max_lines i pi
 2584 
 2585     case ${proc_sorting} in
 2586         "pid") selected="Pid:";;
 2587         "program") selected="Program:";;
 2588         "arguments") selected="Arguments:";;
 2589         "threads") selected="Threads:";;
 2590         "user") selected="User:";;
 2591         "memory") selected="Mem%";;
 2592         "cpu lazy"|"cpu responsive") selected="Cpu%";;
 2593     esac
 2594 
 2595     if [[ ${proc_tree} == true && ${proc_sorting} =~ pid|program|arguments ]]; then selected="Tree:"; fi
 2596 
 2597     if [[ -n ${proc[reverse]} ]]; then symbol="▲"; fi
 2598 
 2599     if ((proc[detailed]==0)) && [[ -n ${proc[detailed_name]} ]]; then
 2600         unset 'proc[detailed_name]' 'proc[detailed_killed]' 'proc[detailed_cpu_int]' 'proc[detailed_cmd]'
 2601         unset 'proc[detailed_mem]' 'proc[detailed_mem_int]' 'proc[detailed_user]' 'proc[detailed_threads]'
 2602         unset 'detail_graph[@]' 'detail_mem_graph' 'detail_history[@]' 'detail_mem_history[@]'
 2603         unset 'proc[detailed_runtime]' 'proc[detailed_mem_string]' 'proc[detailed_parent_pid]' 'proc[detailed_parent_name]'
 2604     fi
 2605 
 2606     unset 'proc[detailed_cpu]'
 2607 
 2608 
 2609     if ((width>60)); then
 2610         arg_len=$((width-55))
 2611         prog_len=15
 2612     else
 2613         prog_len=$((width-40))
 2614         arg_len=0
 2615         if [[ $proc_sorting == "threads" ]]; then selected="Tr:"; fi
 2616     fi
 2617 
 2618 
 2619     unset 'proc_array[@]'
 2620     if ! py_command -a proc_array "get_proc(sorting='${proc_sorting}', tree=${proc_tree^}, prog_len=${prog_len}, arg_len=${arg_len}, search='${filter}', reverse=${proc_reversed^}, proc_per_cpu=${proc_per_core^})"; then
 2621         proc_array=(""); return
 2622     fi
 2623 
 2624     proc_array[0]="${proc_array[0]/ ${selected}/${symbol}${selected}}"
 2625 
 2626     for((i=1;i<${#proc_array[@]};i++)); do
 2627         if [[ -z ${proc_array[i]} ]]; then continue; fi
 2628 
 2629         out_arr=(${proc_array[i]})
 2630 
 2631         pi=0
 2632         if [[ $proc_tree == true ]]; then
 2633             while ! is_int "${out_arr[pi]}" && ((pi<${#out_arr[@]}-1)); do ((++pi)); done
 2634         fi
 2635         pid="${out_arr[pi]}"
 2636         if ! is_int "${pid}"; then continue; fi
 2637 
 2638         pcpu_usage="${out_arr[-1]}"
 2639 
 2640         if ! printf -v cpu_int "%.0f" "${pcpu_usage}" 2>/dev/null; then continue; fi
 2641 
 2642         pid_history[${pid}]="1"
 2643 
 2644         #* Create small graphs for all visible processes using more than 1% rounded cpu time
 2645         pid_graph="pid_${pid}_graph"
 2646         if ! local -n pid_count="pid_${pid}_count" 2>/dev/null; then continue; fi
 2647 
 2648         if [[ ${cpu_int} -gt 0 ]]; then pid_count=5; fi
 2649 
 2650         if [[ -z ${!pid_graph} && ${cpu_int} -gt 0 ]]; then
 2651             tmp_value_array=("$((cpu_int+4))")
 2652             create_mini_graph -o "pid_${pid}_graph" -nc -w 5 "tmp_value_array"
 2653         elif [[ ${pid_count} -gt 0 ]]; then
 2654             if [[ ${cpu_int} -gt 9 ]]; then
 2655                 create_mini_graph -nc -add-value "pid_${pid}_graph" "$((cpu_int+15))"
 2656             elif [[ ${cpu_int} -gt 0 ]]; then
 2657                 create_mini_graph -nc -add-value "pid_${pid}_graph" "$((cpu_int+9))"
 2658             else
 2659                 create_mini_graph -nc -add-value "pid_${pid}_graph" "0"
 2660             fi
 2661             pid_count=$((${pid_count}-1))
 2662         elif [[ ${pid_count} == "0" ]]; then
 2663             unset "pid_${pid}_graph" "pid_${pid}_graph_even" "pid_${pid}_graph_odd" "pid_${pid}_graph_last_type" "pid_${pid}_graph_last_val"
 2664             unset "pid_${pid}_count"
 2665         fi
 2666 
 2667         #* Get info for detailed box if enabled
 2668         if [[ ${pid} == "${proc[detailed_pid]}" ]]; then
 2669             local -a det_array
 2670             if [[ -z ${proc[detailed_name]} ]]; then
 2671                 local get_mem mem_string cmdline=""
 2672 
 2673                 py_command -a det_array "get_detailed_names_cmd(${pid})"
 2674 
 2675                 if [[ -z ${det_array[0]} ]]; then continue; fi
 2676                 proc[detailed_name]="${det_array[0]::15}"
 2677                 proc[detailed_parent_name]="${det_array[1]}"
 2678                 proc[detailed_user]="${det_array[2]}"
 2679                 proc[detailed_cmd]="${det_array[3]}"
 2680             fi
 2681             proc[detailed_cpu]="${out_arr[-1]}"
 2682             proc[detailed_cpu_int]="${cpu_int}"
 2683             proc[detailed_threads]="${out_arr[-4]}"
 2684 
 2685             unset 'det_array[@]'
 2686             py_command -a det_array "get_detailed_mem_time(${pid})"
 2687 
 2688             if [[ -z ${det_array[0]} ]]; then continue; fi
 2689             unset 'proc[detailed_mem_string]'
 2690             floating_humanizer -v proc[detailed_mem_string] -B ${det_array[0]}
 2691             if [[ -z ${proc[detailed_mem_string]} ]]; then proc[detailed_mem_string]="? Byte"; fi
 2692             if ((${#det_array[1]}>8)); then proc[detailed_runtime]="${det_array[1]/ days, /-}"
 2693             else proc[detailed_runtime]="${det_array[1]}"; fi
 2694 
 2695             proc[detailed_mem_count]=0
 2696             proc[detailed_mem]="${out_arr[-2]}"
 2697             proc[detailed_mem_int]="${proc[detailed_mem]/./}"
 2698             if [[ ${proc[detailed_mem_int]::1} == "0" ]]; then proc[detailed_mem_int]="${proc[detailed_mem_int]:1}0"; fi
 2699             #* Scale up low mem values to see any changes on mini graph
 2700             if ((proc[detailed_mem_int]>900)); then proc[detailed_mem_int]=$((proc[detailed_mem_int]/10))
 2701             elif ((proc[detailed_mem_int]>600)); then proc[detailed_mem_int]=$((proc[detailed_mem_int]/8))
 2702             elif ((proc[detailed_mem_int]>300)); then proc[detailed_mem_int]=$((proc[detailed_mem_int]/5))
 2703             elif ((proc[detailed_mem_int]>100)); then proc[detailed_mem_int]=$((proc[detailed_mem_int]/2))
 2704             elif ((proc[detailed_mem_int]<50)); then proc[detailed_mem_int]=$((proc[detailed_mem_int]*2)); fi
 2705 
 2706             #* Copy process cpu usage to history array and trim earlier entries
 2707             if ((${#detail_history[@]}>box[details_width]*2)); then
 2708                 detail_history=( "${detail_history[@]:${box[details_width]}}" "$((cpu_int+4))")
 2709             else
 2710                 detail_history+=("$((cpu_int+4))")
 2711             fi
 2712 
 2713             #* Copy process mem usage to history array and trim earlier entries
 2714             if ((${#detail_mem_history[@]}>box[details_width])); then
 2715                 detail_mem_history=( "${detail_mem_history[@]:$((box[details_width]/2))}" "${proc[detailed_mem_int]}")
 2716             else
 2717                 detail_mem_history+=("${proc[detailed_mem_int]}")
 2718             fi
 2719         fi
 2720 
 2721         if ((i==height-2)); then
 2722             if [[ ${proc[selected]} -gt 0 || -n $filter || ${proc[start]} -gt 1 ]] || [[ ${proc[detailed]} -eq 1 && -z ${proc[detailed_cpu]} && -z ${proc[detailed_killed]} ]]; then :
 2723             else break; fi
 2724         fi
 2725 
 2726     done
 2727 
 2728     if ((proc[detailed]==1)) && [[ -z ${proc[detailed_cpu]} && -z ${proc[detailed_killed]} ]]; then proc[detailed_killed]=1; proc[detailed_change]=1
 2729     elif [[ -n ${proc[detailed_cpu]} ]]; then unset 'proc[detailed_killed]'; fi
 2730 
 2731 
 2732 
 2733     #* Clear up memory
 2734     ((++proc[general_counter]))
 2735     if ((proc[general_counter]>100)); then
 2736         proc[general_counter]=0
 2737         for pids in ${!pid_history[@]}; do
 2738             unset "pid_${pids}_graph" "pid_${pids}_graph_even" "pid_${pids}_graph_odd" "pid_${pids}_graph_last_type" "pid_${pids}_graph_last_val"
 2739             unset "pid_${pids}_count"
 2740             unset "pid_history[${pids}]"
 2741         done
 2742     fi
 2743 
 2744 }
 2745 
 2746 collect_net() { #? Collect information from "/proc/net/dev"
 2747     local operations operation direction index unit_selector speed speed_B total
 2748     local -a net_dev history_sorted history_last
 2749 
 2750     if [[ -n ${net[no_device]} ]]; then return; fi
 2751 
 2752     if [[ $1 == "init" ]]; then
 2753         for direction in "download" "upload"; do
 2754         net[${direction}_max]=0
 2755         net[${direction}_new_low]=0
 2756         net[${direction}_new_max]=0
 2757         net[${direction}_max_current]=0
 2758         net[${direction}_graph_max]=$((50<<10))
 2759         done
 2760         unset 'download_graph[@]' 'upload_graph[@]' 'net_history_download[@]' 'net_history_upload[@]'
 2761     fi
 2762 
 2763     #* Get the line with relevant net device from /proc/net/dev or psutil into array net_dev, index 1 is download, index 9 is upload
 2764     if [[ $use_psutil == true ]]; then
 2765         py_command -v net_dev "get_net('${net[device]}')" || return
 2766         net_dev=(${net_dev})
 2767         if ! is_int "${net_dev[0]}"; then net[no_device]=1; return; fi
 2768     else
 2769         if ! get_value -map net_dev -sf "/proc/net/dev" -k "${net[device]}" -a; then net[no_device]=1; return; fi
 2770     fi
 2771 
 2772     #* Timestamp the values to accurately calculate values in seconds
 2773     get_ms net[new_timestamp]
 2774     for direction in "download" "upload"; do
 2775         if [[ $direction == "download" ]]; then index=1
 2776         else index=9; fi
 2777 
 2778         net[new_${direction}]=${net_dev[index]}
 2779 
 2780         if [[ -n ${net[old_${direction}]} ]]; then
 2781             #* Get total, convert to floating point and format string to best fitting unit in Bytes
 2782             if ((net[nic_change]==1 & net[reset]==1)); then unset "net[total_offset_${direction}]"; net[reset]=0; fi
 2783             if ((net[reset]==1)) && [[ -z ${net[total_offset_${direction}]} || ${net[total_offset_${direction}]} -gt ${net[new_${direction}]} ]]; then net[total_offset_${direction}]=${net[new_${direction}]}
 2784             elif ((net[reset]==0)) && [[ -n ${net[total_offset_${direction}]} ]]; then unset "net[total_offset_${direction}]"; fi
 2785 
 2786             floating_humanizer -Byte -v net[total_${direction}] $((${net[new_${direction}]}-${net[total_offset_${direction}]:-0}))
 2787 
 2788             #* Calculate current speeds: ("New value" - "Old value") * 1000(for ms to seconds) / ("new_timestamp" - "old_timestamp")
 2789             net[speed_${direction}]=$(( (${net[new_${direction}]}-${net[old_${direction}]})*1000/(net[new_timestamp]-net[old_timestamp]) ))
 2790 
 2791             #* Convert to floating point and format string to best fitting unit in Bytes and Bits per second
 2792             floating_humanizer -Byte -per-second -v net[speed_${direction}_byteps] ${net[speed_${direction}]}
 2793             floating_humanizer -bit -per-second -v net[speed_${direction}_bitps] ${net[speed_${direction}]}
 2794 
 2795             #* Update download and upload max values for graph
 2796             if ((${net[speed_${direction}]}>${net[${direction}_max]})); then
 2797                 net[${direction}_max]=${net[speed_${direction}]}
 2798             fi
 2799 
 2800             if ((${net[speed_${direction}]}>${net[${direction}_graph_max]})); then
 2801                     ((++net[${direction}_new_max]))
 2802                     if ((net[${direction}_new_low]>0)); then ((net[${direction}_new_low]--)); fi
 2803             elif ((${net[${direction}_graph_max]}>10<<10 & ${net[speed_${direction}]}<${net[${direction}_graph_max]}/10)); then
 2804                 ((++net[${direction}_new_low]))
 2805                 if ((net[${direction}_new_max]>0)); then ((net[${direction}_new_max]--)); fi
 2806             fi
 2807 
 2808             #* Copy download and upload speed to history arrays and trim earlier entries
 2809             local -n history="net_history_${direction}"
 2810             if ((${#history[@]}>box[net_width]*4)); then
 2811                 history=( "${history[@]:$((box[net_width]*2))}" "${net[speed_${direction}]}")
 2812             else
 2813                 history+=("${net[speed_${direction}]}")
 2814             fi
 2815 
 2816             #* Check for new max value and set flag to adjust resolution of graph if needed
 2817             if ((${net[${direction}_new_max]}>=5)); then
 2818                 net[${direction}_graph_max]=$((${net[${direction}_max]}+(${net[${direction}_max]}/3) ))
 2819                 net[${direction}_redraw]=1
 2820                 net[${direction}_new_max]=0
 2821 
 2822             #* If current max value isn't relevant, sort array to get the next largest value to set graph resolution
 2823             elif ((${net[${direction}_new_low]}>=5 & ${#history[@]}>5)); then
 2824                 history_last=("${history[@]:(-5)}")
 2825                 sort_array_int "history_last" "history_sorted"
 2826                 net[${direction}_max]=${history_sorted[0]}
 2827                 net[${direction}_graph_max]=$(( ${net[${direction}_max]}*3 ))
 2828                 if ((${net[${direction}_graph_max]}<10<<10)); then net[${direction}_graph_max]=$((10<<10)); fi
 2829                 net[${direction}_redraw]=1
 2830                 net[${direction}_new_low]=0
 2831             fi
 2832         fi
 2833 
 2834         floating_humanizer -Byte -short -v net[${direction}_max_string] ${net[${direction}_graph_max]}
 2835 
 2836         net[old_${direction}]=${net[new_${direction}]}
 2837     done
 2838 
 2839     net[old_timestamp]=${net[new_timestamp]}
 2840 
 2841 }
 2842 
 2843 calc_sizes() { #? Calculate width and height of all boxes
 2844     local pos calc_size calc_total percent threads=${cpu[threads]}
 2845 
 2846     #* Calculate heights
 2847     for pos in ${box[boxes]/processes/}; do
 2848         if [[ $pos = "cpu" ]]; then percent=32;
 2849         elif [[ $pos = "mem" ]]; then percent=40;
 2850         else percent=28; fi
 2851 
 2852         #* Multiplying with 10 to convert to floating point
 2853         calc_size=$(( (tty_height*10)*(percent*10)/100 ))
 2854 
 2855         #* Round down if last 2 digits of value is below "50" and round up if above
 2856         if ((${calc_size:(-2):1}==0)); then calc_size=$((calc_size+10)); fi
 2857         if ((${calc_size:(-2)}<50)); then
 2858             calc_size=$((${calc_size::-2}))
 2859         else
 2860             calc_size=$((${calc_size::-2}+1))
 2861         fi
 2862 
 2863         #* Subtract from last value if the total of all rounded numbers is larger then terminal height
 2864         while ((calc_total+calc_size>tty_height)); do ((--calc_size)); done
 2865         calc_total=$((calc_total+calc_size))
 2866 
 2867         #* Set calculated values in box array
 2868         box[${pos}_line]=$((calc_total-calc_size+1))
 2869         box[${pos}_col]=1
 2870         box[${pos}_height]=$calc_size
 2871         box[${pos}_width]=$tty_width
 2872     done
 2873 
 2874 
 2875     #* Calculate widths
 2876     unset calc_total
 2877     for pos in net processes; do
 2878         if [[ $pos = "net" ]]; then percent=45; else percent=55; fi
 2879 
 2880         #* Multiplying with 10 to convert to floating point
 2881         calc_size=$(( (tty_width*10)*(percent*10)/100 ))
 2882 
 2883         #* Round down if last 2 digits of value is below "50" and round up if above
 2884         if ((${calc_size:(-2)}<50)); then
 2885             calc_size=$((${calc_size::-2}))
 2886         else
 2887             calc_size=$((${calc_size::-2}+1))
 2888         fi
 2889 
 2890         #* Subtract from last value if the total of all rounded numbers is larger then terminal width
 2891         while ((calc_total+calc_size>tty_width)); do ((--calc_size)); done
 2892         calc_total=$((calc_total+calc_size))
 2893 
 2894         #* Set calculated values in box array
 2895         box[${pos}_col]=$((calc_total-calc_size+1))
 2896         box[${pos}_width]=$calc_size
 2897     done
 2898 
 2899     #* Copy numbers around to get target layout
 2900     box[mem_width]=${box[net_width]}
 2901     box[processes_line]=${box[mem_line]}
 2902     box[processes_height]=$((box[mem_height]+box[net_height]))
 2903 
 2904     #  threads=${box[testing]} #! For testing, remove <--------------
 2905 
 2906     #* Recalculate size of process box if currently showing detailed process information
 2907     if ((proc[detailed]==1)); then
 2908         box[details_line]=${box[processes_line]}
 2909         box[details_col]=${box[processes_col]}
 2910         box[details_width]=${box[processes_width]}
 2911         box[details_height]=8
 2912         box[processes_line]=$((box[processes_line]+box[details_height]))
 2913         box[processes_height]=$((box[processes_height]-box[details_height]))
 2914     fi
 2915 
 2916     #* Calculate number of columns and placement of cpu meter box
 2917     local cpu_line=$((box[cpu_line]+1)) cpu_width=$((box[cpu_width]-2)) cpu_height=$((box[cpu_height]-2)) box_cols
 2918     if ((threads>(cpu_height-3)*3 && tty_width>=200)); then box[p_width]=$((24*4)); box[p_height]=$((threads/4+4)); box_cols=4
 2919     elif ((threads>(cpu_height-3)*2 && tty_width>=150)); then box[p_width]=$((24*3)); box[p_height]=$((threads/3+5)); box_cols=3
 2920     elif ((threads>cpu_height-3 && tty_width>=100)); then box[p_width]=$((24*2)); box[p_height]=$((threads/2+4)); box_cols=2
 2921     else box[p_width]=24; box[p_height]=$((threads+4)); box_cols=1
 2922     fi
 2923 
 2924     if [[ $check_temp == true ]]; then
 2925         box[p_width]=$(( box[p_width]+13*box_cols))
 2926     fi
 2927 
 2928     if ((box[p_height]>cpu_height)); then box[p_height]=$cpu_height; fi
 2929     box[p_col]="$((cpu_width-box[p_width]+2))"
 2930     box[p_line]="$((cpu_line+(cpu_height/2)-(box[p_height]/2)+1))"
 2931 
 2932     #* Calculate placement of mem divider
 2933     local mem_line=$((box[mem_line]+1)) mem_width=$((box[mem_width]-2)) mem_height=$((box[mem_height]-2)) mem_col=$((box[mem_col]+1))
 2934     box[m_width]=$((mem_width/2))
 2935     box[m_width2]=${box[m_width]}
 2936     if ((box[m_width]+box[m_width2]<mem_width)); then ((box[m_width]++)); fi
 2937     box[m_height]=$mem_height
 2938     box[m_col]=$((mem_col+1))
 2939     box[m_line]=$mem_line
 2940 
 2941     #* Calculate placement of net value box
 2942     local net_line=$((box[net_line]+1)) net_width=$((box[net_width]-2)) net_height=$((box[net_height]-2))
 2943     box[n_width]=24
 2944     if ((net_height>9)); then box[n_height]=9
 2945     else box[n_height]=$net_height; fi
 2946     box[n_col]="$((net_width-box[n_width]+2))"
 2947     box[n_line]="$((net_line+(net_height/2)-(box[n_height]/2)+1))"
 2948 
 2949 
 2950 }
 2951 
 2952 draw_bg() { #? Draw all box outlines
 2953     local this_box cpu_p_width i cpu_model_len
 2954 
 2955     unset boxes_out
 2956     for this_box in ${box[boxes]}; do
 2957         create_box -v boxes_out -col ${box[${this_box}_col]} -line ${box[${this_box}_line]} -width ${box[${this_box}_width]} -height ${box[${this_box}_height]} -fill -lc "${box[${this_box}_color]}" -title ${this_box}
 2958     done
 2959 
 2960     #* Misc cpu box
 2961     if [[ $check_temp == true ]]; then cpu_model_len=18; else cpu_model_len=9; fi
 2962     create_box -v boxes_out -col $((box[p_col]-1)) -line $((box[p_line]-1)) -width ${box[p_width]} -height ${box[p_height]} -lc ${theme[div_line]} -t "${cpu[model]:0:${cpu_model_len}}"
 2963     print -v boxes_out -m ${box[cpu_line]} $((box[cpu_col]+10)) -rs \
 2964     -fg ${box[cpu_color]} -t "┤" -b -fg ${theme[hi_fg]} -t "m" -fg ${theme[title]} -t "enu" -rs -fg ${box[cpu_color]} -t "├"
 2965 
 2966     #* Misc mem
 2967     print -v boxes_out -m ${box[mem_line]} $((box[mem_col]+box[m_width]+2)) -rs -fg ${box[mem_color]} -t "┤" -fg ${theme[title]} -b -t "disks" -rs -fg ${box[mem_color]} -t "├"
 2968     print -v boxes_out -m ${box[mem_line]} $((box[mem_col]+box[m_width])) -rs -fg ${box[mem_color]} -t "┬"
 2969     print -v boxes_out -m $((box[mem_line]+box[mem_height]-1)) $((box[mem_col]+box[m_width])) -fg ${box[mem_color]} -t "┴"
 2970     for((i=1;i<=box[mem_height]-2;i++)); do
 2971         print -v boxes_out -m $((box[mem_line]+i)) $((box[mem_col]+box[m_width])) -fg ${theme[div_line]} -t "│"
 2972     done
 2973 
 2974 
 2975     #* Misc net box
 2976     create_box -v boxes_out -col $((box[n_col]-1)) -line $((box[n_line]-1)) -width ${box[n_width]} -height ${box[n_height]} -lc ${theme[div_line]} -t "Download"
 2977     print -v boxes_out -m $((box[n_line]+box[n_height]-2)) $((box[n_col]+1)) -rs -fg ${theme[div_line]} -t "┤" -fg ${theme[title]} -b -t "Upload" -rs -fg ${theme[div_line]} -t "├"
 2978 
 2979 
 2980     if [[ $1 == "quiet" ]]; then draw_out="${boxes_out}"
 2981     else echo -en "${boxes_out}"; fi
 2982     draw_update_string $1
 2983 }
 2984 
 2985 draw_cpu() { #? Draw cpu and core graphs and print percentages
 2986     local cpu_out i name cpu_p_color temp_color y pt_line pt_col p_normal_color="${theme[main_fg]}" threads=${cpu[threads]}
 2987     local meter meter_size meter_width temp_var cpu_out_var core_name temp_name temp_width
 2988 
 2989     #* Get variables from previous calculations
 2990     local col=$((box[cpu_col]+1)) line=$((box[cpu_line]+1)) width=$((box[cpu_width]-2)) height=$((box[cpu_height]-2))
 2991     local p_width=${box[p_width]} p_height=${box[p_height]} p_col=${box[p_col]} p_line=${box[p_line]}
 2992 
 2993     #* If resized recreate cpu meter/graph box, cpu graph and core graphs
 2994     if ((resized>0)); then
 2995         local graph_a_size graph_b_size
 2996         graph_a_size=$((height/2)); graph_b_size=${graph_a_size}
 2997 
 2998         if ((graph_a_size*2<height)); then ((graph_a_size++)); fi
 2999         create_graph -o cpu_graph_a -d ${line} ${col} ${graph_a_size} $((width-p_width-2)) -c color_cpu_graph -n cpu_history
 3000         create_graph -o cpu_graph_b -d $((line+graph_a_size)) ${col} ${graph_b_size} $((width-p_width-2)) -c color_cpu_graph -i -n cpu_history
 3001 
 3002         if [[ -z ${cpu_core_1_graph} ]]; then
 3003             for((i=1;i<=threads;i++)); do
 3004                 create_mini_graph -o "cpu_core_${i}_graph" -w 10 -nc "cpu_core_history_${i}"
 3005             done
 3006         fi
 3007 
 3008         if [[ $check_temp == true && -z ${cpu_temp_0_graph} ]]; then
 3009             for((i=0;i<=threads;i++)); do
 3010                 if [[ -n ${cpu[temp_${i}]} ]]; then create_mini_graph -o "cpu_temp_${i}_graph" -w 5 -nc "cpu_temp_history_${i}"; fi
 3011             done
 3012         fi
 3013         ((resized++))
 3014     fi
 3015 
 3016     #* Add new values to cpu and core graphs unless just resized
 3017     if ((resized==0)); then
 3018         create_graph -add-last cpu_graph_a cpu_history
 3019         create_graph -i -add-last cpu_graph_b cpu_history
 3020         for((i=1;i<=threads;i++)); do
 3021             create_mini_graph -w 10 -nc -add-last "cpu_core_${i}_graph" "cpu_core_history_${i}"
 3022         done
 3023         if [[ $check_temp == true ]]; then
 3024             for((i=0;i<=threads;i++)); do
 3025                 if [[ -n ${cpu[temp_${i}]} ]]; then
 3026                     create_mini_graph -w 5 -nc -add-last "cpu_temp_${i}_graph" "cpu_temp_history_${i}"
 3027                 fi
 3028             done
 3029         fi
 3030     fi
 3031 
 3032     #* Print CPU total and all cpu core percentage meters in box
 3033     for((i=0;i<=threads;i++)); do
 3034         if ((i==0)); then name="CPU"; else name="Core${i}"; fi
 3035 
 3036         #* Get color of cpu text depending on current usage
 3037         cpu_p_color="${color_cpu_graph[cpu_usage[i]]}"
 3038 
 3039         pt_col=$p_col; pt_line=$p_line; meter_size="small"; meter_width=10
 3040 
 3041         #* Set temperature string if "sensors" is available
 3042         if [[ $check_temp == true ]]; then
 3043             #* Get color of temperature text depending on current temp vs factory high temp
 3044             declare -n temp_hist="cpu_temp_history_${i}[-1]"
 3045             temp_color="${color_temp_graph[${temp_hist}]}"
 3046             temp_name="cpu_temp_${i}_graph"
 3047             temp_width=13
 3048         fi
 3049 
 3050         if ((i==0 & p_width>24+temp_width)); then
 3051             name="CPU Total "; meter_width=$((p_width-17-temp_width))
 3052         fi
 3053 
 3054 
 3055         #* Create cpu usage meter
 3056         if ((i==0)); then
 3057             create_meter -v meter -w $meter_width -f -c color_cpu_graph ${cpu_usage[i]}
 3058         else
 3059             core_name="cpu_core_${i}_graph"
 3060             meter="${!core_name}"
 3061         fi
 3062 
 3063         if ((p_width>84+temp_width & i>=(p_height-2)*3-2)); then pt_line=$((p_line+i-y*4)); pt_col=$((p_col+72+temp_width*3))
 3064         elif ((p_width>54+temp_width & i>=(p_height-2)*2-1)); then pt_line=$((p_line+i-y*3)); pt_col=$((p_col+48+temp_width*2))
 3065         elif ((p_width>24+temp_width & i>=p_height-2)); then pt_line=$((p_line+i-y*2)); pt_col=$((p_col+24+temp_width))
 3066         else y=$i; fi
 3067 
 3068         print -v cpu_out_var -m $((pt_line+y)) $pt_col -rs -fg $p_normal_color -jl 7 -t "$name" -fg ${theme[inactive_fg]} "⡀⡀⡀⡀⡀⡀⡀⡀⡀⡀" -l 10 -fg $cpu_p_color -t "$meter"\
 3069         -jr 4 -fg $cpu_p_color -t "${cpu_usage[i]}" -fg $p_normal_color -t "%"
 3070         if [[ $check_temp == true && -n ${cpu[temp_${i}]} ]]; then
 3071             print -v cpu_out_var -fg ${theme[inactive_fg]} "  ⡀⡀⡀⡀⡀" -l 7 -fg $temp_color -jl 7 -t "  ${!temp_name}" -jr 4 -t ${cpu[temp_${i}]} -fg $p_normal_color -t ${cpu[temp_unit]}
 3072         fi
 3073 
 3074         if (( i>(p_height-2)*( p_width/(24+temp_width) )-( p_width/(24+temp_width) )-1 )); then break; fi
 3075     done
 3076 
 3077     #* Print load average and uptime
 3078     if ((pt_line+y+3<p_line+p_height)); then
 3079         local avg_string avg_width
 3080         if [[ $check_temp == true ]]; then avg_string="Load Average: "; avg_width=7; else avg_string="L AVG: "; avg_width=5; fi
 3081         print -v cpu_out_var -m $((pt_line+y+1)) $pt_col -fg ${theme[main_fg]} -t "${avg_string}"
 3082         for avg_string in ${cpu[load_avg]}; do
 3083             print -v cpu_out_var -jc $avg_width -t "${avg_string::4}"
 3084         done
 3085     fi
 3086     print -v cpu_out_var -m $((line+height-1)) $((col+1)) -fg ${theme[inactive_fg]} -trans -t "up ${cpu[uptime]}"
 3087 
 3088     #* Print current CPU frequency right of the title in the meter box
 3089     if [[ -n ${cpu[freq_string]} ]]; then print -v cpu_out_var -m $((p_line-1)) $((p_col+p_width-5-${#cpu[freq_string]})) -fg ${theme[div_line]} -t "┤" -fg ${theme[title]} -b -t "${cpu[freq_string]}" -rs -fg ${theme[div_line]} -t "├"; fi
 3090 
 3091     #* Print created text, graph and meters to output variable
 3092     draw_out+="${cpu_graph_a[*]}${cpu_graph_b[*]}${cpu_out_var}"
 3093 
 3094 }
 3095 
 3096 draw_mem() { #? Draw mem, swap and disk statistics
 3097 
 3098     if ((mem[counter]>0 & resized==0)); then return; fi
 3099 
 3100     local i swap_used_meter swap_free_meter mem_available_meter mem_free_meter mem_used_meter mem_cached_meter normal_color="${theme[main_fg]}" value_text
 3101     local meter_mod_w meter_mod_pos value type m_title meter_options values="used available cached free"
 3102     local -a types=("mem")
 3103     unset mem_out
 3104 
 3105     if [[ -n ${swap[total]} && ${swap[total]} -gt 0 ]]; then types+=("swap"); fi
 3106 
 3107     #* Get variables from previous calculations
 3108     local col=$((box[mem_col]+1)) line=$((box[mem_line]+1)) width=$((box[mem_width]-2)) height=$((box[mem_height]-2))
 3109     local m_width=${box[m_width]} m_height=${box[m_height]} m_col=${box[m_col]} m_line=${box[m_line]} mem_line=$((box[mem_col]+box[m_width]))
 3110 
 3111     #* Create text and meters for memory and swap and adapt sizes based on available height
 3112     local y_pos=$m_line v_height=8 list value meter inv_meter
 3113 
 3114     for type in ${types[@]}; do
 3115         local -n type_name="$type"
 3116         if [[ $type == "mem" ]]; then
 3117             m_title="memory"
 3118         else
 3119             m_title="$type"
 3120             if ((height>14)); then ((y_pos++)); fi
 3121         fi
 3122 
 3123         #* Print name of type and total amount in humanized base 2 bytes
 3124         print -v mem_out -m $y_pos $m_col -rs -fg ${theme[title]} -b -jl 9 -t "${m_title^}:" -m $((y_pos++)) $((mem_line-10)) -jr 9 -t " ${type_name[total_string]::$((m_width-11))}"
 3125 
 3126         for value in ${values}; do
 3127             if [[ $type == "swap" && $value =~ available|cached ]]; then continue; fi
 3128 
 3129             if [[ $system == "MacOS" && $value == "cached" ]]; then value_text="active"
 3130             else value_text="${value::$((m_width-12))}"; fi
 3131             if ((height<14)); then value_text="${value_text::5}"; fi
 3132 
 3133             #* Print name of value and value amount in humanized base 2 bytes
 3134             print -v mem_out -m $y_pos $m_col -rs -fg $normal_color -jl 9 -t "${value_text^}:" -m $((y_pos++)) $((mem_line-10)) -jr 9 -t " ${type_name[${value}_string]::$((m_width-11))}"
 3135 
 3136             #* Create meter for value and calculate size and placement depending on terminal size
 3137             if ((height>v_height++ | tty_width>100)); then
 3138                 if ((height<=v_height & tty_width<150)); then
 3139                     meter_mod_w=12
 3140                     meter_mod_pos=7
 3141                     ((y_pos--))
 3142                 elif ((height<=v_height)); then
 3143                     print -v mem_out -m $((--y_pos)) $((m_col+5)) -jr 4 -t "${type_name[${value}_percent]}%"
 3144                     meter_mod_w=14
 3145                     meter_mod_pos=10
 3146                 fi
 3147                 create_meter -v ${type}_${value}_meter -w $((m_width-7-meter_mod_w)) -f -c color_${value}_graph ${type_name[${value}_percent]}
 3148 
 3149                 meter="${type}_${value}_meter"
 3150                 print -v mem_out -m $((y_pos++)) $((m_col+meter_mod_pos)) -t "${!meter}" -rs -fg $normal_color
 3151 
 3152                 if [[ -z $meter_mod_w ]]; then print -v mem_out  -jr 4 -t "${type_name[${value}_percent]}%"; fi
 3153             fi
 3154         #if [[ $system == "MacOS" && -z $swap_on ]] && ((height>14)); then ((y_pos++)); fi
 3155         done
 3156     done
 3157 
 3158 
 3159     #* Create text and meters for disks and adapt sizes based on available height
 3160     local disk_num disk_name disk_value v_height2 just_val name_len
 3161     y_pos=$m_line
 3162     m_col=$((m_col+m_width))
 3163     m_width=${box[m_width2]}
 3164     v_height=$((${#disks_name[@]}))
 3165     unset meter_mod_w meter_mod_pos
 3166 
 3167     for disk_name in "${disks_name[@]}"; do
 3168         if ((y_pos>m_line+height-2)); then break; fi
 3169 
 3170         #* Print folder disk is mounted on, total size in humanized base 2 bytes and io stats if enabled
 3171         print -v mem_out -m $((y_pos++)) $m_col -rs -fg ${theme[title]} -b -t "${disks_name[disk_num]::10}"
 3172         name_len=${#disks_name[disk_num]}; if ((name_len>10)); then name_len=10; fi
 3173         if [[ -n ${disks_io[disk_num]} && ${disks_io[disk_num]} != "0" ]] && ((m_width-11-name_len>6)); then
 3174             print -v mem_out -jc $((m_width-name_len-10)) -rs -fg ${theme[main_fg]} -t "${disks_io[disk_num]::$((m_width-10-name_len))}"
 3175             just_val=8
 3176         else
 3177             just_val=$((m_width-name_len-2))
 3178         fi
 3179         print -v mem_out -jr ${just_val} -fg ${theme[title]} -b -t "${disks_total[disk_num]::$((m_width-11))}"
 3180 
 3181         for value in "used" "free"; do
 3182             if ((height<v_height*3)) && [[ $value == "free" ]]; then break; fi
 3183             local -n disk_value="disks_${value}"
 3184 
 3185             #* Print name of value and value amount in humanized base 2 bytes
 3186             print -v mem_out -m $((y_pos++)) $m_col -rs -fg $normal_color -jl 9 -t "${value^}:" -jr $((m_width-11)) -t "${disk_value[disk_num]::$((m_width-11))}"
 3187 
 3188             #* Create meter for value and calculate size and placement depending on terminal size
 3189             if ((height>=v_height*5 | tty_width>100)); then
 3190                 local -n disk_value_percent="disks_${value}_percent"
 3191                 if ((height<=v_height*5 & tty_width<150)); then
 3192                     meter_mod_w=12
 3193                     meter_mod_pos=7
 3194                     ((y_pos--))
 3195                 elif ((height<=v_height*5)); then
 3196                     print -v mem_out -m $((--y_pos)) $((m_col+5)) -jr 4 -t "${disk_value_percent[disk_num]}%"
 3197                     meter_mod_w=14
 3198                     meter_mod_pos=10
 3199                 fi
 3200                 create_meter -v disk_${disk_num}_${value}_meter -w $((m_width-7-meter_mod_w)) -f -c color_${value}_graph ${disk_value_percent[disk_num]}
 3201 
 3202                 meter="disk_${disk_num}_${value}_meter"
 3203                 print -v mem_out -m $((y_pos++)) $((m_col+meter_mod_pos)) -t "${!meter}" -rs -fg $normal_color
 3204 
 3205                 if [[ -z $meter_mod_w ]]; then print -v mem_out -jr 4 -t "${disk_value_percent[disk_num]}%"; fi
 3206             fi
 3207             if ((y_pos>m_line+height-1)); then break; fi
 3208         done
 3209         if ((height>=v_height*4 & height<v_height*5 | height>=v_height*6)); then ((y_pos++)); fi
 3210         ((++disk_num))
 3211     done
 3212 
 3213     if ((resized>0)); then ((resized++)); fi
 3214     #* Print created text, graph and meters to output variable
 3215     draw_out+="${mem_graph[*]}${swap_graph[*]}${mem_out}"
 3216 
 3217 }
 3218 
 3219 draw_processes() { #? Draw processes and values to screen
 3220     local argument="$1"
 3221     if [[ -n $skip_process_draw && $argument != "now" ]]; then return; fi
 3222     local line=${box[processes_line]} col=${box[processes_col]} width=${box[processes_width]} height=${box[processes_height]} out_line y=1 fg_step_r=0 fg_step_g=0 fg_step_b=0 checker=2 page_string sel_string
 3223     local reverse_string reverse_pos order_left="───────────┤" filter_string current_num detail_location det_no_add com_fg pg_arrow_up_fg pg_arrow_down_fg p_height=$((height-3))
 3224     local pid=0 pid_graph pid_step_r pid_step_g pid_step_b pid_add_r pid_add_g pid_add_b bg_add bg_step proc_start up_fg down_fg page_up_fg page_down_fg this_box=processes
 3225     local d_width=${box[details_width]} d_height=${box[details_height]} d_line=${box[details_line]} d_col=${box[details_col]}
 3226     local detail_graph_width=$((d_width/3+2)) detail_graph_height=$((d_height-1)) kill_fg det_mod fg_add_r fg_add_g fg_add_b
 3227     local right_width=$((d_width-detail_graph_width-2))
 3228     local right_col=$((d_col+detail_graph_width+4))
 3229     local -a pid_rgb=(${theme[proc_misc]}) fg_rgb=(${theme[main_fg_dec]})
 3230     local pid_r=${pid_rgb[0]} pid_g=${pid_rgb[1]} pid_b=${pid_rgb[2]} fg_r=${fg_rgb[0]} fg_g=${fg_rgb[1]} fg_b=${fg_rgb[2]}
 3231 
 3232     if [[ $argument == "now" ]]; then skip_process_draw=1; fi
 3233 
 3234     if [[ $proc_gradient == true ]]; then
 3235         if ((fg_r+fg_g+fg_b<(255*3)/2)); then
 3236             fg_add_r="$(( (fg_r-255-((fg_r-255)/6) )/height))"
 3237             fg_add_g="$(( (fg_g-255-((fg_g-255)/6) )/height))"
 3238             fg_add_b="$(( (fg_b-255-((fg_b-255)/6) )/height))"
 3239 
 3240             pid_add_r="$(( (pid_r-255-((pid_r-255)/6) )/height))"
 3241             pid_add_g="$(( (pid_g-255-((pid_g-255)/6) )/height))"
 3242             pid_add_b="$(( (pid_b-255-((pid_b-255)/6) )/height))"
 3243         else
 3244             fg_add_r="$(( (fg_r-(fg_r/6) )/height))"
 3245             fg_add_g="$(( (fg_g-(fg_g/6) )/height))"
 3246             fg_add_b="$(( (fg_b-(fg_b/6) )/height))"
 3247 
 3248             pid_add_r="$(( (pid_r-(pid_r/6) )/height))"
 3249             pid_add_g="$(( (pid_g-(pid_g/6) )/height))"
 3250             pid_add_b="$(( (pid_b-(pid_b/6) )/height))"
 3251         fi
 3252     fi
 3253 
 3254     unset proc_out
 3255 
 3256     #* Details box
 3257     if ((proc[detailed_change]>0)) || ((proc[detailed]>0 & resized>0)); then
 3258         proc[detailed_change]=0
 3259         proc[order_change]=1
 3260         proc[page_change]=1
 3261         if ((proc[detailed]==1)); then
 3262             unset proc_det
 3263             local enter_fg enter_a_fg misc_fg misc_a_fg i det_y=6 dets cmd_y
 3264 
 3265             if [[ ${#detail_history[@]} -eq 1 ]] || ((resized>0)); then
 3266                 unset proc_det2
 3267                 create_graph -o detail_graph -d $((d_line+1)) $((d_col+1)) ${detail_graph_height} ${detail_graph_width} -c color_cpu_graph -n detail_history
 3268                 if ((tty_width>120)); then create_mini_graph -o detail_mem_graph -w $((right_width/3-3)) -nc detail_mem_history; fi
 3269                 det_no_add=1
 3270 
 3271                 for detail_location in "${d_line}" "$((d_line+d_height))"; do
 3272                     print -v proc_det2 -m ${detail_location} $((d_col+1)) -rs -fg ${box[processes_color]} -rp $((d_width-2)) -t "─"
 3273                 done
 3274                 for((i=1;i<d_height;i++)); do
 3275                     print -v proc_det2 -m $((d_line+i)) $((d_col+3+detail_graph_width)) -rp $((right_width-1)) -t " "
 3276                     print -v proc_det2 -m $((d_line+i)) ${d_col} -fg ${box[processes_color]} -t "│" -r $((detail_graph_width+1)) -fg ${theme[div_line]} -t "│" -r $((right_width+1)) -fg ${box[processes_color]} -t "│"
 3277                 done
 3278 
 3279                 print -v proc_det2 -m ${d_line} ${d_col} -t "┌" -m ${d_line} $((d_col+d_width-1)) -t "┐"
 3280                 print -v proc_det2 -m ${d_line} $((d_col+2+detail_graph_width)) -t "┬" -m $((d_line+d_height)) $((d_col+detail_graph_width+2)) -t "┴"
 3281                 print -v proc_det2 -m $((d_line+d_height)) ${d_col} -t "├" -r 1 -t "┤" -fg ${theme[title]} -b -t "${this_box}" -rs -fg ${box[processes_color]} -t "├" -r $((d_width-5-${#this_box})) -t "┤"
 3282                 print -v proc_det2 -m ${d_line} $((d_col+2)) -t "┤" -fg ${theme[title]} -b -t "${proc[detailed_name],,}" -rs -fg ${box[processes_color]} -t "├"
 3283                 if ((tty_width>128)); then print -v proc_det2 -r 1 -t "┤" -fg ${theme[title]} -b -t "${proc[detailed_pid]}" -rs -fg ${box[processes_color]} -t "├"; fi
 3284 
 3285 
 3286 
 3287                 if ((${#proc[detailed_cmd]}>(right_width-6)*2)); then ((det_y--)); dets=2
 3288                 elif ((${#proc[detailed_cmd]}>right_width-6)); then dets=1; fi
 3289 
 3290                 print -v proc_det2 -fg ${theme[title]} -b
 3291                 for i in C M D; do
 3292                     print -v proc_det2 -m $((d_line+5+cmd_y++)) $right_col -t "$i"
 3293                 done
 3294 
 3295 
 3296                 print -v proc_det2 -m $((d_line+det_y++)) $((right_col+1)) -jc $((right_width-4)) -rs -fg ${theme[main_fg]} -t "${proc[detailed_cmd]::$((right_width-6))}"
 3297                 if ((dets>0)); then print -v proc_det2 -m $((d_line+det_y++)) $((right_col+2)) -jl $((right_width-6)) -t "${proc[detailed_cmd]:$((right_width-6)):$((right_width-6))}"; fi
 3298                 if ((dets>1)); then print -v proc_det2 -m $((d_line+det_y)) $((right_col+2)) -jl $((right_width-6)) -t "${proc[detailed_cmd]:$(( (right_width-6)*2 )):$((right_width-6))}"; fi
 3299 
 3300             fi
 3301 
 3302 
 3303             if ((proc[selected]>0)); then enter_fg="${theme[inactive_fg]}"; enter_a_fg="${theme[inactive_fg]}"; else enter_fg="${theme[title]}"; enter_a_fg="${theme[hi_fg]}"; fi
 3304             if [[ -n ${proc[detailed_killed]} ]]; then misc_fg="${theme[title]}"; misc_a_fg="${theme[hi_fg]}"
 3305             else misc_fg=$enter_fg; misc_a_fg=$enter_a_fg; fi
 3306             print -v proc_det -m ${d_line} $((d_col+d_width-11)) -fg ${box[processes_color]} -t "┤" -fg $enter_fg -b -t "close " -fg $enter_a_fg -t "↲" -rs -fg ${box[processes_color]} -t "├"
 3307             if ((tty_width<129)); then det_mod="-8"; fi
 3308 
 3309             print -v proc_det -m ${d_line} $((d_col+detail_graph_width+4+det_mod)) -t "┤" -fg $misc_a_fg -b -t "t" -fg $misc_fg -t "erminate" -rs -fg ${box[processes_color]} -t "├"
 3310             print -v proc_det -r 1 -t "┤" -fg $misc_a_fg -b -t "k" -fg $misc_fg -t "ill" -rs -fg ${box[processes_color]} -t "├"
 3311             if ((tty_width>104)); then print -v proc_det -r 1 -t "┤" -fg $misc_a_fg -b -t "i" -fg $misc_fg -t "nterrupt" -rs -fg ${box[processes_color]} -t "├"; fi
 3312 
 3313 
 3314             proc_det="${proc_det2}${proc_det}"
 3315             proc_out="${proc_det}"
 3316 
 3317         elif ((resized==0)); then
 3318             unset proc_det
 3319             create_box -v proc_out -col ${box[${this_box}_col]} -line ${box[${this_box}_line]} -width ${box[${this_box}_width]} -height ${box[${this_box}_height]} -fill -lc "${box[${this_box}_color]}" -title ${this_box}
 3320         fi
 3321     fi
 3322 
 3323     if [[ ${proc[detailed]} -eq 1 ]]; then
 3324         local det_status status_color det_columns=3
 3325         if ((tty_width>140)); then ((det_columns++)); fi
 3326         if ((tty_width>150)); then ((det_columns++)); fi
 3327         if [[ -z $det_no_add && $1 != "now" && -z ${proc[detailed_killed]} ]]; then
 3328             create_graph -add-last detail_graph detail_history
 3329             if ((tty_width>120)); then create_mini_graph -w $((right_width/3-3)) -nc -add-last detail_mem_graph detail_mem_history; fi
 3330         fi
 3331 
 3332         print -v proc_out -fg ${theme[title]} -b
 3333         cmd_y=0
 3334         for i in C P U; do
 3335             print -v proc_out -m $((d_line+3+cmd_y++)) $((d_col+1)) -t "$i"
 3336         done
 3337         print -v proc_out -m $((d_line+1)) $((d_col+1)) -fg ${theme[title]} -t "${proc[detailed_cpu]}%"
 3338 
 3339         if [[ -n ${proc[detailed_killed]} ]]; then det_status="stopped"; status_color="${theme[inactive_fg]}"
 3340         else det_status="running"; status_color="${theme[proc_misc]}"; fi
 3341         print -v proc_out -m $((d_line+1)) ${right_col} -fg ${theme[title]} -b -jc $((right_width/det_columns-1)) -t "Status:" -jc $((right_width/det_columns)) -t "Elapsed:" -jc $((right_width/det_columns)) -t "Parent:"
 3342         if ((det_columns>=4)); then print -v proc_out -jc $((right_width/det_columns-1)) -t "User:"; fi
 3343         if ((det_columns>=5)); then print -v proc_out -jc $((right_width/det_columns-1)) -t "Threads:"; fi
 3344         print -v proc_out -m $((d_line+2)) ${right_col} -rs -fg ${status_color} -jc $((right_width/det_columns-1)) -t "${det_status}" -jc $((right_width/det_columns)) -fg ${theme[main_fg]} -t "${proc[detailed_runtime]::$((right_width/det_columns-1))}" -jc $((right_width/det_columns)) -t "${proc[detailed_parent_name]::$((right_width/det_columns-2))}"
 3345         if ((det_columns>=4)); then print -v proc_out -jc $((right_width/det_columns-1)) -t "${proc[detailed_user]::$((right_width/det_columns-2))}"; fi
 3346         if ((det_columns>=5)); then print -v proc_out -jc $((right_width/det_columns-1)) -t "${proc[detailed_threads]}"; fi
 3347 
 3348         print -v proc_out -m $((d_line+4)) ${right_col} -fg ${theme[title]} -b -jr $((right_width/3+2)) -t "Memory: ${proc[detailed_mem]}%" -t " "
 3349         if ((tty_width>120)); then print -v proc_out -rs -fg ${theme[inactive_fg]} -rp $((right_width/3-3)) "⡀" -l $((right_width/3-3)) -fg ${theme[proc_misc]} -t "${detail_mem_graph}" -t " "; fi
 3350         print -v proc_out -fg ${theme[title]} -b -t "${proc[detailed_mem_string]}"
 3351     fi
 3352 
 3353     #* Print processes
 3354     if ((${#proc_array[@]}<=p_height)); then
 3355         proc[start]=1
 3356     elif (( proc[start]>(${#proc_array[@]}-1)-p_height )); then
 3357         proc[start]=$(( (${#proc_array[@]}-1)-p_height ))
 3358     fi
 3359 
 3360     if ((proc[selected]>${#proc_array[@]}-1)); then proc[selected]=$((${#proc_array[@]}-1)); fi
 3361 
 3362     if [[ $proc_gradient == true ]] && ((proc[selected]>1)); then
 3363         fg_r="$(( fg_r-( fg_add_r*(proc[selected]-1) ) ))"
 3364         fg_g="$(( fg_g-( fg_add_g*(proc[selected]-1) ) ))"
 3365         fg_b="$(( fg_b-( fg_add_b*(proc[selected]-1) ) ))"
 3366 
 3367         pid_r="$(( pid_r-( pid_add_r*(proc[selected]-1) ) ))"
 3368         pid_g="$(( pid_g-( pid_add_g*(proc[selected]-1) ) ))"
 3369         pid_b="$(( pid_b-( pid_add_b*(proc[selected]-1) ) ))"
 3370     fi
 3371 
 3372     current_num=1
 3373 
 3374     print -v proc_out -rs -m $((line+y++)) $((col+1)) -fg ${theme[title]} -b -t "${proc_array[0]::$((width-3))} " -rs
 3375 
 3376     local -a out_arr
 3377     for out_line in "${proc_array[@]:${proc[start]}}"; do
 3378 
 3379         if [[ $use_psutil == true ]]; then
 3380             out_arr=(${out_line})
 3381             pi=0
 3382             if [[ $proc_tree == true ]]; then
 3383                 while [[ ! ${out_arr[pi]} =~ ^[0-9]+$ ]]; do ((++pi)); done
 3384             fi
 3385             pid="${out_arr[pi]}"
 3386 
 3387         else
 3388             pid="${out_line::$((proc[pid_len]+1))}"; pid="${pid// /}"
 3389             out_line="${out_line//'\'/'\\'}"
 3390             out_line="${out_line//'$'/'\$'}"
 3391             out_line="${out_line//'"'/'\"'}"
 3392         fi
 3393 
 3394         pid_graph="pid_${pid}_graph"
 3395 
 3396         if ((current_num==proc[selected])); then print -v proc_out -bg ${theme[selected_bg]} -fg ${theme[selected_fg]} -b; proc[selected_pid]="$pid"
 3397         else print -v proc_out -rs -fg $((fg_r-fg_step_r)) $((fg_g-fg_step_g)) $((fg_b-fg_step_b)); fi
 3398 
 3399         print -v proc_out -m $((line+y)) $((col+1)) -t "${out_line::$((width-3))} "
 3400 
 3401         if ((current_num==proc[selected])); then print -v proc_out -rs -bg ${theme[selected_bg]}; fi
 3402 
 3403         print -v proc_out -m $((line+y)) $((col+width-12)) -fg ${theme[inactive_fg]} -t "⡀⡀⡀⡀⡀"
 3404 
 3405         if [[ -n ${!pid_graph} ]]; then
 3406             print -v proc_out -m $((line+y)) $((col+width-12)) -fg $((pid_r-pid_step_r)) $((pid_g-pid_step_g)) $((pid_b-pid_step_b)) -t "${!pid_graph}"
 3407         fi
 3408 
 3409         ((y++))
 3410         ((current_num++))
 3411         if ((y>height-2)); then break; fi
 3412         if [[ $proc_gradient == false ]]; then :
 3413         elif ((current_num<proc[selected]+1)); then
 3414             fg_step_r=$((fg_step_r-fg_add_r)); fg_step_g=$((fg_step_g-fg_add_g)); fg_step_b=$((fg_step_b-fg_add_b))
 3415             pid_step_r=$((pid_step_r-pid_add_r)); pid_step_g=$((pid_step_g-pid_add_g)); pid_step_b=$((pid_step_b-pid_add_b))
 3416         elif ((current_num>=proc[selected])); then
 3417             fg_step_r=$((fg_step_r+fg_add_r)); fg_step_g=$((fg_step_g+fg_add_g)); fg_step_b=$((fg_step_b+fg_add_b))
 3418             pid_step_r=$((pid_step_r+pid_add_r)); pid_step_g=$((pid_step_g+pid_add_g)); pid_step_b=$((pid_step_b+pid_add_b))
 3419         fi
 3420 
 3421     done
 3422         print -v proc_out -rs
 3423         while ((y<=height-2)); do
 3424             print -v proc_out -m $((line+y++)) $((col+1)) -rp $((width-2)) -t " "
 3425         done
 3426 
 3427         if ((proc[selected]>0)); then sel_string=$((proc[start]-1+proc[selected])); else sel_string=0; fi
 3428         page_string="${sel_string}/$((${#proc_array[@]}-2${filter:++1}))"
 3429         print -v proc_out -m $((line+height-1)) $((col+width-20)) -fg ${box[processes_color]} -rp 19 -t "─"
 3430         print -v proc_out -m $((line+height-1)) $((col+width-${#page_string}-4)) -fg ${box[processes_color]} -t "┤" -b -fg ${theme[title]} -t "$page_string" -rs -fg ${box[processes_color]} -t "├"
 3431 
 3432 
 3433     if ((proc[order_change]==1 | proc[filter_change]==1 | resized>0)); then
 3434         unset proc_misc
 3435         proc[order_change]=0
 3436         proc[filter_change]=0
 3437         proc[page_change]=1
 3438         print -v proc_misc -m $line $((col+13)) -fg ${box[processes_color]} -rp $((box[processes_width]-14)) -t "─" -rs
 3439 
 3440         if ((proc[detailed]==1)); then
 3441             print -v proc_misc -m $((d_line+d_height)) $((d_col+detail_graph_width+2)) -fg ${box[processes_color]} -t "┴" -rs
 3442         fi
 3443 
 3444         if ((tty_width>100)); then
 3445             reverse_string="-fg ${box[processes_color]} -t ┤ -fg ${theme[hi_fg]}${proc[reverse]:+ -ul} -b -t r -fg ${theme[title]} -t everse -rs -fg ${box[processes_color]} -t ├"
 3446             reverse_pos=9
 3447         fi
 3448         print -v proc_misc -m $line $((col+width-${#proc_sorting}-14-reverse_pos)) -rs\
 3449         ${reverse_string}\
 3450         -fg ${box[processes_color]} -t-fg ${theme[title]}${proc[tree]:+ -ul} -b -t "tre" -fg ${theme[hi_fg]} -t "e" -rs -fg ${box[processes_color]} -t ├\
 3451         -fg ${box[processes_color]} -t "┤" -fg ${theme[hi_fg]} -b -t "‹" -fg ${theme[title]} -t " ${proc_sorting} "  -fg ${theme[hi_fg]} -t "›" -rs -fg ${box[processes_color]} -t "├"
 3452 
 3453         if [[ -z $filter && -z $input_to_filter ]]; then
 3454             print -v proc_misc -m $line $((col+14)) -fg ${box[processes_color]} -t "┤" -fg ${theme[hi_fg]} -b -t "f" -fg ${theme[title]} -t "ilter" -rs -fg ${box[processes_color]} -t "├"
 3455         elif [[ -n $input_to_filter ]]; then
 3456             if [[ ${#filter} -le $((width-35-reverse_pos)) ]]; then filter_string="${filter}"
 3457             elif [[ ${#filter} -gt $((width-35-reverse_pos)) ]]; then filter_string="${filter: (-$((width-35-reverse_pos)))}"
 3458             fi
 3459             print -v proc_misc -m $line $((col+14)) -fg ${box[processes_color]} -t "┤" -fg ${theme[title]} -b -t "${filter_string}" -fg ${theme[proc_misc]} -bl -t "█" -rs -fg ${box[processes_color]} -t "├"
 3460         elif [[ -n $filter ]]; then
 3461             if [[ ${#filter} -le $((width-35-reverse_pos-4)) ]]; then filter_string="${filter}"
 3462             elif [[ ${#filter} -gt $((width-35-reverse_pos-4)) ]]; then filter_string="${filter::$((width-35-reverse_pos-4))}"
 3463             fi
 3464             print -v proc_misc -m $line $((col+14)) -fg ${box[processes_color]} -t "┤" -fg ${theme[hi_fg]} -b -t "f" -fg ${theme[title]} -t " ${filter_string} " -fg ${theme[hi_fg]} -t "c" -rs -fg ${box[processes_color]} -t "├"
 3465         fi
 3466 
 3467         proc_out+="${proc_misc}"
 3468     fi
 3469 
 3470     if ((proc[page_change]==1 | resized>0)); then
 3471         unset proc_misc2
 3472         proc[page_change]=0
 3473         if ((proc[selected]>0)); then kill_fg="${theme[hi_fg]}"; com_fg="${theme[title]}"; else kill_fg="${theme[inactive_fg]}"; com_fg="${theme[inactive_fg]}"; fi
 3474         if ((proc[selected]==(${#proc_array[@]}-1${filter:++1})-proc[start])); then down_fg="${theme[inactive_fg]}"; else down_fg="${theme[hi_fg]}"; fi
 3475         if ((proc[selected]>0 | proc[start]>1)); then up_fg="${theme[hi_fg]}"; else up_fg="${theme[inactive_fg]}"; fi
 3476 
 3477         print -v proc_misc2 -m $((line+height-1)) $((col+2)) -fg ${box[processes_color]} -t "┤" -fg $up_fg -b -t "↑" -fg ${theme[title]} -t " select " -fg $down_fg -t "↓" -rs -fg ${box[processes_color]} -t "├"
 3478         print -v proc_misc2 -r 1 -fg ${box[processes_color]} -t "┤" -fg $com_fg -b -t "info " -fg $kill_fg "↲" -rs -fg ${box[processes_color]} -t "├"
 3479         if ((tty_width>100)); then print -v proc_misc2 -r 1 -t "┤" -fg $kill_fg -b -t "t" -fg $com_fg -t "erminate" -rs -fg ${box[processes_color]} -t "├"; fi
 3480         if ((tty_width>111)); then print -v proc_misc2 -r 1 -t "┤" -fg $kill_fg -b -t "k" -fg $com_fg -t "ill" -rs -fg ${box[processes_color]} -t "├"; fi
 3481         if ((tty_width>126)); then print -v proc_misc2 -r 1 -t "┤" -fg $kill_fg -b -t "i" -fg $com_fg -t "nterrupt" -rs -fg ${box[processes_color]} -t "├"; fi
 3482 
 3483         proc_out+="${proc_misc2}"
 3484     fi
 3485 
 3486     proc_out="${detail_graph[*]}${proc_out}"
 3487 
 3488     if ((resized>0)); then ((resized++)); fi
 3489 
 3490     if [[ $argument == "now" ]]; then
 3491         echo -en "${proc_out}"
 3492     fi
 3493 
 3494 }
 3495 
 3496 draw_net() { #? Draw net information and graphs to screen
 3497     local net_out argument=$1
 3498     if [[ -n ${net[no_device]} ]]; then return; fi
 3499     if [[ -n $skip_net_draw && $argument != "now" ]]; then return; fi
 3500     if [[ $argument == "now" ]]; then skip_net_draw=1; fi
 3501 
 3502     #* Get variables from previous calculations
 3503     local col=$((box[net_col]+1)) line=$((box[net_line]+1)) width=$((box[net_width]-2)) height=$((box[net_height]-2))
 3504     local n_width=${box[n_width]} n_height=${box[n_height]} n_col=${box[n_col]} n_line=${box[n_line]} main_fg="${theme[main_fg]}"
 3505 
 3506     #* If resized recreate net meter box and net graphs
 3507     if ((resized>0)); then
 3508         local graph_a_size graph_b_size
 3509         graph_a_size=$(( (height)/2 )); graph_b_size=${graph_a_size}
 3510         if ((graph_a_size*2<height)); then ((graph_a_size++)); fi
 3511         net[graph_a_size]=$graph_a_size
 3512         net[graph_b_size]=$graph_b_size
 3513         net[download_redraw]=0
 3514         net[upload_redraw]=0
 3515         ((resized++))
 3516     fi
 3517 
 3518     #* Update graphs if graph resolution update is needed or just resized, otherwise just add new values
 3519     if ((net[download_redraw]==1 | net[nic_change]==1 | resized>0)); then
 3520         create_graph -o download_graph -d $line $col ${net[graph_a_size]} $((width-n_width-2)) -c color_download_graph -n -max "${net[download_graph_max]}" net_history_download
 3521     else
 3522         create_graph -max "${net[download_graph_max]}" -add-last download_graph net_history_download
 3523     fi
 3524     if ((net[upload_redraw]==1 | net[nic_change]==1 | resized>0)); then
 3525         create_graph -o upload_graph -d $((line+net[graph_a_size])) $col ${net[graph_b_size]} $((width-n_width-2)) -c color_upload_graph -i -n -max "${net[upload_graph_max]}" net_history_upload
 3526     else
 3527         create_graph -max "${net[upload_graph_max]}" -i -add-last upload_graph net_history_upload
 3528     fi
 3529 
 3530     if ((net[nic_change]==1 | resized>0)); then
 3531         local dev_len=${#net[device]}
 3532         if ((dev_len>15)); then dev_len=15; fi
 3533         unset net_misc 'net[nic_change]'
 3534         print -v net_out -m $((line-1)) $((width-23)) -rs -fg ${box[net_color]} -rp 23 -t "─"
 3535         print -v net_misc -m $((line-1)) $((width-7-dev_len)) -rs -fg ${box[net_color]} -t "┤" -fg ${theme[hi_fg]} -b -t "‹b " -fg ${theme[title]} -t "${net[device]::15}" -fg ${theme[hi_fg]} -t " n›" -rs -fg ${box[net_color]} -t "├"
 3536         net_out+="${net_misc}"
 3537     fi
 3538 
 3539     #* Create text depening on box height
 3540     local ypos=$n_line
 3541 
 3542     print -v net_out -fg ${main_fg} -m $((ypos++)) $n_col -jl 10 -t "▼ Byte:" -jr 12 -t "${net[speed_download_byteps]}"
 3543     if ((height>4)); then print -v net_out -fg ${main_fg} -m $((ypos++)) $n_col -jl 10 -t "▼ Bit:" -jr 12 -t "${net[speed_download_bitps]}"; fi
 3544     if ((height>6)); then print -v net_out -fg ${main_fg} -m $((ypos++)) $n_col -jl 10 -t "▼ Total:" -jr 12 -t "${net[total_download]}"; fi
 3545 
 3546     if ((height>8)); then ((ypos++)); fi
 3547     print -v net_out -fg ${main_fg} -m $((ypos++)) $n_col -jl 10 -t "▲ Byte:" -jr 12 -t "${net[speed_upload_byteps]}"
 3548     if ((height>7)); then print -v net_out -fg ${main_fg} -m $((ypos++)) $n_col -jl 10 -t "▲ Bit:" -jr 12 -t "${net[speed_upload_bitps]}"; fi
 3549     if ((height>5)); then print -v net_out -fg ${main_fg} -m $((ypos++)) $n_col -jl 10 -t "▲ Total:" -jr 12 -t "${net[total_upload]}"; fi
 3550 
 3551     print -v net_out -fg ${theme[inactive_fg]} -m $line $col -t "${net[download_max_string]}"
 3552     print -v net_out -fg ${theme[inactive_fg]} -m $((line+height-1)) $col -t "${net[upload_max_string]}"
 3553 
 3554 
 3555     #* Print graphs and text to output variable
 3556     draw_out+="${download_graph[*]}${upload_graph[*]}${net_out}"
 3557     if [[ $argument == "now" ]]; then echo -en "${download_graph[*]}${upload_graph[*]}${net_out}"; fi
 3558 }
 3559 
 3560 draw_clock() { #? Draw a clock at top of screen
 3561     if [[ -z $draw_clock ]]; then return; fi
 3562     if [[ $resized -gt 0 && $resized -lt 5 ]]; then unset clock_out; return; fi
 3563     local width=${box[cpu_width]} color=${box[cpu_color]} old_time_string="${time_string}"
 3564     #time_string="$(date ${draw_clock})"
 3565     printf -v time_string "%(${draw_clock})T"
 3566     if [[ $old_time_string != "$time_string" || -z $clock_out ]]; then
 3567         unset clock_out
 3568         print -v clock_out -m 1 $((width/2-${#time_string}/2)) -rs -fg ${color} -t "┤" -fg ${theme[title]} -b -t "${time_string}" -fg ${color} -t "├"
 3569     fi
 3570     if [[ $1 == "now" ]]; then echo -en "${clock_out}"; fi
 3571 }
 3572 
 3573 draw_update_string() {
 3574     unset update_string
 3575     print -v update_string -m ${box[cpu_line]} $((box[cpu_col]+box[cpu_width]-${#update_ms}-14)) -rs -fg ${box[cpu_color]} -t "────┤"  -fg ${theme[hi_fg]} -b -t "+" -fg ${theme[title]} -b -t " ${update_ms}ms "  -fg ${theme[hi_fg]} -b -t "-" -rs -fg ${box[cpu_color]} -t "├"
 3576     if [[ $1 == "quiet" ]]; then draw_out+="${update_string}"
 3577     else echo -en "${update_string}"; fi
 3578 }
 3579 
 3580 pause_() { #? Pause input and draw a darkened version of main ui
 3581     local pause_out ext_var
 3582     if [[ -n $1 && $1 != "off" ]]; then local -n pause_out=${1}; ext_var=1; fi
 3583     if [[ $1 != "off" ]]; then
 3584         prev_screen="${boxes_out}${proc_det}${last_screen}${net_misc}${mem_out}${detail_graph[*]}${proc_out}${proc_misc}${proc_misc2}${update_string}${clock_out}"
 3585         if [[ -n $skip_process_draw ]]; then
 3586             prev_screen+="${proc_out}"
 3587             unset skip_process_draw proc_out
 3588         fi
 3589 
 3590         unset pause_screen
 3591         print -v pause_screen -rs -b -fg ${theme[inactive_fg]}
 3592         pause_screen+="${theme[main_bg]}m$(${sed} -E 's/\\e\[[0-9;\-]*m//g' <<< "${prev_screen}")\e[0m" #\e[1;38;5;236
 3593 
 3594         if [[ -z $ext_var ]]; then echo -en "${pause_screen}"
 3595         else pause_out="${pause_screen}"; fi
 3596 
 3597     elif [[ $1 == "off" ]]; then
 3598         echo -en "${prev_screen}"
 3599         unset pause_screen prev_screen
 3600     fi
 3601 }
 3602 
 3603 unpause_() { #? Unpause
 3604     pause_ off
 3605 }
 3606 
 3607 menu_() { #? Shows the main menu overlay
 3608     local menu i count keypress selected_int=0 selected up local_rez d_banner=1 menu_out bannerd skipped menu_pause out_out wait_string trans
 3609     local -a menus=("options" "help" "quit") color
 3610     unset bannerd menu_out
 3611     until false; do
 3612 
 3613         #* Put program to sleep if caught ctrl-z
 3614         if ((sleepy==1)); then sleep_; fi
 3615 
 3616         if [[ $background_update == true || -z $menu_out ]]; then
 3617             draw_clock
 3618             pause_ menu_pause
 3619         else
 3620             unset menu_pause
 3621         fi
 3622 
 3623         unset draw_out
 3624 
 3625         if [[ -z ${bannerd} ]]; then
 3626             draw_banner "$((tty_height/2-10))" bannerd
 3627             unset d_banner
 3628         fi
 3629         if [[ -n ${keypress} || -z ${menu_out} ]]; then
 3630             unset menu_out
 3631             print -v menu_out -t "${bannerd}"
 3632             print -v menu_out -d 1 -rs
 3633             selected="${menus[selected_int]}"
 3634             unset up
 3635             if [[ -n ${theme[main_bg_dec]} ]] && ((${theme[main_bg_dec]// /*}>255**3/2)); then print -v menu_out -bg "#00"; unset trans; else trans=" -trans"; fi
 3636             for menu in "${menus[@]}"; do
 3637                 if [[ $menu == "$selected" ]]; then
 3638                     local -n menu_array="menu_${menu}_selected"
 3639                     color=("#c55e5e" "#c23d3d" "#a13030" "#8c2626")
 3640                 else
 3641                     local -n menu_array="menu_${menu}"
 3642                     color=("#bb" "#aa" "#99" "#88")
 3643                 fi
 3644                 up=$((up+${#menu_array[@]}))
 3645                 for((i=0;i<${#menu_array[@]};i++)); do
 3646                     print -v menu_out -d 1 -fg ${color[i]} -c${trans} -t "${menu_array[i]}"
 3647                 done
 3648             done
 3649             print -v menu_out -rs -u ${up}
 3650         fi
 3651         unset out_out
 3652         out_out="${menu_pause}${menu_out}"
 3653         echo -e "${out_out}"
 3654 
 3655 
 3656         get_ms timestamp_end
 3657         time_left=$((timestamp_start+update_ms-timestamp_end))
 3658 
 3659         if ((time_left>1000)); then wait_string=10; time_left=$((time_left-1000))
 3660         elif ((time_left>100)); then wait_string=$((time_left/100)); time_left=0
 3661         else wait_string="0"; time_left=0; fi
 3662 
 3663         get_key -v keypress -w ${wait_string}
 3664         if [[ $(${stty} size) != "$tty_height $tty_width" ]]; then resized; fi
 3665         if ((resized>0)); then
 3666             calc_sizes; draw_bg quiet; time_left=0; unset menu_out
 3667             unset bannerd
 3668             echo -en "${clear_screen}"
 3669         fi
 3670 
 3671         case "$keypress" in
 3672             up|shift_tab) if ((selected_int>0)); then ((selected_int--)); else selected_int=$((${#menus[@]}-1)); fi ;;
 3673             down|tab) if ((selected_int<${#menus[@]}-1)); then ((++selected_int)); else selected_int=0; fi ;;
 3674             enter|space)
 3675                 case "$selected" in
 3676                     options) options_ ;;
 3677                     help) help_ ;;
 3678                     quit) quit_ ;;
 3679                 esac
 3680             ;;
 3681             m|M|escape|backspace) break ;;
 3682             q|Q) quit_ ;;
 3683         esac
 3684 
 3685         if ((time_left==0)) && [[ -z $keypress ]]; then get_ms timestamp_start; collect_and_draw; fi
 3686         if ((resized>=5)); then resized=0; fi
 3687 
 3688     done
 3689     unpause_
 3690 
 3691 }
 3692 
 3693 help_() { #? Shows the help overlay
 3694     local help_key from_menu col line y i help_out help_pause redraw=1 wait_string pages page=1 height
 3695     local -a shortcuts descriptions
 3696 
 3697     shortcuts=(
 3698         "(Esc, M, m)"
 3699         "(F2, O, o)"
 3700         "(F1, H, h)"
 3701         "(Ctrl-C, Q, q)"
 3702         "(+, A, a) (-, S, s)"
 3703         "(Up) (Down)"
 3704         "(Enter)"
 3705         "(Pg Up) (Pg Down)"
 3706         "(Home) (End)"
 3707         "(Left) (Right)"
 3708         "(b, B) (n, N)"
 3709         "(E, e)"
 3710         "(R, r)"
 3711         "(F, f)"
 3712         "(C, c)"
 3713         "Selected (T, t)"
 3714         "Selected (K, k)"
 3715         "Selected (I, i)"
 3716         " "
 3717         " "
 3718         " "
 3719     )
 3720     descriptions=(
 3721         "Shows main menu."
 3722         "Shows options."
 3723         "Shows this window."
 3724         "Quits program."
 3725         "Add/Subtract 100ms to/from update timer."
 3726         "Select in process list."
 3727         "Show detailed information for selected process."
 3728         "Jump 1 page in process list."
 3729         "Jump to first or last page in process list."
 3730         "Select previous/next sorting column."
 3731         "Select previous/next network device."
 3732         "Toggle processes tree view"
 3733         "Reverse sorting order in processes box."
 3734         "Input a string to filter processes with."
 3735         "Clear any entered filter."
 3736         "Terminate selected process with SIGTERM - 15."
 3737         "Kill selected process with SIGKILL - 9."
 3738         "Interrupt selected process with SIGINT - 2."
 3739         " "
 3740         "For bug reporting and project updates, visit:"
 3741         "\e[1mhttps://github.com/aristocratos/bashtop"
 3742     )
 3743 
 3744     if [[ -n $pause_screen ]]; then from_menu=1; fi
 3745 
 3746     until [[ -n $help_key ]]; do
 3747 
 3748         #* Put program to sleep if caught ctrl-z
 3749         if ((sleepy==1)); then sleep_; redraw=1; fi
 3750 
 3751         if [[ $background_update == true || -n $redraw ]]; then
 3752             draw_clock
 3753             pause_ help_pause
 3754         else
 3755             unset help_pause
 3756         fi
 3757 
 3758 
 3759         if [[ -n $redraw ]]; then
 3760             col=$((tty_width/2-36)); line=$((tty_height/2-4)); y=1; height=$((tty_height-2-line))
 3761             if ((${#shortcuts[@]}>height)); then pages=$(( (${#shortcuts[@]}/height)+1 )); else height=${#shortcuts[@]}; unset pages; fi
 3762             unset redraw help_out
 3763             draw_banner "$((tty_height/2-11))" help_out
 3764             print -d 1
 3765             create_box -v help_out -w 72 -h $((height+3)) -l $((line++)) -c $((col++)) -fill -lc ${theme[div_line]} -title "help"
 3766 
 3767             if [[ -n $pages ]]; then
 3768                 print -v help_out -m $((line+height+1)) $((col+72-16)) -rs -fg ${theme[div_line]} -t "┤" -fg ${theme[title]} -b -t "pg" -fg ${theme[hi_fg]} -t "↑"\
 3769                 -fg ${theme[title]} -t " ${page}/${pages} " -fg ${theme[title]} -t "pg" -fg ${theme[hi_fg]} -t "↓" -rs -fg ${theme[div_line]} -t "├"
 3770             fi
 3771             ((++col))
 3772 
 3773             print -v help_out -m $line $col -fg ${theme[title]} -b -jl 20 -t "Key:" -jl 48 -t "Description:" -m $((line+y++)) $col
 3774 
 3775             for((i=(page-1)*height;i<page*height;i++)); do
 3776                 print -v help_out -fg ${theme[main_fg]} -b -jl 20 -t "${shortcuts[i]}" -rs -fg ${theme[main_fg]} -jl 48 -t "${descriptions[i]}" -m $((line+y++)) $col
 3777             done
 3778         fi
 3779 
 3780 
 3781         unset draw_out
 3782         echo -en "${help_pause}${help_out}"
 3783 
 3784         get_ms timestamp_end
 3785         time_left=$((timestamp_start+update_ms-timestamp_end))
 3786 
 3787         if ((time_left>1000)); then wait_string=10; time_left=$((time_left-1000))
 3788         elif ((time_left>100)); then wait_string=$((time_left/100)); time_left=0
 3789         else wait_string="0"; time_left=0; fi
 3790 
 3791         get_key -v help_key -w "${wait_string}"
 3792 
 3793         if [[ -n $pages ]]; then
 3794             case $help_key in
 3795                 down|page_down) if ((page<pages)); then ((page++)); else page=1; fi; redraw=1; unset help_key ;;
 3796                 up|page_up) if ((page>1)); then ((page--)); else page=${pages}; fi; redraw=1; unset help_key ;;
 3797             esac
 3798         fi
 3799 
 3800         if [[ $(${stty} size) != "$tty_height $tty_width" ]]; then resized; fi
 3801         if ((resized>0)); then
 3802             ${sleep} 0.5
 3803             calc_sizes; draw_bg quiet; redraw=1
 3804             d_banner=1
 3805             unset bannerd menu_out
 3806         fi
 3807         if ((time_left==0)); then get_ms timestamp_start; collect_and_draw; fi
 3808         if ((resized>0)); then resized=0; fi
 3809     done
 3810 
 3811     if [[ -n $from_menu ]]; then pause_
 3812     else unpause_; fi
 3813 }
 3814 
 3815 options_() { #? Shows the options overlay
 3816     local keypress from_menu col line y=1 i=1 options_out selected_int=0 ypos option_string options_misc option_value bg fg skipped start_t end_t left_t changed_cpu_name theme_int=0 page=1 pages height
 3817     local desc_col right left enter lr inp valid updated_ms local_rez redraw_misc=1 desc_pos desc_height options_pause updated_proc inputting inputting_value inputting_key file theme_check net_totals_reset
 3818 
 3819     if ((net[reset]==1)); then net_totals_reset="On"; else net_totals_reset="Off"; fi
 3820 
 3821     #* Check theme folder for theme files
 3822     get_themes
 3823 
 3824     desc_color_theme=(  "Set bashtop color theme."
 3825                         " "
 3826                         "Choose between theme files located in"
 3827                         "\"\$HOME/.config/bashtop/themes\" &"
 3828                         "\"\$HOME/.config/bashtop/user_themes"
 3829                         " "
 3830                         "User themes are prefixed with \"*\"."
 3831                         "\"Default\" for builtin default."
 3832                         " ")
 3833     if [[ -z $curled ]]; then desc_color_theme+=("Get more themes at:"
 3834                         "https://github.com/aristocratos/bashtop")
 3835     else desc_color_theme+=("\e[1mPress ENTER to download the default themes."
 3836                             "Will overwrite changes made to the default"
 3837                             "themes if not copied to user_themes folder."); fi
 3838 
 3839     desc_update_ms=(    "Update time in milliseconds."
 3840                         "Recommended 2000 ms or above for better sample"
 3841                         "times for graphs."
 3842                         " "
 3843                         "Increases automatically if set below internal"
 3844                         "loops processing time."
 3845                         " "
 3846                         "Max value: 86400000 ms = 24 hours.")
 3847     desc_use_psutil=(   "Enable the use of psutil python3 module for"
 3848                         "data collection. Default on non Linux."
 3849                         ""
 3850                         "Program will automatically restart if changing"
 3851                         "this setting to check for compatibility."
 3852                         " "
 3853                         "True or false."
 3854                         " "
 3855                         "Can only be switched off when on Linux.")
 3856     desc_proc_sorting=( "Processes sorting."
 3857                         "Valid values are \"pid\", \"program\", \"arguments\","
 3858                         "\"threads\", \"user\", \"memory\", \"cpu lazy\""
 3859                         "\"cpu responsive\" and \"tree\"."
 3860                         " "
 3861                         "\"cpu lazy\" shows cpu usage over the lifetime"
 3862                         "of a process."
 3863                         " "
 3864                         "\"cpu responsive\" updates sorting directly at a"
 3865                         "cost of cpu time (unless using psutil)."
 3866                         " "
 3867                         "\"tree\" shows a tree structure of running"
 3868                         "processes. (not available with psutil)")
 3869     desc_proc_tree=(    "Processes tree view."
 3870                         " "
 3871                         "Set true to show processes grouped by parents,"
 3872                         "with lines drawn between parent and child"
 3873                         "process."
 3874                         " "
 3875                         "True or false.")
 3876     desc_check_temp=(   "Check cpu temperature."
 3877                         " "
 3878                         "True or false."
 3879                         " "
 3880                         "Only works if sensors, vcgencmd or osx-cpu-temp"
 3881                         "commands is available.")
 3882     desc_draw_clock=(   "Draw a clock at top of screen."
 3883                         " "
 3884                         "Formatting according to strftime, empty"
 3885                         "string to disable."
 3886                         " "
 3887                         "\"%X\" locale HH:MM:SS"
 3888                         "\"%H\" 24h hour, \"%I\" 12h hour"
 3889                         "\"%M\" minute, \"%S\" second"
 3890                         "\"%d\" day, \"%m\" month, \"%y\" year")
 3891     desc_background_update=( "Update main ui when menus are showing."
 3892                             " "
 3893                             "True or false."
 3894                             " "
 3895                             "Set this to false if the menus is flickering"
 3896                             "too much for a comfortable experience.")
 3897     desc_custom_cpu_name=(  "Custom cpu model name in cpu percentage box."
 3898                             " "
 3899                             "Empty string to disable.")
 3900     desc_error_logging=("Enable error logging to"
 3901                         "\"\$HOME/.config/bashtop/error.log\""
 3902                         " "
 3903                         "Program will be automatically restarted if"
 3904                         "changing this option."
 3905                         " "
 3906                         "True or false.")
 3907     desc_proc_reversed=("Reverse sorting order."
 3908                         " "
 3909                         "True or false.")
 3910     desc_proc_gradient=("Show color gradient in process list."
 3911                         " "
 3912                         "True or False.")
 3913     desc_disks_filter=("Optional filter for shown disks."
 3914                         " "
 3915                         "Should be names of mountpoints."
 3916                         "\"root\" replaces \"/\""
 3917                         " "
 3918                         "Separate multiple values with space."
 3919                         "Example: \"root home external\"")
 3920     desc_net_totals_reset=("Press ENTER to toggle network upload"
 3921                             "and download totals reset."
 3922                             " "
 3923                             "Shows totals since system start or"
 3924                             "network adapter reset when Off.")
 3925     desc_proc_per_core=("Process usage per core."
 3926                         " "
 3927                         "If process cpu usage should be of the core"
 3928                         "it's running on or usage of the total"
 3929                         "available cpu power."
 3930                         ""
 3931                         "If true and process is multithreaded"
 3932                         "cpu usage can reach over 100%.")
 3933     desc_update_check=( "Check for updates."
 3934                         " "
 3935                         "Enable check for new version from"
 3936                         "github.com/aristocratos/bashtop at start."
 3937                         " "
 3938                         "True or False.")
 3939     desc_hires_graphs=("Enable high resolution graphs."
 3940                         " "
 3941                         "Doubles the horizontal resolution of all"
 3942                         "graphs. At a cpu usage cost."
 3943                         "Needs restart to take effect."
 3944                         " "
 3945                         "True or False.")
 3946 
 3947     if [[ -n $pause_screen ]]; then from_menu=1; fi
 3948 
 3949     until false; do
 3950 
 3951         #* Put program to sleep if caught ctrl-z
 3952         if ((sleepy==1)); then sleep_; fi
 3953 
 3954 
 3955         if [[ $background_update == true || -n $redraw_misc ]]; then
 3956             draw_clock
 3957             if [[ -z $inputting ]]; then pause_ options_pause; fi
 3958         else
 3959             unset options_pause
 3960         fi
 3961 
 3962         if [[ -n $redraw_misc ]]; then
 3963             unset options_misc redraw_misc
 3964             col=$((tty_width/2-39))
 3965             line=$((tty_height/2-4))
 3966             height=$(( (tty_height-2-line)/2 ))
 3967             if ((${#options_array[@]}>height)); then pages=$(( (${#options_array[@]}/height)+1 )); else height=${#options_array[@]}; unset pages; fi
 3968             desc_col=$((col+30))
 3969             draw_banner "$((tty_height/2-11))" options_misc
 3970             create_box -v options_misc -w 29 -h $((height*2+2)) -l $line -c $((col-1)) -fill -lc ${theme[div_line]} -title "options"
 3971             if [[ -n $pages ]]; then
 3972                 print -v options_misc -m $((line+height*2+1)) $((col+29-16)) -rs -fg ${theme[div_line]} -t "┤" -fg ${theme[title]} -b -t "pg" -fg ${theme[hi_fg]} -t "↑"\
 3973                 -fg ${theme[title]} -t " ${page}/${pages} " -fg ${theme[title]} -t "pg" -fg ${theme[hi_fg]} -t "↓" -rs -fg ${theme[div_line]} -t "├"
 3974             fi
 3975         fi
 3976 
 3977         if [[ -n $keypress || -z $options_out ]]; then
 3978             unset options_out desc_height lr inp valid
 3979             selected="${options_array[selected_int]}"
 3980             local -n selected_desc="desc_${selected}"
 3981             if [[ $background_update == false ]]; then desc_pos=$line; desc_height=$((height*2+2))
 3982             elif (( (selected_int-( (page-1)*height) )*2+${#selected_desc[@]}<height*2 )); then desc_pos=$((line+(selected_int-( (page-1)*height) )*2))
 3983             else desc_pos=$((line+height*2-${#selected_desc[@]})); fi
 3984             create_box -v options_out -w 50 -h ${desc_height:-$((${#selected_desc[@]}+2))} -l $desc_pos -c $((desc_col-1)) -fill -lc ${theme[div_line]} -title "description"
 3985             for((i=(page-1)*height,ypos=1;i<page*height;i++,ypos=ypos+2)); do
 3986                 if [[ -z ${options_array[i]} ]]; then break; fi
 3987                 option_string="${options_array[i]}"
 3988                 if [[ -n $inputting && ${option_string} == "${selected}" ]]; then
 3989                     if [[ ${#inputting_value} -gt 14 ]]; then option_value="${inputting_value:(-14)}_"
 3990                     else option_value="${inputting_value}_"; fi
 3991                 else
 3992                     option_value="${!option_string}"
 3993                 fi
 3994 
 3995                 if [[ ${option_string} == "${selected}" ]]; then
 3996                     if is_int "$option_value" || [[ $selected == "color_theme" && -n $curled ]]; then
 3997                         enter="↲"; inp=1
 3998                     fi
 3999                     if is_int "$option_value" || [[ $option_value =~ true|false || $selected =~ proc_sorting|color_theme ]] && [[ -z $inputting ]]; then
 4000                         left="←"; right="→"; lr=1
 4001                     else
 4002                         enter="↲"; inp=1
 4003                     fi
 4004                     bg=" -bg ${theme[selected_bg]}"
 4005                     fg="${theme[selected_fg]}"
 4006                 fi
 4007                 option_string="${option_string//_/ }:"
 4008                 if [[ $option_string == "proc sorting:" ]]; then
 4009                     option_string+=" $((proc[sorting_int]+1))/${#sorting[@]}"
 4010                 elif [[ $option_string == "color theme:" ]]; then
 4011                     option_string+=" $((theme_int+1))/${#themes[@]}"
 4012                     if [[ ${option_value::12} == "user_themes/" ]]; then option_value="*${option_value#*/}"
 4013                     else option_value="${option_value#*/}"; fi
 4014                 fi
 4015                 print -v options_out -m $((line+ypos)) $((col+1)) -rs -fg ${fg:-${theme[title]}}${bg} -b -jc 25 -t "${option_string^}"
 4016                 print -v options_out -m $((line+ypos+1)) $((col+1)) -rs -fg ${fg:-${theme[main_fg]}}${bg} -jc 25 -t "${enter:+ } ${left} \"${option_value::15}\" ${right} ${enter}"
 4017                 unset right left enter bg fg
 4018             done
 4019 
 4020             for((i=0,ypos=1;i<${#selected_desc[@]};i++,ypos++)); do
 4021                 print -v options_out -m $((desc_pos+ypos)) $((desc_col+1)) -rs -fg ${theme[main_fg]} -jl 46 -t "${selected_desc[i]}"
 4022             done
 4023         fi
 4024 
 4025         echo -en "${options_pause}${options_misc}${options_out}"
 4026         unset draw_out keypress
 4027 
 4028         if [[ -n $theme_check ]]; then
 4029             local -a theme_index
 4030             local git_theme new_themes=0 down_themes=0 new_theme
 4031             unset 'theme_index[@]' 'desc_color_theme[-1]' 'desc_color_theme[-1]' 'desc_color_theme[-1]' options_out
 4032             theme_index=($(curl -m 3 --raw https://raw.githubusercontent.com/aristocratos/bashtop/master/themes/index.txt 2>/dev/null))
 4033             if [[ ${theme_index[*]} =~ .theme ]]; then
 4034                 for git_theme in ${theme_index[@]}; do
 4035                     unset new_theme
 4036                     if [[ ! -e "${config_dir}/themes/${git_theme}" ]]; then new_theme=1; fi
 4037                     if curl -m 3 --raw "https://raw.githubusercontent.com/aristocratos/bashtop/master/themes/${git_theme}" >"${config_dir}/themes/${git_theme}" 2>/dev/null; then
 4038                         ((++down_themes))
 4039                         if [[ -n $new_theme ]]; then
 4040                             ((++new_themes))
 4041                             themes+=("themes/${git_theme%.theme}")
 4042                         fi
 4043                     fi
 4044                 done
 4045                 desc_color_theme+=("Downloaded ${down_themes} theme(s).")
 4046                 desc_color_theme+=("Found ${new_themes} new theme(s)!")
 4047             else
 4048                 desc_color_theme+=("ERROR: Couldn't get theme index!")
 4049             fi
 4050         fi
 4051 
 4052 
 4053         get_ms timestamp_end
 4054         if [[ -z $theme_check ]]; then time_left=$((timestamp_start+update_ms-timestamp_end))
 4055         else unset theme_check; time_left=0; fi
 4056 
 4057         if ((time_left>500)); then wait_string=5; time_left=$((time_left-500))
 4058         elif ((time_left>100)); then wait_string=$((time_left/100)); time_left=0
 4059         else wait_string="0"; time_left=0; fi
 4060 
 4061         get_key -v keypress -w ${wait_string}
 4062 
 4063         if [[ -n $inputting ]]; then
 4064             case "$keypress" in
 4065                 escape) unset inputting inputting_value ;;
 4066                 enter|backspace) valid=1 ;;
 4067                 *) if [[ ${#keypress} -eq 1 ]]; then valid=1; fi ;;
 4068             esac
 4069         else
 4070             case "$keypress" in
 4071                 escape|q|backspace) break 1 ;;
 4072                 down|tab) if ((selected_int<${#options_array[@]}-1)); then ((++selected_int)); else selected_int=0; fi ;;
 4073                 up|shift_tab) if ((selected_int>0)); then ((selected_int--)); else selected_int=$((${#options_array[@]}-1)); fi ;;
 4074                 left|right) if [[ -n $lr && -z $inputting ]]; then valid=1; fi ;;
 4075                 enter) if [[ -n $inp ]]; then valid=1; fi ;;
 4076                 page_down) if ((page<pages)); then ((page++)); else page=1; selected_int=0; fi; redraw_misc=1; selected_int=$(( (page-1)*height )) ;;
 4077                 page_up) if ((page>1)); then ((page--)); else page=${pages}; fi; redraw_misc=1; selected_int=$(( (page-1)*height )) ;;
 4078             esac
 4079             if (( selected_int<(page-1)*height | selected_int>=page*height )); then page=$(( (selected_int/height)+1 )); redraw_misc=1; fi
 4080         fi
 4081 
 4082         if [[ ${selected} == "color_theme" && ${keypress} =~ left|right && ${#themes} -lt 2 ]]; then unset valid; fi
 4083 
 4084         if [[ -n $valid ]]; then
 4085             case "${selected} ${keypress}" in
 4086                 "update_ms right")
 4087                         if ((update_ms<86399900)); then
 4088                             update_ms=$((update_ms+100))
 4089                             updated_ms=1
 4090                         fi
 4091                     ;;
 4092                 "update_ms left")
 4093                         if ((update_ms>100)); then
 4094                             update_ms=$((update_ms-100))
 4095                             updated_ms=1
 4096                         fi
 4097                     ;;
 4098                 "update_ms enter")
 4099                         if [[ -z $inputting ]]; then inputting=1; inputting_value="${update_ms}"
 4100                         else
 4101                             if ((inputting_value<86400000)); then update_ms="${inputting_value:-0}"; updated_ms=1; fi
 4102                             unset inputting inputting_value
 4103                         fi
 4104                     ;;
 4105                 "update_ms backspace"|"draw_clock backspace"|"custom_cpu_name backspace"|"disks_filter backspace")
 4106                         if [[ ${#inputting_value} -gt 0 ]]; then
 4107                             inputting_value="${inputting_value::-1}"
 4108