You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

141 lines
5.4 KiB

#!/usr/bin/env python3
# Copyright 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com>
# Copyright 2020 Aristocratos (https://github.com/aristocratos/bpytop)
#
# 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.
import os, sys, io, threading, signal, re, subprocess, logging, logging.handlers, argparse
import queue
from select import select
from time import time, sleep, strftime, localtime
from typing import List, Set, Dict, Tuple, Optional, Union, Any, Callable, ContextManager, Iterable, Type, NamedTuple
try: import fcntl, termios, tty, pwd
except Exception as e:
print(f'ERROR: {e}')
exit(1)
class Mv:
"""Class with collection of cursor movement functions: .t[o](line, column) | .r[ight](columns) | .l[eft](columns) | .u[p](lines) | .d[own](lines) | .save() | .restore()"""
@staticmethod
def to(line: int, col: int) -> str:
return f'\033[{line};{col}f' #* Move cursor to line, column
@staticmethod
def right(x: int) -> str: #* Move cursor right x columns
return f'\033[{x}C'
@staticmethod
def left(x: int) -> str: #* Move cursor left x columns
return f'\033[{x}D'
@staticmethod
def up(x: int) -> str: #* Move cursor up x lines
return f'\033[{x}A'
@staticmethod
def down(x: int) -> str: #* Move cursor down x lines
return f'\033[{x}B'
save: str = "\033[s" #* Save cursor position
restore: str = "\033[u" #* Restore saved cursor postion
t = to
r = right
l = left
u = up
d = down
class Term:
"""Terminal info and commands"""
title: str = "TermTk"
mouse: bool = True
width: int = 0
height: int = 0
fg: str = "" #* Default foreground color
bg: str = "" #* Default background color
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 = "\033[2J\033[0;0f" #* Clear screen and set cursor to position 0,0
mouse_on = "\033[?1002h\033[?1015h\033[?1006h" #* Enable reporting of mouse position on click and release
mouse_off = "\033[?1002l" #* Disable mouse reporting
mouse_direct_on = "\033[?1003h" #* Enable reporting of mouse position at any movement
mouse_direct_off = "\033[?1003l" #* Disable direct mouse reporting
_sigWinChCb = None
@staticmethod
def init(mouse: bool = True, title: str = "TermTk"):
Term.title = title
Term.mouse = mouse
Term.push(Term.alt_screen, Term.clear, Term.hide_cursor, Term.escTitle(Term.title))
if Term.mouse:
Term.push(Term.mouse_on)
Term.echo(False)
@staticmethod
def stop():
Term.push(Term.mouse_off, Term.mouse_direct_off)
Term.push(Term.clear, Term.normal_screen, Term.show_cursor, Term.escTitle())
Term.echo(True)
@staticmethod
def cont():
Term.push(Term.alt_screen, Term.clear, Term.hide_cursor, Term.escTitle(Term.title))
if Term.mouse:
Term.push(Term.mouse_on)
Term.echo(False)
@staticmethod
def exit():
Term.push(Term.mouse_off, Term.mouse_direct_off)
Term.push(Term.clear, Term.normal_screen, Term.show_cursor, Term.escTitle())
Term.echo(True)
@staticmethod
def echo(on: bool):
"""Toggle input echo"""
(iflag, oflag, cflag, lflag, ispeed, ospeed, cc) = termios.tcgetattr(sys.stdin.fileno())
if on:
lflag |= termios.ECHO # type: ignore
else:
lflag &= ~termios.ECHO # type: ignore
new_attr = [iflag, oflag, cflag, lflag, ispeed, ospeed, cc]
termios.tcsetattr(sys.stdin.fileno(), termios.TCSANOW, new_attr)
@staticmethod
def push(*args):
try:
print(*args, sep="", end="", flush=True)
except BlockingIOError:
pass
print(*args, sep="", end="", flush=True)
@staticmethod
def escTitle(text: str = "") -> str:
out: str = f'{os.environ.get("TERMINAL_TITLE", "")}'
if out and text: out += " "
if text: out += f'{text}'
return f'\033]0;{out}\a'
@staticmethod
def _sigWinCh(signum, frame):
Term.width, Term.height = os.get_terminal_size()
if Term._sigWinChCb is not None:
Term._sigWinChCb(Term.width, Term.height)
@staticmethod
def registerResizeCb(callback):
Term._sigWinChCb = callback
# Dummy call to retrieve the terminal size
Term._sigWinCh(signal.SIGWINCH, None)
signal.signal(signal.SIGWINCH, Term._sigWinCh)