mirror of https://github.com/aristocratos/bashtop
5321 lines
202 KiB
Bash
Executable File
5321 lines
202 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# indent type=tab
|
||
# tab size=4
|
||
# shellcheck disable=SC2034 #Unused variables
|
||
# shellcheck disable=SC2068 #Double quote array warning
|
||
# shellcheck disable=SC2086 # Double quote warning
|
||
# shellcheck disable=SC2140 # Word form warning
|
||
# shellcheck disable=SC2162 #Read without -r
|
||
# shellcheck disable=SC2206 #Word split warning
|
||
# shellcheck disable=SC2178 #Array to string warning
|
||
# shellcheck disable=SC2102 #Ranges only match single
|
||
# shellcheck disable=SC2004 #arithmetic brackets warning
|
||
# shellcheck disable=SC2017 #arithmetic precision warning
|
||
# shellcheck disable=SC2207 #split array warning
|
||
# shellcheck disable=SC2154 #variable referenced but not assigned
|
||
# shellcheck disable=SC1003 #info: single quote escape
|
||
# shellcheck disable=SC2179 # array append warning
|
||
# shellcheck disable=SC2128 # expanding array without index warning
|
||
|
||
|
||
# Copyright 2020 Aristocratos (jakob@qvantnet.com)
|
||
|
||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||
# you may not use this file except in compliance with the License.
|
||
# You may obtain a copy of the License at
|
||
|
||
# http://www.apache.org/licenses/LICENSE-2.0
|
||
|
||
# Unless required by applicable law or agreed to in writing, software
|
||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
# See the License for the specific language governing permissions and
|
||
# limitations under the License.
|
||
|
||
declare -x LC_MESSAGES="C" LC_NUMERIC="C" LC_ALL=""
|
||
|
||
#* Fail if running on unsupported OS
|
||
case "$(uname -s)" in
|
||
Linux*) system=Linux;;
|
||
*BSD) system=BSD;;
|
||
Darwin*) system=MacOS;;
|
||
CYGWIN*) system=Cygwin;;
|
||
MINGW*) system=MinGw;;
|
||
*) system="Other"
|
||
esac
|
||
if [[ ! $system =~ Linux|MacOS|BSD ]]; then
|
||
echo "This version of bashtop does not support $system platform."
|
||
exit 1
|
||
fi
|
||
|
||
#* Fail if Bash version is below 4.4
|
||
bash_version_major=${BASH_VERSINFO[0]}
|
||
bash_version_minor=${BASH_VERSINFO[1]}
|
||
if [[ "$bash_version_major" -lt 4 ]] || [[ "$bash_version_major" == 4 && "$bash_version_minor" -lt 4 ]]; then
|
||
echo "ERROR: Bash 4.4 or later is required (you are using Bash $bash_version_major.$bash_version_minor)."
|
||
exit 1
|
||
fi
|
||
|
||
shopt -qu failglob nullglob
|
||
shopt -qs extglob globasciiranges globstar
|
||
|
||
#* Check for UTF-8 locale and set LANG variable if not set
|
||
if [[ ! $LANG =~ UTF-8 ]]; then
|
||
if [[ -n $LANG && ${LANG::1} != "C" ]]; then old_lang="${LANG%.*}"; fi
|
||
for set_lang in $(locale -a); do
|
||
if [[ $set_lang =~ utf8|UTF-8 ]]; then
|
||
if [[ -n $old_lang && $set_lang =~ ${old_lang} ]]; then
|
||
declare -x LANG="${set_lang/utf8/UTF-8}"
|
||
set_lang_search="found"
|
||
break
|
||
elif [[ -z $first_lang ]]; then
|
||
first_lang="${set_lang/utf8/UTF-8}"
|
||
set_lang_first="found"
|
||
fi
|
||
if [[ -z $old_lang ]]; then break; fi
|
||
fi
|
||
done
|
||
if [[ $set_lang_search != "found" && $set_lang_first != "found" ]]; then
|
||
echo "ERROR: No UTF-8 locale found!"
|
||
exit 1
|
||
elif [[ $set_lang_search != "found" ]]; then
|
||
declare -x LANG="${first_lang/utf8/UTF-8}"
|
||
fi
|
||
unset old_lang set_lang first_lang set_lang_search set_lang_first
|
||
fi
|
||
|
||
declare -a banner banner_colors
|
||
|
||
banner=(
|
||
"██████╗ █████╗ ███████╗██╗ ██╗████████╗ ██████╗ ██████╗ "
|
||
"██╔══██╗██╔══██╗██╔════╝██║ ██║╚══██╔══╝██╔═══██╗██╔══██╗"
|
||
"██████╔╝███████║███████╗███████║ ██║ ██║ ██║██████╔╝"
|
||
"██╔══██╗██╔══██║╚════██║██╔══██║ ██║ ██║ ██║██╔═══╝ "
|
||
"██████╔╝██║ ██║███████║██║ ██║ ██║ ╚██████╔╝██║ "
|
||
"╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ")
|
||
declare version="0.9.24"
|
||
|
||
#* Get latest version of BashTOP from https://github.com/aristocratos/bashtop
|
||
|
||
declare banner_width=${#banner[0]}
|
||
banner_colors=("#E62525" "#CD2121" "#B31D1D" "#9A1919" "#801414")
|
||
|
||
#* Set correct names for GNU tools depending on OS
|
||
if [[ $system != "Linux" ]]; then tool_prefix="g"; fi
|
||
for tool in "dd" "df" "stty" "tail" "realpath" "wc" "rm" "mv" "sleep" "stdbuf" "mkfifo" "date" "kill" "sed"; do
|
||
declare -n set_tool="${tool}"
|
||
set_tool="${tool_prefix}${tool}"
|
||
done
|
||
|
||
if ! command -v ${dd} >/dev/null 2>&1; then
|
||
echo "ERROR: Missing GNU coreutils!"
|
||
exit 1
|
||
elif ! command -v ${sed} >/dev/null 2>&1; then
|
||
echo "ERROR: Missing GNU sed!"
|
||
exit 1
|
||
fi
|
||
|
||
read tty_height tty_width < <(${stty} size)
|
||
|
||
#? Start default variables------------------------------------------------------------------------------>
|
||
#? These values are used to create "$HOME/.config/bashtop/bashtop.cfg"
|
||
#? Any changes made here will be ignored if config file exists
|
||
aaa_config() { : ; } #! Do not remove this line!
|
||
|
||
#* Color theme, looks for a .theme file in "$HOME/.config/bashtop/themes" and "$HOME/.config/bashtop/user_themes"
|
||
#* Should be prefixed with either "themes/" or "user_themes/" depending on location, "Default" for builtin default theme
|
||
color_theme="Default"
|
||
|
||
#* Update time in milliseconds, increases automatically if set below internal loops processing time, recommended 2000 ms or above for better sample times for graphs
|
||
update_ms="2500"
|
||
|
||
#* Processes sorting, "pid" "program" "arguments" "threads" "user" "memory" "cpu lazy" "cpu responsive"
|
||
#* "cpu lazy" updates sorting over time, "cpu responsive" updates sorting directly
|
||
proc_sorting="cpu lazy"
|
||
|
||
#* Reverse sorting order, "true" or "false"
|
||
proc_reversed="false"
|
||
|
||
#* Show processes as a tree
|
||
proc_tree="false"
|
||
|
||
#* Check cpu temperature, only works if "sensors", "vcgencmd" or "osx-cpu-temp" commands is available
|
||
check_temp="true"
|
||
|
||
#* Draw a clock at top of screen, formatting according to strftime, empty string to disable
|
||
draw_clock="%X"
|
||
|
||
#* Update main ui when menus are showing, set this to false if the menus is flickering too much for comfort
|
||
background_update="true"
|
||
|
||
#* Custom cpu model name, empty string to disable
|
||
custom_cpu_name=""
|
||
|
||
#* Enable error logging to "$HOME/.config/bashtop/error.log", "true" or "false"
|
||
error_logging="true"
|
||
|
||
#* Show color gradient in process list, "true" or "false"
|
||
proc_gradient="true"
|
||
|
||
#* If process cpu usage should be of the core it's running on or usage of the total available cpu power
|
||
proc_per_core="false"
|
||
|
||
#* Optional filter for shown disks, should be names of mountpoints, "root" replaces "/", separate multiple values with space
|
||
disks_filter=""
|
||
|
||
#* Enable check for new version from github.com/aristocratos/bashtop at start
|
||
update_check="true"
|
||
|
||
#* Enable graphs with double the horizontal resolution, increases cpu usage
|
||
hires_graphs="false"
|
||
|
||
#* Enable the use of psutil python3 module for data collection, default on OSX
|
||
use_psutil="true"
|
||
|
||
aaz_config() { : ; } #! Do not remove this line!
|
||
#? End default variables-------------------------------------------------------------------------------->
|
||
|
||
declare -a menu_options menu_help menu_quit
|
||
|
||
menu_options=(
|
||
"┌─┐┌─┐┌┬┐┬┌─┐┌┐┌┌─┐"
|
||
"│ │├─┘ │ ││ ││││└─┐"
|
||
"└─┘┴ ┴ ┴└─┘┘└┘└─┘")
|
||
menu_help=(
|
||
"┬ ┬┌─┐┬ ┌─┐"
|
||
"├─┤├┤ │ ├─┘"
|
||
"┴ ┴└─┘┴─┘┴ ")
|
||
menu_quit=(
|
||
"┌─┐ ┬ ┬ ┬┌┬┐"
|
||
"│─┼┐│ │ │ │ "
|
||
"└─┘└└─┘ ┴ ┴ ")
|
||
|
||
menu_options_selected=(
|
||
"╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗"
|
||
"║ ║╠═╝ ║ ║║ ║║║║╚═╗"
|
||
"╚═╝╩ ╩ ╩╚═╝╝╚╝╚═╝")
|
||
menu_help_selected=(
|
||
"╦ ╦╔═╗╦ ╔═╗"
|
||
"╠═╣║╣ ║ ╠═╝"
|
||
"╩ ╩╚═╝╩═╝╩ ")
|
||
menu_quit_selected=(
|
||
"╔═╗ ╦ ╦ ╦╔╦╗ "
|
||
"║═╬╗║ ║ ║ ║ "
|
||
"╚═╝╚╚═╝ ╩ ╩ ")
|
||
|
||
declare -A cpu mem swap proc net box theme disks
|
||
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
|
||
declare -a mem_history swap_history net_history_download net_history_upload mem_graph swap_graph proc_array download_graph upload_graph trace_array
|
||
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
|
||
declare -a options_array=("color_theme" "update_ms" "use_psutil" "proc_sorting" "proc_tree" "check_temp" "draw_clock" "background_update" "custom_cpu_name"
|
||
"proc_per_core" "proc_reversed" "proc_gradient" "disks_filter" "hires_graphs" "net_totals_reset" "update_check" "error_logging")
|
||
declare -a save_array=(${options_array[*]/net_totals_reset/})
|
||
declare -a sorting=( "pid" "program" "arguments" "threads" "user" "memory" "cpu lazy" "cpu responsive")
|
||
declare -a detail_graph detail_history detail_mem_history disks_io
|
||
declare -A pid_history
|
||
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
|
||
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
|
||
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
|
||
declare psutil_disk_fail
|
||
declare -a disks_free disks_total disks_name disks_free_percent saved_key themes nic_list old_procs
|
||
printf -v esc_character "\u1b"
|
||
printf -v tab "\u09"
|
||
printf -v backspace "\u7F" #? Backspace set to DELETE
|
||
printf -v backspace_real "\u08" #? Real backspace
|
||
#printf -v enter_key "\uA"
|
||
printf -v enter_key "\uD"
|
||
printf -v ctrl_c "\u03"
|
||
printf -v ctrl_z "\u1A"
|
||
|
||
hide_cursor='\033[?25l' #* Hide terminal cursor
|
||
show_cursor='\033[?25h' #* Show terminal cursor
|
||
alt_screen='\033[?1049h' #* Switch to alternate screen
|
||
normal_screen='\033[?1049l' #* Switch to normal screen
|
||
clear_screen='\033[2J' #* Clear screen
|
||
|
||
#* Symbols for graphs
|
||
declare -a graph_symbol
|
||
graph_symbol=(" " "⡀" "⣀" "⣄" "⣤" "⣦" "⣴" "⣶" "⣷" "⣾" "⣿")
|
||
graph_symbol+=( " " "⣿" "⢿" "⡿" "⠿" "⠻" "⠟" "⠛" "⠙" "⠉" "⠈")
|
||
declare -A graph_symbol_up='(
|
||
[0_0]=⠀ [0_1]=⢀ [0_2]=⢠ [0_3]=⢰ [0_4]=⢸
|
||
[1_0]=⡀ [1_1]=⣀ [1_2]=⣠ [1_3]=⣰ [1_4]=⣸
|
||
[2_0]=⡄ [2_1]=⣄ [2_2]=⣤ [2_3]=⣴ [2_4]=⣼
|
||
[3_0]=⡆ [3_1]=⣆ [3_2]=⣦ [3_3]=⣶ [3_4]=⣾
|
||
[4_0]=⡇ [4_1]=⣇ [4_2]=⣧ [4_3]=⣷ [4_4]=⣿
|
||
)'
|
||
declare -A graph_symbol_down='(
|
||
[0_0]=⠀ [0_1]=⠈ [0_2]=⠘ [0_3]=⠸ [0_4]=⢸
|
||
[1_0]=⠁ [1_1]=⠉ [1_2]=⠙ [1_3]=⠹ [1_4]=⢹
|
||
[2_0]=⠃ [2_1]=⠋ [2_2]=⠛ [2_3]=⠻ [2_4]=⢻
|
||
[3_0]=⠇ [3_1]=⠏ [3_2]=⠟ [3_3]=⠿ [3_4]=⢿
|
||
[4_0]=⡇ [4_1]=⡏ [4_2]=⡟ [4_3]=⡿ [4_4]=⣿
|
||
)'
|
||
declare -A graph
|
||
box[boxes]="cpu mem net processes"
|
||
|
||
cpu[threads]=0
|
||
|
||
#* Symbols for subscript function
|
||
subscript=("₀" "₁" "₂" "₃" "₄" "₅" "₆" "₇" "₈" "₉")
|
||
|
||
#* Symbols for create_box function
|
||
box[single_hor_line]="─"
|
||
box[single_vert_line]="│"
|
||
box[single_left_corner_up]="┌"
|
||
box[single_right_corner_up]="┐"
|
||
box[single_left_corner_down]="└"
|
||
box[single_right_corner_down]="┘"
|
||
box[single_title_left]="├"
|
||
box[single_title_right]="┤"
|
||
|
||
box[double_hor_line]="═"
|
||
box[double_vert_line]="║"
|
||
box[double_left_corner_up]="╔"
|
||
box[double_right_corner_up]="╗"
|
||
box[double_left_corner_down]="╚"
|
||
box[double_right_corner_down]="╝"
|
||
box[double_title_left]="╟"
|
||
box[double_title_right]="╢"
|
||
|
||
init_() { #? Collect needed information and set options before startig main loop
|
||
if [[ -z $1 ]]; then
|
||
local i stx=0
|
||
#* Set terminal options, save and clear screen
|
||
saved_stty="$(${stty} -g)"
|
||
echo -en "${alt_screen}${hide_cursor}${clear_screen}"
|
||
echo -en "\033]0;${TERMINAL_TITLE} BashTOP\a"
|
||
${stty} -echo
|
||
|
||
#* Wait for resize if terminal size is smaller then 80x24
|
||
if (($tty_width<80 | $tty_height<24)); then resized; echo -en "${clear_screen}"; fi
|
||
|
||
#* Draw banner to banner array
|
||
local letter b_color banner_line y=0
|
||
local -a banner_out
|
||
#print -v banner_out[0] -t "\e[0m"
|
||
for banner_line in "${banner[@]}"; do
|
||
#* Read banner array letter by letter to set correct color for filled vs outline characters
|
||
while read -rN1 letter; do
|
||
if [[ $letter == "█" ]]; then b_color="${banner_colors[$y]}"
|
||
else b_color="#$((80-y*6))"; fi
|
||
if [[ $letter == " " ]]; then
|
||
print -v banner_out[y] -r 1
|
||
else
|
||
print -v banner_out[y] -fg ${b_color} "${letter}"
|
||
fi
|
||
done <<<"$banner_line"
|
||
((++y))
|
||
done
|
||
banner=("${banner_out[@]}")
|
||
|
||
#* Draw banner to screen and show status while running init
|
||
draw_banner $((tty_height/2-10))
|
||
|
||
#* Start psutil coprocess if enabled
|
||
if [[ $use_psutil == true ]]; then
|
||
print -m $(( (tty_height/2-3)+stx++ )) 0 -bg "#00" -fg "#cc" -b -c "Creating psutil coprocess..."
|
||
return
|
||
fi
|
||
fi
|
||
|
||
if [[ -n $1 ]]; then local i stx=1; print -bg "#00" -fg "#30ff50" -r 1 -t "√"; fi
|
||
|
||
#* Check if "sensors", "osx-cpu-temp" or "vcgencmd" commands is available, if not, disable temperature collection
|
||
print -m $(( (tty_height/2-3)+stx++ )) 0 -bg "#00" -fg "#cc" -b -c "Checking available tools..."
|
||
if [[ $check_temp == true ]]; then
|
||
local has_temp
|
||
sensor_comm=""
|
||
if [[ $use_psutil == true ]]; then
|
||
py_command -v has_temp "get_sensors_check()"
|
||
if [[ $has_temp == true ]]; then sensor_comm="psutil"; fi
|
||
fi
|
||
if [[ -z $sensor_comm ]]; then
|
||
local checker
|
||
for checker in "vcgencmd" "sensors" "osx-cpu-temp"; do
|
||
if command -v "${checker}" >/dev/null 2>&1; then sensor_comm="${checker}"; break; fi
|
||
done
|
||
fi
|
||
if [[ -z $sensor_comm ]]; then check_temp="false"; fi
|
||
fi
|
||
|
||
#* Check if "curl" command is available, if not, disable update check and theme downloads
|
||
if command -v curl >/dev/null 2>&1; then curled=1; else unset curled; fi
|
||
|
||
#* Check if "notify-send" command is available, if not, disable update notifier
|
||
if [[ -n $curled ]] && command -v notify-send >/dev/null 2>&1; then notifier=1; else unset notifier; fi
|
||
|
||
#* Check if "iostat" command is available, if not, disable disk io stat collection
|
||
if command -v iostat >/dev/null 2>&1; then has_iostat=1; else unset has_iostat; fi
|
||
|
||
#* Get number of cores and cpu threads
|
||
print -bg "#00" -fg "#30ff50" -r 1 -t "√"
|
||
print -m $(( (tty_height/2-3)+stx++ )) 0 -bg "#00" -fg "#cc" -c -b "Checking cpu..."
|
||
get_cpu_info
|
||
|
||
#* Set graph resolution
|
||
graph[hires]="${hires_graphs}"
|
||
|
||
#* Get processor BCLK
|
||
local param_var
|
||
if [[ $use_psutil == false ]] && [[ -e /usr/include/asm-generic/param.h ]]; then
|
||
param_var="$(</usr/include/asm-generic/param.h)"
|
||
get_value -v 'cpu[hz]' -sv "param_var" -k "define HZ" -i
|
||
else
|
||
cpu[hz]="100"
|
||
fi
|
||
|
||
#* Get max pid value and length
|
||
if [[ $use_psutil == false ]]; then
|
||
proc[pid_max]="$(</proc/sys/kernel/pid_max)"
|
||
proc[pid_len]=${#proc[pid_max]}
|
||
if [[ ${proc[pid_len]} -lt 5 ]]; then proc[pid_len]=5; fi
|
||
else
|
||
proc[pid_len]="7"
|
||
fi
|
||
|
||
#* Calculate sizes of boxes
|
||
print -bg "#00" -fg "#30ff50" -r 1 -t "√"
|
||
print -m $(( (tty_height/2-3)+stx++ )) 0 -bg "#00" -fg "#cc" -c -b "Calculating sizes..."
|
||
calc_sizes
|
||
|
||
#* Call init for cpu data collection
|
||
print -bg "#00" -fg "#30ff50" -r 1 -t "√"
|
||
print -m $(( (tty_height/2-3)+stx++ )) 0 -bg "#00" -fg "#cc" -c -b "Running cpu collection init..."
|
||
collect_cpu init
|
||
|
||
#* Call init for memory data collection and check if swap is available
|
||
print -bg "#00" -fg "#30ff50" -r 1 -t "√"
|
||
print -m $(( (tty_height/2-3)+stx++ )) 0 -bg "#00" -fg "#cc" -c -b "Running mem collection init..."
|
||
mem[counter]=10
|
||
collect_mem init
|
||
|
||
#* Get default network device from "ip route" command and call init for net collection if device is found
|
||
print -bg "#00" -fg "#30ff50" -r 1 -t "√"
|
||
print -m $(( (tty_height/2-3)+stx++ )) 0 -bg "#00" -fg "#cc" -c -b "Checking network devices..."
|
||
get_net_device
|
||
|
||
#* Check if newer version of bashtop is available from https://github.com/aristocratos/bashtop
|
||
if [[ -n $curled && $update_check == "true" ]]; then
|
||
print -bg "#00" -fg "#30ff50" -r 1 -t "√"
|
||
print -m $(( (tty_height/2-3)+stx++ )) 0 -bg "#00" -fg "#cc" -c -b "Checking for updates..."
|
||
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
|
||
fi
|
||
|
||
#* Add update notification to banner if new version is available
|
||
local banner_out_up
|
||
print -v banner_out_up -rs -fg "#cc" -b "← esc"
|
||
if [[ -n $git_version && $git_version != "$version" ]]; then
|
||
print -v banner_out_up -rs -fg "#80cc80" -r 15 "[${git_version} available!]" -r $((9-${#git_version}))
|
||
if [[ -n $notifier ]]; then
|
||
notify-send -u normal\
|
||
"Bashtop Update!" "New version of Bashtop available\!\nCurrent version: ${version}\n\New version: ${git_version}\nDownload at github.com/aristocratos/bashtop"\
|
||
-i face-glasses -t 10000
|
||
fi
|
||
else
|
||
print -v banner_out_up -r 37
|
||
fi
|
||
print -v banner_out_up -fg "#cc" -i -b "Version: ${version}" -rs
|
||
banner+=("${banner_out_up}")
|
||
|
||
#* Get theme and set colors
|
||
print -bg "#00" -fg "#30ff50" -r 1 -t "√"
|
||
print -m $(( (tty_height/2-3)+stx++ )) 0 -bg "#00" -fg "#cc" -c -b "Generating colors for theme..."
|
||
color_init_
|
||
|
||
#* Set up internals for quick processes sorting switching
|
||
for((i=0;i<${#sorting[@]};i++)); do
|
||
if [[ ${sorting[i]} == "${proc_sorting}" ]]; then
|
||
proc[sorting_int]=$i
|
||
break
|
||
fi
|
||
done
|
||
if [[ -z ${proc[sorting_int]} ]]; then
|
||
proc[sorting_int]=0
|
||
proc_sorting="${sorting[0]}"
|
||
fi
|
||
|
||
if [[ ${proc_reversed} == true ]]; then
|
||
proc[reverse]="+"
|
||
else
|
||
unset 'proc[reverse]'
|
||
fi
|
||
|
||
if [[ ${proc_tree} == true ]]; then
|
||
proc[tree]="+"
|
||
else
|
||
unset 'proc[tree]'
|
||
fi
|
||
|
||
#* Call init for processes data collection
|
||
print -bg "#00" -fg "#30ff50" -r 1 -t "√"
|
||
print -m $(( (tty_height/2-3)+stx++ )) 0 -bg "#00" -fg "#cc" -c -b "Running process collection init..."
|
||
proc[selected]=0
|
||
proc[start]=1
|
||
collect_processes init
|
||
|
||
#* Draw first screen
|
||
print -bg "#00" -fg "#30ff50" -r 1 -t "√"
|
||
print -m $(( (tty_height/2-3)+stx++ )) 0 -bg "#00" -fg "#cc" -c -b "Drawing screen..."
|
||
|
||
draw_bg quiet
|
||
get_ms timestamp_start
|
||
|
||
for task in processes cpu mem net; do
|
||
collect_${task}
|
||
draw_${task}
|
||
done
|
||
last_screen="${draw_out}"
|
||
|
||
print -bg "#00" -fg "#30ff50" -r 1 -t "√" -rs
|
||
sleep 0.5
|
||
|
||
draw_clock
|
||
echo -en "${clear_screen}${draw_out}${proc_out}${clock_out}"
|
||
resized=0
|
||
unset draw_out
|
||
}
|
||
|
||
color_init_() { #? Check for theme file and set colors
|
||
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"
|
||
local temp_start="#4897d4" temp_mid="#5474e8" temp_end="#ff40b6" cpu_start="#50f095" cpu_mid="#f2e266" cpu_end="#fa1e1e" div_line="#30"
|
||
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"
|
||
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"
|
||
local hex2rgb color_name array_name this_color main_fg_dec sourced theme_unset
|
||
local -i i y
|
||
local -A rgb
|
||
local -a dec_test
|
||
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")
|
||
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")
|
||
|
||
for theme_unset in ${!theme[@]}; do
|
||
unset 'theme[${theme_unset}]'
|
||
done
|
||
|
||
#* Check if theme set in config exists and source it if it does
|
||
if [[ -n ${color_theme} && ${color_theme} != "Default" && ${color_theme} =~ (themes/)|(user_themes/) && -e "${config_dir}/${color_theme%.theme}.theme" ]]; then
|
||
# shellcheck source=/dev/null
|
||
source "${config_dir}/${color_theme%.theme}.theme"
|
||
sourced=1
|
||
else
|
||
color_theme="Default"
|
||
fi
|
||
|
||
main_fg_dec="${theme[main_fg]:-$main_fg}"
|
||
theme[main_fg_dec]="${main_fg_dec}"
|
||
|
||
#* Convert colors for graphs and meters from rgb hexadecimal to rgb decimal if needed
|
||
for color_name in ${convert_color[@]}; do
|
||
if [[ -n $sourced ]]; then hex2rgb="${theme[${color_name}]}"
|
||
else hex2rgb="${!color_name}"; fi
|
||
|
||
hex2rgb=${hex2rgb//#/}
|
||
|
||
if [[ ${#hex2rgb} == 6 ]] && is_hex "$hex2rgb"; then hex2rgb="$((${hex}${hex2rgb:0:2})) $((${hex}${hex2rgb:2:2})) $((${hex}${hex2rgb:4:2}))"
|
||
elif [[ ${#hex2rgb} == 2 ]] && is_hex "$hex2rgb"; then hex2rgb="$((${hex}${hex2rgb:0:2})) $((${hex}${hex2rgb:0:2})) $((${hex}${hex2rgb:0:2}))"
|
||
else
|
||
dec_test=(${hex2rgb})
|
||
if [[ ${#dec_test[@]} -eq 3 ]] && is_int "${dec_test[@]}"; then hex2rgb="${dec_test[*]}"
|
||
else unset hex2rgb; fi
|
||
fi
|
||
|
||
theme[${color_name}]="${hex2rgb}"
|
||
done
|
||
|
||
#* Set background color if set, otherwise use terminal default
|
||
if [[ -n ${theme[main_bg]} ]]; then theme[main_bg_dec]="${theme[main_bg]}"; theme[main_bg]=";48;2;${theme[main_bg]// /;}"; fi
|
||
|
||
#* Set colors from theme file if found and valid hexadecimal or integers, otherwise use default values
|
||
for color_name in "${set_color[@]}"; do
|
||
if [[ -z ${theme[$color_name]} ]] || ! is_hex "${theme[$color_name]}" && ! is_int "${theme[$color_name]}"; then theme[${color_name}]="${!color_name}"; fi
|
||
done
|
||
|
||
box[cpu_color]="${theme[cpu_box]}"
|
||
box[mem_color]="${theme[mem_box]}"
|
||
box[net_color]="${theme[net_box]}"
|
||
box[processes_color]="${theme[proc_box]}"
|
||
|
||
#* Create color arrays from one, two or three color gradient, 100 values in each
|
||
for array_name in "temp" "cpu" "upload" "download" "used" "available" "cached" "free"; do
|
||
local -n color_array="color_${array_name}_graph"
|
||
local -a rgb_start=(${theme[${array_name}_start]}) rgb_mid=(${theme[${array_name}_mid]}) rgb_end=(${theme[${array_name}_end]})
|
||
local pf_calc middle=1
|
||
|
||
rgb[red]=${rgb_start[0]}; rgb[green]=${rgb_start[1]}; rgb[blue]=${rgb_start[2]}
|
||
|
||
if [[ -z ${rgb_mid[*]} ]] && ((rgb_end[0]+rgb_end[1]+rgb_end[2]>rgb_start[0]+rgb_start[1]+rgb_start[2])); then
|
||
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) )) )
|
||
elif [[ -z ${rgb_mid[*]} ]]; then
|
||
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) )) )
|
||
fi
|
||
|
||
for((i=0;i<=100;i++,y=0)); do
|
||
|
||
if [[ -n ${rgb_end[*]} ]]; then
|
||
for this_color in "red" "green" "blue"; do
|
||
if ((i==50)); then rgb_start[y]=${rgb[$this_color]}; fi
|
||
|
||
if ((middle==1 & rgb[$this_color]<rgb_mid[y])); then
|
||
printf -v pf_calc "%.0f" "$(( i*( (rgb_mid[y]-rgb_start[y])*100/50*100) ))e-4"
|
||
|
||
elif ((middle==1 & rgb[$this_color]>rgb_mid[y])); then
|
||
printf -v pf_calc "%.0f" "-$(( i*( (rgb_start[y]-rgb_mid[y])*100/50*100) ))e-4"
|
||
|
||
elif ((middle==0 & rgb[$this_color]<rgb_end[y])); then
|
||
printf -v pf_calc "%.0f" "$(( (i-50)*( (rgb_end[y]-rgb_start[y])*100/50*100) ))e-4"
|
||
|
||
elif ((middle==0 & rgb[$this_color]>rgb_end[y])); then
|
||
printf -v pf_calc "%.0f" "-$(( (i-50)*( (rgb_start[y]-rgb_end[y])*100/50*100) ))e-4"
|
||
|
||
else
|
||
pf_calc=0
|
||
fi
|
||
|
||
rgb[$this_color]=$((rgb_start[y]+pf_calc))
|
||
if ((rgb[$this_color]<0)); then rgb[$this_color]=0
|
||
elif ((rgb[$this_color]>255)); then rgb[$this_color]=255; fi
|
||
|
||
y+=1
|
||
if ((i==49 & y==3 & middle==1)); then middle=0; fi
|
||
done
|
||
fi
|
||
color_array[i]="${rgb[red]} ${rgb[green]} ${rgb[blue]}"
|
||
done
|
||
|
||
done
|
||
}
|
||
|
||
quit_() { #? Clean exit
|
||
#* Restore terminal options and screen
|
||
if [[ $use_psutil == true && $2 != "psutil" ]]; then
|
||
py_command quit
|
||
sleep 0.1
|
||
rm -rf "${pytmpdir}"
|
||
fi
|
||
echo -en "${clear_screen}${normal_screen}${show_cursor}"
|
||
${stty} "${saved_stty}"
|
||
echo -en "\033]0;\a"
|
||
|
||
#* Save any changed values to config file
|
||
if [[ $config_file != "/dev/null" ]]; then
|
||
save_config "${save_array[@]}"
|
||
fi
|
||
|
||
if [[ $1 == "restart" ]]; then exec "$(${realpath} "$0")"; fi
|
||
|
||
exit ${1:-0}
|
||
}
|
||
|
||
sleep_() { #? Restore terminal options, stop and send to background if caught SIGTSTP (ctrl+z)
|
||
echo -en "${clear_screen}${normal_screen}${show_cursor}"
|
||
${stty} "${saved_stty}"
|
||
echo -en "\033]0;\a"
|
||
|
||
if [[ $use_psutil == true ]]; then
|
||
if ((failed_pipes>1)); then ((failed_pipes--)); fi
|
||
py_command quit
|
||
failed_pipe=1
|
||
wait ${pycoproc_PID}
|
||
fi
|
||
|
||
${kill} -s SIGSTOP $$
|
||
}
|
||
|
||
resume_() { #? Set terminal options and resume if caught SIGCONT ('fg' from terminal)
|
||
sleepy=0
|
||
echo -en "${alt_screen}${hide_cursor}${clear_screen}"
|
||
echo -en "\033]0;${TERMINAL_TITLE} BashTOP\a"
|
||
${stty} -echo
|
||
|
||
if [[ -n $pause_screen ]]; then
|
||
echo -en "$pause_screen"
|
||
else
|
||
echo -en "${boxes_out}${proc_det}${last_screen}${mem_out}${proc_misc}${proc_misc2}${update_string}${clock_out}"
|
||
fi
|
||
}
|
||
|
||
traperr() { #? Function for reporting error line numbers
|
||
local match len trap_muted err="${BASH_LINENO[0]}"
|
||
|
||
len=$((${#trace_array[@]}))
|
||
if ((len-->=1)); then
|
||
while ((len>=${#trace_array[@]}-2)); do
|
||
if [[ $err == "${trace_array[$((len--))]}" ]]; then ((++match)) ; fi
|
||
done
|
||
if ((match==2 & len != -2)); then return
|
||
elif ((match>=1)); then trap_muted="(MUTED!)"
|
||
fi
|
||
fi
|
||
if ((len>100)); then unset 'trace_array[@]'; fi
|
||
trace_array+=("$err")
|
||
printf "%(%X)T ERROR: On line %s %s\n" -1 "$err" "$trap_muted" >> "${config_dir}/error.log"
|
||
|
||
}
|
||
|
||
resized() { #? Get new terminal size if terminal is resized
|
||
resized=1
|
||
unset winches
|
||
while ((++winches<5)); do
|
||
read tty_height tty_width < <(${stty} size)
|
||
if (($tty_width<80 | $tty_height<24)); then
|
||
size_error_msg
|
||
winches=0
|
||
else
|
||
echo -en "${clear_screen}"
|
||
create_box -w 30 -h 3 -c 1 -l 1 -lc "#EE2020" -title "resizing"
|
||
print -jc 28 -fg ${theme[title]} "New size: ${tty_width}x${tty_height}"
|
||
${sleep} 0.2
|
||
if [[ $(${stty} size) != "$tty_height $tty_width" ]]; then winches=0; fi
|
||
fi
|
||
done
|
||
}
|
||
|
||
size_error_msg() { #? Shows error message if terminal size is below 80x25
|
||
local width=$tty_width
|
||
local height=$tty_height
|
||
echo -en "${clear_screen}"
|
||
create_box -full -lc "#EE2020" -title "resize window"
|
||
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
|
||
print -d 1 -fg ${theme[title]} -c -l 15 "Need to be atleast:" -bg "#00" -fg "#30dd50" -d 1 -c "80x24" -rs
|
||
while [[ $(${stty} size) == "$tty_height $tty_width" ]]; do ${sleep} 0.2; if [[ -n $quitting ]]; then quit_; fi ; done
|
||
}
|
||
|
||
draw_banner() { #? Draw banner, usage: draw_banner <line> [output variable]
|
||
local y letter b_color x_color xpos ypos=$1 banner_out
|
||
if [[ -n $2 ]]; then local -n banner_out=$2; fi
|
||
xpos=$(( (tty_width/2)-(banner_width/2) ))
|
||
|
||
for banner_line in "${banner[@]}"; do
|
||
print -v banner_out -rs -move $((ypos+++y)) $xpos -t "${banner_line}"
|
||
done
|
||
|
||
if [[ -z $2 ]]; then echo -en "${banner_out}"; fi
|
||
}
|
||
|
||
create_config() { #? Creates a new config file with default values from above
|
||
local c_line c_read this_file
|
||
this_file="$(${realpath} "$0")"
|
||
echo "#? Config file for bashtop v. ${version}" > "$config_file"
|
||
while IFS= read -r c_line; do
|
||
if [[ $c_line =~ aaz_config() ]]; then break
|
||
elif [[ $c_read == "1" ]]; then echo "$c_line" >> "$config_file"
|
||
elif [[ $c_line =~ aaa_config() ]]; then c_read=1; fi
|
||
done < "$this_file"
|
||
}
|
||
|
||
save_config() { #? Saves variables to config file if not same, usage: save_config "var1" ["var2"] ["var3"]...
|
||
if [[ -z $1 || $config_file == "/dev/null" ]]; then return; fi
|
||
local var tmp_conf tmp_value quote original new
|
||
tmp_conf="$(<"$config_file")"
|
||
for var in "$@"; do
|
||
if [[ ${tmp_conf} =~ ${var} ]]; then
|
||
get_value -v "tmp_value" -sv "tmp_conf" -k "${var}="
|
||
if [[ ${tmp_value//\"/} != "${!var}" ]]; then
|
||
original="${var}=${tmp_value}"
|
||
new="${var}=\"${!var}\""
|
||
original="${original//'/'/'\/'}"
|
||
new="${new//'/'/'\/'}"
|
||
${sed} -i "s/${original}/${new}/" "${config_file}"
|
||
fi
|
||
else
|
||
echo "${var}=\"${!var}\"" >> "$config_file"
|
||
fi
|
||
done
|
||
}
|
||
|
||
set_font() { #? Take a string and generate a string of unicode characters of given font, usage; set_font "font-name [bold] [italic]" "string"
|
||
local i letter letter_hex new_hex add_hex start font="$1" string_in="$2" string_out hex="16#"
|
||
if [[ -z $font || -z $string_in ]]; then return; fi
|
||
case "$font" in
|
||
"sans-serif") lower_start="1D5BA"; upper_start="1D5A0"; digit_start="1D7E2";;
|
||
"sans-serif bold") lower_start="1D5EE"; upper_start="1D5D4"; digit_start="1D7EC";;
|
||
"sans-serif italic") lower_start="1D622"; upper_start="1D608"; digit_start="1D7E2";;
|
||
#"sans-serif bold italic") start="1D656"; upper_start="1D63C"; digit_start="1D7EC";;
|
||
"script") lower_start="1D4B6"; upper_start="1D49C"; digit_start="1D7E2";;
|
||
"script bold") lower_start="1D4EA"; upper_start="1D4D0"; digit_start="1D7EC";;
|
||
"fraktur") lower_start="1D51E"; upper_start="1D504"; digit_start="1D7E2";;
|
||
"fraktur bold") lower_start="1D586"; upper_start="1D56C"; digit_start="1D7EC";;
|
||
"monospace") lower_start="1D68A"; upper_start="1D670"; digit_start="1D7F6";;
|
||
"double-struck") lower_start="1D552"; upper_start="1D538"; digit_start="1D7D8";;
|
||
*) echo -n "${string_in}"; return;;
|
||
esac
|
||
|
||
for((i=0;i<${#string_in};i++)); do
|
||
letter=${string_in:i:1}
|
||
if [[ $letter =~ [a-z] ]]; then #61
|
||
printf -v letter_hex '%X\n' "'$letter"
|
||
printf -v add_hex '%X' "$((${hex}${letter_hex}-${hex}61))"
|
||
printf -v new_hex '%X' "$((${hex}${lower_start}+${hex}${add_hex}))"
|
||
string_out="${string_out}\U${new_hex}"
|
||
#if [[ $font =~ sans-serif && $letter =~ m|w ]]; then string_out="${string_out} "; fi
|
||
#\U205F
|
||
elif [[ $letter =~ [A-Z] ]]; then #41
|
||
printf -v letter_hex '%X\n' "'$letter"
|
||
printf -v add_hex '%X' "$((${hex}${letter_hex}-${hex}41))"
|
||
printf -v new_hex '%X' "$((${hex}${upper_start}+${hex}${add_hex}))"
|
||
string_out="${string_out}\U${new_hex}"
|
||
#if [[ $font =~ sans-serif && $letter =~ M|W ]]; then string_out="${string_out} "; fi
|
||
elif [[ $letter =~ [0-9] ]]; then #30
|
||
printf -v letter_hex '%X\n' "'$letter"
|
||
printf -v add_hex '%X' "$((${hex}${letter_hex}-${hex}30))"
|
||
printf -v new_hex '%X' "$((${hex}${digit_start}+${hex}${add_hex}))"
|
||
string_out="${string_out}\U${new_hex}"
|
||
else
|
||
string_out="${string_out} \e[1D${letter}"
|
||
fi
|
||
done
|
||
|
||
echo -en "${string_out}"
|
||
}
|
||
|
||
sort_array_int() { #? Copy and sort an array of integers from largest to smallest value, usage: sort_array_int "input array" "output array"
|
||
#* Return if given array has no values
|
||
if [[ -z ${!1} ]]; then return; fi
|
||
local start_n search_n tmp_array
|
||
|
||
#* Create pointers to arrays
|
||
local -n in_arr="$1"
|
||
local -n out_arr="$2"
|
||
|
||
#* Create local copy of array
|
||
local array=("${in_arr[@]}")
|
||
|
||
#* Start sorting
|
||
for ((start_n=0;start_n<=${#array[@]}-1;++start_n)); do
|
||
for ((search_n=start_n+1;search_n<=${#array[@]}-1;++search_n)); do
|
||
if ((array[start_n]<array[search_n])); then
|
||
tmp_array=${array[start_n]}
|
||
array[start_n]=${array[search_n]}
|
||
array[search_n]=$tmp_array
|
||
fi
|
||
done
|
||
done
|
||
|
||
#* Write the sorted array to output array
|
||
out_arr=("${array[@]}")
|
||
}
|
||
|
||
subscript() { #? Convert an integer to a string of subscript numbers
|
||
local i out int=$1
|
||
for((i=0;i<${#int};i++)); do
|
||
out="${out}${subscript[${int:$i:1}]}"
|
||
done
|
||
echo -n "${out}"
|
||
}
|
||
|
||
spaces() { #? Prints back spaces, usage: spaces "number of spaces"
|
||
printf "%${1}s" ""
|
||
}
|
||
|
||
is_int() { #? Check if value(s) is integer
|
||
local param
|
||
for param; do
|
||
if [[ ! $param =~ ^[\-]?[0-9]+$ ]]; then return 1; fi
|
||
done
|
||
}
|
||
|
||
is_float() { #? Check if value(s) is floating point
|
||
local param
|
||
for param; do
|
||
if [[ ! $param =~ ^[\-]?[0-9]*[,.][0-9]+$ ]]; then return 1; fi
|
||
done
|
||
}
|
||
|
||
is_hex() { #? Check if value(s) is hexadecimal
|
||
local param
|
||
for param; do
|
||
if [[ ! ${param//#/} =~ ^[0-9a-fA-F]*$ ]]; then return 1; fi
|
||
done
|
||
}
|
||
|
||
floating_humanizer() { #? Convert integer to floating point and scale up in steps of 1024 to highest positive unit
|
||
#? Usage: floating_humanizer <-b,-bit|-B,-Byte> [-ps,-per-second] [-s,-start "1024 multiplier start"] [-v,-variable-output] <input>
|
||
local value selector per_second unit_mult decimals out_var ext_var short sep=" "
|
||
local -a unit
|
||
until (($#==0)); do
|
||
case "$1" in
|
||
-b|-bit) unit=(bit Kib Mib Gib Tib Pib); unit_mult=8;;
|
||
-B|-Byte) unit=(Byte KiB MiB GiB TiB PiB); unit_mult=1;;
|
||
-ps|-per-second) per_second=1;;
|
||
-short) short=1; sep="";;
|
||
-s|-start) selector="$2"; shift;;
|
||
-v|-variable-output) local -n out_var="$2"; ext_var=1; shift;;
|
||
*) if is_int "$1"; then value=$1; break; fi;;
|
||
esac
|
||
shift
|
||
done
|
||
|
||
if [[ -z $value || $value -lt 0 || -z $unit_mult ]]; then return; fi
|
||
|
||
if ((per_second==1 & unit_mult==1)); then per_second="/s"
|
||
elif ((per_second==1)); then per_second="ps"; fi
|
||
|
||
if ((value>0)); then
|
||
value=$((value*100*unit_mult))
|
||
|
||
until ((${#value}<6)); do
|
||
value=$((value>>10))
|
||
if ((value<100)); then value=100; fi
|
||
((++selector))
|
||
done
|
||
|
||
if ((${#value}<5 & ${#value}>=2 & selector>0)); then
|
||
decimals=$((5-${#value}))
|
||
value="${value::-2}.${value:(-${decimals})}"
|
||
elif ((${#value}>=2)); then
|
||
value="${value::-2}"
|
||
fi
|
||
fi
|
||
|
||
if [[ -n $short ]]; then value="${value%.*}"; fi
|
||
|
||
out_var="${value}${sep}${unit[$selector]::${short:-${#unit[$selector]}}}${per_second}"
|
||
if [[ -z $ext_var ]]; then echo -n "${out_var}"; fi
|
||
}
|
||
|
||
get_cpu_info() {
|
||
local lscpu_var pyin
|
||
if [[ $use_psutil == true ]]; then
|
||
if [[ -z ${cpu[threads]} || -z ${cpu[cores]} ]]; then
|
||
py_command -v pyin "get_cpu_cores()"
|
||
read cpu[cores] cpu[threads] <<<"${pyin}"
|
||
fi
|
||
|
||
else
|
||
if command -v lscpu >/dev/null 2>&1; then lscpu_var="$(lscpu)"; fi
|
||
if [[ -z ${cpu[threads]} || -z ${cpu[cores]} ]]; then
|
||
if ! get_value -v 'cpu[threads]' -sv "lscpu_var" -k "CPU(s):" -i || [[ ${cpu[threads]} == "0" ]]; then
|
||
cpu[threads]="$(nproc 2>/dev/null ||true)"
|
||
if [[ -z ${cpu[threads]} ]]; then cpu[threads]="1"; fi
|
||
cpu[cores]=${cpu[threads]}
|
||
else
|
||
get_value -v 'cpu[cores]' -sv "lscpu_var" -k "Core(s)" -i
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
if [[ $use_psutil == false && -z $custom_cpu_name ]]; then
|
||
if ! get_value -v 'cpu[model]' -sv "lscpu_var" -k "Model name:" -a -b -k "CPU" -mk -1; then
|
||
if ! get_value -v 'cpu[model]' -sv "lscpu_var" -k "Model name:" -r " "; then
|
||
cpu[model]="cpu"
|
||
fi
|
||
fi
|
||
elif [[ $use_psutil == true && -z $custom_cpu_name ]]; then
|
||
py_command -v cpu[model] "get_cpu_name()"
|
||
else
|
||
cpu[model]="${custom_cpu_name}"
|
||
fi
|
||
}
|
||
|
||
get_value() { #? Get a value from a file, variable or array by searching for a non spaced "key name" on the same line
|
||
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
|
||
local -a remove
|
||
until (($#==0)); do
|
||
until (($#==0)); do
|
||
case "$1" in
|
||
-k|-key) key="$2"; shift;; #? Key "string" on the same line as target value
|
||
-m|-match) match="$2"; shift;; #? If multiple matches on a line, match occurrence "x"
|
||
-mk|-match-key) match_key=$2; line_pos=0; shift;; #? Match in relation to key position, -1 for previous value, 1 for next value
|
||
-b|-break) shift; break;; #? Break up arguments for multiple searches
|
||
-a|-all) all=1;; #? Prints back found line including key
|
||
-l|-line) line_nr="$2"; shift;; #? Set target line if no key is available
|
||
-ss|-source-string) input="$2"; shift;; #? Argument string as source
|
||
-sf|-source-file) input="$(<"$2")"; shift;; #? File as source
|
||
-sv|-source-var) input="${!2}"; shift;; #? Variable as source
|
||
-sa|-source-array) local -n tmp_array=$2; input="${tmp_array[*]}"; shift;; #? Array as source
|
||
-fp|-floating-point) reg="[\-]?[0-9]*[.,][0-9]+"; match=1;; #? Match floating point value
|
||
-math) math="$2"; shift;; #? Perform math on a integer value, "x" represents value, only works if "integer" argument is given
|
||
-i|-integer) reg="[\-]?[0-9]+[.,]?[0-9]*"; int=1; match=1;; #? Match integer value or float and convert to int
|
||
-r|-remove) remove+=("$2"); shift;; #? Format output by removing entered regex, can be used multiple times
|
||
-v|-variable-out) local -n found="$2"; ext_var=1; shift;; #? Output to variable
|
||
-map|-map-array) local -n array_out="$2"; ext_var=1; ext_arr=1; shift;; #? Map output to array
|
||
esac
|
||
shift
|
||
done
|
||
|
||
found=""
|
||
|
||
if [[ -z $input ]]; then return 1; fi
|
||
if [[ -z $line_nr && -z $key ]]; then line_nr=1; fi
|
||
|
||
while IFS='' read -r input_line; do
|
||
((++current_line))
|
||
if [[ -n $line_nr && $current_line -eq $line_nr || -z $line_nr && -n $key && ${input_line/${key}/} != "$input_line" ]]; then
|
||
if [[ -n $all ]]; then
|
||
found="${input_line}"
|
||
break
|
||
|
||
elif [[ -z $match && -z $match_key && -z $reg ]]; then
|
||
found="${input_line/${key}/}"
|
||
break
|
||
|
||
else
|
||
line_array=(${input_line/${key}/${key// /}})
|
||
|
||
fi
|
||
|
||
for line_val in "${line_array[@]}"; do
|
||
if [[ -n $match_key && $line_val == "${key// /}" ]]; then
|
||
if ((match_key<0 & line_pos+match_key>=0)) || ((match_key>=0 & line_pos+match_key<${#line_array[@]})); then
|
||
found="${line_array[$((line_pos+match_key))]}"
|
||
break 2
|
||
else
|
||
return 1
|
||
fi
|
||
|
||
elif [[ -n $match_key ]]; then
|
||
((++line_pos))
|
||
|
||
elif [[ -n $reg && $line_val =~ ^${reg}$ || -z $reg && -n $match ]]; then
|
||
if ((line_pos==match)); then
|
||
found=${line_val}
|
||
break 2
|
||
fi
|
||
((++line_pos))
|
||
fi
|
||
done
|
||
fi
|
||
done <<<"${input}"
|
||
|
||
if [[ -z $found ]]; then return 1; fi
|
||
|
||
if [[ -n ${remove[*]} ]]; then
|
||
for removing in "${remove[@]}"; do
|
||
found="${found//${removing}/}"
|
||
done
|
||
fi
|
||
|
||
if [[ -n $int && $found =~ [.,] ]]; then
|
||
found="${found/,/.}"
|
||
printf -v found "%.0f" "${found}"
|
||
fi
|
||
|
||
if [[ -n $math && -n $int ]]; then
|
||
math="${math//x/$found}"
|
||
found=$((${math}))
|
||
fi
|
||
|
||
if (($#>0)); then
|
||
input="${found}"
|
||
unset key match match_key all reg found int 'remove[@]' current_line
|
||
line_pos=1
|
||
fi
|
||
|
||
done
|
||
|
||
if [[ -z $ext_var ]]; then echo "${found}"; fi
|
||
if [[ -n $ext_arr ]]; then array_out=(${found}); fi
|
||
}
|
||
|
||
get_themes() {
|
||
local file
|
||
theme_int=0
|
||
themes=("Default")
|
||
for file in "${config_dir}/themes"/*.theme; do
|
||
file="${file##*/}"
|
||
if [[ ${file} != "*.theme" ]]; then themes+=("themes/${file%.theme}"); fi
|
||
if [[ ${themes[-1]} == "${color_theme}" ]]; then theme_int=${#themes[@]}-1; fi
|
||
done
|
||
for file in "${config_dir}/user_themes"/*.theme; do
|
||
file="${file##*/}"
|
||
if [[ ${file} != "*.theme" ]]; then themes+=("user_themes/${file%.theme}"); fi
|
||
if [[ ${themes[-1]} == "${color_theme}" ]]; then theme_int=${#themes[@]}-1; fi
|
||
done
|
||
}
|
||
|
||
get_net_device() { #? Check for internet connection, name of default network device and create list of all devices
|
||
if [[ $use_psutil == true ]]; then get_net_device_psutil; return; fi
|
||
local -a netdev
|
||
local ndline
|
||
if ! get_value -v 'net[device]' -ss "$(ip route get 1.1.1.1 2>/dev/null)" -k "dev" -mk 1; then
|
||
net[no_device]=1
|
||
else
|
||
unset 'net[no_device]' 'nic_list[@]' nic_int
|
||
readarray -t netdev </proc/net/dev
|
||
for ndline in "${netdev[@]:2}"; do
|
||
ndline="${ndline%:*}"; ndline="${ndline// /}"
|
||
nic_list+=("${ndline}")
|
||
if [[ ${ndline} == "${net[device]}" ]]; then
|
||
nic_int=$((${#nic_list[@]}-1))
|
||
fi
|
||
done
|
||
collect_net init
|
||
fi
|
||
}
|
||
|
||
get_net_device_psutil() {
|
||
unset 'nic_list[@]'
|
||
py_command -a nic_list "get_nics()"
|
||
net[device]="${nic_list[0]}"
|
||
nic_int=0
|
||
if [[ -z ${net[device]} ]]; then
|
||
net[no_device]=1
|
||
else
|
||
unset 'net[no_device]'
|
||
collect_net init
|
||
fi
|
||
}
|
||
|
||
cur_pos() { #? Get cursor postion, argument "line" prints current line, argument "col" prints current column, no argument prints both in format "line column"
|
||
local line col
|
||
IFS=';' read -sdR -p $'\E[6n' line col
|
||
if [[ -z $1 || $1 == "line" ]]; then echo -n "${line#*[}${1:-" "}"; fi
|
||
if [[ -z $1 || $1 == "col" ]]; then echo -n "$col"; fi
|
||
}
|
||
|
||
create_box() { #? Draw a box with an optional title at given location
|
||
local width height col line title ltype hpos vpos i hlines vlines color line_color c_rev=0 box_out ext_var fill
|
||
until (($#==0)); do
|
||
case $1 in
|
||
-f|-full) col=1; line=1; width=$((tty_width)); height=$((tty_height));; #? Use full terminal size for box
|
||
-c|-col) if is_int "$2"; then col=$2; shift; fi;; #? Column position to start box
|
||
-l|-line) if is_int "$2"; then line=$2; shift; fi;; #? Line position to start box
|
||
-w|-width) if is_int "$2"; then width=$2; shift; fi;; #? Width of box
|
||
-h|-height) if is_int "$2"; then height=$2; shift; fi;; #? Height of box
|
||
-t|-title) if [[ -n $2 ]]; then title="$2"; shift; fi;; #? Draw title without titlebar
|
||
-s|-single) ltype="single";; #? Use single lines
|
||
-d|-double) ltype="double";; #? Use double lines
|
||
-lc|-line-color) line_color="$2"; shift;; #? Color of the lines
|
||
-fill) fill=1;; #? Fill background of box
|
||
-v|-variable) local -n box_out=$2; ext_var=1; shift;; #? Output box to a variable
|
||
esac
|
||
shift
|
||
done
|
||
if [[ -z $col || -z $line || -z $width || -z $height ]]; then return; fi
|
||
|
||
ltype=${ltype:-"single"}
|
||
vlines+=("$col" "$((col+width-1))")
|
||
hlines+=("$line" "$((line+height-1))")
|
||
|
||
print -v box_out -rs
|
||
|
||
#* Fill box if enabled
|
||
if [[ -n $fill ]]; then
|
||
for((i=line+1;i<line+height-1;i++)); do
|
||
print -v box_out -m $i $((col+1)) -rp $((width-2)) -t " "
|
||
done
|
||
fi
|
||
|
||
#* Draw all horizontal lines
|
||
print -v box_out -fg ${line_color:-${theme[div_line]}}
|
||
for hpos in "${hlines[@]}"; do
|
||
print -v box_out -m $hpos $col -rp $((width-1)) -t "${box[${ltype}_hor_line]}"
|
||
done
|
||
|
||
#* Draw all vertical lines
|
||
for vpos in "${vlines[@]}"; do
|
||
print -v box_out -m $line $vpos
|
||
for((hpos=line;hpos<=line+height-1;hpos++)); do
|
||
print -v box_out -m $hpos $vpos -t "${box[${ltype}_vert_line]}"
|
||
done
|
||
done
|
||
|
||
#* Draw corners
|
||
print -v box_out -m $line $col -t "${box[${ltype}_left_corner_up]}"
|
||
print -v box_out -m $line $((col+width-1)) -t "${box[${ltype}_right_corner_up]}"
|
||
print -v box_out -m $((line+height-1)) $col -t "${box[${ltype}_left_corner_down]}"
|
||
print -v box_out -m $((line+height-1)) $((col+width-1)) -t "${box[${ltype}_right_corner_down]}"
|
||
|
||
#* Draw small title without titlebar
|
||
if [[ -n $title ]]; then
|
||
print -v box_out -m $line $((col+2)) -t "┤" -fg ${theme[title]} -b -t "$title" -rs -fg ${line_color:-${theme[div_line]}} -t "├"
|
||
fi
|
||
|
||
print -v box_out -rs -m $((line+1)) $((col+1))
|
||
|
||
if [[ -z $ext_var ]]; then echo -en "${box_out}"; fi
|
||
|
||
|
||
}
|
||
|
||
create_meter() { #? Create a horizontal percentage meter, usage; create_meter <value 0-100>
|
||
#? Optional arguments: [-p, -place <line> <col>] [-w, -width <columns>] [-f, -fill-empty]
|
||
#? [-c, -color "array-name"] [-i, -invert-color] [-v, -variable "variable-name"]
|
||
if [[ -z $1 ]]; then return; fi
|
||
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]}
|
||
|
||
#* Argument parsing
|
||
until (($#==0)); do
|
||
case $1 in
|
||
-p|-place) if is_int "${@:2:2}"; then line=$2; col=$3; shift 2; fi;; #? Placement for meter
|
||
-w|-width) width=$2; shift;; #? Width of meter in columns
|
||
-c|-color) local -n colors=$2; shift;; #? Name of an array containing colors from index 0-100
|
||
-i|-invert) invert=1;; #? Invert meter
|
||
-f|-fill-empty) fill_empty=1;; #? Fill unused space with dark blocks
|
||
-v|-variable) local -n meter_var=$2; ext_var=1; shift;; #? Output meter to a variable
|
||
*) if is_int "$1"; then val=$1; fi;;
|
||
esac
|
||
shift
|
||
done
|
||
|
||
if [[ -z $val ]]; then return; fi
|
||
|
||
#* Set default width if not given
|
||
width=${width:-10}
|
||
|
||
#* If no color array was given, create a simple greyscale array
|
||
if [[ -z $colors ]]; then
|
||
for ((i=0,ic=50;i<=100;i++,ic=ic+2)); do
|
||
colors[i]="${ic} ${ic} ${ic}"
|
||
done
|
||
fi
|
||
|
||
#* Create the meter
|
||
meter_var=""
|
||
if [[ -n $line && -n $col ]]; then print -v meter_var -rs -m $line $col
|
||
else print -v meter_var -rs; fi
|
||
|
||
if [[ -n $invert ]]; then print -v meter_var -r $((width+1)); fi
|
||
for((i=1;i<=width;i++)); do
|
||
if [[ -n $invert ]]; then print -v meter_var -l 2; fi
|
||
|
||
if ((val>=i*100/width)); then
|
||
print -v meter_var -fg ${colors[$((i*100/width))]} -t "${block}"
|
||
elif ((fill_empty==1)); then
|
||
if [[ -n $invert ]]; then print -v meter_var -l $((width-i)); fi
|
||
print -v meter_var -fg $bg_color -rp $((1+width-i)) -t "${block}"; break
|
||
else
|
||
if [[ -n $invert ]]; then break; print -v meter_var -l $((1+width-i))
|
||
else print -v meter_var -r $((1+width-i)); break; fi
|
||
fi
|
||
done
|
||
if [[ -z $ext_var ]]; then echo -en "${meter_var}"; fi
|
||
}
|
||
|
||
create_graph() { #? Create a graph from an array of percentage values, usage; create_graph <options> <value-array>
|
||
#? Create a graph from an array of non percentage values: create_graph <options> <-max "max value"> <value-array>
|
||
#? Add a value to existing graph; create_graph [-i, -invert] [-max "max value"] -add-value "graph_array" <value>
|
||
#? Add last value from an array to existing graph; create_graph [-i, -invert] [-max "max value"] -add-last "graph_array" "value-array"
|
||
#? Options: < -d, -dimensions <line> <col> <height> <width> > [-i, -invert] [-n, -no-guide] [-c, -color "array-name"] [-o, -output-array "variable-name"]
|
||
if [[ -z $1 ]]; then return; fi
|
||
if [[ ${graph[hires]} == true ]]; then create_graph_hires "$@"; return; fi
|
||
|
||
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
|
||
local -a graph_array input_array
|
||
|
||
#* Argument parsing
|
||
until (($#==0)); do
|
||
case $1 in
|
||
-d|-dimensions) if is_int "${@:2:4}"; then line=$2; col=$3; height=$4; width=$5; shift 4; fi;; #? Graph dimensions
|
||
-c|-color) local -n colors=$2; shift;; #? Name of an array containing colors from index 0-100
|
||
-o|-output-array) local -n output_array=$2; ext_var=1; shift;; #? Output meter to an array
|
||
-add-value) if is_int "$3"; then local -n output_array=$2; add=$3; break; else return; fi;; #? Add a value to existing graph
|
||
-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
|
||
-i|-invert) invert=1;; #? Invert graph, drawing from top to bottom
|
||
-n|-no-guide) no_guide=1;; #? Don't print side and bottom guide lines
|
||
-max) if is_int "$2"; then max=$2; shift; fi;; #? Needed max value for non percentage arrays
|
||
*) local -n tmp_in_array=$1; input_array=("${tmp_in_array[@]}");;
|
||
esac
|
||
shift
|
||
done
|
||
|
||
if [[ -z $no_guide ]]; then
|
||
((--height))
|
||
else
|
||
if [[ -n $invert ]]; then ((line--)); fi
|
||
fi
|
||
|
||
|
||
if ((width<3)); then width=3; fi
|
||
if ((height<1)); then height=1; fi
|
||
|
||
|
||
#* If argument "add" was passed check for existing graph and make room for new value(s)
|
||
local add_start add_end
|
||
if [[ -n $add ]]; then
|
||
local cut_left search
|
||
if [[ -n ${input_array[0]} ]]; then return; fi
|
||
if [[ -n $output_array ]]; then
|
||
graph_array=("${output_array[@]}")
|
||
if [[ -z ${graph_array[0]} ]]; then return; fi
|
||
else
|
||
return
|
||
fi
|
||
height=$((${#graph_array[@]}-1))
|
||
input_array[0]=${add}
|
||
|
||
#* Remove last value in current graph
|
||
|
||
for ((i=0;i<height;i++)); do
|
||
cut_left="${graph_array[i]%m*}"
|
||
search=$((${#cut_left}+1))
|
||
graph_array[i]="${graph_array[i]::$search}${graph_array[i]:$((search+1))}"
|
||
done
|
||
|
||
fi
|
||
|
||
#* Initialize graph if no "add" argument was given
|
||
if [[ -z $add ]]; then
|
||
#* Scale down graph one line if height is even
|
||
local inv_offset h_inv normal_vals=1
|
||
local -a side_num=(100 0) g_char=(" ⡇" " ⠓" "⠒") g_index
|
||
|
||
if [[ -n $invert ]]; then
|
||
for((i=height;i>=0;i--)); do
|
||
g_index+=($i)
|
||
done
|
||
|
||
else
|
||
for((i=0;i<=height;i++)); do
|
||
g_index+=($i)
|
||
done
|
||
fi
|
||
|
||
if [[ -n $no_guide ]]; then unset normal_vals
|
||
elif [[ -n $invert ]]; then g_char=(" ⡇" " ⡤" "⠤")
|
||
fi
|
||
|
||
#* Set up graph array print side numbers and lines
|
||
print -v graph_array[0] -rs
|
||
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]}
|
||
for((i=1;i<height;i++)); do
|
||
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))]}
|
||
done
|
||
|
||
if [[ -z $no_guide ]]; then width=$((width-5)); fi
|
||
|
||
graph_array[height]=""
|
||
if [[ -z $no_guide ]]; then
|
||
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]}"
|
||
fi
|
||
|
||
#* If no color array was given, create a simple greyscale array
|
||
if [[ -z $colors ]]; then
|
||
for ((i=0,ic=50;i<=100;i++,ic=ic+2)); do
|
||
colors[i]="${ic} ${ic} ${ic}"
|
||
done
|
||
fi
|
||
fi
|
||
|
||
#* Create the graph
|
||
local value_width x y a cur_value prev_value=100 symbol tmp_out compare found count virt_height=$((height*10))
|
||
if [[ -n $add ]]; then
|
||
value_width=1
|
||
elif ((${#input_array[@]}<=width)); then
|
||
value_width=${#input_array[@]};
|
||
else
|
||
value_width=${width}
|
||
input_array=("${input_array[@]:(-$width)}")
|
||
fi
|
||
|
||
if [[ -n $invert ]]; then
|
||
y=$((height-1))
|
||
done_val="-1"
|
||
else
|
||
y=0
|
||
done_val=$height
|
||
fi
|
||
|
||
#* Convert input array to percentage values of max if a max value was given
|
||
if [[ -n $max ]]; then
|
||
for((i=0;i<${#input_array[@]};i++)); do
|
||
if ((input_array[i]>=max)); then
|
||
input_array[i]=100
|
||
else
|
||
input_array[i]=$((input_array[i]*100/max))
|
||
fi
|
||
done
|
||
fi
|
||
|
||
until ((y==done_val)); do
|
||
|
||
#* Print spaces to right-justify graph if number of values is less than graph width
|
||
if [[ -z $add ]] && ((value_width<width)); then print -v graph_array[y] -rp $((width-value_width)) -t " "; fi
|
||
|
||
cur_value=$(( virt_height-(y*10) ))
|
||
next_value=$(( virt_height-((y+1)*10) ))
|
||
|
||
count=0
|
||
x=0
|
||
|
||
#* Create graph by walking through all values for each line, speed up by counting similar values and print once, when difference is met
|
||
while ((x<value_width)); do
|
||
|
||
if [[ -z ${input_array[x]} ]] || ((input_array[x]<1)) || ((${#input_array[x]}>3)); then input_array[x]=0; fi
|
||
|
||
#* Print empty space if current value is less than percentage for current line
|
||
while ((x<value_width & input_array[x]*virt_height/100<next_value)); do
|
||
((++count))
|
||
((++x))
|
||
done
|
||
if ((count>0)); then
|
||
print -v graph_array[y] -rp ${count} -t " "
|
||
count=0
|
||
fi
|
||
|
||
#* Print current value in percent relative to graph size if current value is less than line percentage but greater than next line percentage
|
||
while ((x<value_width & input_array[x]*virt_height/100<cur_value & input_array[x]*virt_height/100>=next_value)); do
|
||
print -v graph_array[y] -t "${graph_symbol[${invert:+-}$(( (input_array[x]*virt_height/100)-next_value ))]}"
|
||
((++x))
|
||
done
|
||
|
||
#* Print full block if current value is greater than percentage for current line
|
||
while ((x<value_width & input_array[x]*virt_height/100>=cur_value)); do
|
||
((++count))
|
||
((++x))
|
||
done
|
||
if ((count>0)); then
|
||
print -v graph_array[y] -rp ${count} -t "${graph_symbol[10]}"
|
||
count=0
|
||
fi
|
||
done
|
||
|
||
if [[ -n $invert ]]; then
|
||
((y--)) || true
|
||
else
|
||
((++y))
|
||
fi
|
||
done
|
||
|
||
#* Echo out graph if no argument for a output array was given
|
||
if [[ -z $ext_var && -z $add ]]; then echo -en "${graph_array[*]}"
|
||
else output_array=("${graph_array[@]}"); fi
|
||
}
|
||
|
||
create_mini_graph() { #? Create a one line high graph from an array of percentage values, usage; create_mini_graph <options> <value-array>
|
||
#? Add a value to existing graph; create_mini_graph [-i, -invert] [-nc, -no-color] [-c, -color "array-name"] -add-value "graph_variable" <value>
|
||
#? 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"
|
||
#? Options: [-w, -width <width>] [-i, -invert] [-nc, -no-color] [-c, -color "array-name"] [-o, -output-variable "variable-name"]
|
||
if [[ -z $1 ]]; then return; fi
|
||
|
||
if [[ ${graph[hires]} == true ]]; then create_mini_graph_hires "$@"; return; fi
|
||
|
||
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
|
||
|
||
#* Argument parsing
|
||
until (($#==0)); do
|
||
case $1 in
|
||
-w|-width) if is_int "$2"; then width=$2; shift; fi;; #? Graph width
|
||
-c|-color) local -n colors=$2; shift;; #? Name of an array containing colors from index 0-100
|
||
-nc|-no-color) no_color=1;; #? Set no color
|
||
-o|-output-variable) local -n output_var=$2; ext_var=1; shift;; #? Output graph to a variable
|
||
-add-value) if is_int "$3"; then local -n output_var=$2; add=$3; break; else return; fi;; #? Add a value to existing graph
|
||
-add-last) local -n output_var=$2 add_array=$3; add="${add_array[-1]}"; break;; #? Add last value from array to existing graph
|
||
-i|-invert) invert=1;; #? Invert graph, drawing from top to bottom
|
||
*) local -n input_array=$1;;
|
||
esac
|
||
shift
|
||
done
|
||
|
||
if ((width<1)); then width=1; fi
|
||
|
||
#* If argument "add" was passed check for existing graph and make room for new value(s)
|
||
local add_start add_end
|
||
if [[ -n $add ]]; then
|
||
local cut_left search
|
||
#if [[ -n ${input_array[0]} ]]; then return; fi
|
||
if [[ -n $output_var ]]; then
|
||
graph_var="${output_var}"
|
||
if [[ -z ${graph_var} ]]; then return; fi
|
||
else
|
||
return
|
||
fi
|
||
|
||
declare -a input_array
|
||
input_array[0]=${add}
|
||
|
||
#* Remove last value in current graph
|
||
if [[ -n ${graph_var} && -z $no_color ]]; then
|
||
if [[ ${graph_var::5} == "\e[1C" ]]; then
|
||
graph_var="${graph_var#'\e[1C'}"
|
||
else
|
||
cut_left="${graph_var%%m*}"
|
||
search=$((${#cut_left}+1))
|
||
graph_var="${graph_var:$((search+1))}"
|
||
fi
|
||
elif [[ -n ${graph_var} && -n $no_color ]]; then
|
||
if [[ ${graph_var::5} == "\e[1C" ]]; then
|
||
#cut_left="${graph_var%%C*}"
|
||
#search=$((${#cut_left}+1))
|
||
#graph_var="${graph_var:$((search))}"
|
||
graph_var="${graph_var#'\e[1C'}"
|
||
else
|
||
graph_var="${graph_var:1}"
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
|
||
#* If no color array was given, create a simple greyscale array
|
||
if [[ -z $colors && -z $no_color ]]; then
|
||
for ((i=0,ic=50;i<=100;i++,ic=ic+2)); do
|
||
colors[i]="${ic} ${ic} ${ic}"
|
||
done
|
||
fi
|
||
|
||
|
||
#* Create the graph
|
||
local value_width x=0 y a cur_value virt_height=$((height*10)) offset=0 org_value
|
||
if [[ -n $add ]]; then
|
||
value_width=1
|
||
elif ((${#input_array[@]}<=width)); then
|
||
value_width=${#input_array[@]};
|
||
else
|
||
value_width=${width}
|
||
offset=$((${#input_array[@]}-width))
|
||
fi
|
||
|
||
#* Print spaces to right-justify graph if number of values is less than graph width
|
||
if [[ -z $add && -z $no_color ]] && ((value_width<width)); then print -v graph_var -rp $((width-value_width)) -t "\e[1C"
|
||
elif [[ -z $add && -n $no_color ]] && ((value_width<width)); then print -v graph_var -rp $((width-value_width)) -t "\e[1C"; fi
|
||
#* Create graph
|
||
while ((x<value_width)); do
|
||
#* Round current input_array value divided by 10 to closest whole number
|
||
org_value=${input_array[offset+x]}
|
||
if ((org_value<=0)); then org_value=0; fi
|
||
if ((org_value>=100)); then cur_value=10; org_value=100
|
||
elif [[ ${#org_value} -gt 1 && ${org_value:(-1)} -ge 5 ]]; then cur_value=$((${org_value::1}+1))
|
||
elif [[ ${#org_value} -gt 1 && ${org_value:(-1)} -lt 5 ]]; then cur_value=$((${org_value::1}))
|
||
elif [[ ${org_value:(-1)} -ge 5 ]]; then cur_value=1
|
||
else cur_value=0
|
||
fi
|
||
if [[ -z $no_color ]]; then
|
||
color="-fg ${colors[$org_value]} "
|
||
else
|
||
color=""
|
||
fi
|
||
|
||
if [[ $cur_value == 0 ]]; then
|
||
print -v graph_var -t "\e[1C"
|
||
else
|
||
print -v graph_var ${color}-t "${graph_symbol[${invert:+-}$cur_value]}"
|
||
fi
|
||
((++x))
|
||
done
|
||
|
||
#* Echo out graph if no argument for a output array was given
|
||
if [[ -z $ext_var && -z $add ]]; then echo -en "${graph_var}"
|
||
else output_var="${graph_var}"; fi
|
||
}
|
||
|
||
create_graph_hires() { #? Create a graph from an array of percentage values, usage; create_graph <options> <value-array>
|
||
#? Create a graph from an array of non percentage values: create_graph <options> <-max "max value"> <value-array>
|
||
#? Add a value to existing graph; create_graph [-i, -invert] [-max "max value"] -add-value "graph_array" <value>
|
||
#? Add last value from an array to existing graph; create_graph [-i, -invert] [-max "max value"] -add-last "graph_array" "value-array"
|
||
#? Options: < -d, -dimensions <line> <col> <height> <width> > [-i, -invert] [-n, -no-guide] [-c, -color "array-name"] [-o, -output-array "variable-name"]
|
||
if [[ -z $1 ]]; then return; fi
|
||
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
|
||
local -a input_array
|
||
local -i i
|
||
|
||
#* Argument parsing
|
||
until (($#==0)); do
|
||
case $1 in
|
||
-d|-dimensions) if is_int "${@:2:4}"; then line=$2; col=$3; height=$4; width=$5; shift 4; fi;; #? Graph dimensions
|
||
-c|-color) local -n colors=$2; shift;; #? Name of an array containing colors from index 0-100
|
||
-o|-output-array) local -n output_array=$2; graph_name=$2; ext_var=1; shift;; #? Output meter to an array
|
||
-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
|
||
-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
|
||
-i|-invert) invert=1;; #? Invert graph, drawing from top to bottom
|
||
-n|-no-guide) no_guide=1;; #? Don't print side and bottom guide lines
|
||
-max) if is_int "$2"; then max=$2; shift; fi;; #? Needed max value for non percentage arrays
|
||
*) local -n tmp_in_array="$1"; input_array=("${tmp_in_array[@]}");;
|
||
esac
|
||
shift
|
||
done
|
||
|
||
local -n last_val="graph[${graph_name}_last_val]"
|
||
local -n last_type="graph[${graph_name}_last_type]"
|
||
|
||
|
||
if [[ -z $add ]]; then
|
||
last_type="even"
|
||
last_val=0
|
||
local -n graph_array="${graph_name}_odd"
|
||
local -n graph_even="${graph_name}_even"
|
||
graph_even=("")
|
||
graph_array=("")
|
||
elif [[ ${last_type} == "even" ]]; then
|
||
local -n graph_array="${graph_name}_odd"
|
||
last_type="odd"
|
||
elif [[ ${last_type} == "odd" ]]; then
|
||
local -n graph_array="${graph_name}_even"
|
||
last_type="even"
|
||
fi
|
||
|
||
if [[ -z $no_guide ]]; then ((--height))
|
||
elif [[ -n $invert ]]; then ((line--))
|
||
fi
|
||
|
||
if ((width<3)); then width=3; fi
|
||
if ((height<1)); then height=1; fi
|
||
|
||
|
||
#* If argument "add" was passed check for existing graph and make room for new value(s)
|
||
local add_start add_end
|
||
if [[ -n $add ]]; then
|
||
local cut_left search
|
||
if [[ -n ${input_array[*]} || -z ${graph_array[0]} ]]; then return; fi
|
||
|
||
height=$((${#graph_array[@]}-1))
|
||
input_array=("${add}")
|
||
|
||
#* Remove last value in current graph
|
||
|
||
for ((i=0;i<height;i++)); do
|
||
cut_left="${graph_array[i]%m*}"
|
||
search=$((${#cut_left}+1))
|
||
graph_array[i]="${graph_array[i]::$search}${graph_array[i]:$((search+1))}"
|
||
done
|
||
|
||
fi
|
||
|
||
#* Initialize graph if no "add" argument was given
|
||
if [[ -z $add ]]; then
|
||
#* Scale down graph one line if height is even
|
||
local inv_offset h_inv normal_vals=1
|
||
local -a side_num=(100 0) g_char=(" ⡇" " ⠓" "⠒") g_index
|
||
|
||
if [[ -n $invert ]]; then
|
||
for((i=height;i>=0;i--)); do
|
||
g_index+=($i)
|
||
done
|
||
|
||
else
|
||
for((i=0;i<=height;i++)); do
|
||
g_index+=($i)
|
||
done
|
||
fi
|
||
|
||
if [[ -n $no_guide ]]; then unset normal_vals
|
||
elif [[ -n $invert ]]; then g_char=(" ⡇" " ⡤" "⠤")
|
||
fi
|
||
|
||
#* Set up graph array print side numbers and lines
|
||
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]}
|
||
for((i=1;i<height;i++)); do
|
||
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))]}
|
||
done
|
||
|
||
if [[ -z $no_guide ]]; then width=$((width-5)); fi
|
||
|
||
graph_array[$height]=""
|
||
if [[ -z $no_guide ]]; then
|
||
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]}"
|
||
fi
|
||
|
||
graph_even=("${graph_array[@]}")
|
||
|
||
#* If no color array was given, create a simple greyscale array
|
||
if [[ -z $colors ]]; then
|
||
for ((i=0,ic=50;i<=100;i++,ic=ic+2)); do
|
||
colors[i]="${ic} ${ic} ${ic}"
|
||
done
|
||
fi
|
||
fi
|
||
|
||
#* Create the graph
|
||
local value_width next_line prev_value cur_value virt_height=$((height*4)) converted
|
||
local -i x y c_val p_val l_val
|
||
if [[ -n $add ]]; then
|
||
value_width=1
|
||
elif ((${#input_array[@]}<=width*2)); then
|
||
value_width=$((${#input_array[@]}*2))
|
||
else
|
||
value_width=$((width*2))
|
||
input_array=("${input_array[@]:(-${value_width})}")
|
||
fi
|
||
|
||
if [[ -z $add ]] && ! ((${#input_array[@]}%2)); then last_val=${input_array[0]}; input_array=("${input_array[@]:1}"); converted=1; fi
|
||
|
||
#* Print spaces to right-justify graph if number of values is less than graph width
|
||
if [[ -z $add ]] && ((${#input_array[@]}/2<width)); then
|
||
for((i=0;i<height;i++)); do
|
||
print -v graph_array[i] -rp $((width-1-${#input_array[@]}/2)) -t " "
|
||
done
|
||
graph_even=("${graph_array[@]}")
|
||
fi
|
||
|
||
if [[ -n $invert ]]; then
|
||
y=$((height-1))
|
||
done_val="-1"
|
||
else
|
||
y=0
|
||
done_val=$height
|
||
fi
|
||
|
||
#* Convert input array to percentage values of max if a max value was given
|
||
if [[ -n $max ]]; then
|
||
for((i=0;i<${#input_array[@]};i++)); do
|
||
if ((input_array[i]>=max)); then
|
||
input_array[i]=100
|
||
else
|
||
input_array[i]=$((input_array[i]*100/max))
|
||
fi
|
||
done
|
||
if [[ -n $converted ]]; then
|
||
last_val=$((${last_val}*100/max))
|
||
if ((${last_val}>100)); then last_val=100; fi
|
||
fi
|
||
fi
|
||
|
||
if [[ -n $invert ]]; then local -n symbols=graph_symbol_down
|
||
else local -n symbols=graph_symbol_up
|
||
fi
|
||
|
||
until ((y==done_val)); do
|
||
|
||
next_line=$(( virt_height-((y+1)*4) ))
|
||
unset p_val
|
||
|
||
#* Create graph by walking through all values for each line
|
||
for ((x=0;x<${#input_array[@]};x++)); do
|
||
c_val=${input_array[x]}
|
||
p_val=${p_val:-${last_val}}
|
||
cur_value="$((c_val*virt_height/100-next_line))"
|
||
prev_value=$((p_val*virt_height/100-next_line))
|
||
|
||
if ((cur_value<0)); then cur_value=0
|
||
elif ((cur_value>4)); then cur_value=4; fi
|
||
if ((prev_value<0)); then prev_value=0
|
||
elif ((prev_value>4)); then prev_value=4; fi
|
||
|
||
if [[ -z $add ]] && ((x==0)); then
|
||
print -v graph_even[y] -t "${symbols[${prev_value}_${cur_value}]}"
|
||
print -v graph_array[y] -t "${symbols[0_${prev_value}]}"
|
||
elif [[ -z $add ]] && ! ((x%2)); then
|
||
print -v graph_even[y] -t "${symbols[${prev_value}_${cur_value}]}"
|
||
else
|
||
print -v graph_array[y] -t "${symbols[${prev_value}_${cur_value}]}"
|
||
fi
|
||
|
||
if [[ -z $add ]]; then p_val=${input_array[x]}; else unset p_val; fi
|
||
|
||
done
|
||
|
||
if [[ -n $invert ]]; then
|
||
((y--)) || true
|
||
else
|
||
((++y))
|
||
fi
|
||
|
||
done
|
||
|
||
if [[ -z $add && ${last_type} == "even" ]]; then
|
||
declare -n graph_array="${graph_name}_even"
|
||
fi
|
||
|
||
last_val=$c_val
|
||
|
||
output_array=("${graph_array[@]}")
|
||
}
|
||
|
||
|
||
create_mini_graph_hires() { #? Create a one line high graph from an array of percentage values, usage; create_mini_graph <options> <value-array>
|
||
#? Add a value to existing graph; create_mini_graph [-i, -invert] [-nc, -no-color] [-c, -color "array-name"] -add-value "graph_variable" <value>
|
||
#? 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"
|
||
#? Options: [-w, -width <width>] [-i, -invert] [-nc, -no-color] [-c, -color "array-name"] [-o, -output-variable "variable-name"]
|
||
if [[ -z $1 ]]; then return; fi
|
||
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
|
||
local -a input_array
|
||
local -i i
|
||
|
||
#* Argument parsing
|
||
until (($#==0)); do
|
||
case $1 in
|
||
-w|-width) if is_int "$2"; then width=$2; shift; fi;; #? Graph width
|
||
-c|-color) local -n colors=$2; shift;; #? Name of an array containing colors from index 0-100
|
||
-nc|-no-color) no_color=1;; #? Set no color
|
||
-o|-output-variable) local -n output_var=$2; graph_name=$2; ext_var=1; shift;; #? Output graph to a variable
|
||
-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
|
||
-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
|
||
-i|-invert) invert=1;; #? Invert graph, drawing from top to bottom
|
||
*) local -n tmp_in_arr=$1; input_array=("${tmp_in_arr[@]}");;
|
||
esac
|
||
shift
|
||
done
|
||
|
||
local -n last_val="${graph_name}_last_val"
|
||
local -n last_type="${graph_name}_last_type"
|
||
|
||
if [[ -z $add ]]; then
|
||
last_type="even"
|
||
last_val=0
|
||
local -n graph_var="${graph_name}_odd"
|
||
local -n graph_other="${graph_name}_even"
|
||
graph_var=""; graph_other=""
|
||
elif [[ ${last_type} == "even" ]]; then
|
||
local -n graph_var="${graph_name}_odd"
|
||
last_type="odd"
|
||
elif [[ ${last_type} == "odd" ]]; then
|
||
local -n graph_var="${graph_name}_even"
|
||
last_type="even"
|
||
fi
|
||
|
||
if ((width<1)); then width=1; fi
|
||
|
||
#* If argument "add" was passed check for existing graph and make room for new value(s)
|
||
local add_start add_end
|
||
if [[ -n $add ]]; then
|
||
local cut_left search
|
||
input_array[0]=${add}
|
||
|
||
#* Remove last value in current graph
|
||
if [[ -n ${graph_var} && -z $no_color ]]; then
|
||
if [[ ${graph_var::5} == '\e[1C' ]]; then
|
||
graph_var="${graph_var#'\e[1C'}"
|
||
else
|
||
cut_left="${graph_var%m*}"
|
||
search=$((${#cut_left}+1))
|
||
graph_var="${graph_var::$search}${graph_var:$((search+1))}"
|
||
fi
|
||
elif [[ -n ${graph_var} && -n $no_color ]]; then
|
||
if [[ ${graph_var::5} == "\e[1C" ]]; then
|
||
#cut_left="${graph_var%%C*}"
|
||
#search=$((${#cut_left}+1))
|
||
#graph_var="${graph_var:$((search))}"
|
||
graph_var="${graph_var#'\e[1C'}"
|
||
else
|
||
graph_var="${graph_var:1}"
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
|
||
#* If no color array was given, create a simple greyscale array
|
||
if [[ -z $colors && -z $no_color ]]; then
|
||
for ((i=0,ic=50;i<=100;i++,ic=ic+2)); do
|
||
colors[i]="${ic} ${ic} ${ic}"
|
||
done
|
||
fi
|
||
|
||
|
||
#* Create the graph
|
||
local value_width x=0 y a cur_value prev_value p_val c_val acolor jump odd offset=0
|
||
if [[ -n $add ]]; then
|
||
value_width=1
|
||
elif ((${#input_array[@]}<=width*2)); then
|
||
value_width=$((${#input_array[@]}*2))
|
||
else
|
||
value_width=$((width*2))
|
||
input_array=("${input_array[@]:(-${value_width})}")
|
||
fi
|
||
|
||
if [[ -z $add ]] && ! ((${#input_array[@]}%2)); then last_val=${input_array[0]}; input_array=("${input_array[@]:1}"); fi
|
||
|
||
#* Print spaces to right-justify graph if number of values is less than graph width
|
||
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
|
||
|
||
if [[ -n $invert ]]; then local -n symbols=graph_symbol_down
|
||
else local -n symbols=graph_symbol_up
|
||
fi
|
||
|
||
unset p_val
|
||
|
||
#* Create graph
|
||
for((i=0;i<${#input_array[@]};i++)); do
|
||
|
||
c_val=${input_array[i]}
|
||
p_val=${p_val:-${last_val}}
|
||
|
||
if ((c_val>=85)); then cur_value=4
|
||
elif ((c_val>=60)); then cur_value=3
|
||
elif ((c_val>=30)); then cur_value=2
|
||
elif ((c_val>=10)); then cur_value=1
|
||
elif ((c_val<10)); then cur_value=0; fi
|
||
|
||
if ((p_val>=85)); then prev_value=4
|
||
elif ((p_val>=60)); then prev_value=3
|
||
elif ((p_val>=30)); then prev_value=2
|
||
elif ((p_val>=10)); then prev_value=1
|
||
elif ((p_val<10)); then prev_value=0; fi
|
||
|
||
if [[ -z $no_color ]]; then
|
||
if ((c_val>p_val)); then acolor=$((c_val-p_val))
|
||
else acolor=$((p_val-c_val)); fi
|
||
if ((acolor>100)); then acolor=100; elif ((acolor<0)); then acolor=0; fi
|
||
color="-fg ${colors[${acolor:-0}]} "
|
||
else
|
||
unset color
|
||
fi
|
||
|
||
if ((cur_value==0 & prev_value==0)); then jump="\e[1C"; else unset jump; fi
|
||
|
||
if [[ -z $add ]] && ((i==0)); then
|
||
print -v graph_other ${color}-t "${jump:-${symbols[${prev_value}_${cur_value}]}}"
|
||
print -v graph_var ${color}-t "${jump:-${symbols[0_${prev_value}]}}"
|
||
elif [[ -z $add ]] && ((i%2)); then
|
||
print -v graph_other ${color}-t "${jump:-${symbols[${prev_value}_${cur_value}]}}"
|
||
else
|
||
print -v graph_var ${color}-t "${jump:-${symbols[${prev_value}_${cur_value}]}}"
|
||
fi
|
||
|
||
if [[ -z $add ]]; then p_val=$c_val; else unset p_val; fi
|
||
done
|
||
|
||
#if [[ -z $add ]]; then
|
||
# declare -n graph_var="${graph_name}_even"
|
||
# #echo "yup" >&2
|
||
#fi
|
||
|
||
last_val=$c_val
|
||
|
||
output_var="${graph_var}"
|
||
}
|
||
|
||
print() { #? Print text, set true-color foreground/background color, add effects, center text, move cursor, save cursor position and restore cursor postion
|
||
#? 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]
|
||
#? [-/+ul, -/+underline] [-/+i, -/+italic] [-/+bl, -/+blink] [-f, -font "sans-serif|script|fraktur|monospace|double-struck"]
|
||
#? Manipulation: [-m, -move <line> <column>] [-l, -left <x>] [-r, -right <x>] [-u, -up <x>] [-d, -down <x>] [-c, -center] [-sc, -save] [-rc, -restore]
|
||
#? [-jl, -justify-left <width>] [-jr, -justify-right <width>] [-jc, -justify-center <width>] [-rp, -repeat <x>]
|
||
#? Text: [-v, -variable "variable-name"] [-stdin] [-t, -text "string"] ["string"]
|
||
|
||
#* Return if no arguments is given
|
||
if [[ -z $1 ]]; then return; fi
|
||
|
||
#* Just echo and return if only one argument and not a valid option
|
||
if [[ $# -eq 1 && ${1::1} != "-" ]]; then echo -en "$1"; return; fi
|
||
|
||
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#"
|
||
local justify_left justify_right justify_center repeat r_tmp trans
|
||
|
||
|
||
#* Loop function until we are out of arguments
|
||
until (($#==0)); do
|
||
|
||
#* Argument parsing
|
||
until (($#==0)); do
|
||
case $1 in
|
||
-t|-text) text="$2"; shift 2; break;; #? String to print
|
||
-stdin) text="$(</dev/stdin)"; shift; break;; #? Print from stdin
|
||
-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>"
|
||
if [[ ${2::1} == "#" ]]; then
|
||
val=${2//#/}
|
||
if [[ ${#val} == 6 ]]; then fgc="\e[38;2;$((${hex}${val:0:2}));$((${hex}${val:2:2}));$((${hex}${val:4:2}))m"; shift
|
||
elif [[ ${#val} == 2 ]]; then fgc="\e[38;2;$((${hex}${val:0:2}));$((${hex}${val:0:2}));$((${hex}${val:0:2}))m"; shift
|
||
fi
|
||
elif is_int "${@:2:3}"; then fgc="\e[38;2;$2;$3;$4m"; shift 3
|
||
fi
|
||
;;
|
||
-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>"
|
||
if [[ ${2::1} == "#" ]]; then
|
||
val=${2//#/}
|
||
if [[ ${#val} == 6 ]]; then bgc="\e[48;2;$((${hex}${val:0:2}));$((${hex}${val:2:2}));$((${hex}${val:4:2}))m"; shift
|
||
elif [[ ${#val} == 2 ]]; then bgc="\e[48;2;$((${hex}${val:0:2}));$((${hex}${val:0:2}));$((${hex}${val:0:2}))m"; shift
|
||
fi
|
||
elif is_int "${@:2:3}"; then bgc="\e[48;2;$2;$3;$4m"; shift 3
|
||
fi
|
||
;;
|
||
-c|-center) center=1;; #? Center text horizontally on screen
|
||
-rs|-reset) effect="0${effect}${theme[main_bg]}";; #? Reset text colors and effects
|
||
-b|-bold) effect="${effect}${effect:+;}1"; bold=1;; #? Enable bold text
|
||
+b|+bold) effect="${effect}${effect:+;}21"; bold=0;; #? Disable bold text
|
||
-da|-dark) effect="${effect}${effect:+;}2";; #? Enable dark text
|
||
+da|+dark) effect="${effect}${effect:+;}22";; #? Disable dark text
|
||
-i|-italic) effect="${effect}${effect:+;}3"; italic=1;; #? Enable italic text
|
||
+i|+italic) effect="${effect}${effect:+;}23"; italic=0;; #? Disable italic text
|
||
-ul|-underline) effect="${effect}${effect:+;}4";; #? Enable underlined text
|
||
+ul|+underline) effect="${effect}${effect:+;}24";; #? Disable underlined text
|
||
-bl|-blink) effect="${effect}${effect:+;}5";; #? Enable blinking text
|
||
+bl|+blink) effect="${effect}${effect:+;}25";; #? Disable blinking text
|
||
-f|-font) if [[ $2 =~ ^(sans-serif|script|fraktur|monospace|double-struck)$ ]]; then custom_font="$2"; shift; fi;; #? Set custom font
|
||
-m|-move) add_command="${add_command}\e[${2};${3}f"; shift 2;; #? Move to postion "LINE" "COLUMN"
|
||
-l|-left) add_command="${add_command}\e[${2}D"; shift;; #? Move left x columns
|
||
-r|-right) add_command="${add_command}\e[${2}C"; shift;; #? Move right x columns
|
||
-u|-up) add_command="${add_command}\e[${2}A"; shift;; #? Move up x lines
|
||
-d|-down) add_command="${add_command}\e[${2}B"; shift;; #? Move down x lines
|
||
-jl|-justify-left) justify_left="${2}"; shift;; #? Justify string left within given width
|
||
-jr|-justify-right) justify_right="${2}"; shift;; #? Justify string right within given width
|
||
-jc|-justify-center) justify_center="${2}"; shift;; #? Justify string center within given width
|
||
-rp|-repeat) repeat=${2}; shift;; #? Repeat next string x number of times
|
||
-sc|-save) add_command="\e[s${add_command}";; #? Save cursor position
|
||
-rc|-restore) add_command="${add_command}\e[u";; #? Restore cursor position
|
||
-trans) trans=1;; #? Make whitespace transparent
|
||
-v|-variable) local -n var=$2; ext_var=1; shift;; #? Send output to a variable, appending if not unset
|
||
*) text="$1"; shift; break;; #? Assumes text string if no argument is found
|
||
esac
|
||
shift
|
||
done
|
||
|
||
#* Repeat string if repeat is enabled
|
||
if [[ -n $repeat ]]; then
|
||
printf -v r_tmp "%${repeat}s" ""
|
||
text="${r_tmp// /$text}"
|
||
fi
|
||
|
||
#* Set correct placement for screen centered text
|
||
if ((center==1 & ${#text}>0 & ${#text}<tty_width-4)); then
|
||
add_command="${add_command}\e[${tty_width}D\e[$(( (tty_width/2)-(${#text}/2) ))C"
|
||
fi
|
||
|
||
#* Convert text string to custom font if set and remove non working effects
|
||
if [[ -n $custom_font ]]; then
|
||
unset effect
|
||
text=$(set_font "${custom_font}${bold:+" bold"}${italic:+" italic"}" "${text}")
|
||
fi
|
||
|
||
#* Set text justification if set
|
||
if [[ -n $justify_left ]] && ((${#text}<justify_left)); then
|
||
printf -v text "%s%$((justify_left-${#text}))s" "${text}" ""
|
||
elif [[ -n $justify_right ]] && ((${#text}<justify_right)); then
|
||
printf -v text "%$((justify_right-${#text}))s%s" "" "${text}"
|
||
elif [[ -n $justify_center ]] && ((${#text}<justify_center)); then
|
||
printf -v text "%$(( (justify_center/2)-(${#text}/2) ))s%s" "" "${text}"
|
||
printf -v text "%s%-$((justify_center-${#text}))s" "${text}" ""
|
||
fi
|
||
|
||
if [[ -n $trans ]]; then
|
||
text="${text// /'\e[1C'}"
|
||
fi
|
||
|
||
#* Create text string
|
||
if [[ -n $effect ]]; then effect="\e[${effect}m"; fi
|
||
out="${out}${add_command}${effect}${bgc}${fgc}${text}"
|
||
unset add_command effect fgc bgc center justify_left justify_right justify_center custom_font text repeat trans justify
|
||
done
|
||
|
||
#* Print the string to stdout if variable out hasn't been set
|
||
if [[ -z $ext_var ]]; then echo -en "$out"
|
||
else var="${var}${out}"; fi
|
||
|
||
}
|
||
|
||
collect_cpu() { #? Collects cpu stats from /proc/stat and compares with previously collected sample to get cpu usage
|
||
#? Returns cpu usage in array "cpu_usage", index 0 is usage for all threads, following indices corresponds to thread usage in multicore/hyperthreading cpus
|
||
local freq thread i threads=${cpu[threads]}
|
||
local -a stat_array stat_input
|
||
|
||
#* Get values from /proc/stat or psutil, compare to get cpu usage
|
||
if [[ $use_psutil == true ]]; then
|
||
local -a usage_arr
|
||
local x=1
|
||
py_command -a usage_arr "get_cpu_usage()"
|
||
cpu_usage[0]=${usage_arr[0]}
|
||
for thread in ${usage_arr[@]:1}; do
|
||
cpu_usage[$((x++))]=$thread
|
||
done
|
||
py_command -v cpu[freq] "get_cpu_freq()"
|
||
py_command -v cpu[uptime] "get_uptime()"
|
||
py_command -v cpu[load_avg] "get_load_avg()"
|
||
else
|
||
readarray -t stat_input </proc/stat
|
||
thread=0
|
||
while ((thread<threads+1)); do
|
||
stat_array=(${stat_input[thread]})
|
||
cpu[new_${thread}]=$((stat_array[1]+stat_array[2]+stat_array[3]+stat_array[4]))
|
||
cpu[idle_new_${thread}]=${stat_array[4]}
|
||
if [[ -n ${cpu[old_${thread}]} && -n ${cpu[idle_new_${thread}]} && ${cpu[old_${thread}]} -ne ${cpu[new_${thread}]} ]]; then
|
||
cpu_usage[${thread}]="$(( ( 100*(${cpu[old_${thread}]}-${cpu[new_${thread}]}-${cpu[idle_old_${thread}]}+${cpu[idle_new_${thread}]}) ) / (${cpu[old_${thread}]}-${cpu[new_${thread}]}) ))"
|
||
fi
|
||
cpu[old_${thread}]=${cpu[new_${thread}]}
|
||
cpu[idle_old_${thread}]=${cpu[idle_new_${thread}]}
|
||
((++thread))
|
||
done
|
||
fi
|
||
|
||
#* Copy cpu usage for cpu package and cores to cpu history arrays and trim earlier entries
|
||
if ((${#cpu_history[@]}>tty_width*4)); then
|
||
cpu_history=( "${cpu_history[@]:$((tty_width*2))}" "${cpu_usage[0]}")
|
||
else
|
||
cpu_history+=("${cpu_usage[0]}")
|
||
fi
|
||
|
||
for((i=1;i<=threads;i++)); do
|
||
local -n cpu_core_history="cpu_core_history_$i"
|
||
if ((${#cpu_core_history[@]}>40)); then
|
||
cpu_core_history=( "${cpu_core_history[@]:20}" "${cpu_usage[$i]}")
|
||
else
|
||
cpu_core_history+=("${cpu_usage[$i]}")
|
||
fi
|
||
done
|
||
|
||
#* Get current cpu frequency from "/proc/cpuinfo" and convert to appropriate unit
|
||
if [[ $use_psutil == false && -z ${cpu[no_cpu_info]} ]] && ! get_value -v 'cpu[freq]' -sf "/proc/cpuinfo" -k "cpu MHz" -i; then
|
||
cpu[no_cpu_info]=1
|
||
fi
|
||
|
||
#* If getting cpu frequency from "proc/cpuinfo" was unsuccessfull try "/sys/devices/../../scaling_cur_freq"
|
||
if [[ $use_psutil == false && -n ${cpu[no_cpu_info]} && -e "/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq" ]]; then
|
||
get_value -v 'cpu[freq]' -sf "/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq" -i
|
||
printf -v 'cpu[freq]' "%.0f0" "${cpu[freq]}e-4"
|
||
fi
|
||
|
||
if ((${#cpu[freq]}>3)); then cpu[freq_string]="${cpu[freq]::-3}.${cpu[freq]:(-3):1} GHz"
|
||
elif ((${#cpu[freq]}>1)); then cpu[freq_string]="${cpu[freq]} MHz"
|
||
else cpu[freq_string]=""; fi
|
||
|
||
#* Get load average and uptime from uptime command
|
||
if [[ $use_psutil == false ]]; then
|
||
local uptime_var
|
||
read -r uptime_var < <(uptime 2>/dev/null || true)
|
||
cpu[load_avg]="${uptime_var#*average: }"
|
||
cpu[load_avg]="${cpu[load_avg]//,/}"
|
||
cpu[uptime]="${uptime_var#*up }"
|
||
cpu[uptime]="${cpu[uptime]%%, *}"
|
||
fi
|
||
|
||
#* Collect cpu temps if enabled
|
||
if [[ $check_temp == true ]]; then collect_cpu_temps; fi
|
||
}
|
||
|
||
collect_cpu_temps() { #? Collect cpu temperatures
|
||
local unit c div threads=${cpu[threads]} sens_var i it ccd_value breaking core_value misc_var
|
||
local -a ccd_array core_array
|
||
|
||
#* Fetch output from "sensors" command or psutil to a variable
|
||
if [[ $sensor_comm == "psutil" ]]; then
|
||
if ! py_command -vn sens_var "get_sensors()"; then
|
||
if command -v sensors >/dev/null 2>&1; then sensor_comm="sensors"
|
||
else sensor_comm=""; check_temp="false"; resized=1; return; fi
|
||
fi
|
||
fi
|
||
if [[ $sensor_comm == "sensors" ]]; then
|
||
if [[ $use_psutil == true ]]; then
|
||
py_command -vn sens_var "get_cmd_out('sensors 2>/dev/null')"
|
||
else
|
||
read -rd '' sens_var < <(sensors 2>/dev/null || true) || true
|
||
fi
|
||
elif [[ $sensor_comm != "sensors" && $sensor_comm != "psutil" ]]; then
|
||
if [[ $use_psutil == true ]]; then
|
||
py_command -v misc_var "get_cmd_out('${sensor_comm} measure_temp 2>/dev/null')"
|
||
else
|
||
read -r misc_var < <(${sensor_comm} measure_temp 2>/dev/null ||true)
|
||
fi
|
||
fi
|
||
|
||
#* Get CPU package temp for intel cpus
|
||
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
|
||
#* If successful get temperature unit, convert temp to integer and get high and crit
|
||
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
|
||
if [[ -z ${cpu[temp_high]} ]]; then
|
||
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))
|
||
else get_value -v 'cpu[temp_crit]' -sv "sens_var" -k "Package*crit =" -m 2 -r "[^0-9.]" -b -i; fi
|
||
fi
|
||
|
||
#* Get core temps
|
||
i=0
|
||
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
|
||
|
||
if [[ -z ${core_array[0]} ]]; then core_array=("${cpu[temp_0]}"); fi
|
||
|
||
if ((${#core_array[@]}<threads/2)); then
|
||
for((i=${#core_array[@]};i<threads/2;i++)); do
|
||
core_array+=("${cpu[temp_0]}")
|
||
done
|
||
fi
|
||
|
||
#* Copy core values to hyperthreading cores
|
||
i=1
|
||
for core_value in "${core_array[@]}"; do
|
||
cpu[temp_$((i))]="${core_value}"
|
||
cpu[temp_$((threads/2+i))]="${core_value}"
|
||
((i++))
|
||
done
|
||
|
||
#* Get CPU package temp for amd ryzen cpus
|
||
elif get_value -v 'cpu[temp_0]' -sv "sens_var" -k "Tdie:" -mk 1; then
|
||
#* If successful get temperature unit, convert temp to integer and get high
|
||
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
|
||
if [[ -z ${cpu[temp_high]} ]]; then
|
||
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
|
||
cpu[temp_crit]=$((cpu[temp_high]+10))
|
||
fi
|
||
|
||
#* Get ccd module temps
|
||
i=1
|
||
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
|
||
|
||
if [[ -z ${ccd_array[0]} ]]; then ccd_array=("${cpu[temp_0]}"); fi
|
||
|
||
#* Copy ccd values to cores in each ccd
|
||
z=1
|
||
for ccd_value in "${ccd_array[@]}"; do
|
||
for((i=0;i<threads/${#ccd_array[@]};i++)); do
|
||
cpu[temp_$((z+i))]="${ccd_value}"
|
||
done
|
||
z=$((z+i))
|
||
done
|
||
|
||
#* Get CPU package temp for Rapberry Pi cpus and for OSX
|
||
elif [[ $sensor_comm != "sensors" && -n ${misc_var} ]]; then
|
||
cpu[temp_0]="${misc_var#temp=}"
|
||
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
|
||
if [[ -z ${cpu[temp_high]} ]]; then
|
||
cpu[temp_high]="75"; cpu[temp_crit]=$((cpu[temp_high]+10))
|
||
fi
|
||
|
||
#* Copy cpu temp to cores
|
||
for((i=1;i<=threads;i++)); do
|
||
cpu[temp_${i}]="${cpu[temp_0]}"
|
||
done
|
||
|
||
#* If unsuccessful turn off temperature checking
|
||
else
|
||
check_temp="false"
|
||
resized=1
|
||
fi
|
||
|
||
if [[ $check_temp == true ]]; then
|
||
local tmp_temp
|
||
for((i=0;i<=threads;i++)); do
|
||
tmp_temp="$(( (${cpu[temp_${i}]}-20)*100/(cpu[temp_high]-20) ))"
|
||
if ((tmp_temp>100)); then tmp_temp=100; elif ((tmp_temp<0)); then tmp_temp=0; fi
|
||
local -n cpu_temp_history="cpu_temp_history_$i"
|
||
if ((${#cpu_temp_history[@]}>20)); then
|
||
cpu_temp_history=( "${cpu_temp_history[@]:10}" "${tmp_temp}")
|
||
else
|
||
cpu_temp_history+=("${tmp_temp}")
|
||
fi
|
||
done
|
||
fi
|
||
}
|
||
|
||
collect_mem() { #? Collect memory information from "/proc/meminfo"
|
||
((++mem[counter]))
|
||
|
||
#if [[ $use_psutil == false ]] && ((mem[counter]<4)); then return; fi
|
||
if ((mem[counter]<4)); then return; fi
|
||
mem[counter]=0
|
||
|
||
local i tmp value array mem_info height=$((box[mem_height]-2)) skip filter_value
|
||
local -a mem_array swap_array available=("mem")
|
||
unset 'mem[total]'
|
||
|
||
#* Get memory and swap information from "/proc/meminfo" or psutil and calculate percentages
|
||
if [[ $use_psutil == true ]]; then
|
||
local pymemout
|
||
|
||
py_command -v pymemout "get_mem()" || return
|
||
read mem[total] mem[free] mem[available] mem[cached] swap[total] swap[free] <<<"$pymemout"
|
||
|
||
if [[ -z ${mem[total]} ]]; then return; fi
|
||
if [[ -n ${swap[total]} ]] && ((swap[total]>0)); then
|
||
swap[free_percent]=$((swap[free]*100/swap[total]))
|
||
swap[used]=$((swap[total]-swap[free]))
|
||
swap[used_percent]=$((swap[used]*100/swap[total]))
|
||
available+=("swap")
|
||
else
|
||
unset swap_on
|
||
fi
|
||
else
|
||
read -rd '' mem_info </proc/meminfo ||true
|
||
get_value -v 'mem[total]' -sv "mem_info" -k "MemTotal:" -i
|
||
get_value -v 'mem[free]' -sv "mem_info" -k "MemFree:" -i
|
||
if ! get_value -v 'mem[available]' -sv "mem_info" -k "MemAvailable:" -i; then
|
||
get_value -v 'mem[available]' -sv "mem_info" -k "Inactive:" -i
|
||
mem[available]=$((mem[available]+mem[free]))
|
||
fi
|
||
get_value -v 'mem[cached]' -sv "mem_info" -k "Cached:" -i
|
||
fi
|
||
|
||
mem[available_percent]=$((mem[available]*100/mem[total]))
|
||
mem[used]=$((mem[total]-mem[available]))
|
||
mem[used_percent]=$((mem[used]*100/mem[total]))
|
||
mem[free_percent]=$((mem[free]*100/mem[total]))
|
||
mem[cached_percent]=$((mem[cached]*100/mem[total]))
|
||
|
||
if [[ $use_psutil == false ]] && get_value -v swap[total] -sv "mem_info" -k "SwapTotal:" -i && ((swap[total]>0)); then
|
||
get_value -v 'swap[free]' -sv "mem_info" -k "SwapFree:" -i
|
||
swap[free_percent]=$((swap[free]*100/swap[total]))
|
||
|
||
swap[used]=$((swap[total]-swap[free]))
|
||
swap[used_percent]=$((swap[used]*100/swap[total]))
|
||
|
||
available+=("swap")
|
||
elif [[ $use_psutil == false ]]; then
|
||
unset swap_on
|
||
fi
|
||
|
||
#* Convert values to floating point and humanize
|
||
for array in ${available[@]}; do
|
||
for value in total used free available cached; do
|
||
if [[ $array == "swap" && $value == "available" ]]; then break 2; fi
|
||
local -n this_value="${array}[${value}]" this_string="${array}[${value}_string]"
|
||
floating_humanizer -v this_string -s 1 -B "${this_value}"
|
||
done
|
||
done
|
||
|
||
#* Get disk information
|
||
local df_line line_array dev_path dev_name iostat_var disk_read disk_write disk_io_string df_count=0 filtering psutil_on
|
||
local -a device_array iostat_array df_array
|
||
unset 'disks_free[@]' 'disks_used[@]' 'disks_used_percent[@]' 'disks_total[@]' 'disks_name[@]' 'disks_free_percent[@]' 'disks_io[@]'
|
||
if [[ -n $psutil_disk_fail ]]; then psutil_on="false"; else psutil_on="$use_psutil"; fi
|
||
if [[ $psutil_on == true ]]; then
|
||
if [[ -n $disks_filter ]]; then filtering=", filtering='${disks_filter}'"; fi
|
||
if ! py_command -a df_array "get_disks(exclude='squashfs'${filtering})"; then psutil_disk_fail=1; psutil_on="false"; fi
|
||
fi
|
||
if [[ $psutil_on == false ]]; then
|
||
readarray -t df_array < <(${df} -x squashfs -x tmpfs -x devtmpfs -x overlay -x 9p 2>/dev/null || true)
|
||
fi
|
||
for df_line in "${df_array[@]:1}"; do
|
||
line_array=(${df_line})
|
||
if ! is_int "${line_array[1]}" || ((line_array[1]<=0)); then continue; fi
|
||
|
||
if [[ $psutil_on == false && ${line_array[5]} == "/" ]]; then disks_name+=("root")
|
||
elif [[ $psutil_on == false ]]; then disks_name+=("${line_array[5]##*/}")
|
||
elif [[ $psutil_on == true ]]; then disks_name+=("${line_array[*]:7}"); fi
|
||
|
||
#* Filter disks showed if $disks_filter is set
|
||
if [[ $psutil_on == false && -n $disks_filter ]]; then
|
||
unset found
|
||
for filter_value in ${disks_filter}; do
|
||
if [[ $filter_value == "${disks_name[-1]}" ]]; then found=1; fi
|
||
done
|
||
fi
|
||
|
||
if [[ $psutil_on == true || -z $disks_filter || -n $found ]]; then
|
||
disks_total+=("$(floating_humanizer -s 1 -B ${line_array[1]})")
|
||
disks_used+=("$(floating_humanizer -s 1 -B ${line_array[2]})")
|
||
disks_used_percent+=("${line_array[4]%'%'}")
|
||
disks_free+=("$(floating_humanizer -s 1 -B ${line_array[3]})")
|
||
disks_free_percent+=("$((100-${line_array[4]%'%'}))")
|
||
|
||
#* Get read/write stats for disk from iostat or psutil if available
|
||
if [[ $psutil_on == true || -n $has_iostat ]]; then
|
||
unset disk_io_string
|
||
dev_name="${line_array[0]##*/}"
|
||
if [[ $psutil_on == false && ${dev_name::2} == "md" ]]; then dev_name="${dev_name::3}"; fi
|
||
if [[ $psutil_on == false ]]; then
|
||
unset iostat_var 'iostat_array[@]'
|
||
dev_path="${line_array[0]%${dev_name}}"
|
||
read -r iostat_var < <(iostat -dkz "${dev_path}${dev_name}" | tail -n +4)
|
||
iostat_array=(${iostat_var})
|
||
fi
|
||
if [[ $psutil_on == true || -n ${iostat_var} ]]; then
|
||
|
||
if [[ $psutil_on == true ]]; then
|
||
disk_read=${line_array[5]}
|
||
disk_write=${line_array[6]}
|
||
else
|
||
disk_read=$((iostat_array[-2]-${disks[${dev_name}_read]:-${iostat_array[-2]}}))
|
||
disk_write=$((iostat_array[-1]-${disks[${dev_name}_write]:-${iostat_array[-1]}}))
|
||
fi
|
||
|
||
if ((box[m_width2]>25)); then
|
||
if ((disk_read>0)); then disk_io_string="▲$(floating_humanizer -s 1 -short -B ${disk_read}) "; fi
|
||
if ((disk_write>0)); then disk_io_string+="▼$(floating_humanizer -s 1 -short -B ${disk_write})"; fi
|
||
elif ((disk_read+disk_write>0)); then
|
||
disk_io_string+="▼▲$(floating_humanizer -s 1 -short -B $((disk_read+disk_write)))"
|
||
fi
|
||
|
||
if [[ $psutil_on == false ]]; then
|
||
disks[${dev_name}_read]="${iostat_array[-2]}"
|
||
disks[${dev_name}_write]="${iostat_array[-1]}"
|
||
fi
|
||
fi
|
||
disks_io+=("${disk_io_string:-0}")
|
||
fi
|
||
else
|
||
unset 'disks_name[-1]'
|
||
disks_name=("${disks_name[@]}")
|
||
fi
|
||
|
||
if ((${#disks_name[@]}>=height/2)); then break; fi
|
||
|
||
done
|
||
|
||
|
||
}
|
||
|
||
collect_processes() { #? Collect process information and calculate accurate cpu usage
|
||
if [[ $use_psutil == true ]]; then collect_processes_psutil $1; return; fi
|
||
local argument="$1"
|
||
if [[ -n $skip_process_draw && $argument != "now" ]]; then return; fi
|
||
local width=${box[processes_width]} height=${box[processes_height]} format_args format_cmd readline sort symbol="▼" cpu_title options pid_string tmp selected
|
||
local tree tree_compare1 tree_compare2 tree_compare3 no_core_divide pids
|
||
local -a grep_array saved_proc_array
|
||
|
||
if [[ $argument == "now" ]]; then skip_process_draw=1; fi
|
||
|
||
if [[ -n ${proc[reverse]} ]]; then symbol="▲"; fi
|
||
case ${proc_sorting} in
|
||
"pid") selected="Pid:"; sort="pid";;
|
||
"program") selected="Program:"; sort="comm";;
|
||
"arguments") selected="Arguments:"; sort="args";;
|
||
"threads") selected="Threads:"; sort="nlwp";;
|
||
"user") selected="User:"; sort="euser";;
|
||
"memory") selected="Mem%"; sort="pmem";;
|
||
"cpu lazy"|"cpu responsive") sort="pcpu"; selected="Cpu%";;
|
||
esac
|
||
|
||
if [[ $proc_tree == true ]]; then tree="Tree:"; fi
|
||
if [[ $proc_per_core == true ]]; then no_core_divide="1"; fi
|
||
|
||
#* Collect output from ps command to array
|
||
if ((width>60)) && [[ $proc_tree != true ]] ; then format_args=",args:$(( width-(47+proc[pid_len]) ))=Arguments:"; format_cmd=15
|
||
else format_cmd=$(( width-(31+proc[pid_len]) )); fi
|
||
saved_proc_array=("${proc_array[@]}")
|
||
unset 'proc_array[@]' 'pid_array[@]'
|
||
|
||
if ((proc[detailed]==0)) && [[ -n ${proc[detailed_name]} ]]; then
|
||
unset 'proc[detailed_name]' 'proc[detailed_killed]' 'proc[detailed_cpu_int]' 'proc[detailed_cmd]'
|
||
unset 'proc[detailed_mem]' 'proc[detailed_mem_int]' 'proc[detailed_user]' 'proc[detailed_threads]'
|
||
unset 'detail_graph[@]' 'detail_mem_graph' 'detail_history[@]' 'detail_mem_history[@]'
|
||
unset 'proc[detailed_runtime]' 'proc[detailed_mem_string]' 'proc[detailed_parent_pid]' 'proc[detailed_parent_name]'
|
||
fi
|
||
|
||
unset 'proc[detailed_cpu]'
|
||
|
||
if [[ -z $filter ]]; then
|
||
options="-t"
|
||
fi
|
||
|
||
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})
|
||
|
||
proc_array[0]="${proc_array[0]/ Tr:/ Threads:}"
|
||
proc_array[0]="${proc_array[0]/ ${selected}/${symbol}${selected}}"
|
||
|
||
if [[ -n $filter ]]; then
|
||
grep_array[0]="${proc_array[0]}"
|
||
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)
|
||
proc_array=("${grep_array[@]}")
|
||
fi
|
||
|
||
|
||
#* Get accurate cpu usage by fetching and comparing values in /proc/"pid"/stat
|
||
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
|
||
local -a cpu_percent statfile work_array
|
||
|
||
#* Timestamp the values in milliseconds to accurately calculate cpu usage
|
||
get_ms proc[new_timestamp]
|
||
|
||
for readline in "${proc_array[@]:1}"; do
|
||
((++count))
|
||
|
||
if ((count==height-3 & breaking==0)); then
|
||
if [[ -n $filter || $proc_sorting != "cpu lazy" || ${proc[selected]} -gt 0 || ${proc[start]} -gt 1 || ${proc_reversed} == true ]]; then :
|
||
else breaking=1; fi
|
||
fi
|
||
|
||
#if get_key -save && [[ ${#saved_key[@]} -gt 0 ]]; then proc_array=("${saved_proc_array[@]}"); return; fi
|
||
|
||
if ((breaking==2)); then
|
||
work_array=(${proc_array[-1]})
|
||
else
|
||
work_array=(${readline})
|
||
fi
|
||
|
||
pid="${work_array[0]}"
|
||
pcpu_usage="${work_array[-1]}"
|
||
|
||
#* If showing tree structure replace slashes and pipes with actual lines and terminate them at the correct places
|
||
if [[ $proc_tree == true ]]; then
|
||
tree_compare1="${proc_array[$((count+1))]%'\_'*}"
|
||
tree_compare2="${proc_array[count]%'\_'*}"
|
||
tree_compare3="${proc_array[$((count+1))]%'|'*}"
|
||
proc_array[count]="${proc_array[count]//'|'/│}"
|
||
proc_array[count]="${proc_array[count]//'\_'/└─}"
|
||
if ((count<${#proc_array[@]}-1)) && [[ ${#tree_compare1} -eq ${#tree_compare2} || ${#tree_compare2} -eq ${#tree_compare3} ]]; then
|
||
proc_array[count]="${proc_array[count]//'└'/├}"
|
||
fi
|
||
fi
|
||
|
||
pid_history[${pid}]="1"
|
||
|
||
if [[ -n $filter || $proc_sorting == "cpu responsive" ]] && [[ ${proc_array[count]:${proc[pid_len]}:1} != " " ]]; then
|
||
unset pid_string
|
||
printf -v pid_string "%${proc[pid_len]}s" "${pid}"
|
||
proc_array[count]="${pid_string}${proc_array[count]#*${pid}}"
|
||
fi
|
||
|
||
if [[ -r "/proc/${pid}/stat" ]] && read -ra statfile </proc/${pid}/stat 2>/dev/null; then
|
||
|
||
utime=${statfile[13]}
|
||
stime=${statfile[14]}
|
||
|
||
proc[new_${pid}_ticks]=$((utime+stime))
|
||
|
||
|
||
if [[ -n ${proc[old_${pid}_ticks]} ]]; then
|
||
|
||
time_elapsed=$((proc[new_timestamp]-proc[old_timestamp]))
|
||
|
||
#* Calculate current cpu usage for process, * 1000 (for conversion from ms to seconds) * 1000 (for conversion to floating point)
|
||
cpu_percent[count]=$(( ( ( ${proc[new_${pid}_ticks]}-${proc[old_${pid}_ticks]} ) * 1000 * 1000 ) / ( cpu[hz]*time_elapsed*${no_core_divide:-${cpu[threads]}} ) ))
|
||
|
||
if ((cpu_percent[count]<0)); then cpu_percent[count]=0
|
||
elif [[ -z $no_core_divide ]] && ((cpu_percent[count]>1000)); then cpu_percent[count]=1000; fi
|
||
|
||
if ((${#cpu_percent[count]}<=3)); then
|
||
printf -v cpu_percent_string "%01d%s" "${cpu_percent[count]::-1}" ".${cpu_percent[count]:(-1)}"
|
||
else
|
||
cpu_percent_string=${cpu_percent[count]::-1}
|
||
fi
|
||
|
||
printf -v cpu_percent_string "%5s" "${cpu_percent_string::4}"
|
||
|
||
proc_array[count]="${proc_array[count]::-5}${cpu_percent_string}"
|
||
|
||
|
||
pid_graph="pid_${pid}_graph"
|
||
local -n pid_count="pid_${pid}_count"
|
||
|
||
printf -v cpu_int "%01d" "${cpu_percent[count]::-1}"
|
||
|
||
#* Get info for detailed box if enabled
|
||
if [[ ${pid} == "${proc[detailed_pid]}" ]]; then
|
||
if [[ -z ${proc[detailed_name]} ]]; then
|
||
local get_mem mem_string cmdline=""
|
||
local -a det_array
|
||
read -r proc[detailed_name] </proc/${pid}/comm ||true
|
||
mapfile -d $'\0' -n 0 cmdline </proc/${pid}/cmdline ||true
|
||
proc[detailed_cmd]="${cmdline[*]}"
|
||
proc[detailed_name]="${proc[detailed_name]::15}"
|
||
read -ra det_array < <(ps -o ppid:4,euser:15 --no-headers -p $pid || true)
|
||
proc[detailed_parent_pid]="${det_array[0]}"
|
||
proc[detailed_user]="${det_array[*]:1}"
|
||
read -r proc[detailed_parent_name] < <(ps -o comm --no-headers -p ${det_array[0]} || true)
|
||
get_mem=1
|
||
fi
|
||
proc[detailed_cpu]="${cpu_percent_string// /}"
|
||
proc[detailed_cpu_int]="${cpu_int}"
|
||
proc[detailed_threads]="${work_array[-4]}"
|
||
read -r proc[detailed_runtime] < <(ps -o etime:4 --no-headers -p $pid || true)
|
||
|
||
if [[ ${proc[detailed_mem]} != "${work_array[-2]}" || -n $get_mem ]] || ((++proc[detailed_mem_count]>5)); then
|
||
proc[detailed_mem_count]=0
|
||
proc[detailed_mem]="${work_array[-2]}"
|
||
proc[detailed_mem_int]="${proc[detailed_mem]/./}"
|
||
if [[ ${proc[detailed_mem_int]::1} == "0" ]]; then proc[detailed_mem_int]="${proc[detailed_mem_int]:1}0"; fi
|
||
#* Scale up low mem values to see any changes on mini graph
|
||
if ((proc[detailed_mem_int]>900)); then proc[detailed_mem_int]=$((proc[detailed_mem_int]/10))
|
||
elif ((proc[detailed_mem_int]>600)); then proc[detailed_mem_int]=$((proc[detailed_mem_int]/8))
|
||
elif ((proc[detailed_mem_int]>300)); then proc[detailed_mem_int]=$((proc[detailed_mem_int]/5))
|
||
elif ((proc[detailed_mem_int]>100)); then proc[detailed_mem_int]=$((proc[detailed_mem_int]/2))
|
||
elif ((proc[detailed_mem_int]<50)); then proc[detailed_mem_int]=$((proc[detailed_mem_int]*2)); fi
|
||
unset 'proc[detailed_mem_string]'
|
||
read -r mem_string < <(ps -o rss:1 --no-headers -p ${pid} || true)
|
||
floating_humanizer -v proc[detailed_mem_string] -B -s 1 $mem_string
|
||
if [[ -z ${proc[detailed_mem_string]} ]]; then proc[detailed_mem_string]="? Byte"; fi
|
||
fi
|
||
|
||
#* Copy process cpu usage to history array and trim earlier entries
|
||
if ((${#detail_history[@]}>box[details_width]*2)); then
|
||
detail_history=( "${detail_history[@]:${box[details_width]}}" "$((cpu_int+4))")
|
||
else
|
||
detail_history+=("$((cpu_int+4))")
|
||
fi
|
||
|
||
#* Copy process mem usage to history array and trim earlier entries
|
||
if ((${#detail_mem_history[@]}>box[details_width])); then
|
||
detail_mem_history=( "${detail_mem_history[@]:$((box[details_width]/2))}" "${proc[detailed_mem_int]}")
|
||
else
|
||
detail_mem_history+=("${proc[detailed_mem_int]}")
|
||
fi
|
||
|
||
#* Remove selected process from array if process is excluded by filtering or not on first page
|
||
if [[ -n $filter && ! ${proc[detailed_name]} =~ $filter ]]; then
|
||
unset 'proc_array[count]'
|
||
cpu_int=0; pid_count=0
|
||
fi
|
||
fi
|
||
|
||
#* Create small graphs for all visible processes using more than 1% cpu time
|
||
if [[ ${cpu_int} -gt 0 ]]; then pid_count=5; fi
|
||
|
||
if [[ -z ${!pid_graph} && ${cpu_int} -gt 0 ]]; then
|
||
tmp_value_array=("$((cpu_int+4))")
|
||
create_mini_graph -o "pid_${pid}_graph" -nc -w 5 "tmp_value_array"
|
||
elif [[ ${pid_count} -gt 0 ]]; then
|
||
if [[ ${cpu_int} -gt 9 ]]; then
|
||
create_mini_graph -nc -add-value "pid_${pid}_graph" "$((cpu_int+15))"
|
||
else
|
||
create_mini_graph -nc -add-value "pid_${pid}_graph" "$((cpu_int+9))"
|
||
fi
|
||
|
||
pid_count=$((${pid_count}-1))
|
||
elif [[ ${pid_count} == "0" ]]; then
|
||
unset "pid_${pid}_graph" "pid_${pid}_graph_even" "pid_${pid}_graph_odd" "pid_${pid}_graph_last_type" "pid_${pid}_graph_last_val"
|
||
unset "pid_${pid}_count"
|
||
fi
|
||
else
|
||
tmp_percent="${proc_array[count]:(-5)}"; tmp_percent="${tmp_percent// /}"; if [[ ${tmp_percent//./} != "$tmp_percent" ]]; then tmp_percent="${tmp_percent::-2}"; fi
|
||
if ((tmp_percent>100)); then
|
||
proc_array[count]="${proc_array[count]::-5} 100"
|
||
fi
|
||
fi
|
||
|
||
proc[old_${pid}_ticks]=${proc[new_${pid}_ticks]}
|
||
|
||
fi
|
||
|
||
if ((breaking==1)); then
|
||
if [[ ${proc[detailed]} == "1" && -z ${proc[detailed_cpu]} ]] && ps ${proc[detailed_pid]} >/dev/null 2>&1; then
|
||
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)
|
||
((++breaking))
|
||
else
|
||
break
|
||
fi
|
||
elif ((breaking==2)); then
|
||
unset 'proc_array[-1]'
|
||
break
|
||
fi
|
||
|
||
done
|
||
|
||
|
||
proc[old_timestamp]=${proc[new_timestamp]}
|
||
|
||
if ((proc[detailed]==1)) && [[ -z ${proc[detailed_cpu]} && -z ${proc[detailed_killed]} ]]; then proc[detailed_killed]=1; proc[detailed_change]=1
|
||
elif [[ -n ${proc[detailed_cpu]} ]]; then unset 'proc[detailed_killed]'; fi
|
||
|
||
#* Sort output array based on cpu usage if "cpu responsive" is selected
|
||
if [[ ${proc_sorting} == "cpu responsive" && ${proc_tree} != true ]]; then
|
||
local -a sort_array
|
||
if [[ -z ${proc[reverse]} ]]; then local sort_rev="-r"; fi
|
||
sort_array[0]="${proc_array[0]}"
|
||
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\.]* //')
|
||
proc_array=("${sort_array[@]}")
|
||
fi
|
||
|
||
#* Clear up memory by removing variables and graphs of no longer running processes
|
||
((++proc[general_counter]))
|
||
if ((proc[general_counter]>100)); then
|
||
proc[general_counter]=0
|
||
for pids in ${!pid_history[@]}; do
|
||
if [[ ! -e /proc/${pids} ]]; then
|
||
unset "pid_${pids}_graph" "pid_${pids}_graph_even" "pid_${pids}_graph_odd" "pid_${pids}_graph_last_type" "pid_${pids}_graph_last_val"
|
||
unset "pid_${pids}_count"
|
||
unset "proc[new_${pids}_ticks]"
|
||
unset "proc[old_${pids}_ticks]"
|
||
unset "pid_history[${pids}]"
|
||
fi
|
||
done
|
||
fi
|
||
|
||
}
|
||
|
||
collect_processes_psutil() {
|
||
local argument=$1
|
||
if [[ -n $skip_process_draw && $argument != "now" ]]; then return; fi
|
||
if [[ $argument == "now" ]]; then skip_process_draw=1; fi
|
||
local prog_len arg_len symbol="▼" selected width=${box[processes_width]} height=${box[processes_height]}
|
||
local pcpu_usage pids p_count cpu_int pids max_lines i pi
|
||
|
||
case ${proc_sorting} in
|
||
"pid") selected="Pid:";;
|
||
"program") selected="Program:";;
|
||
"arguments") selected="Arguments:";;
|
||
"threads") selected="Threads:";;
|
||
"user") selected="User:";;
|
||
"memory") selected="Mem%";;
|
||
"cpu lazy"|"cpu responsive") selected="Cpu%";;
|
||
esac
|
||
|
||
if [[ ${proc_tree} == true && ${proc_sorting} =~ pid|program|arguments ]]; then selected="Tree:"; fi
|
||
|
||
if [[ -n ${proc[reverse]} ]]; then symbol="▲"; fi
|
||
|
||
if ((proc[detailed]==0)) && [[ -n ${proc[detailed_name]} ]]; then
|
||
unset 'proc[detailed_name]' 'proc[detailed_killed]' 'proc[detailed_cpu_int]' 'proc[detailed_cmd]'
|
||
unset 'proc[detailed_mem]' 'proc[detailed_mem_int]' 'proc[detailed_user]' 'proc[detailed_threads]'
|
||
unset 'detail_graph[@]' 'detail_mem_graph' 'detail_history[@]' 'detail_mem_history[@]'
|
||
unset 'proc[detailed_runtime]' 'proc[detailed_mem_string]' 'proc[detailed_parent_pid]' 'proc[detailed_parent_name]'
|
||
fi
|
||
|
||
unset 'proc[detailed_cpu]'
|
||
|
||
|
||
if ((width>60)); then
|
||
arg_len=$((width-55))
|
||
prog_len=15
|
||
else
|
||
prog_len=$((width-40))
|
||
arg_len=0
|
||
if [[ $proc_sorting == "threads" ]]; then selected="Tr:"; fi
|
||
fi
|
||
|
||
|
||
unset 'proc_array[@]'
|
||
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
|
||
proc_array=(""); return
|
||
fi
|
||
|
||
proc_array[0]="${proc_array[0]/ ${selected}/${symbol}${selected}}"
|
||
|
||
for((i=1;i<${#proc_array[@]};i++)); do
|
||
if [[ -z ${proc_array[i]} ]]; then continue; fi
|
||
|
||
out_arr=(${proc_array[i]})
|
||
|
||
pi=0
|
||
if [[ $proc_tree == true ]]; then
|
||
while ! is_int "${out_arr[pi]}" && ((pi<${#out_arr[@]}-1)); do ((++pi)); done
|
||
fi
|
||
pid="${out_arr[pi]}"
|
||
if ! is_int "${pid}"; then continue; fi
|
||
|
||
pcpu_usage="${out_arr[-1]}"
|
||
|
||
if ! printf -v cpu_int "%.0f" "${pcpu_usage}" 2>/dev/null; then continue; fi
|
||
|
||
pid_history[${pid}]="1"
|
||
|
||
#* Create small graphs for all visible processes using more than 1% rounded cpu time
|
||
pid_graph="pid_${pid}_graph"
|
||
if ! local -n pid_count="pid_${pid}_count" 2>/dev/null; then continue; fi
|
||
|
||
if [[ ${cpu_int} -gt 0 ]]; then pid_count=5; fi
|
||
|
||
if [[ -z ${!pid_graph} && ${cpu_int} -gt 0 ]]; then
|
||
tmp_value_array=("$((cpu_int+4))")
|
||
create_mini_graph -o "pid_${pid}_graph" -nc -w 5 "tmp_value_array"
|
||
elif [[ ${pid_count} -gt 0 ]]; then
|
||
if [[ ${cpu_int} -gt 9 ]]; then
|
||
create_mini_graph -nc -add-value "pid_${pid}_graph" "$((cpu_int+15))"
|
||
elif [[ ${cpu_int} -gt 0 ]]; then
|
||
create_mini_graph -nc -add-value "pid_${pid}_graph" "$((cpu_int+9))"
|
||
else
|
||
create_mini_graph -nc -add-value "pid_${pid}_graph" "0"
|
||
fi
|
||
pid_count=$((${pid_count}-1))
|
||
elif [[ ${pid_count} == "0" ]]; then
|
||
unset "pid_${pid}_graph" "pid_${pid}_graph_even" "pid_${pid}_graph_odd" "pid_${pid}_graph_last_type" "pid_${pid}_graph_last_val"
|
||
unset "pid_${pid}_count"
|
||
fi
|
||
|
||
#* Get info for detailed box if enabled
|
||
if [[ ${pid} == "${proc[detailed_pid]}" ]]; then
|
||
local -a det_array
|
||
if [[ -z ${proc[detailed_name]} ]]; then
|
||
local get_mem mem_string cmdline=""
|
||
|
||
py_command -a det_array "get_detailed_names_cmd(${pid})"
|
||
|
||
if [[ -z ${det_array[0]} ]]; then continue; fi
|
||
proc[detailed_name]="${det_array[0]::15}"
|
||
proc[detailed_parent_name]="${det_array[1]}"
|
||
proc[detailed_user]="${det_array[2]}"
|
||
proc[detailed_cmd]="${det_array[3]}"
|
||
fi
|
||
proc[detailed_cpu]="${out_arr[-1]}"
|
||
proc[detailed_cpu_int]="${cpu_int}"
|
||
proc[detailed_threads]="${out_arr[-4]}"
|
||
|
||
unset 'det_array[@]'
|
||
py_command -a det_array "get_detailed_mem_time(${pid})"
|
||
|
||
if [[ -z ${det_array[0]} ]]; then continue; fi
|
||
unset 'proc[detailed_mem_string]'
|
||
floating_humanizer -v proc[detailed_mem_string] -B ${det_array[0]}
|
||
if [[ -z ${proc[detailed_mem_string]} ]]; then proc[detailed_mem_string]="? Byte"; fi
|
||
if ((${#det_array[1]}>8)); then proc[detailed_runtime]="${det_array[1]/ days, /-}"
|
||
else proc[detailed_runtime]="${det_array[1]}"; fi
|
||
|
||
proc[detailed_mem_count]=0
|
||
proc[detailed_mem]="${out_arr[-2]}"
|
||
proc[detailed_mem_int]="${proc[detailed_mem]/./}"
|
||
if [[ ${proc[detailed_mem_int]::1} == "0" ]]; then proc[detailed_mem_int]="${proc[detailed_mem_int]:1}0"; fi
|
||
#* Scale up low mem values to see any changes on mini graph
|
||
if ((proc[detailed_mem_int]>900)); then proc[detailed_mem_int]=$((proc[detailed_mem_int]/10))
|
||
elif ((proc[detailed_mem_int]>600)); then proc[detailed_mem_int]=$((proc[detailed_mem_int]/8))
|
||
elif ((proc[detailed_mem_int]>300)); then proc[detailed_mem_int]=$((proc[detailed_mem_int]/5))
|
||
elif ((proc[detailed_mem_int]>100)); then proc[detailed_mem_int]=$((proc[detailed_mem_int]/2))
|
||
elif ((proc[detailed_mem_int]<50)); then proc[detailed_mem_int]=$((proc[detailed_mem_int]*2)); fi
|
||
|
||
#* Copy process cpu usage to history array and trim earlier entries
|
||
if ((${#detail_history[@]}>box[details_width]*2)); then
|
||
detail_history=( "${detail_history[@]:${box[details_width]}}" "$((cpu_int+4))")
|
||
else
|
||
detail_history+=("$((cpu_int+4))")
|
||
fi
|
||
|
||
#* Copy process mem usage to history array and trim earlier entries
|
||
if ((${#detail_mem_history[@]}>box[details_width])); then
|
||
detail_mem_history=( "${detail_mem_history[@]:$((box[details_width]/2))}" "${proc[detailed_mem_int]}")
|
||
else
|
||
detail_mem_history+=("${proc[detailed_mem_int]}")
|
||
fi
|
||
fi
|
||
|
||
if ((i==height-2)); then
|
||
if [[ ${proc[selected]} -gt 0 || -n $filter || ${proc[start]} -gt 1 ]] || [[ ${proc[detailed]} -eq 1 && -z ${proc[detailed_cpu]} && -z ${proc[detailed_killed]} ]]; then :
|
||
else break; fi
|
||
fi
|
||
|
||
done
|
||
|
||
if ((proc[detailed]==1)) && [[ -z ${proc[detailed_cpu]} && -z ${proc[detailed_killed]} ]]; then proc[detailed_killed]=1; proc[detailed_change]=1
|
||
elif [[ -n ${proc[detailed_cpu]} ]]; then unset 'proc[detailed_killed]'; fi
|
||
|
||
|
||
|
||
#* Clear up memory
|
||
((++proc[general_counter]))
|
||
if ((proc[general_counter]>100)); then
|
||
proc[general_counter]=0
|
||
for pids in ${!pid_history[@]}; do
|
||
unset "pid_${pids}_graph" "pid_${pids}_graph_even" "pid_${pids}_graph_odd" "pid_${pids}_graph_last_type" "pid_${pids}_graph_last_val"
|
||
unset "pid_${pids}_count"
|
||
unset "pid_history[${pids}]"
|
||
done
|
||
fi
|
||
|
||
}
|
||
|
||
collect_net() { #? Collect information from "/proc/net/dev"
|
||
local operations operation direction index unit_selector speed speed_B total
|
||
local -a net_dev history_sorted history_last
|
||
|
||
if [[ -n ${net[no_device]} ]]; then return; fi
|
||
|
||
if [[ $1 == "init" ]]; then
|
||
for direction in "download" "upload"; do
|
||
net[${direction}_max]=0
|
||
net[${direction}_new_low]=0
|
||
net[${direction}_new_max]=0
|
||
net[${direction}_max_current]=0
|
||
net[${direction}_graph_max]=$((50<<10))
|
||
done
|
||
unset 'download_graph[@]' 'upload_graph[@]' 'net_history_download[@]' 'net_history_upload[@]'
|
||
fi
|
||
|
||
#* 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
|
||
if [[ $use_psutil == true ]]; then
|
||
py_command -v net_dev "get_net('${net[device]}')" || return
|
||
net_dev=(${net_dev})
|
||
if ! is_int "${net_dev[0]}"; then net[no_device]=1; return; fi
|
||
else
|
||
if ! get_value -map net_dev -sf "/proc/net/dev" -k "${net[device]}" -a; then net[no_device]=1; return; fi
|
||
fi
|
||
|
||
#* Timestamp the values to accurately calculate values in seconds
|
||
get_ms net[new_timestamp]
|
||
for direction in "download" "upload"; do
|
||
if [[ $direction == "download" ]]; then index=1
|
||
else index=9; fi
|
||
|
||
net[new_${direction}]=${net_dev[index]}
|
||
|
||
if [[ -n ${net[old_${direction}]} ]]; then
|
||
#* Get total, convert to floating point and format string to best fitting unit in Bytes
|
||
if ((net[nic_change]==1 & net[reset]==1)); then unset "net[total_offset_${direction}]"; net[reset]=0; fi
|
||
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}]}
|
||
elif ((net[reset]==0)) && [[ -n ${net[total_offset_${direction}]} ]]; then unset "net[total_offset_${direction}]"; fi
|
||
|
||
floating_humanizer -Byte -v net[total_${direction}] $((${net[new_${direction}]}-${net[total_offset_${direction}]:-0}))
|
||
|
||
#* Calculate current speeds: ("New value" - "Old value") * 1000(for ms to seconds) / ("new_timestamp" - "old_timestamp")
|
||
net[speed_${direction}]=$(( (${net[new_${direction}]}-${net[old_${direction}]})*1000/(net[new_timestamp]-net[old_timestamp]) ))
|
||
|
||
#* Convert to floating point and format string to best fitting unit in Bytes and Bits per second
|
||
floating_humanizer -Byte -per-second -v net[speed_${direction}_byteps] ${net[speed_${direction}]}
|
||
floating_humanizer -bit -per-second -v net[speed_${direction}_bitps] ${net[speed_${direction}]}
|
||
|
||
#* Update download and upload max values for graph
|
||
if ((${net[speed_${direction}]}>${net[${direction}_max]})); then
|
||
net[${direction}_max]=${net[speed_${direction}]}
|
||
fi
|
||
|
||
if ((${net[speed_${direction}]}>${net[${direction}_graph_max]})); then
|
||
((++net[${direction}_new_max]))
|
||
if ((net[${direction}_new_low]>0)); then ((net[${direction}_new_low]--)); fi
|
||
elif ((${net[${direction}_graph_max]}>10<<10 & ${net[speed_${direction}]}<${net[${direction}_graph_max]}/10)); then
|
||
((++net[${direction}_new_low]))
|
||
if ((net[${direction}_new_max]>0)); then ((net[${direction}_new_max]--)); fi
|
||
fi
|
||
|
||
#* Copy download and upload speed to history arrays and trim earlier entries
|
||
local -n history="net_history_${direction}"
|
||
if ((${#history[@]}>box[net_width]*4)); then
|
||
history=( "${history[@]:$((box[net_width]*2))}" "${net[speed_${direction}]}")
|
||
else
|
||
history+=("${net[speed_${direction}]}")
|
||
fi
|
||
|
||
#* Check for new max value and set flag to adjust resolution of graph if needed
|
||
if ((${net[${direction}_new_max]}>=5)); then
|
||
net[${direction}_graph_max]=$((${net[${direction}_max]}+(${net[${direction}_max]}/3) ))
|
||
net[${direction}_redraw]=1
|
||
net[${direction}_new_max]=0
|
||
|
||
#* If current max value isn't relevant, sort array to get the next largest value to set graph resolution
|
||
elif ((${net[${direction}_new_low]}>=5 & ${#history[@]}>5)); then
|
||
history_last=("${history[@]:(-5)}")
|
||
sort_array_int "history_last" "history_sorted"
|
||
net[${direction}_max]=${history_sorted[0]}
|
||
net[${direction}_graph_max]=$(( ${net[${direction}_max]}*3 ))
|
||
if ((${net[${direction}_graph_max]}<10<<10)); then net[${direction}_graph_max]=$((10<<10)); fi
|
||
net[${direction}_redraw]=1
|
||
net[${direction}_new_low]=0
|
||
fi
|
||
fi
|
||
|
||
floating_humanizer -Byte -short -v net[${direction}_max_string] ${net[${direction}_graph_max]}
|
||
|
||
net[old_${direction}]=${net[new_${direction}]}
|
||
done
|
||
|
||
net[old_timestamp]=${net[new_timestamp]}
|
||
|
||
}
|
||
|
||
calc_sizes() { #? Calculate width and height of all boxes
|
||
local pos calc_size calc_total percent threads=${cpu[threads]}
|
||
|
||
#* Calculate heights
|
||
for pos in ${box[boxes]/processes/}; do
|
||
if [[ $pos = "cpu" ]]; then percent=32;
|
||
elif [[ $pos = "mem" ]]; then percent=40;
|
||
else percent=28; fi
|
||
|
||
#* Multiplying with 10 to convert to floating point
|
||
calc_size=$(( (tty_height*10)*(percent*10)/100 ))
|
||
|
||
#* Round down if last 2 digits of value is below "50" and round up if above
|
||
if ((${calc_size:(-2):1}==0)); then calc_size=$((calc_size+10)); fi
|
||
if ((${calc_size:(-2)}<50)); then
|
||
calc_size=$((${calc_size::-2}))
|
||
else
|
||
calc_size=$((${calc_size::-2}+1))
|
||
fi
|
||
|
||
#* Subtract from last value if the total of all rounded numbers is larger then terminal height
|
||
while ((calc_total+calc_size>tty_height)); do ((--calc_size)); done
|
||
calc_total=$((calc_total+calc_size))
|
||
|
||
#* Set calculated values in box array
|
||
box[${pos}_line]=$((calc_total-calc_size+1))
|
||
box[${pos}_col]=1
|
||
box[${pos}_height]=$calc_size
|
||
box[${pos}_width]=$tty_width
|
||
done
|
||
|
||
|
||
#* Calculate widths
|
||
unset calc_total
|
||
for pos in net processes; do
|
||
if [[ $pos = "net" ]]; then percent=45; else percent=55; fi
|
||
|
||
#* Multiplying with 10 to convert to floating point
|
||
calc_size=$(( (tty_width*10)*(percent*10)/100 ))
|
||
|
||
#* Round down if last 2 digits of value is below "50" and round up if above
|
||
if ((${calc_size:(-2)}<50)); then
|
||
calc_size=$((${calc_size::-2}))
|
||
else
|
||
calc_size=$((${calc_size::-2}+1))
|
||
fi
|
||
|
||
#* Subtract from last value if the total of all rounded numbers is larger then terminal width
|
||
while ((calc_total+calc_size>tty_width)); do ((--calc_size)); done
|
||
calc_total=$((calc_total+calc_size))
|
||
|
||
#* Set calculated values in box array
|
||
box[${pos}_col]=$((calc_total-calc_size+1))
|
||
box[${pos}_width]=$calc_size
|
||
done
|
||
|
||
#* Copy numbers around to get target layout
|
||
box[mem_width]=${box[net_width]}
|
||
box[processes_line]=${box[mem_line]}
|
||
box[processes_height]=$((box[mem_height]+box[net_height]))
|
||
|
||
# threads=${box[testing]} #! For testing, remove <--------------
|
||
|
||
#* Recalculate size of process box if currently showing detailed process information
|
||
if ((proc[detailed]==1)); then
|
||
box[details_line]=${box[processes_line]}
|
||
box[details_col]=${box[processes_col]}
|
||
box[details_width]=${box[processes_width]}
|
||
box[details_height]=8
|
||
box[processes_line]=$((box[processes_line]+box[details_height]))
|
||
box[processes_height]=$((box[processes_height]-box[details_height]))
|
||
fi
|
||
|
||
#* Calculate number of columns and placement of cpu meter box
|
||
local cpu_line=$((box[cpu_line]+1)) cpu_width=$((box[cpu_width]-2)) cpu_height=$((box[cpu_height]-2)) box_cols
|
||
if ((threads>(cpu_height-3)*3 && tty_width>=200)); then box[p_width]=$((24*4)); box[p_height]=$((threads/4+4)); box_cols=4
|
||
elif ((threads>(cpu_height-3)*2 && tty_width>=150)); then box[p_width]=$((24*3)); box[p_height]=$((threads/3+5)); box_cols=3
|
||
elif ((threads>cpu_height-3 && tty_width>=100)); then box[p_width]=$((24*2)); box[p_height]=$((threads/2+4)); box_cols=2
|
||
else box[p_width]=24; box[p_height]=$((threads+4)); box_cols=1
|
||
fi
|
||
|
||
if [[ $check_temp == true ]]; then
|
||
box[p_width]=$(( box[p_width]+13*box_cols))
|
||
fi
|
||
|
||
if ((box[p_height]>cpu_height)); then box[p_height]=$cpu_height; fi
|
||
box[p_col]="$((cpu_width-box[p_width]+2))"
|
||
box[p_line]="$((cpu_line+(cpu_height/2)-(box[p_height]/2)+1))"
|
||
|
||
#* Calculate placement of mem divider
|
||
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))
|
||
box[m_width]=$((mem_width/2))
|
||
box[m_width2]=${box[m_width]}
|
||
if ((box[m_width]+box[m_width2]<mem_width)); then ((box[m_width]++)); fi
|
||
box[m_height]=$mem_height
|
||
box[m_col]=$((mem_col+1))
|
||
box[m_line]=$mem_line
|
||
|
||
#* Calculate placement of net value box
|
||
local net_line=$((box[net_line]+1)) net_width=$((box[net_width]-2)) net_height=$((box[net_height]-2))
|
||
box[n_width]=24
|
||
if ((net_height>9)); then box[n_height]=9
|
||
else box[n_height]=$net_height; fi
|
||
box[n_col]="$((net_width-box[n_width]+2))"
|
||
box[n_line]="$((net_line+(net_height/2)-(box[n_height]/2)+1))"
|
||
|
||
|
||
}
|
||
|
||
draw_bg() { #? Draw all box outlines
|
||
local this_box cpu_p_width i cpu_model_len
|
||
|
||
unset boxes_out
|
||
for this_box in ${box[boxes]}; do
|
||
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}
|
||
done
|
||
|
||
#* Misc cpu box
|
||
if [[ $check_temp == true ]]; then cpu_model_len=18; else cpu_model_len=9; fi
|
||
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}}"
|
||
print -v boxes_out -m ${box[cpu_line]} $((box[cpu_col]+10)) -rs \
|
||
-fg ${box[cpu_color]} -t "┤" -b -fg ${theme[hi_fg]} -t "m" -fg ${theme[title]} -t "enu" -rs -fg ${box[cpu_color]} -t "├"
|
||
|
||
#* Misc mem
|
||
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 "├"
|
||
print -v boxes_out -m ${box[mem_line]} $((box[mem_col]+box[m_width])) -rs -fg ${box[mem_color]} -t "┬"
|
||
print -v boxes_out -m $((box[mem_line]+box[mem_height]-1)) $((box[mem_col]+box[m_width])) -fg ${box[mem_color]} -t "┴"
|
||
for((i=1;i<=box[mem_height]-2;i++)); do
|
||
print -v boxes_out -m $((box[mem_line]+i)) $((box[mem_col]+box[m_width])) -fg ${theme[div_line]} -t "│"
|
||
done
|
||
|
||
|
||
#* Misc net box
|
||
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"
|
||
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 "├"
|
||
|
||
|
||
if [[ $1 == "quiet" ]]; then draw_out="${boxes_out}"
|
||
else echo -en "${boxes_out}"; fi
|
||
draw_update_string $1
|
||
}
|
||
|
||
draw_cpu() { #? Draw cpu and core graphs and print percentages
|
||
local cpu_out i name cpu_p_color temp_color y pt_line pt_col p_normal_color="${theme[main_fg]}" threads=${cpu[threads]}
|
||
local meter meter_size meter_width temp_var cpu_out_var core_name temp_name temp_width
|
||
|
||
#* Get variables from previous calculations
|
||
local col=$((box[cpu_col]+1)) line=$((box[cpu_line]+1)) width=$((box[cpu_width]-2)) height=$((box[cpu_height]-2))
|
||
local p_width=${box[p_width]} p_height=${box[p_height]} p_col=${box[p_col]} p_line=${box[p_line]}
|
||
|
||
#* If resized recreate cpu meter/graph box, cpu graph and core graphs
|
||
if ((resized>0)); then
|
||
local graph_a_size graph_b_size
|
||
graph_a_size=$((height/2)); graph_b_size=${graph_a_size}
|
||
|
||
if ((graph_a_size*2<height)); then ((graph_a_size++)); fi
|
||
create_graph -o cpu_graph_a -d ${line} ${col} ${graph_a_size} $((width-p_width-2)) -c color_cpu_graph -n cpu_history
|
||
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
|
||
|
||
if [[ -z ${cpu_core_1_graph} ]]; then
|
||
for((i=1;i<=threads;i++)); do
|
||
create_mini_graph -o "cpu_core_${i}_graph" -w 10 -nc "cpu_core_history_${i}"
|
||
done
|
||
fi
|
||
|
||
if [[ $check_temp == true && -z ${cpu_temp_0_graph} ]]; then
|
||
for((i=0;i<=threads;i++)); do
|
||
if [[ -n ${cpu[temp_${i}]} ]]; then create_mini_graph -o "cpu_temp_${i}_graph" -w 5 -nc "cpu_temp_history_${i}"; fi
|
||
done
|
||
fi
|
||
((resized++))
|
||
fi
|
||
|
||
#* Add new values to cpu and core graphs unless just resized
|
||
if ((resized==0)); then
|
||
create_graph -add-last cpu_graph_a cpu_history
|
||
create_graph -i -add-last cpu_graph_b cpu_history
|
||
for((i=1;i<=threads;i++)); do
|
||
create_mini_graph -w 10 -nc -add-last "cpu_core_${i}_graph" "cpu_core_history_${i}"
|
||
done
|
||
if [[ $check_temp == true ]]; then
|
||
for((i=0;i<=threads;i++)); do
|
||
if [[ -n ${cpu[temp_${i}]} ]]; then
|
||
create_mini_graph -w 5 -nc -add-last "cpu_temp_${i}_graph" "cpu_temp_history_${i}"
|
||
fi
|
||
done
|
||
fi
|
||
fi
|
||
|
||
#* Print CPU total and all cpu core percentage meters in box
|
||
for((i=0;i<=threads;i++)); do
|
||
if ((i==0)); then name="CPU"; else name="Core${i}"; fi
|
||
|
||
#* Get color of cpu text depending on current usage
|
||
cpu_p_color="${color_cpu_graph[cpu_usage[i]]}"
|
||
|
||
pt_col=$p_col; pt_line=$p_line; meter_size="small"; meter_width=10
|
||
|
||
#* Set temperature string if "sensors" is available
|
||
if [[ $check_temp == true ]]; then
|
||
#* Get color of temperature text depending on current temp vs factory high temp
|
||
declare -n temp_hist="cpu_temp_history_${i}[-1]"
|
||
temp_color="${color_temp_graph[${temp_hist}]}"
|
||
temp_name="cpu_temp_${i}_graph"
|
||
temp_width=13
|
||
fi
|
||
|
||
if ((i==0 & p_width>24+temp_width)); then
|
||
name="CPU Total "; meter_width=$((p_width-17-temp_width))
|
||
fi
|
||
|
||
|
||
#* Create cpu usage meter
|
||
if ((i==0)); then
|
||
create_meter -v meter -w $meter_width -f -c color_cpu_graph ${cpu_usage[i]}
|
||
else
|
||
core_name="cpu_core_${i}_graph"
|
||
meter="${!core_name}"
|
||
fi
|
||
|
||
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))
|
||
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))
|
||
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))
|
||
else y=$i; fi
|
||
|
||
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"\
|
||
-jr 4 -fg $cpu_p_color -t "${cpu_usage[i]}" -fg $p_normal_color -t "%"
|
||
if [[ $check_temp == true && -n ${cpu[temp_${i}]} ]]; then
|
||
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]}
|
||
fi
|
||
|
||
if (( i>(p_height-2)*( p_width/(24+temp_width) )-( p_width/(24+temp_width) )-1 )); then break; fi
|
||
done
|
||
|
||
#* Print load average and uptime
|
||
if ((pt_line+y+3<p_line+p_height)); then
|
||
local avg_string avg_width
|
||
if [[ $check_temp == true ]]; then avg_string="Load Average: "; avg_width=7; else avg_string="L AVG: "; avg_width=5; fi
|
||
print -v cpu_out_var -m $((pt_line+y+1)) $pt_col -fg ${theme[main_fg]} -t "${avg_string}"
|
||
for avg_string in ${cpu[load_avg]}; do
|
||
print -v cpu_out_var -jc $avg_width -t "${avg_string::4}"
|
||
done
|
||
fi
|
||
print -v cpu_out_var -m $((line+height-1)) $((col+1)) -fg ${theme[inactive_fg]} -trans -t "up ${cpu[uptime]}"
|
||
|
||
#* Print current CPU frequency right of the title in the meter box
|
||
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
|
||
|
||
#* Print created text, graph and meters to output variable
|
||
draw_out+="${cpu_graph_a[*]}${cpu_graph_b[*]}${cpu_out_var}"
|
||
|
||
}
|
||
|
||
draw_mem() { #? Draw mem, swap and disk statistics
|
||
|
||
if ((mem[counter]>0 & resized==0)); then return; fi
|
||
|
||
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
|
||
local meter_mod_w meter_mod_pos value type m_title meter_options values="used available cached free"
|
||
local -a types=("mem")
|
||
unset mem_out
|
||
|
||
if [[ -n ${swap[total]} && ${swap[total]} -gt 0 ]]; then types+=("swap"); fi
|
||
|
||
#* Get variables from previous calculations
|
||
local col=$((box[mem_col]+1)) line=$((box[mem_line]+1)) width=$((box[mem_width]-2)) height=$((box[mem_height]-2))
|
||
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]))
|
||
|
||
#* Create text and meters for memory and swap and adapt sizes based on available height
|
||
local y_pos=$m_line v_height=8 list value meter inv_meter
|
||
|
||
for type in ${types[@]}; do
|
||
local -n type_name="$type"
|
||
if [[ $type == "mem" ]]; then
|
||
m_title="memory"
|
||
else
|
||
m_title="$type"
|
||
if ((height>14)); then ((y_pos++)); fi
|
||
fi
|
||
|
||
#* Print name of type and total amount in humanized base 2 bytes
|
||
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))}"
|
||
|
||
for value in ${values}; do
|
||
if [[ $type == "swap" && $value =~ available|cached ]]; then continue; fi
|
||
|
||
if [[ $system == "MacOS" && $value == "cached" ]]; then value_text="active"
|
||
else value_text="${value::$((m_width-12))}"; fi
|
||
if ((height<14)); then value_text="${value_text::5}"; fi
|
||
|
||
#* Print name of value and value amount in humanized base 2 bytes
|
||
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))}"
|
||
|
||
#* Create meter for value and calculate size and placement depending on terminal size
|
||
if ((height>v_height++ | tty_width>100)); then
|
||
if ((height<=v_height & tty_width<150)); then
|
||
meter_mod_w=12
|
||
meter_mod_pos=7
|
||
((y_pos--))
|
||
elif ((height<=v_height)); then
|
||
print -v mem_out -m $((--y_pos)) $((m_col+5)) -jr 4 -t "${type_name[${value}_percent]}%"
|
||
meter_mod_w=14
|
||
meter_mod_pos=10
|
||
fi
|
||
create_meter -v ${type}_${value}_meter -w $((m_width-7-meter_mod_w)) -f -c color_${value}_graph ${type_name[${value}_percent]}
|
||
|
||
meter="${type}_${value}_meter"
|
||
print -v mem_out -m $((y_pos++)) $((m_col+meter_mod_pos)) -t "${!meter}" -rs -fg $normal_color
|
||
|
||
if [[ -z $meter_mod_w ]]; then print -v mem_out -jr 4 -t "${type_name[${value}_percent]}%"; fi
|
||
fi
|
||
#if [[ $system == "MacOS" && -z $swap_on ]] && ((height>14)); then ((y_pos++)); fi
|
||
done
|
||
done
|
||
|
||
|
||
#* Create text and meters for disks and adapt sizes based on available height
|
||
local disk_num disk_name disk_value v_height2 just_val name_len
|
||
y_pos=$m_line
|
||
m_col=$((m_col+m_width))
|
||
m_width=${box[m_width2]}
|
||
v_height=$((${#disks_name[@]}))
|
||
unset meter_mod_w meter_mod_pos
|
||
|
||
for disk_name in "${disks_name[@]}"; do
|
||
if ((y_pos>m_line+height-2)); then break; fi
|
||
|
||
#* Print folder disk is mounted on, total size in humanized base 2 bytes and io stats if enabled
|
||
print -v mem_out -m $((y_pos++)) $m_col -rs -fg ${theme[title]} -b -t "${disks_name[disk_num]::10}"
|
||
name_len=${#disks_name[disk_num]}; if ((name_len>10)); then name_len=10; fi
|
||
if [[ -n ${disks_io[disk_num]} && ${disks_io[disk_num]} != "0" ]] && ((m_width-11-name_len>6)); then
|
||
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))}"
|
||
just_val=8
|
||
else
|
||
just_val=$((m_width-name_len-2))
|
||
fi
|
||
print -v mem_out -jr ${just_val} -fg ${theme[title]} -b -t "${disks_total[disk_num]::$((m_width-11))}"
|
||
|
||
for value in "used" "free"; do
|
||
if ((height<v_height*3)) && [[ $value == "free" ]]; then break; fi
|
||
local -n disk_value="disks_${value}"
|
||
|
||
#* Print name of value and value amount in humanized base 2 bytes
|
||
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))}"
|
||
|
||
#* Create meter for value and calculate size and placement depending on terminal size
|
||
if ((height>=v_height*5 | tty_width>100)); then
|
||
local -n disk_value_percent="disks_${value}_percent"
|
||
if ((height<=v_height*5 & tty_width<150)); then
|
||
meter_mod_w=12
|
||
meter_mod_pos=7
|
||
((y_pos--))
|
||
elif ((height<=v_height*5)); then
|
||
print -v mem_out -m $((--y_pos)) $((m_col+5)) -jr 4 -t "${disk_value_percent[disk_num]}%"
|
||
meter_mod_w=14
|
||
meter_mod_pos=10
|
||
fi
|
||
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]}
|
||
|
||
meter="disk_${disk_num}_${value}_meter"
|
||
print -v mem_out -m $((y_pos++)) $((m_col+meter_mod_pos)) -t "${!meter}" -rs -fg $normal_color
|
||
|
||
if [[ -z $meter_mod_w ]]; then print -v mem_out -jr 4 -t "${disk_value_percent[disk_num]}%"; fi
|
||
fi
|
||
if ((y_pos>m_line+height-1)); then break; fi
|
||
done
|
||
if ((height>=v_height*4 & height<v_height*5 | height>=v_height*6)); then ((y_pos++)); fi
|
||
((++disk_num))
|
||
done
|
||
|
||
if ((resized>0)); then ((resized++)); fi
|
||
#* Print created text, graph and meters to output variable
|
||
draw_out+="${mem_graph[*]}${swap_graph[*]}${mem_out}"
|
||
|
||
}
|
||
|
||
draw_processes() { #? Draw processes and values to screen
|
||
local argument="$1"
|
||
if [[ -n $skip_process_draw && $argument != "now" ]]; then return; fi
|
||
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
|
||
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))
|
||
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
|
||
local d_width=${box[details_width]} d_height=${box[details_height]} d_line=${box[details_line]} d_col=${box[details_col]}
|
||
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
|
||
local right_width=$((d_width-detail_graph_width-2))
|
||
local right_col=$((d_col+detail_graph_width+4))
|
||
local -a pid_rgb=(${theme[proc_misc]}) fg_rgb=(${theme[main_fg_dec]})
|
||
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]}
|
||
|
||
if [[ $argument == "now" ]]; then skip_process_draw=1; fi
|
||
|
||
if [[ $proc_gradient == true ]]; then
|
||
if ((fg_r+fg_g+fg_b<(255*3)/2)); then
|
||
fg_add_r="$(( (fg_r-255-((fg_r-255)/6) )/height))"
|
||
fg_add_g="$(( (fg_g-255-((fg_g-255)/6) )/height))"
|
||
fg_add_b="$(( (fg_b-255-((fg_b-255)/6) )/height))"
|
||
|
||
pid_add_r="$(( (pid_r-255-((pid_r-255)/6) )/height))"
|
||
pid_add_g="$(( (pid_g-255-((pid_g-255)/6) )/height))"
|
||
pid_add_b="$(( (pid_b-255-((pid_b-255)/6) )/height))"
|
||
else
|
||
fg_add_r="$(( (fg_r-(fg_r/6) )/height))"
|
||
fg_add_g="$(( (fg_g-(fg_g/6) )/height))"
|
||
fg_add_b="$(( (fg_b-(fg_b/6) )/height))"
|
||
|
||
pid_add_r="$(( (pid_r-(pid_r/6) )/height))"
|
||
pid_add_g="$(( (pid_g-(pid_g/6) )/height))"
|
||
pid_add_b="$(( (pid_b-(pid_b/6) )/height))"
|
||
fi
|
||
fi
|
||
|
||
unset proc_out
|
||
|
||
#* Details box
|
||
if ((proc[detailed_change]>0)) || ((proc[detailed]>0 & resized>0)); then
|
||
proc[detailed_change]=0
|
||
proc[order_change]=1
|
||
proc[page_change]=1
|
||
if ((proc[detailed]==1)); then
|
||
unset proc_det
|
||
local enter_fg enter_a_fg misc_fg misc_a_fg i det_y=6 dets cmd_y
|
||
|
||
if [[ ${#detail_history[@]} -eq 1 ]] || ((resized>0)); then
|
||
unset proc_det2
|
||
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
|
||
if ((tty_width>120)); then create_mini_graph -o detail_mem_graph -w $((right_width/3-3)) -nc detail_mem_history; fi
|
||
det_no_add=1
|
||
|
||
for detail_location in "${d_line}" "$((d_line+d_height))"; do
|
||
print -v proc_det2 -m ${detail_location} $((d_col+1)) -rs -fg ${box[processes_color]} -rp $((d_width-2)) -t "─"
|
||
done
|
||
for((i=1;i<d_height;i++)); do
|
||
print -v proc_det2 -m $((d_line+i)) $((d_col+3+detail_graph_width)) -rp $((right_width-1)) -t " "
|
||
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 "│"
|
||
done
|
||
|
||
print -v proc_det2 -m ${d_line} ${d_col} -t "┌" -m ${d_line} $((d_col+d_width-1)) -t "┐"
|
||
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 "┴"
|
||
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 "┤"
|
||
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 "├"
|
||
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
|
||
|
||
|
||
|
||
if ((${#proc[detailed_cmd]}>(right_width-6)*2)); then ((det_y--)); dets=2
|
||
elif ((${#proc[detailed_cmd]}>right_width-6)); then dets=1; fi
|
||
|
||
print -v proc_det2 -fg ${theme[title]} -b
|
||
for i in C M D; do
|
||
print -v proc_det2 -m $((d_line+5+cmd_y++)) $right_col -t "$i"
|
||
done
|
||
|
||
|
||
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))}"
|
||
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
|
||
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
|
||
|
||
fi
|
||
|
||
|
||
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
|
||
if [[ -n ${proc[detailed_killed]} ]]; then misc_fg="${theme[title]}"; misc_a_fg="${theme[hi_fg]}"
|
||
else misc_fg=$enter_fg; misc_a_fg=$enter_a_fg; fi
|
||
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 "├"
|
||
if ((tty_width<129)); then det_mod="-8"; fi
|
||
|
||
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 "├"
|
||
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 "├"
|
||
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
|
||
|
||
|
||
proc_det="${proc_det2}${proc_det}"
|
||
proc_out="${proc_det}"
|
||
|
||
elif ((resized==0)); then
|
||
unset proc_det
|
||
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}
|
||
fi
|
||
fi
|
||
|
||
if [[ ${proc[detailed]} -eq 1 ]]; then
|
||
local det_status status_color det_columns=3
|
||
if ((tty_width>140)); then ((det_columns++)); fi
|
||
if ((tty_width>150)); then ((det_columns++)); fi
|
||
if [[ -z $det_no_add && $1 != "now" && -z ${proc[detailed_killed]} ]]; then
|
||
create_graph -add-last detail_graph detail_history
|
||
if ((tty_width>120)); then create_mini_graph -w $((right_width/3-3)) -nc -add-last detail_mem_graph detail_mem_history; fi
|
||
fi
|
||
|
||
print -v proc_out -fg ${theme[title]} -b
|
||
cmd_y=0
|
||
for i in C P U; do
|
||
print -v proc_out -m $((d_line+3+cmd_y++)) $((d_col+1)) -t "$i"
|
||
done
|
||
print -v proc_out -m $((d_line+1)) $((d_col+1)) -fg ${theme[title]} -t "${proc[detailed_cpu]}%"
|
||
|
||
if [[ -n ${proc[detailed_killed]} ]]; then det_status="stopped"; status_color="${theme[inactive_fg]}"
|
||
else det_status="running"; status_color="${theme[proc_misc]}"; fi
|
||
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:"
|
||
if ((det_columns>=4)); then print -v proc_out -jc $((right_width/det_columns-1)) -t "User:"; fi
|
||
if ((det_columns>=5)); then print -v proc_out -jc $((right_width/det_columns-1)) -t "Threads:"; fi
|
||
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))}"
|
||
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
|
||
if ((det_columns>=5)); then print -v proc_out -jc $((right_width/det_columns-1)) -t "${proc[detailed_threads]}"; fi
|
||
|
||
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 " "
|
||
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
|
||
print -v proc_out -fg ${theme[title]} -b -t "${proc[detailed_mem_string]}"
|
||
fi
|
||
|
||
#* Print processes
|
||
if ((${#proc_array[@]}<=p_height)); then
|
||
proc[start]=1
|
||
elif (( proc[start]>(${#proc_array[@]}-1)-p_height )); then
|
||
proc[start]=$(( (${#proc_array[@]}-1)-p_height ))
|
||
fi
|
||
|
||
if ((proc[selected]>${#proc_array[@]}-1)); then proc[selected]=$((${#proc_array[@]}-1)); fi
|
||
|
||
if [[ $proc_gradient == true ]] && ((proc[selected]>1)); then
|
||
fg_r="$(( fg_r-( fg_add_r*(proc[selected]-1) ) ))"
|
||
fg_g="$(( fg_g-( fg_add_g*(proc[selected]-1) ) ))"
|
||
fg_b="$(( fg_b-( fg_add_b*(proc[selected]-1) ) ))"
|
||
|
||
pid_r="$(( pid_r-( pid_add_r*(proc[selected]-1) ) ))"
|
||
pid_g="$(( pid_g-( pid_add_g*(proc[selected]-1) ) ))"
|
||
pid_b="$(( pid_b-( pid_add_b*(proc[selected]-1) ) ))"
|
||
fi
|
||
|
||
current_num=1
|
||
|
||
print -v proc_out -rs -m $((line+y++)) $((col+1)) -fg ${theme[title]} -b -t "${proc_array[0]::$((width-3))} " -rs
|
||
|
||
local -a out_arr
|
||
for out_line in "${proc_array[@]:${proc[start]}}"; do
|
||
|
||
if [[ $use_psutil == true ]]; then
|
||
out_arr=(${out_line})
|
||
pi=0
|
||
if [[ $proc_tree == true ]]; then
|
||
while [[ ! ${out_arr[pi]} =~ ^[0-9]+$ ]]; do ((++pi)); done
|
||
fi
|
||
pid="${out_arr[pi]}"
|
||
|
||
else
|
||
pid="${out_line::$((proc[pid_len]+1))}"; pid="${pid// /}"
|
||
out_line="${out_line//'\'/'\\'}"
|
||
out_line="${out_line//'$'/'\$'}"
|
||
out_line="${out_line//'"'/'\"'}"
|
||
fi
|
||
|
||
pid_graph="pid_${pid}_graph"
|
||
|
||
if ((current_num==proc[selected])); then print -v proc_out -bg ${theme[selected_bg]} -fg ${theme[selected_fg]} -b; proc[selected_pid]="$pid"
|
||
else print -v proc_out -rs -fg $((fg_r-fg_step_r)) $((fg_g-fg_step_g)) $((fg_b-fg_step_b)); fi
|
||
|
||
print -v proc_out -m $((line+y)) $((col+1)) -t "${out_line::$((width-3))} "
|
||
|
||
if ((current_num==proc[selected])); then print -v proc_out -rs -bg ${theme[selected_bg]}; fi
|
||
|
||
print -v proc_out -m $((line+y)) $((col+width-12)) -fg ${theme[inactive_fg]} -t "⡀⡀⡀⡀⡀"
|
||
|
||
if [[ -n ${!pid_graph} ]]; then
|
||
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}"
|
||
fi
|
||
|
||
((y++))
|
||
((current_num++))
|
||
if ((y>height-2)); then break; fi
|
||
if [[ $proc_gradient == false ]]; then :
|
||
elif ((current_num<proc[selected]+1)); then
|
||
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))
|
||
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))
|
||
elif ((current_num>=proc[selected])); then
|
||
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))
|
||
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))
|
||
fi
|
||
|
||
done
|
||
print -v proc_out -rs
|
||
while ((y<=height-2)); do
|
||
print -v proc_out -m $((line+y++)) $((col+1)) -rp $((width-2)) -t " "
|
||
done
|
||
|
||
if ((proc[selected]>0)); then sel_string=$((proc[start]-1+proc[selected])); else sel_string=0; fi
|
||
page_string="${sel_string}/$((${#proc_array[@]}-2${filter:++1}))"
|
||
print -v proc_out -m $((line+height-1)) $((col+width-20)) -fg ${box[processes_color]} -rp 19 -t "─"
|
||
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 "├"
|
||
|
||
|
||
if ((proc[order_change]==1 | proc[filter_change]==1 | resized>0)); then
|
||
unset proc_misc
|
||
proc[order_change]=0
|
||
proc[filter_change]=0
|
||
proc[page_change]=1
|
||
print -v proc_misc -m $line $((col+13)) -fg ${box[processes_color]} -rp $((box[processes_width]-14)) -t "─" -rs
|
||
|
||
if ((proc[detailed]==1)); then
|
||
print -v proc_misc -m $((d_line+d_height)) $((d_col+detail_graph_width+2)) -fg ${box[processes_color]} -t "┴" -rs
|
||
fi
|
||
|
||
if ((tty_width>100)); then
|
||
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 ├"
|
||
reverse_pos=9
|
||
fi
|
||
print -v proc_misc -m $line $((col+width-${#proc_sorting}-14-reverse_pos)) -rs\
|
||
${reverse_string}\
|
||
-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 ├\
|
||
-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 "├"
|
||
|
||
if [[ -z $filter && -z $input_to_filter ]]; then
|
||
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 "├"
|
||
elif [[ -n $input_to_filter ]]; then
|
||
if [[ ${#filter} -le $((width-35-reverse_pos)) ]]; then filter_string="${filter}"
|
||
elif [[ ${#filter} -gt $((width-35-reverse_pos)) ]]; then filter_string="${filter: (-$((width-35-reverse_pos)))}"
|
||
fi
|
||
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 "├"
|
||
elif [[ -n $filter ]]; then
|
||
if [[ ${#filter} -le $((width-35-reverse_pos-4)) ]]; then filter_string="${filter}"
|
||
elif [[ ${#filter} -gt $((width-35-reverse_pos-4)) ]]; then filter_string="${filter::$((width-35-reverse_pos-4))}"
|
||
fi
|
||
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 "├"
|
||
fi
|
||
|
||
proc_out+="${proc_misc}"
|
||
fi
|
||
|
||
if ((proc[page_change]==1 | resized>0)); then
|
||
unset proc_misc2
|
||
proc[page_change]=0
|
||
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
|
||
if ((proc[selected]==(${#proc_array[@]}-1${filter:++1})-proc[start])); then down_fg="${theme[inactive_fg]}"; else down_fg="${theme[hi_fg]}"; fi
|
||
if ((proc[selected]>0 | proc[start]>1)); then up_fg="${theme[hi_fg]}"; else up_fg="${theme[inactive_fg]}"; fi
|
||
|
||
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 "├"
|
||
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 "├"
|
||
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
|
||
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
|
||
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
|
||
|
||
proc_out+="${proc_misc2}"
|
||
fi
|
||
|
||
proc_out="${detail_graph[*]}${proc_out}"
|
||
|
||
if ((resized>0)); then ((resized++)); fi
|
||
|
||
if [[ $argument == "now" ]]; then
|
||
echo -en "${proc_out}"
|
||
fi
|
||
|
||
}
|
||
|
||
draw_net() { #? Draw net information and graphs to screen
|
||
local net_out argument=$1
|
||
if [[ -n ${net[no_device]} ]]; then return; fi
|
||
if [[ -n $skip_net_draw && $argument != "now" ]]; then return; fi
|
||
if [[ $argument == "now" ]]; then skip_net_draw=1; fi
|
||
|
||
#* Get variables from previous calculations
|
||
local col=$((box[net_col]+1)) line=$((box[net_line]+1)) width=$((box[net_width]-2)) height=$((box[net_height]-2))
|
||
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]}"
|
||
|
||
#* If resized recreate net meter box and net graphs
|
||
if ((resized>0)); then
|
||
local graph_a_size graph_b_size
|
||
graph_a_size=$(( (height)/2 )); graph_b_size=${graph_a_size}
|
||
if ((graph_a_size*2<height)); then ((graph_a_size++)); fi
|
||
net[graph_a_size]=$graph_a_size
|
||
net[graph_b_size]=$graph_b_size
|
||
net[download_redraw]=0
|
||
net[upload_redraw]=0
|
||
((resized++))
|
||
fi
|
||
|
||
#* Update graphs if graph resolution update is needed or just resized, otherwise just add new values
|
||
if ((net[download_redraw]==1 | net[nic_change]==1 | resized>0)); then
|
||
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
|
||
else
|
||
create_graph -max "${net[download_graph_max]}" -add-last download_graph net_history_download
|
||
fi
|
||
if ((net[upload_redraw]==1 | net[nic_change]==1 | resized>0)); then
|
||
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
|
||
else
|
||
create_graph -max "${net[upload_graph_max]}" -i -add-last upload_graph net_history_upload
|
||
fi
|
||
|
||
if ((net[nic_change]==1 | resized>0)); then
|
||
local dev_len=${#net[device]}
|
||
if ((dev_len>15)); then dev_len=15; fi
|
||
unset net_misc 'net[nic_change]'
|
||
print -v net_out -m $((line-1)) $((width-23)) -rs -fg ${box[net_color]} -rp 23 -t "─"
|
||
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 "├"
|
||
net_out+="${net_misc}"
|
||
fi
|
||
|
||
#* Create text depening on box height
|
||
local ypos=$n_line
|
||
|
||
print -v net_out -fg ${main_fg} -m $((ypos++)) $n_col -jl 10 -t "▼ Byte:" -jr 12 -t "${net[speed_download_byteps]}"
|
||
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
|
||
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
|
||
|
||
if ((height>8)); then ((ypos++)); fi
|
||
print -v net_out -fg ${main_fg} -m $((ypos++)) $n_col -jl 10 -t "▲ Byte:" -jr 12 -t "${net[speed_upload_byteps]}"
|
||
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
|
||
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
|
||
|
||
print -v net_out -fg ${theme[inactive_fg]} -m $line $col -t "${net[download_max_string]}"
|
||
print -v net_out -fg ${theme[inactive_fg]} -m $((line+height-1)) $col -t "${net[upload_max_string]}"
|
||
|
||
|
||
#* Print graphs and text to output variable
|
||
draw_out+="${download_graph[*]}${upload_graph[*]}${net_out}"
|
||
if [[ $argument == "now" ]]; then echo -en "${download_graph[*]}${upload_graph[*]}${net_out}"; fi
|
||
}
|
||
|
||
draw_clock() { #? Draw a clock at top of screen
|
||
if [[ -z $draw_clock ]]; then return; fi
|
||
if [[ $resized -gt 0 && $resized -lt 5 ]]; then unset clock_out; return; fi
|
||
local width=${box[cpu_width]} color=${box[cpu_color]} old_time_string="${time_string}"
|
||
#time_string="$(date ${draw_clock})"
|
||
printf -v time_string "%(${draw_clock})T"
|
||
if [[ $old_time_string != "$time_string" || -z $clock_out ]]; then
|
||
unset clock_out
|
||
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 "├"
|
||
fi
|
||
if [[ $1 == "now" ]]; then echo -en "${clock_out}"; fi
|
||
}
|
||
|
||
draw_update_string() {
|
||
unset update_string
|
||
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 "├"
|
||
if [[ $1 == "quiet" ]]; then draw_out+="${update_string}"
|
||
else echo -en "${update_string}"; fi
|
||
}
|
||
|
||
pause_() { #? Pause input and draw a darkened version of main ui
|
||
local pause_out ext_var
|
||
if [[ -n $1 && $1 != "off" ]]; then local -n pause_out=${1}; ext_var=1; fi
|
||
if [[ $1 != "off" ]]; then
|
||
prev_screen="${boxes_out}${proc_det}${last_screen}${net_misc}${mem_out}${detail_graph[*]}${proc_out}${proc_misc}${proc_misc2}${update_string}${clock_out}"
|
||
if [[ -n $skip_process_draw ]]; then
|
||
prev_screen+="${proc_out}"
|
||
unset skip_process_draw proc_out
|
||
fi
|
||
|
||
unset pause_screen
|
||
print -v pause_screen -rs -b -fg ${theme[inactive_fg]}
|
||
pause_screen+="${theme[main_bg]}m$(${sed} -E 's/\\e\[[0-9;\-]*m//g' <<< "${prev_screen}")\e[0m" #\e[1;38;5;236
|
||
|
||
if [[ -z $ext_var ]]; then echo -en "${pause_screen}"
|
||
else pause_out="${pause_screen}"; fi
|
||
|
||
elif [[ $1 == "off" ]]; then
|
||
echo -en "${prev_screen}"
|
||
unset pause_screen prev_screen
|
||
fi
|
||
}
|
||
|
||
unpause_() { #? Unpause
|
||
pause_ off
|
||
}
|
||
|
||
menu_() { #? Shows the main menu overlay
|
||
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
|
||
local -a menus=("options" "help" "quit") color
|
||
unset bannerd menu_out
|
||
until false; do
|
||
|
||
#* Put program to sleep if caught ctrl-z
|
||
if ((sleepy==1)); then sleep_; fi
|
||
|
||
if [[ $background_update == true || -z $menu_out ]]; then
|
||
draw_clock
|
||
pause_ menu_pause
|
||
else
|
||
unset menu_pause
|
||
fi
|
||
|
||
unset draw_out
|
||
|
||
if [[ -z ${bannerd} ]]; then
|
||
draw_banner "$((tty_height/2-10))" bannerd
|
||
unset d_banner
|
||
fi
|
||
if [[ -n ${keypress} || -z ${menu_out} ]]; then
|
||
unset menu_out
|
||
print -v menu_out -t "${bannerd}"
|
||
print -v menu_out -d 1 -rs
|
||
selected="${menus[selected_int]}"
|
||
unset up
|
||
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
|
||
for menu in "${menus[@]}"; do
|
||
if [[ $menu == "$selected" ]]; then
|
||
local -n menu_array="menu_${menu}_selected"
|
||
color=("#c55e5e" "#c23d3d" "#a13030" "#8c2626")
|
||
else
|
||
local -n menu_array="menu_${menu}"
|
||
color=("#bb" "#aa" "#99" "#88")
|
||
fi
|
||
up=$((up+${#menu_array[@]}))
|
||
for((i=0;i<${#menu_array[@]};i++)); do
|
||
print -v menu_out -d 1 -fg ${color[i]} -c${trans} -t "${menu_array[i]}"
|
||
done
|
||
done
|
||
print -v menu_out -rs -u ${up}
|
||
fi
|
||
unset out_out
|
||
out_out="${menu_pause}${menu_out}"
|
||
echo -e "${out_out}"
|
||
|
||
|
||
get_ms timestamp_end
|
||
time_left=$((timestamp_start+update_ms-timestamp_end))
|
||
|
||
if ((time_left>1000)); then wait_string=10; time_left=$((time_left-1000))
|
||
elif ((time_left>100)); then wait_string=$((time_left/100)); time_left=0
|
||
else wait_string="0"; time_left=0; fi
|
||
|
||
get_key -v keypress -w ${wait_string}
|
||
if [[ $(${stty} size) != "$tty_height $tty_width" ]]; then resized; fi
|
||
if ((resized>0)); then
|
||
calc_sizes; draw_bg quiet; time_left=0; unset menu_out
|
||
unset bannerd
|
||
echo -en "${clear_screen}"
|
||
fi
|
||
|
||
case "$keypress" in
|
||
up|shift_tab) if ((selected_int>0)); then ((selected_int--)); else selected_int=$((${#menus[@]}-1)); fi ;;
|
||
down|tab) if ((selected_int<${#menus[@]}-1)); then ((++selected_int)); else selected_int=0; fi ;;
|
||
enter|space)
|
||
case "$selected" in
|
||
options) options_ ;;
|
||
help) help_ ;;
|
||
quit) quit_ ;;
|
||
esac
|
||
;;
|
||
m|M|escape|backspace) break ;;
|
||
q|Q) quit_ ;;
|
||
esac
|
||
|
||
if ((time_left==0)) && [[ -z $keypress ]]; then get_ms timestamp_start; collect_and_draw; fi
|
||
if ((resized>=5)); then resized=0; fi
|
||
|
||
done
|
||
unpause_
|
||
|
||
}
|
||
|
||
help_() { #? Shows the help overlay
|
||
local help_key from_menu col line y i help_out help_pause redraw=1 wait_string pages page=1 height
|
||
local -a shortcuts descriptions
|
||
|
||
shortcuts=(
|
||
"(Esc, M, m)"
|
||
"(F2, O, o)"
|
||
"(F1, H, h)"
|
||
"(Ctrl-C, Q, q)"
|
||
"(+, A, a) (-, S, s)"
|
||
"(Up) (Down)"
|
||
"(Enter)"
|
||
"(Pg Up) (Pg Down)"
|
||
"(Home) (End)"
|
||
"(Left) (Right)"
|
||
"(b, B) (n, N)"
|
||
"(E, e)"
|
||
"(R, r)"
|
||
"(F, f)"
|
||
"(C, c)"
|
||
"Selected (T, t)"
|
||
"Selected (K, k)"
|
||
"Selected (I, i)"
|
||
" "
|
||
" "
|
||
" "
|
||
)
|
||
descriptions=(
|
||
"Shows main menu."
|
||
"Shows options."
|
||
"Shows this window."
|
||
"Quits program."
|
||
"Add/Subtract 100ms to/from update timer."
|
||
"Select in process list."
|
||
"Show detailed information for selected process."
|
||
"Jump 1 page in process list."
|
||
"Jump to first or last page in process list."
|
||
"Select previous/next sorting column."
|
||
"Select previous/next network device."
|
||
"Toggle processes tree view"
|
||
"Reverse sorting order in processes box."
|
||
"Input a string to filter processes with."
|
||
"Clear any entered filter."
|
||
"Terminate selected process with SIGTERM - 15."
|
||
"Kill selected process with SIGKILL - 9."
|
||
"Interrupt selected process with SIGINT - 2."
|
||
" "
|
||
"For bug reporting and project updates, visit:"
|
||
"\e[1mhttps://github.com/aristocratos/bashtop"
|
||
)
|
||
|
||
if [[ -n $pause_screen ]]; then from_menu=1; fi
|
||
|
||
until [[ -n $help_key ]]; do
|
||
|
||
#* Put program to sleep if caught ctrl-z
|
||
if ((sleepy==1)); then sleep_; redraw=1; fi
|
||
|
||
if [[ $background_update == true || -n $redraw ]]; then
|
||
draw_clock
|
||
pause_ help_pause
|
||
else
|
||
unset help_pause
|
||
fi
|
||
|
||
|
||
if [[ -n $redraw ]]; then
|
||
col=$((tty_width/2-36)); line=$((tty_height/2-4)); y=1; height=$((tty_height-2-line))
|
||
if ((${#shortcuts[@]}>height)); then pages=$(( (${#shortcuts[@]}/height)+1 )); else height=${#shortcuts[@]}; unset pages; fi
|
||
unset redraw help_out
|
||
draw_banner "$((tty_height/2-11))" help_out
|
||
print -d 1
|
||
create_box -v help_out -w 72 -h $((height+3)) -l $((line++)) -c $((col++)) -fill -lc ${theme[div_line]} -title "help"
|
||
|
||
if [[ -n $pages ]]; then
|
||
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 "↑"\
|
||
-fg ${theme[title]} -t " ${page}/${pages} " -fg ${theme[title]} -t "pg" -fg ${theme[hi_fg]} -t "↓" -rs -fg ${theme[div_line]} -t "├"
|
||
fi
|
||
((++col))
|
||
|
||
print -v help_out -m $line $col -fg ${theme[title]} -b -jl 20 -t "Key:" -jl 48 -t "Description:" -m $((line+y++)) $col
|
||
|
||
for((i=(page-1)*height;i<page*height;i++)); do
|
||
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
|
||
done
|
||
fi
|
||
|
||
|
||
unset draw_out
|
||
echo -en "${help_pause}${help_out}"
|
||
|
||
get_ms timestamp_end
|
||
time_left=$((timestamp_start+update_ms-timestamp_end))
|
||
|
||
if ((time_left>1000)); then wait_string=10; time_left=$((time_left-1000))
|
||
elif ((time_left>100)); then wait_string=$((time_left/100)); time_left=0
|
||
else wait_string="0"; time_left=0; fi
|
||
|
||
get_key -v help_key -w "${wait_string}"
|
||
|
||
if [[ -n $pages ]]; then
|
||
case $help_key in
|
||
down|page_down) if ((page<pages)); then ((page++)); else page=1; fi; redraw=1; unset help_key ;;
|
||
up|page_up) if ((page>1)); then ((page--)); else page=${pages}; fi; redraw=1; unset help_key ;;
|
||
esac
|
||
fi
|
||
|
||
if [[ $(${stty} size) != "$tty_height $tty_width" ]]; then resized; fi
|
||
if ((resized>0)); then
|
||
${sleep} 0.5
|
||
calc_sizes; draw_bg quiet; redraw=1
|
||
d_banner=1
|
||
unset bannerd menu_out
|
||
fi
|
||
if ((time_left==0)); then get_ms timestamp_start; collect_and_draw; fi
|
||
if ((resized>0)); then resized=0; fi
|
||
done
|
||
|
||
if [[ -n $from_menu ]]; then pause_
|
||
else unpause_; fi
|
||
}
|
||
|
||
options_() { #? Shows the options overlay
|
||
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
|
||
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
|
||
|
||
if ((net[reset]==1)); then net_totals_reset="On"; else net_totals_reset="Off"; fi
|
||
|
||
#* Check theme folder for theme files
|
||
get_themes
|
||
|
||
desc_color_theme=( "Set bashtop color theme."
|
||
" "
|
||
"Choose between theme files located in"
|
||
"\"\$HOME/.config/bashtop/themes\" &"
|
||
"\"\$HOME/.config/bashtop/user_themes"
|
||
" "
|
||
"User themes are prefixed with \"*\"."
|
||
"\"Default\" for builtin default."
|
||
" ")
|
||
if [[ -z $curled ]]; then desc_color_theme+=("Get more themes at:"
|
||
"https://github.com/aristocratos/bashtop")
|
||
else desc_color_theme+=("\e[1mPress ENTER to download the default themes."
|
||
"Will overwrite changes made to the default"
|
||
"themes if not copied to user_themes folder."); fi
|
||
|
||
desc_update_ms=( "Update time in milliseconds."
|
||
"Recommended 2000 ms or above for better sample"
|
||
"times for graphs."
|
||
" "
|
||
"Increases automatically if set below internal"
|
||
"loops processing time."
|
||
" "
|
||
"Max value: 86400000 ms = 24 hours.")
|
||
desc_use_psutil=( "Enable the use of psutil python3 module for"
|
||
"data collection. Default on non Linux."
|
||
""
|
||
"Program will automatically restart if changing"
|
||
"this setting to check for compatibility."
|
||
" "
|
||
"True or false."
|
||
" "
|
||
"Can only be switched off when on Linux.")
|
||
desc_proc_sorting=( "Processes sorting."
|
||
"Valid values are \"pid\", \"program\", \"arguments\","
|
||
"\"threads\", \"user\", \"memory\", \"cpu lazy\""
|
||
"\"cpu responsive\" and \"tree\"."
|
||
" "
|
||
"\"cpu lazy\" shows cpu usage over the lifetime"
|
||
"of a process."
|
||
" "
|
||
"\"cpu responsive\" updates sorting directly at a"
|
||
"cost of cpu time (unless using psutil)."
|
||
" "
|
||
"\"tree\" shows a tree structure of running"
|
||
"processes. (not available with psutil)")
|
||
desc_proc_tree=( "Processes tree view."
|
||
" "
|
||
"Set true to show processes grouped by parents,"
|
||
"with lines drawn between parent and child"
|
||
"process."
|
||
" "
|
||
"True or false.")
|
||
desc_check_temp=( "Check cpu temperature."
|
||
" "
|
||
"True or false."
|
||
" "
|
||
"Only works if sensors, vcgencmd or osx-cpu-temp"
|
||
"commands is available.")
|
||
desc_draw_clock=( "Draw a clock at top of screen."
|
||
" "
|
||
"Formatting according to strftime, empty"
|
||
"string to disable."
|
||
" "
|
||
"\"%X\" locale HH:MM:SS"
|
||
"\"%H\" 24h hour, \"%I\" 12h hour"
|
||
"\"%M\" minute, \"%S\" second"
|
||
"\"%d\" day, \"%m\" month, \"%y\" year")
|
||
desc_background_update=( "Update main ui when menus are showing."
|
||
" "
|
||
"True or false."
|
||
" "
|
||
"Set this to false if the menus is flickering"
|
||
"too much for a comfortable experience.")
|
||
desc_custom_cpu_name=( "Custom cpu model name in cpu percentage box."
|
||
" "
|
||
"Empty string to disable.")
|
||
desc_error_logging=("Enable error logging to"
|
||
"\"\$HOME/.config/bashtop/error.log\""
|
||
" "
|
||
"Program will be automatically restarted if"
|
||
"changing this option."
|
||
" "
|
||
"True or false.")
|
||
desc_proc_reversed=("Reverse sorting order."
|
||
" "
|
||
"True or false.")
|
||
desc_proc_gradient=("Show color gradient in process list."
|
||
" "
|
||
"True or False.")
|
||
desc_disks_filter=("Optional filter for shown disks."
|
||
" "
|
||
"Should be names of mountpoints."
|
||
"\"root\" replaces \"/\""
|
||
" "
|
||
"Separate multiple values with space."
|
||
"Example: \"root home external\"")
|
||
desc_net_totals_reset=("Press ENTER to toggle network upload"
|
||
"and download totals reset."
|
||
" "
|
||
"Shows totals since system start or"
|
||
"network adapter reset when Off.")
|
||
desc_proc_per_core=("Process usage per core."
|
||
" "
|
||
"If process cpu usage should be of the core"
|
||
"it's running on or usage of the total"
|
||
"available cpu power."
|
||
""
|
||
"If true and process is multithreaded"
|
||
"cpu usage can reach over 100%.")
|
||
desc_update_check=( "Check for updates."
|
||
" "
|
||
"Enable check for new version from"
|
||
"github.com/aristocratos/bashtop at start."
|
||
" "
|
||
"True or False.")
|
||
desc_hires_graphs=("Enable high resolution graphs."
|
||
" "
|
||
"Doubles the horizontal resolution of all"
|
||
"graphs. At a cpu usage cost."
|
||
"Needs restart to take effect."
|
||
" "
|
||
"True or False.")
|
||
|
||
if [[ -n $pause_screen ]]; then from_menu=1; fi
|
||
|
||
until false; do
|
||
|
||
#* Put program to sleep if caught ctrl-z
|
||
if ((sleepy==1)); then sleep_; fi
|
||
|
||
|
||
if [[ $background_update == true || -n $redraw_misc ]]; then
|
||
draw_clock
|
||
if [[ -z $inputting ]]; then pause_ options_pause; fi
|
||
else
|
||
unset options_pause
|
||
fi
|
||
|
||
if [[ -n $redraw_misc ]]; then
|
||
unset options_misc redraw_misc
|
||
col=$((tty_width/2-39))
|
||
line=$((tty_height/2-4))
|
||
height=$(( (tty_height-2-line)/2 ))
|
||
if ((${#options_array[@]}>height)); then pages=$(( (${#options_array[@]}/height)+1 )); else height=${#options_array[@]}; unset pages; fi
|
||
desc_col=$((col+30))
|
||
draw_banner "$((tty_height/2-11))" options_misc
|
||
create_box -v options_misc -w 29 -h $((height*2+2)) -l $line -c $((col-1)) -fill -lc ${theme[div_line]} -title "options"
|
||
if [[ -n $pages ]]; then
|
||
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 "↑"\
|
||
-fg ${theme[title]} -t " ${page}/${pages} " -fg ${theme[title]} -t "pg" -fg ${theme[hi_fg]} -t "↓" -rs -fg ${theme[div_line]} -t "├"
|
||
fi
|
||
fi
|
||
|
||
if [[ -n $keypress || -z $options_out ]]; then
|
||
unset options_out desc_height lr inp valid
|
||
selected="${options_array[selected_int]}"
|
||
local -n selected_desc="desc_${selected}"
|
||
if [[ $background_update == false ]]; then desc_pos=$line; desc_height=$((height*2+2))
|
||
elif (( (selected_int-( (page-1)*height) )*2+${#selected_desc[@]}<height*2 )); then desc_pos=$((line+(selected_int-( (page-1)*height) )*2))
|
||
else desc_pos=$((line+height*2-${#selected_desc[@]})); fi
|
||
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"
|
||
for((i=(page-1)*height,ypos=1;i<page*height;i++,ypos=ypos+2)); do
|
||
if [[ -z ${options_array[i]} ]]; then break; fi
|
||
option_string="${options_array[i]}"
|
||
if [[ -n $inputting && ${option_string} == "${selected}" ]]; then
|
||
if [[ ${#inputting_value} -gt 14 ]]; then option_value="${inputting_value:(-14)}_"
|
||
else option_value="${inputting_value}_"; fi
|
||
else
|
||
option_value="${!option_string}"
|
||
fi
|
||
|
||
if [[ ${option_string} == "${selected}" ]]; then
|
||
if is_int "$option_value" || [[ $selected == "color_theme" && -n $curled ]]; then
|
||
enter="↲"; inp=1
|
||
fi
|
||
if is_int "$option_value" || [[ $option_value =~ true|false || $selected =~ proc_sorting|color_theme ]] && [[ -z $inputting ]]; then
|
||
left="←"; right="→"; lr=1
|
||
else
|
||
enter="↲"; inp=1
|
||
fi
|
||
bg=" -bg ${theme[selected_bg]}"
|
||
fg="${theme[selected_fg]}"
|
||
fi
|
||
option_string="${option_string//_/ }:"
|
||
if [[ $option_string == "proc sorting:" ]]; then
|
||
option_string+=" $((proc[sorting_int]+1))/${#sorting[@]}"
|
||
elif [[ $option_string == "color theme:" ]]; then
|
||
option_string+=" $((theme_int+1))/${#themes[@]}"
|
||
if [[ ${option_value::12} == "user_themes/" ]]; then option_value="*${option_value#*/}"
|
||
else option_value="${option_value#*/}"; fi
|
||
fi
|
||
print -v options_out -m $((line+ypos)) $((col+1)) -rs -fg ${fg:-${theme[title]}}${bg} -b -jc 25 -t "${option_string^}"
|
||
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}"
|
||
unset right left enter bg fg
|
||
done
|
||
|
||
for((i=0,ypos=1;i<${#selected_desc[@]};i++,ypos++)); do
|
||
print -v options_out -m $((desc_pos+ypos)) $((desc_col+1)) -rs -fg ${theme[main_fg]} -jl 46 -t "${selected_desc[i]}"
|
||
done
|
||
fi
|
||
|
||
echo -en "${options_pause}${options_misc}${options_out}"
|
||
unset draw_out keypress
|
||
|
||
if [[ -n $theme_check ]]; then
|
||
local -a theme_index
|
||
local git_theme new_themes=0 down_themes=0 new_theme
|
||
unset 'theme_index[@]' 'desc_color_theme[-1]' 'desc_color_theme[-1]' 'desc_color_theme[-1]' options_out
|
||
theme_index=($(curl -m 3 --raw https://raw.githubusercontent.com/aristocratos/bashtop/master/themes/index.txt 2>/dev/null))
|
||
if [[ ${theme_index[*]} =~ .theme ]]; then
|
||
for git_theme in ${theme_index[@]}; do
|
||
unset new_theme
|
||
if [[ ! -e "${config_dir}/themes/${git_theme}" ]]; then new_theme=1; fi
|
||
if curl -m 3 --raw "https://raw.githubusercontent.com/aristocratos/bashtop/master/themes/${git_theme}" >"${config_dir}/themes/${git_theme}" 2>/dev/null; then
|
||
((++down_themes))
|
||
if [[ -n $new_theme ]]; then
|
||
((++new_themes))
|
||
themes+=("themes/${git_theme%.theme}")
|
||
fi
|
||
fi
|
||
done
|
||
desc_color_theme+=("Downloaded ${down_themes} theme(s).")
|
||
desc_color_theme+=("Found ${new_themes} new theme(s)!")
|
||
else
|
||
desc_color_theme+=("ERROR: Couldn't get theme index!")
|
||
fi
|
||
fi
|
||
|
||
|
||
get_ms timestamp_end
|
||
if [[ -z $theme_check ]]; then time_left=$((timestamp_start+update_ms-timestamp_end))
|
||
else unset theme_check; time_left=0; fi
|
||
|
||
if ((time_left>500)); then wait_string=5; time_left=$((time_left-500))
|
||
elif ((time_left>100)); then wait_string=$((time_left/100)); time_left=0
|
||
else wait_string="0"; time_left=0; fi
|
||
|
||
get_key -v keypress -w ${wait_string}
|
||
|
||
if [[ -n $inputting ]]; then
|
||
case "$keypress" in
|
||
escape) unset inputting inputting_value ;;
|
||
enter|backspace) valid=1 ;;
|
||
*) if [[ ${#keypress} -eq 1 ]]; then valid=1; fi ;;
|
||
esac
|
||
else
|
||
case "$keypress" in
|
||
escape|q|backspace) break 1 ;;
|
||
down|tab) if ((selected_int<${#options_array[@]}-1)); then ((++selected_int)); else selected_int=0; fi ;;
|
||
up|shift_tab) if ((selected_int>0)); then ((selected_int--)); else selected_int=$((${#options_array[@]}-1)); fi ;;
|
||
left|right) if [[ -n $lr && -z $inputting ]]; then valid=1; fi ;;
|
||
enter) if [[ -n $inp ]]; then valid=1; fi ;;
|
||
page_down) if ((page<pages)); then ((page++)); else page=1; selected_int=0; fi; redraw_misc=1; selected_int=$(( (page-1)*height )) ;;
|
||
page_up) if ((page>1)); then ((page--)); else page=${pages}; fi; redraw_misc=1; selected_int=$(( (page-1)*height )) ;;
|
||
esac
|
||
if (( selected_int<(page-1)*height | selected_int>=page*height )); then page=$(( (selected_int/height)+1 )); redraw_misc=1; fi
|
||
fi
|
||
|
||
if [[ ${selected} == "color_theme" && ${keypress} =~ left|right && ${#themes} -lt 2 ]]; then unset valid; fi
|
||
|
||
if [[ -n $valid ]]; then
|
||
case "${selected} ${keypress}" in
|
||
"update_ms right")
|
||
if ((update_ms<86399900)); then
|
||
update_ms=$((update_ms+100))
|
||
updated_ms=1
|
||
fi
|
||
;;
|
||
"update_ms left")
|
||
if ((update_ms>100)); then
|
||
update_ms=$((update_ms-100))
|
||
updated_ms=1
|
||
fi
|
||
;;
|
||
"update_ms enter")
|
||
if [[ -z $inputting ]]; then inputting=1; inputting_value="${update_ms}"
|
||
else
|
||
if ((inputting_value<86400000)); then update_ms="${inputting_value:-0}"; updated_ms=1; fi
|
||
unset inputting inputting_value
|
||
fi
|
||
;;
|
||
"update_ms backspace"|"draw_clock backspace"|"custom_cpu_name backspace"|"disks_filter backspace")
|
||
if [[ ${#inputting_value} -gt 0 ]]; then
|
||
inputting_value="${inputting_value::-1}"
|
||
fi
|
||
;;
|
||
"update_ms"*)
|
||
inputting_value+="${keypress//[^0-9]/}"
|
||
;;
|
||
"draw_clock enter")
|
||
if [[ -z $inputting ]]; then inputting=1; inputting_value="${draw_clock}"
|
||
else draw_clock="${inputting_value}"; unset inputting inputting_value clock_out; fi
|
||
;;
|
||
"custom_cpu_name enter")
|
||
if [[ -z $inputting ]]; then inputting=1; inputting_value="${custom_cpu_name}"
|
||
else custom_cpu_name="${inputting_value}"; changed_cpu_name=1; unset inputting inputting_value; fi
|
||
;;
|
||
"disks_filter enter")
|
||
if [[ -z $inputting ]]; then inputting=1; inputting_value="${disks_filter}"
|
||
else disks_filter="${inputting_value}"; mem[counter]=10; resized=1; unset inputting inputting_value; fi
|
||
;;
|
||
"net_totals_reset enter")
|
||
if ((net[reset]==1)); then net_totals_reset="Off"; net[reset]=0
|
||
else net_totals_reset="On"; net[reset]=1; fi
|
||
;;
|
||
"check_temp"*|"error_logging"*|"background_update"*|"proc_reversed"*|"proc_gradient"*|"proc_per_core"*|"update_check"*|"hires_graphs"*|"use_psutil"*|"proc_tree"*)
|
||
local -n selected_var=${selected}
|
||
if [[ ${selected_var} == "true" ]]; then
|
||
selected_var="false"
|
||
if [[ $selected == "proc_reversed" ]]; then proc[order_change]=1; unset 'proc[reverse]'
|
||
elif [[ $selected == "proc_tree" ]]; then proc[order_change]=1; unset 'proc[tree]'; fi
|
||
else
|
||
selected_var="true"
|
||
if [[ $selected == "proc_reversed" ]]; then proc[order_change]=1; proc[reverse]="+"
|
||
elif [[ $selected == "proc_tree" ]]; then proc[order_change]=1; proc[tree]="+"; fi
|
||
fi
|
||
if [[ $selected == "check_temp" && $check_temp == true ]]; then
|
||
local has_temp
|
||
sensor_comm=""
|
||
if [[ $use_psutil == true ]]; then
|
||
py_command -v has_temp "get_sensors_check()"
|
||
if [[ $has_temp == true ]]; then sensor_comm="psutil"; fi
|
||
fi
|
||
if [[ -z $sensor_comm ]]; then
|
||
local checker
|
||
for checker in "vcgencmd" "sensors" "osx-cpu-temp"; do
|
||
if command -v "${checker}" >/dev/null 2>&1; then sensor_comm="${checker}"; break; fi
|
||
done
|
||
fi
|
||
if [[ -z $sensor_comm ]]; then check_temp="false"
|
||
else resized=1; fi
|
||
elif [[ $selected == "check_temp" ]]; then
|
||
resized=1
|
||
fi
|
||
if [[ $selected == "use_psutil" && $system != "Linux" ]]; then use_psutil="true"
|
||
elif [[ $selected == "use_psutil" ]]; then quit_ restart psutil; fi
|
||
if [[ $selected == "error_logging" ]]; then quit_ restart; fi
|
||
|
||
;;
|
||
"proc_sorting right")
|
||
if ((proc[sorting_int]<${#sorting[@]}-1)); then ((++proc[sorting_int]))
|
||
else proc[sorting_int]=0; fi
|
||
proc_sorting="${sorting[proc[sorting_int]]}"
|
||
proc[order_change]=1
|
||
;;
|
||
"proc_sorting left")
|
||
if ((proc[sorting_int]>0)); then ((proc[sorting_int]--))
|
||
else proc[sorting_int]=$((${#sorting[@]}-1)); fi
|
||
proc_sorting="${sorting[proc[sorting_int]]}"
|
||
proc[order_change]=1
|
||
;;
|
||
"color_theme right")
|
||
if ((theme_int<${#themes[@]}-1)); then ((++theme_int))
|
||
else theme_int=0; fi
|
||
color_theme="${themes[$theme_int]}"
|
||
color_init_
|
||
resized=1
|
||
;;
|
||
"color_theme left")
|
||
if ((theme_int>0)); then ((theme_int--))
|
||
else theme_int=$((${#themes[@]}-1)); fi
|
||
color_theme="${themes[$theme_int]}"
|
||
color_init_
|
||
resized=1
|
||
;;
|
||
"color_theme enter")
|
||
theme_check=1
|
||
if ((${#desc_color_theme[@]}>8)); then unset 'desc_color_theme[-1]'; fi
|
||
desc_color_theme+=("Checking for new themes...")
|
||
;;
|
||
"draw_clock"*|"custom_cpu_name"*|"disks_filter"*)
|
||
inputting_value+="${keypress//[\\\$\"\']/}"
|
||
;;
|
||
|
||
esac
|
||
|
||
fi
|
||
|
||
if [[ -n $changed_cpu_name ]]; then
|
||
changed_cpu_name=0
|
||
get_cpu_info
|
||
calc_sizes
|
||
draw_bg quiet
|
||
fi
|
||
|
||
if [[ $(${stty} size) != "$tty_height $tty_width" ]]; then resized; fi
|
||
|
||
if ((resized>0)); then
|
||
calc_sizes; draw_bg quiet
|
||
redraw_misc=1
|
||
unset options_out bannerd menu_out
|
||
fi
|
||
|
||
get_ms timestamp_end
|
||
time_left=$((timestamp_start+update_ms-timestamp_end))
|
||
if ((time_left<=0 | resized>0)); then get_ms timestamp_start; if [[ -z $inputting ]]; then collect_and_draw; fi; fi
|
||
if ((resized>0)); then resized=0; page=1; selected_int=0; fi
|
||
|
||
if [[ -n $updated_ms ]] && ((updated_ms++==2)); then
|
||
unset updated_ms
|
||
draw_update_string quiet
|
||
fi
|
||
|
||
done
|
||
|
||
if [[ -n $from_menu ]]; then pause_
|
||
elif [[ -n ${pause_screen} ]]; then unpause_; draw_update_string; fi
|
||
}
|
||
|
||
killer_() { #? Kill process with selected signal
|
||
local kill_op="$1" kill_pid="$2" killer_out killer_box col line program keypress selected selected_int=0 sig confirmed=0 option killer_pause status msg
|
||
local -a options=("yes" "no")
|
||
|
||
if ! program="$(ps -o comm -p ${kill_pid})"; then return
|
||
else program="$(tail -n1 <<<"$program")"; fi
|
||
|
||
case $kill_op in
|
||
t|T) kill_op="terminate"; sig="SIGTERM" ;;
|
||
k|K) kill_op="kill"; sig="SIGKILL" ;;
|
||
i|I) kill_op="interrupt"; sig="SIGINT" ;;
|
||
esac
|
||
|
||
until false; do
|
||
|
||
#* Put program to sleep if caught ctrl-z
|
||
if ((sleepy==1)); then sleep_; fi
|
||
|
||
if [[ $background_update == true || -z $killer_box ]]; then
|
||
draw_clock
|
||
pause_ killer_pause
|
||
else
|
||
unset killer_pause
|
||
fi
|
||
|
||
if [[ -z $killer_box ]]; then
|
||
col=$((tty_width/2-15)); line=$((tty_height/2-4)); y=1
|
||
unset redraw killer_box
|
||
create_box -v killer_box -w 40 -h 9 -l $line -c $((col++)) -fill -lc "${theme[proc_box]}" -title "${kill_op}"
|
||
fi
|
||
|
||
if ((confirmed==0)); then
|
||
selected="${options[selected_int]}"
|
||
print -v killer_out -m $((line+2)) $col -fg ${theme[title]} -b -jc 38 -t "${kill_op^} ${program::20}?" -m $((line+4)) $((col+3))
|
||
for option in "${options[@]}"; do
|
||
if [[ $option == "${selected}" ]]; then print -v killer_out -bg ${theme[selected_bg]} -fg ${theme[selected_fg]}; else print -v killer_out -fg ${theme[title]}; fi
|
||
print -v killer_out -b -r 5 -t "[ ${option^} ]" -rs
|
||
done
|
||
|
||
elif ((confirmed==1)); then
|
||
selected="ok"
|
||
print -v killer_out -m $((line+2)) $col -fg ${theme[title]} -b -jc 38 -t "Sending signal ${sig} to pid ${kill_pid}!"
|
||
print -v killer_out -m $((line+4)) $col -fg ${theme[main_fg]} -jc 38 -t "${status^}!" -m $((line+6)) $col
|
||
if [[ -n $msg ]]; then print -v killer_out -m $((line+5)) $col -fg ${theme[main_fg]} -jc 38 -t "${msg}" -m $((line+7)) $col; fi
|
||
print -v killer_out -fg ${theme[selected_fg]} -bg ${theme[selected_bg]} -b -r 15 -t "[ Ok ]" -rs
|
||
fi
|
||
|
||
echo -en "${killer_pause}${killer_box}${killer_out}"
|
||
unset killer_out draw_out
|
||
|
||
|
||
get_ms timestamp_end
|
||
time_left=$((timestamp_start+update_ms-timestamp_end))
|
||
|
||
if ((time_left>1000)); then wait_string=10; time_left=$((time_left-1000))
|
||
elif ((time_left>100)); then wait_string=$((time_left/100)); time_left=0
|
||
else wait_string="0"; time_left=0; fi
|
||
|
||
get_key -v keypress -w ${wait_string}
|
||
if [[ $(${stty} size) != "$tty_height $tty_width" ]]; then resized; fi
|
||
if ((resized>0)); then
|
||
calc_sizes; draw_bg quiet; time_left=0; unset killer_out killer_box
|
||
fi
|
||
|
||
case "$keypress" in
|
||
right|shift_tab) if ((selected_int>0)); then ((selected_int--)); else selected_int=$((${#options[@]}-1)); fi ;;
|
||
left|tab) if ((selected_int<${#options[@]}-1)); then ((++selected_int)); else selected_int=0; fi ;;
|
||
enter)
|
||
case "$selected" in
|
||
yes) confirmed=1 ;;
|
||
no|ok) confirmed=-1 ;;
|
||
esac
|
||
;;
|
||
q|Q) quit_ ;;
|
||
esac
|
||
|
||
if ((confirmed<0)); then
|
||
unpause_
|
||
break
|
||
elif ((confirmed>0)) && [[ -z $status ]]; then
|
||
if ${kill} -${sig} ${kill_pid} >/dev/null 2>&1; then
|
||
status="success"
|
||
else
|
||
if ! ps -p ${kill_pid} >/dev/null 2>&1; then
|
||
msg="Process not running."
|
||
elif [[ $UID != 0 ]]; then
|
||
msg="Try restarting with sudo."
|
||
else
|
||
msg="Unknown error."
|
||
fi
|
||
status="failed"; fi
|
||
fi
|
||
|
||
|
||
if ((time_left==0)); then get_ms timestamp_start; unset draw_out; collect_and_draw; fi
|
||
if ((resized>=5)); then resized=0; fi
|
||
|
||
done
|
||
|
||
|
||
}
|
||
|
||
get_key() { #? Get one key from standard input and translate key code to readable format
|
||
local key key_out wait_time esc ext_out save
|
||
|
||
if ((quitting==1)); then quit_; fi
|
||
|
||
until (($#==0)); do
|
||
case "$1" in
|
||
-v|-variable) local -n key_out=$2; ext_out=1; shift;; #* Output variable
|
||
-w|-wait) wait_time="$2"; shift;; #* Time to wait for key
|
||
-s|-save) save=1;; #* Save key for later processing
|
||
esac
|
||
shift
|
||
done
|
||
|
||
if [[ -z $save && -n ${saved_key[0]} ]]; then key="${saved_key[0]}"; unset 'saved_key[0]'; saved_key=("${saved_key[@]}")
|
||
else
|
||
unset key
|
||
|
||
key=$(${stty} -cooked min 0 time ${wait_time:-0} 2>/dev/null; ${dd} bs=1 count=1 2>/dev/null)
|
||
if [[ -z ${key:+s} ]]; then
|
||
key_out=""
|
||
${stty} isig
|
||
if [[ -z $save ]]; then return 0
|
||
else return 1; fi
|
||
fi
|
||
|
||
#* Read 3 more characters if a leading escape character is detected
|
||
if [[ $key == "${enter_key}" ]]; then key="enter"
|
||
elif [[ $key == "${ctrl_c}" ]]; then quitting=1; time_left=0
|
||
elif [[ $key == "${ctrl_z}" ]]; then sleepy=1; time_left=0
|
||
elif [[ $key == "${backspace}" || $key == "${backspace_real}" ]]; then key="backspace"
|
||
elif [[ $key == "${tab}" ]]; then key="tab"
|
||
elif [[ $key == "$esc_character" ]]; then
|
||
esc=1; key=$(${stty} -cooked min 0 time 0 2>/dev/null; ${dd} bs=1 count=3 2>/dev/null); fi
|
||
if [[ -z $key && $esc -eq 1 ]]; then key="escape"
|
||
elif [[ $esc -eq 1 ]]; then
|
||
case "${key}" in
|
||
'[A'*|'OA'*) key="up" ;;
|
||
'[B'*|'OB'*) key="down" ;;
|
||
'[D'*|'OD'*) key="left" ;;
|
||
'[C'*|'OC'*) key="right" ;;
|
||
'[2~') key="insert" ;;
|
||
'[3~') key="delete" ;;
|
||
'[H'*) key="home" ;;
|
||
'[F'*) key="end" ;;
|
||
'[5~') key="page_up" ;;
|
||
'[6~') key="page_down" ;;
|
||
'[Z'*) key="shift_tab" ;;
|
||
'OP'*) key="f1";;
|
||
'OQ'*) key="f2";;
|
||
'OR'*) key="f3";;
|
||
'OS'*) key="f4";;
|
||
'[15') key="f5";;
|
||
'[17') key="f6";;
|
||
'[18') key="f7";;
|
||
'[19') key="f8";;
|
||
'[20') key="f9";;
|
||
'[21') key="f10";;
|
||
'[23') key="f11";;
|
||
'[24') key="f12";;
|
||
*) key="" ;;
|
||
esac
|
||
fi
|
||
|
||
fi
|
||
|
||
${stty} -cooked min 0 time 0 >/dev/null 2>&1; ${dd} bs=512 count=1 >/dev/null 2>&1
|
||
${stty} isig
|
||
if [[ -n $save && -n $key ]]; then saved_key+=("${key}"); return 0; fi
|
||
|
||
if [[ -n $ext_out ]]; then key_out="${key}"
|
||
else echo -n "${key}"; fi
|
||
}
|
||
|
||
process_input() { #? Process keypresses for main ui
|
||
local wait_time="$1" keypress esc prev_screen anykey filter_change p_height=$((box[processes_height]-3))
|
||
late_update=0
|
||
#* Wait while reading input
|
||
get_key -v keypress -w "${wait_time}"
|
||
if [[ -z $keypress ]] || [[ -n $failed_pipe ]]; then return; fi
|
||
|
||
if [[ -n $input_to_filter ]]; then
|
||
filter_change=1
|
||
case "$keypress" in
|
||
"enter") unset input_to_filter ;;
|
||
"backspace") if [[ ${#filter} -gt 0 ]]; then filter="${filter:: (-1)}"; else unset filter_change; fi ;;
|
||
"escape") unset input_to_filter filter ;;
|
||
*) if [[ ${#keypress} -eq 1 && $keypress =~ ^[A-Za-z0-9\!\@\#\%\&\/\(\)\[\+\-\_\*\,\;\.\:]$ ]]; then filter+="${keypress//[\\\$\"\']/}"; else unset filter_change; fi ;;
|
||
esac
|
||
|
||
else
|
||
case "$keypress" in
|
||
left) #* Move left in processes sorting column
|
||
if ((proc[sorting_int]>0)); then ((proc[sorting_int]--))
|
||
else proc[sorting_int]=$((${#sorting[@]}-1)); fi
|
||
proc_sorting="${sorting[proc[sorting_int]]}"
|
||
if [[ $proc_sorting == "tree" && $use_psutil == true ]]; then
|
||
((proc[sorting_int]--))
|
||
proc_sorting="${sorting[proc[sorting_int]]}"
|
||
fi
|
||
filter_change=1
|
||
;;
|
||
right) #* Move right in processes sorting column
|
||
if ((proc[sorting_int]<${#sorting[@]}-1)); then ((++proc[sorting_int]))
|
||
else proc[sorting_int]=0; fi
|
||
proc_sorting="${sorting[proc[sorting_int]]}"
|
||
if [[ $proc_sorting == "tree" && $use_psutil == true ]]; then
|
||
proc[sorting_int]=0
|
||
proc_sorting="${sorting[proc[sorting_int]]}"
|
||
fi
|
||
filter_change=1
|
||
;;
|
||
n|N) #* Switch to next network device
|
||
if ((${#nic_list[@]}>1)); then
|
||
if ((nic_int<${#nic_list[@]}-1)); then ((++nic_int))
|
||
else nic_int=0; fi
|
||
net[device]="${nic_list[nic_int]}"
|
||
net[nic_change]=1
|
||
collect_net init
|
||
collect_net
|
||
draw_net now
|
||
fi
|
||
;;
|
||
b|B) #* Switch to previous network device
|
||
if ((${#nic_list[@]}>1)); then
|
||
if ((nic_int>0)); then ((nic_int--))
|
||
else nic_int=$((${#nic_list[@]}-1)); fi
|
||
net[device]="${nic_list[nic_int]}"
|
||
net[nic_change]=1
|
||
collect_net init
|
||
collect_net
|
||
draw_net now
|
||
fi
|
||
;;
|
||
up|shift_tab) #* Move process selector up one
|
||
if ((proc[selected]>1)); then
|
||
((proc[selected]--))
|
||
proc[page_change]=1
|
||
elif ((proc[start]>1)); then
|
||
if ((proc[selected]==0)); then proc[selected]=${p_height}; fi
|
||
((proc[start]--))
|
||
proc[page_change]=1
|
||
elif ((proc[start]==1 & proc[selected]==1)); then
|
||
proc[selected]=0
|
||
proc[page_change]=1
|
||
fi
|
||
;;
|
||
down|tab) #* Move process selector down one
|
||
if ((proc[selected]<p_height & proc[start]+proc[selected]<(${#proc_array[@]}) )); then
|
||
((++proc[selected]))
|
||
proc[page_change]=1
|
||
elif ((proc[start]+proc[selected]<(${#proc_array[@]}) )); then
|
||
((++proc[start]))
|
||
proc[page_change]=1
|
||
fi
|
||
;;
|
||
enter) #* Show detailed info for selected process or close detailed info if no new process is selected
|
||
if ((proc[selected]>0 & proc[detailed_pid]!=proc[selected_pid])) && ps -p ${proc[selected_pid]} > /dev/null 2>&1; then
|
||
proc[detailed]=1
|
||
proc[detailed_change]=1
|
||
proc[detailed_pid]=${proc[selected_pid]}
|
||
proc[selected]=0
|
||
unset 'proc[detailed_name]' 'detail_history[@]' 'detail_mem_history[@]' 'proc[detailed_killed]'
|
||
calc_sizes
|
||
collect_processes now
|
||
elif ((proc[detailed]==1 & proc[detailed_pid]!=proc[selected_pid])); then
|
||
proc[detailed]=0
|
||
proc[detailed_change]=1
|
||
unset 'proc[detailed_pid]'
|
||
calc_sizes
|
||
fi
|
||
;;
|
||
page_up) #* Move up one page in process box
|
||
if ((proc[start]>1)); then
|
||
proc[start]=$(( proc[start]-p_height ))
|
||
if ((proc[start]<1)); then proc[start]=1; fi
|
||
proc[page_change]=1
|
||
elif ((proc[selected]>0)); then
|
||
proc[selected]=0
|
||
proc[start]=1
|
||
proc[page_change]=1
|
||
fi
|
||
;;
|
||
page_down) #* Move down one page in process box
|
||
if ((proc[start]<(${#proc_array[@]}-1)-p_height)); then
|
||
if ((proc[start]==1)) && [[ $use_psutil == false ]]; then collect_processes now; fi
|
||
proc[start]=$(( proc[start]+p_height ))
|
||
if (( proc[start]>(${#proc_array[@]})-p_height )); then proc[start]=$(( (${#proc_array[@]})-p_height )); fi
|
||
proc[page_change]=1
|
||
elif ((proc[selected]>0)); then
|
||
proc[selected]=$((p_height))
|
||
proc[page_change]=1
|
||
fi
|
||
;;
|
||
home) #* Go to first page in process box
|
||
proc[start]=1
|
||
proc[page_change]=1
|
||
;;
|
||
end) #* Go to last page in process box
|
||
if ((proc[selected]==0)) && [[ $use_psutil == false ]]; then collect_processes now; fi
|
||
proc[start]=$(((${#proc_array[@]}-1)-p_height))
|
||
proc[page_change]=1
|
||
;;
|
||
r|R) #* Reverse order of processes sorting column
|
||
if [[ -z ${proc[reverse]} ]]; then
|
||
proc[reverse]="+"
|
||
proc_reversed="true"
|
||
else
|
||
proc_reversed="false"
|
||
unset 'proc[reverse]'
|
||
fi
|
||
filter_change=1
|
||
;;
|
||
e|E) #* Show processes as a tree
|
||
if [[ -z ${proc[tree]} ]]; then
|
||
proc[tree]="+"
|
||
proc_tree="true"
|
||
else
|
||
proc_tree="false"
|
||
unset 'proc[tree]'
|
||
fi
|
||
filter_change=1
|
||
;;
|
||
o|O|f2) #* Options
|
||
options_
|
||
;;
|
||
+|A|a) #* Add 100ms to update timer
|
||
if ((update_ms<86399900)); then
|
||
update_ms=$((update_ms+100))
|
||
draw_update_string
|
||
fi
|
||
;;
|
||
-|S|s) #* Subtract 100ms from update timer
|
||
if ((update_ms>100)); then
|
||
update_ms=$((update_ms-100))
|
||
draw_update_string
|
||
fi
|
||
;;
|
||
h|H|f1) #* Show help
|
||
help_
|
||
;;
|
||
q|Q) #* Quit
|
||
quit_
|
||
;;
|
||
m|M|escape) #* Show main menu
|
||
menu_
|
||
;;
|
||
f|F) #* Start process filtering input
|
||
input_to_filter=1
|
||
filter_change=1
|
||
if ((proc[selected]>1)); then proc[selected]=1; fi
|
||
proc[start]=1
|
||
;;
|
||
c|C) #* Clear process filter
|
||
if [[ -n $filter ]]; then
|
||
unset input_to_filter filter
|
||
filter_change=1
|
||
fi
|
||
;;
|
||
t|T|k|K|i|I) #* Send terminate, kill or interrupt signal
|
||
if [[ ${proc[selected]} -gt 0 ]]; then
|
||
killer_ "$keypress" "${proc[selected_pid]}"
|
||
elif [[ ${proc[detailed]} -eq 1 && -z ${proc[detailed_killed]} ]]; then
|
||
killer_ "$keypress" "${proc[detailed_pid]}"
|
||
fi
|
||
;;
|
||
esac
|
||
fi
|
||
|
||
if [[ -n $filter_change ]]; then
|
||
unset filter_change
|
||
collect_processes now
|
||
proc[filter_change]=1
|
||
draw_processes now
|
||
elif [[ ${proc[page_change]} -eq 1 || ${proc[detailed_change]} == 1 ]]; then
|
||
if ((proc[selected]==0)); then unset 'proc[selected_pid]'; proc[detailed_change]=1; fi
|
||
draw_processes now
|
||
fi
|
||
|
||
#* Subtract time since input start from time left if timer is interrupted
|
||
get_ms timestamp_input_end
|
||
time_left=$(( (timestamp_start+update_ms)-timestamp_input_end ))
|
||
|
||
return 0
|
||
}
|
||
|
||
collect_and_draw() { #? Run all collect and draw functions
|
||
local task_int=0 input_runs
|
||
for task in processes cpu mem net; do
|
||
((++task_int))
|
||
if [[ -n $pause_screen && -n ${saved_key[0]} ]]; then
|
||
return
|
||
elif [[ -z $pause_screen ]]; then
|
||
input_runs=0
|
||
while [[ -n ${saved_key[0]} ]] && ((time_left>0)) && ((++input_runs<=5)); do
|
||
process_input
|
||
unset late_update
|
||
done
|
||
fi
|
||
collect_${task}
|
||
if get_key -save && [[ -z $pause_screen ]]; then process_input; fi
|
||
draw_${task}
|
||
if get_key -save && [[ -z $pause_screen ]]; then process_input; fi
|
||
draw_clock "$1"
|
||
if ((resized>0 & resized<task_int)) || [[ -n $failed_pipe || -n $py_error ]]; then return; fi
|
||
done
|
||
|
||
last_screen="${draw_out}"
|
||
}
|
||
|
||
#? ----------------------------------------------------------------------------------------------------------------------- ?#
|
||
|
||
main_loop() { #? main loop...
|
||
local wait_time wait_string input_runs
|
||
|
||
#* Put program to sleep if caught ctrl-z
|
||
if ((sleepy==1)); then sleep_; fi
|
||
|
||
#* Timestamp for accurate timer
|
||
get_ms timestamp_start
|
||
|
||
if [[ $(${stty} size) != "$tty_height $tty_width" ]]; then resized; fi
|
||
|
||
if ((resized>0)); then
|
||
calc_sizes
|
||
draw_bg
|
||
fi
|
||
|
||
#* Run all collect and draw functions
|
||
collect_and_draw now
|
||
|
||
#* Reset resized variable if resized and all functions have finished redrawing
|
||
if ((resized>=5)); then resized=0
|
||
elif ((resized>0)); then unset draw_out proc_out clock_out; return; fi
|
||
|
||
#* Echo everyting out to screen in one command to get a smooth transition between updates
|
||
echo -en "${draw_out}${proc_out}${clock_out}"
|
||
unset draw_out
|
||
|
||
#* Periodically check for new network device if non was found at start or was removed
|
||
if ((net[device_check]>10)); then
|
||
net[device_check]=0
|
||
get_net_device
|
||
elif [[ -n ${net[no_device]} ]]; then
|
||
((++net[device_check]))
|
||
fi
|
||
|
||
#* Compare timestamps to get exact time needed to wait until next loop
|
||
get_ms timestamp_end
|
||
time_left=$((timestamp_start+update_ms-timestamp_end))
|
||
if ((time_left>update_ms)); then time_left=$update_ms; fi
|
||
if ((time_left>0)); then
|
||
|
||
late_update=0
|
||
|
||
#* Divide waiting time in chunks of 500ms and below to keep program responsive while reading input
|
||
while ((time_left>0 & resized==0)); do
|
||
|
||
#* If NOT waiting for input and time left is greater than 500ms, wait 500ms and loop
|
||
if [[ -z $input_to_filter ]] && ((time_left>=500)); then
|
||
wait_string="5"
|
||
time_left=$((time_left-500))
|
||
|
||
#* If waiting for input and time left is greater than "50 ms", wait 50ms and loop
|
||
elif [[ -n $input_to_filter ]] && ((time_left>=100)); then
|
||
wait_string="1"
|
||
time_left=$((time_left-100))
|
||
|
||
#* Else format wait string with padded zeroes if needed and break loop
|
||
else
|
||
if ((time_left>=100)); then wait_string=$((time_left/100)); else wait_string=0; fi
|
||
time_left=0
|
||
fi
|
||
|
||
#* Wait while reading input
|
||
process_input "${wait_string}"
|
||
if [[ -n $failed_pipe || -n $py_error ]]; then return; fi
|
||
|
||
#* Draw clock if set
|
||
draw_clock now
|
||
|
||
done
|
||
|
||
#* If time left is too low to process any input more than five times in succession, add 100ms to update timer
|
||
elif ((++late_update==5)); then
|
||
update_ms=$((update_ms+100))
|
||
draw_update_string
|
||
fi
|
||
|
||
unset skip_process_draw skip_net_draw
|
||
}
|
||
|
||
#? Pre main loop
|
||
|
||
#* Read config file or create if non existant
|
||
config_dir="${XDG_CONFIG_HOME:-$HOME/.config}/bashtop"
|
||
if [[ -d "${config_dir}" && -w "${config_dir}" ]] || mkdir -p "${config_dir}"; then
|
||
if [[ ! -d "${config_dir}/themes" ]]; then mkdir -p "${config_dir}/themes"; fi
|
||
if [[ ! -d "${config_dir}/user_themes" ]]; then mkdir -p "${config_dir}/user_themes"; fi
|
||
config_file="${config_dir}/bashtop.cfg"
|
||
# shellcheck source=/dev/null
|
||
if [[ -e "$config_file" ]]; then
|
||
source "$config_file"
|
||
|
||
#* If current config is from an older version recreate config file and save user changes
|
||
if [[ $(get_value -sf "${config_file}" -k "bashtop v." -mk 1) != "${version}" ]]; then
|
||
create_config
|
||
save_config "${save_array[@]}"
|
||
fi
|
||
else create_config; fi
|
||
else
|
||
#* If anything goes wrong turn off all writing to filesystem
|
||
echo "ERROR: Could not set config dir!"
|
||
config_dir="/dev/null"
|
||
config_file="/dev/null"
|
||
error_logging="false"
|
||
unset 'save_array[@]'
|
||
fi
|
||
|
||
#* Force the use of python psutil if not on Linux
|
||
if [[ $system != "Linux" ]]; then use_psutil="true"; fi
|
||
|
||
#* Check for python3 and psutil if "use_psutil" is true
|
||
if [[ $use_psutil == true ]]; then
|
||
if ! command -v python3 >/dev/null 2>&1; then
|
||
echo "Error: Missing python3!"
|
||
if [[ $system == "Linux" ]]; then
|
||
use_psutil="false"
|
||
else
|
||
exit 1
|
||
fi
|
||
elif [[ $use_psutil == true ]] && ! (cd / && python3 -c "import psutil") >/dev/null 2>&1; then
|
||
echo "Error: Missing python3 psutil module!"
|
||
if [[ $system == "Linux" ]]; then
|
||
use_psutil="false"
|
||
else
|
||
exit 1
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
#* If using bash version 5, set timestamps with EPOCHREALTIME variable
|
||
if [[ -n $EPOCHREALTIME ]]; then
|
||
get_ms() { #? Set given variable to current epoch millisecond with EPOCHREALTIME varialble
|
||
local -n ms_out=$1
|
||
ms_out=$((${EPOCHREALTIME/[.,]/}/1000))
|
||
}
|
||
|
||
#* If not, but using psutil, set timestamps with python
|
||
elif [[ $use_psutil == true ]]; then
|
||
get_ms() {
|
||
local -n ms_out=$1
|
||
py_command -v ms_out "get_ms()"
|
||
}
|
||
|
||
#* Else use date command
|
||
else
|
||
get_ms() { #? Set given variable to current epoch millisecond with date command
|
||
local -n ms_out=$1
|
||
ms_out=""
|
||
read ms_out < <(${date} +%s%3N)
|
||
}
|
||
fi
|
||
|
||
#* Setup psutil script
|
||
if [[ $use_psutil == true ]]; then
|
||
py_command() {
|
||
if [[ -n $failed_pipe ]]; then return; fi
|
||
local arr var output cmd pyerr=${py_error} ln
|
||
case $1 in
|
||
"quit")
|
||
echo "quit" >&${pycoproc[1]} 2>/dev/null || true
|
||
return
|
||
;;
|
||
"-v") var=1;;
|
||
"-vn") var=1; ln=1;;
|
||
"-a") arr=1;;
|
||
*) return;;
|
||
esac
|
||
local -n pyout=$2
|
||
cmd="$3"
|
||
|
||
echo "${cmd}" >&${pycoproc[1]} #2>/dev/null || true
|
||
if [[ -n $var ]]; then pyout=""
|
||
else pyout=(); fi
|
||
while IFS= read -r -u ${pycoproc[0]} -t 1 output; do #2>/dev/null
|
||
if [[ $output == '/EOL' ]]; then break; fi
|
||
if [[ -n $failed_pipe ]]; then py_out=""; return 1; fi
|
||
if [[ $output == '/ERROR' ]]; then ((++py_error)); unset arr var; fi
|
||
if [[ -n $arr ]]; then pyout+=("${output}")
|
||
elif [[ -n $var ]]; then pyout+="${output}${ln:+\n}"; fi
|
||
done
|
||
if ((py_error>pyerr)); then py_out=""; return 1; fi
|
||
if [[ -n $ln ]]; then printf -v pyout "%b" "${pyout}"; fi
|
||
return 0
|
||
}
|
||
|
||
if ! pytmpdir=$(mktemp -d "${TMPDIR:-/tmp}"/XXXXXXXXXXXX); then
|
||
if [[ $system == "Linux" ]]; then
|
||
use_psutil="false"
|
||
else
|
||
echo "ERROR: Failed setting up temp directory for psutil script!"
|
||
exit 1
|
||
fi
|
||
else
|
||
pywrapper="${pytmpdir}/bashtop.psutil"
|
||
|
||
cat << 'EOF' > "${pywrapper}"
|
||
import os, sys, subprocess, re, time, psutil
|
||
from datetime import timedelta
|
||
from collections import defaultdict
|
||
from typing import List, Set, Dict, Tuple, Optional, Union
|
||
|
||
system: str
|
||
if "linux" in sys.platform: system = "Linux"
|
||
elif "bsd" in sys.platform: system = "BSD"
|
||
elif "darwin" in sys.platform: system = "MacOS"
|
||
else: system = "Other"
|
||
|
||
parent_pid: int = psutil.Process(os.getpid()).ppid()
|
||
|
||
allowed_commands: Tuple[str] = (
|
||
'get_proc',
|
||
'get_disks',
|
||
'get_cpu_name',
|
||
'get_cpu_cores',
|
||
'get_nics',
|
||
'get_cpu_cores',
|
||
'get_cpu_usage',
|
||
'get_cpu_freq',
|
||
'get_uptime',
|
||
'get_load_avg',
|
||
'get_mem',
|
||
'get_detailed_names_cmd',
|
||
'get_detailed_mem_time',
|
||
'get_net',
|
||
'get_cmd_out',
|
||
'get_sensors',
|
||
'get_sensors_check',
|
||
'get_ms'
|
||
)
|
||
command: str = ''
|
||
cpu_count: int = psutil.cpu_count()
|
||
disk_hist: Dict = {}
|
||
|
||
def cleaned(string: str) -> str:
|
||
'''Escape characters not suitable for "echo -e" in bash'''
|
||
return string.replace("\\", "\\\\").replace("$", "\\$").replace("\n", "\\n").replace("\t", "\\t").replace("\"", "\\\"").replace("\'", "\\\'")
|
||
|
||
def get_cmd_out(cmd: str):
|
||
'''Save bash the trouble of creating child processes by running through python instead'''
|
||
print(subprocess.check_output(cmd, shell=True, universal_newlines=True).rstrip())
|
||
|
||
def get_ms():
|
||
'''Get current epoch millisecond'''
|
||
t = str(time.time()).split(".")
|
||
print(f'{t[0]}{t[1][:3]}')
|
||
|
||
def get_sensors():
|
||
'''A clone of "sensors" but using psutil'''
|
||
temps = psutil.sensors_temperatures()
|
||
if not temps:
|
||
return
|
||
try:
|
||
for name, entries in temps.items():
|
||
print(name)
|
||
for entry in entries:
|
||
print(f'{entry.label or name}: {entry.current}°C (high = {entry.high}°C, crit = {entry.critical}°C)')
|
||
print()
|
||
except:
|
||
pass
|
||
|
||
def get_sensors_check():
|
||
'''Check if get_sensors() output contains accepted CPU temperature values'''
|
||
if not hasattr(psutil, "sensors_temperatures"): print("false"); return
|
||
try:
|
||
temps = psutil.sensors_temperatures()
|
||
except:
|
||
pass
|
||
print("false"); return
|
||
if not temps: print("false"); return
|
||
try:
|
||
for _, entries in temps.items():
|
||
for entry in entries:
|
||
if entry.label.startswith(('Package', 'Core 0', 'Tdie')):
|
||
print("true")
|
||
return
|
||
except:
|
||
pass
|
||
print("false")
|
||
|
||
def get_cpu_name():
|
||
'''Fetch a suitable CPU identifier from the CPU model name string'''
|
||
name: str = ""
|
||
command: str = ""
|
||
all_info: str = ""
|
||
rem_line: str = ""
|
||
if system == "Linux":
|
||
command = "cat /proc/cpuinfo"
|
||
rem_line = "model name"
|
||
elif system == "MacOS":
|
||
command ="sysctl -n machdep.cpu.brand_string"
|
||
elif system == "BSD":
|
||
command ="sysctl hw.model"
|
||
rem_line = "hw.model"
|
||
|
||
all_info = subprocess.check_output("LANG=C " + command, shell=True, universal_newlines=True)
|
||
if rem_line:
|
||
for line in all_info.split("\n"):
|
||
if rem_line in line:
|
||
name = re.sub( ".*" + rem_line + ".*:", "", line,1).lstrip()
|
||
else:
|
||
name = all_info
|
||
if "Xeon" in name:
|
||
name = name.split(" ")
|
||
name = name[name.index("CPU")+1]
|
||
elif "Ryzen" in name:
|
||
name = name.split(" ")
|
||
name = " ".join(name[name.index("Ryzen"):name.index("Ryzen")+3])
|
||
elif "CPU" in name:
|
||
name = name.split(" ")
|
||
name = name[name.index("CPU")-1]
|
||
|
||
print(name)
|
||
|
||
def get_cpu_cores():
|
||
'''Get number of CPU cores and threads'''
|
||
cores: int = psutil.cpu_count(logical=False)
|
||
threads: int = psutil.cpu_count(logical=True)
|
||
print(f'{cores} {threads if threads else cores}')
|
||
|
||
def get_cpu_usage():
|
||
cpu: float = psutil.cpu_percent(percpu=False)
|
||
threads: List[float] = psutil.cpu_percent(percpu=True)
|
||
print(f'{cpu:.0f}')
|
||
for thread in threads:
|
||
print(f'{thread:.0f}')
|
||
|
||
def get_cpu_freq():
|
||
'''Get current CPU frequency'''
|
||
try:
|
||
print(f'{psutil.cpu_freq().current:.0f}')
|
||
except:
|
||
print(0)
|
||
|
||
def get_uptime():
|
||
'''Get current system uptime'''
|
||
print(str(timedelta(seconds=round(time.time()-psutil.boot_time(),0)))[:-3])
|
||
|
||
def get_load_avg():
|
||
'''Get CPU load average'''
|
||
for lavg in os.getloadavg():
|
||
print(round(lavg, 2), ' ', end='')
|
||
print()
|
||
|
||
def get_mem():
|
||
'''Get current system memory and swap usage'''
|
||
mem = psutil.virtual_memory()
|
||
swap = psutil.swap_memory()
|
||
try:
|
||
cmem = mem.cached>>10
|
||
except:
|
||
cmem = mem.active>>10
|
||
print(mem.total>>10, mem.free>>10, mem.available>>10, cmem, swap.total>>10, swap.free>>10)
|
||
|
||
def get_nics():
|
||
'''Get a list of all network devices sorted by highest throughput'''
|
||
io_all = psutil.net_io_counters(pernic=True)
|
||
up_stat = psutil.net_if_stats()
|
||
|
||
for nic in sorted(psutil.net_if_addrs(), key=lambda nic: (io_all[nic].bytes_recv + io_all[nic].bytes_sent), reverse=True):
|
||
if up_stat[nic].isup is False:
|
||
continue
|
||
print(nic)
|
||
|
||
def get_net(net_dev: str):
|
||
'''Emulated /proc/net/dev for selected network device'''
|
||
net = psutil.net_io_counters(pernic=True)[net_dev]
|
||
print(0,net.bytes_recv,0,0,0,0,0,0,0,net.bytes_sent)
|
||
|
||
def get_detailed_names_cmd(pid: int):
|
||
'''Get name, parent name, username and arguments for selected pid'''
|
||
p = psutil.Process(pid)
|
||
pa = psutil.Process(p.ppid())
|
||
with p.oneshot():
|
||
print(p.name())
|
||
print(pa.name())
|
||
print(p.username())
|
||
cmd = ' '.join(p.cmdline()) or '[' + p.name() + ']'
|
||
print(cleaned(cmd))
|
||
|
||
def get_detailed_mem_time(pid: int):
|
||
'''Get memory usage and runtime for selected pid'''
|
||
p = psutil.Process(pid)
|
||
with p.oneshot():
|
||
print(p.memory_info().rss)
|
||
print(timedelta(seconds=round(time.time()-p.create_time(),0)))
|
||
|
||
def get_proc(sorting='cpu lazy', tree=False, prog_len=0, arg_len=0, search='', reverse=True, proc_per_cpu=True, max_lines=0):
|
||
'''List all processess with pid, name, arguments, threads, username, memory percent and cpu percent'''
|
||
line_count: int = 0
|
||
err: float = 0.0
|
||
reverse = not reverse
|
||
|
||
if sorting == 'pid':
|
||
sort_cmd = "p.info['pid']"
|
||
elif sorting == 'program' or tree and sorting == "arguments":
|
||
sort_cmd = "p.info['name']"
|
||
reverse = not reverse
|
||
elif sorting == 'arguments':
|
||
sort_cmd = "' '.join(str(p.info['cmdline'])) or p.info['name']"
|
||
reverse = not reverse
|
||
elif sorting == 'threads':
|
||
sort_cmd = "str(p.info['num_threads'])"
|
||
elif sorting == 'user':
|
||
sort_cmd = "p.info['username']"
|
||
reverse = not reverse
|
||
elif sorting == 'memory':
|
||
sort_cmd = "str(p.info['memory_percent'])"
|
||
elif sorting == 'cpu responsive':
|
||
sort_cmd = "p.info['cpu_percent']" if proc_per_cpu else "(p.info['cpu_percent'] / cpu_count)"
|
||
else:
|
||
sort_cmd = "(sum(p.info['cpu_times'][:2] if not p.info['cpu_times'] == 0.0 else [0.0, 0.0]) * 1000 / (time.time() - p.info['create_time']))"
|
||
|
||
if tree:
|
||
proc_tree(width=prog_len + arg_len, sorting=sort_cmd, reverse=reverse, max_lines=max_lines, proc_per_cpu=proc_per_cpu, search=search)
|
||
return
|
||
|
||
|
||
print(f"{'Pid:':>7} {'Program:':<{prog_len}}", f"{'Arguments:':<{arg_len-4}}" if arg_len else '', f"{'Threads:' if arg_len else ' Tr:'} {'User:':<9}Mem%{'Cpu%':>11}", sep='')
|
||
|
||
for p in sorted(psutil.process_iter(['pid', 'name', 'cmdline', 'num_threads', 'username', 'memory_percent', 'cpu_percent', 'cpu_times', 'create_time'], err), key=lambda p: eval(sort_cmd), reverse=reverse):
|
||
if p.info['name'] == 'idle' or p.info['name'] == err or p.info['pid'] == err:
|
||
continue
|
||
if p.info['cmdline'] == err:
|
||
p.info['cmdline'] = ""
|
||
if p.info['username'] == err:
|
||
p.info['username'] = "?"
|
||
if p.info['num_threads'] == err:
|
||
p.info['num_threads'] = 0
|
||
if search:
|
||
found = False
|
||
for value in [ p.info['name'], ' '.join(p.info['cmdline']), str(p.info['pid']), p.info['username'] ]:
|
||
if search in value:
|
||
found = True
|
||
break
|
||
if not found:
|
||
continue
|
||
|
||
cpu = p.info['cpu_percent'] if proc_per_cpu else (p.info['cpu_percent'] / psutil.cpu_count())
|
||
mem = p.info['memory_percent']
|
||
cmd = ' '.join(p.info['cmdline']) or '[' + p.info['name'] + ']'
|
||
print(f"{p.info['pid']:>7} ",
|
||
f"{cleaned(p.info['name']):<{prog_len}.{prog_len-1}}",
|
||
f"{cleaned(cmd):<{arg_len}.{arg_len-1}}" if arg_len else '',
|
||
f"{p.info['num_threads']:>4} " if p.info['num_threads'] < 1000 else '999> ',
|
||
f"{p.info['username']:<9.9}" if len(p.info['username']) < 10 else f"{p.info['username'][:8]:<8}+",
|
||
f"{mem:>4.1f}" if mem < 100 else f"{mem:>4.0f} ",
|
||
f"{cpu:>11.1f} " if cpu < 100 else f"{cpu:>11.0f} ",
|
||
sep='')
|
||
line_count += 1
|
||
if max_lines and line_count == max_lines:
|
||
break
|
||
|
||
def proc_tree(width: int, sorting: str = 'cpu lazy', reverse: bool = True, max_lines: int = 0, proc_per_cpu=True, search=''):
|
||
'''List all processess in a tree view with pid, name, threads, username, memory percent and cpu percent'''
|
||
tree_line_count: int = 0
|
||
err: float = 0.0
|
||
|
||
def create_tree(parent: int, tree, indent: str = '', inindent: str = ' ', found: bool = False):
|
||
nonlocal infolist, tree_line_count, max_lines, tree_width, proc_per_cpu, search
|
||
cont: bool = True
|
||
if max_lines and tree_line_count >= max_lines:
|
||
return
|
||
try:
|
||
name: str = psutil.Process(parent).name()
|
||
if name == "idle": return
|
||
except psutil.Error:
|
||
pass
|
||
name: str = ''
|
||
try:
|
||
getinfo: Dict = infolist[parent]
|
||
except:
|
||
pass
|
||
getinfo: bool = False
|
||
if search and not found:
|
||
for value in [ name, str(parent), getinfo['username'] if getinfo else '' ]:
|
||
if search in value:
|
||
found = True
|
||
break
|
||
if not found:
|
||
cont = False
|
||
if cont: print(f"{f'{inindent}{parent} {cleaned(name)}':<{tree_width}.{tree_width-1}}", sep='', end='')
|
||
if getinfo and cont:
|
||
if getinfo['cpu_times'] == err:
|
||
getinfo['num_threads'] = 0
|
||
if p.info['username'] == err:
|
||
p.info['username'] = "?"
|
||
cpu = getinfo['cpu_percent'] if proc_per_cpu else (getinfo['cpu_percent'] / psutil.cpu_count())
|
||
print(f"{getinfo['num_threads']:>4} " if getinfo['num_threads'] < 1000 else '999> ',
|
||
f"{getinfo['username']:<9.9}" if len(getinfo['username']) < 10 else f"{getinfo['username'][:8]:<8}+",
|
||
f"{getinfo['memory_percent']:>4.1f}" if getinfo['memory_percent'] < 100 else f"{getinfo['memory_percent']:>4.0f} ",
|
||
f"{cpu:>11.1f} " if cpu < 100 else f"{cpu:>11.0f} ",
|
||
sep='')
|
||
elif cont:
|
||
print(f"{'':>14}{'0.0':>4}{'0.0':>11} ", sep='')
|
||
tree_line_count += 1
|
||
if parent not in tree:
|
||
return
|
||
children = tree[parent][:-1]
|
||
for child in children:
|
||
create_tree(child, tree, indent + " │ ", indent + " ├─ ", found=found)
|
||
if max_lines and tree_line_count >= max_lines:
|
||
break
|
||
child = tree[parent][-1]
|
||
create_tree(child, tree, indent + " ", indent + " └─ ")
|
||
|
||
infolist: Dict = {}
|
||
tree: List = defaultdict(list)
|
||
for p in sorted(psutil.process_iter(['pid', 'name', 'num_threads', 'username', 'memory_percent', 'cpu_percent', 'cpu_times', 'create_time'], err), key=lambda p: eval(sorting), reverse=reverse):
|
||
try:
|
||
tree[p.ppid()].append(p.pid)
|
||
except (psutil.NoSuchProcess, psutil.ZombieProcess):
|
||
pass
|
||
else:
|
||
infolist[p.pid] = p.info
|
||
if 0 in tree and 0 in tree[0]:
|
||
tree[0].remove(0)
|
||
|
||
tree_width: int = width + 8
|
||
|
||
print(f"{' Tree:':<{tree_width-4}}", 'Threads: ', f"{'User:':<9}Mem%{'Cpu%':>11}", sep='')
|
||
create_tree(min(tree), tree)
|
||
|
||
def get_disks(exclude: str = None, filtering: str = None):
|
||
'''Get stats, current read and current write for all disks'''
|
||
global disk_hist
|
||
disk_read: int = 0
|
||
disk_write: int = 0
|
||
dev_name: str
|
||
disk_name: str
|
||
disk_list: List[str] = []
|
||
excludes: List[str] = []
|
||
if exclude: excludes = exclude.split(' ')
|
||
if system == "BSD": excludes += ["devfs", "tmpfs", "procfs", "linprocfs", "gvfs", "fusefs"]
|
||
if filtering: filtering: Tuple[str] = tuple(filtering.split(' '))
|
||
io_counters = psutil.disk_io_counters(perdisk=True if system == "Linux" else False, nowrap=True)
|
||
print("Ignored line")
|
||
for disk in psutil.disk_partitions():
|
||
disk_io = None
|
||
disk_name = disk.mountpoint.rsplit('/', 1)[-1] if not disk.mountpoint == "/" else "root"
|
||
while disk_name in disk_list: disk_name += "_"
|
||
disk_list += [disk_name]
|
||
if excludes and disk.fstype in excludes or filtering and not disk_name.endswith(filtering):
|
||
continue
|
||
if system == "MacOS" and disk.mountpoint == "/private/var/vm":
|
||
continue
|
||
try:
|
||
disk_u = psutil.disk_usage(disk.mountpoint)
|
||
except:
|
||
pass
|
||
print(f'{disk.device} {disk_u.total >> 10} {disk_u.used >> 10} {disk_u.free >> 10} {disk_u.percent:.0f} ', end='')
|
||
try:
|
||
if system == "Linux":
|
||
dev_name = os.path.realpath(disk.device).rsplit('/', 1)[-1]
|
||
if dev_name.startswith("md"):
|
||
try:
|
||
dev_name = dev_name[:dev_name.index("p")]
|
||
except:
|
||
pass
|
||
disk_io = io_counters[dev_name]
|
||
elif disk.mountpoint == "/":
|
||
disk_io = io_counters
|
||
else:
|
||
raise Exception
|
||
disk_read = disk_io.read_bytes
|
||
disk_write = disk_io.write_bytes
|
||
|
||
disk_read -= disk_hist[disk.device][0]
|
||
disk_write -= disk_hist[disk.device][1]
|
||
except:
|
||
pass
|
||
disk_read = 0
|
||
disk_write = 0
|
||
|
||
if disk_io: disk_hist[disk.device] = (disk_io.read_bytes, disk_io.write_bytes)
|
||
print(f'{disk_read >> 10} {disk_write >> 10} {disk_name}')
|
||
|
||
#* The script takes input over coproc pipes and runs command if in the accepted commands list
|
||
while command != 'quit':
|
||
if not psutil.pid_exists(parent_pid):
|
||
quit()
|
||
try:
|
||
command = input()
|
||
except:
|
||
pass
|
||
quit()
|
||
|
||
if not command or command == 'test':
|
||
continue
|
||
elif command.startswith(allowed_commands):
|
||
try:
|
||
exec(command)
|
||
except Exception as e:
|
||
pass
|
||
print()
|
||
print('/ERROR')
|
||
print(f'PSUTIL ERROR! Command: {command}\n{e}', file=sys.stderr)
|
||
else:
|
||
continue
|
||
print('/EOL')
|
||
#print(f'{command}', file=sys.stderr)
|
||
EOF
|
||
fi
|
||
fi
|
||
|
||
#* Set up traps for ctrl-c, soft kill, window resize, ctrl-z and resume from ctrl-z
|
||
trap 'quitting=1; time_left=0' SIGINT SIGQUIT SIGTERM
|
||
trap 'resized=1; time_left=0' SIGWINCH
|
||
trap 'sleepy=1; time_left=0' SIGTSTP
|
||
trap 'resume_' SIGCONT
|
||
trap 'failed_pipe=1; time_left=0' PIPE
|
||
|
||
#* Set up error logging to file if enabled
|
||
if [[ $error_logging == true ]]; then
|
||
set -o errtrace
|
||
trap 'traperr' ERR
|
||
|
||
#* Remove everything but the last 500 lines of error log if larger than 500 lines
|
||
if [[ -e "${config_dir}/error.log" && $(${wc} -l <"${config_dir}/error.log") -gt 500 ]]; then
|
||
${tail} -n 500 "${config_dir}/error.log" > "${config_dir}/tmp"
|
||
${rm} -f "${config_dir}/error.log"
|
||
${mv} -f "${config_dir}/tmp" "${config_dir}/error.log"
|
||
fi
|
||
( echo " " ; echo "New instance of bashtop version: ${version} Pid: $$" ) >> "${config_dir}/error.log"
|
||
exec 2>>"${config_dir}/error.log"
|
||
if [[ $1 == "--debug" ]]; then
|
||
exec 19>"${config_dir}/tracing.log"
|
||
BASH_XTRACEFD=19
|
||
set -x
|
||
fi
|
||
else
|
||
exec 2>/dev/null
|
||
fi
|
||
|
||
#* If we have been sourced by another shell, quit. Allows sourcing only function definition.
|
||
[[ "${#BASH_SOURCE[@]}" -gt 1 ]] && { return 0; }
|
||
|
||
#* Call init function
|
||
init_
|
||
|
||
if [[ $use_psutil == true ]]; then
|
||
coproc pycoproc (python3 ${pywrapper})
|
||
sleep 0.1
|
||
init_ cont
|
||
fi
|
||
|
||
#* Start infinite loop
|
||
until false; do
|
||
if [[ $use_psutil == true ]] && [[ -n $failed_pipe ]]; then
|
||
if ((++failed_pipes>10)); then
|
||
if [[ $system == "Linux" ]]; then
|
||
use_psutil="false"
|
||
else
|
||
quit_ 1
|
||
fi
|
||
fi
|
||
coproc pycoproc (python3 ${pywrapper})
|
||
sleep 0.1
|
||
unset failed_pipe
|
||
fi
|
||
if [[ -n $py_error ]]; then
|
||
if ((++py_errors>10)); then
|
||
if [[ $system == "Linux" ]]; then
|
||
use_psutil="false"
|
||
else
|
||
quit_ 1
|
||
fi
|
||
fi
|
||
unset py_error
|
||
fi
|
||
main_loop
|
||
done
|
||
|
||
#* Quit cleanly even if false starts being true...
|
||
quit_
|