Bombsquad-Ballistica-Modded.../dist/ba_data/python/efro/terminal.py
2024-11-28 00:23:35 +05:30

323 lines
8.5 KiB
Python

# Released under the MIT License. See LICENSE for details.
#
"""Functionality related to terminal IO."""
from __future__ import annotations
import sys
import os
from enum import Enum, unique
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from typing import Any, ClassVar
@unique
class TerminalColor(Enum):
"""Color codes for printing to terminals.
Generally the Clr class should be used when incorporating color into
terminal output, as it handles non-color-supporting terminals/etc.
"""
# Styles
RESET = '\033[0m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
INVERSE = '\033[7m'
# Normal foreground colors
BLACK = '\033[30m'
RED = '\033[31m'
GREEN = '\033[32m'
YELLOW = '\033[33m'
BLUE = '\033[34m'
MAGENTA = '\033[35m'
CYAN = '\033[36m'
WHITE = '\033[37m'
# Normal background colors.
BG_BLACK = '\033[40m'
BG_RED = '\033[41m'
BG_GREEN = '\033[42m'
BG_YELLOW = '\033[43m'
BG_BLUE = '\033[44m'
BG_MAGENTA = '\033[45m'
BG_CYAN = '\033[46m'
BG_WHITE = '\033[47m'
# Strong foreground colors
STRONG_BLACK = '\033[90m'
STRONG_RED = '\033[91m'
STRONG_GREEN = '\033[92m'
STRONG_YELLOW = '\033[93m'
STRONG_BLUE = '\033[94m'
STRONG_MAGENTA = '\033[95m'
STRONG_CYAN = '\033[96m'
STRONG_WHITE = '\033[97m'
# Strong background colors.
STRONG_BG_BLACK = '\033[100m'
STRONG_BG_RED = '\033[101m'
STRONG_BG_GREEN = '\033[102m'
STRONG_BG_YELLOW = '\033[103m'
STRONG_BG_BLUE = '\033[104m'
STRONG_BG_MAGENTA = '\033[105m'
STRONG_BG_CYAN = '\033[106m'
STRONG_BG_WHITE = '\033[107m'
def _default_color_enabled() -> bool:
"""Return whether we enable ANSI color codes by default."""
import platform
# If our stdout is not attached to a terminal, go with no-color.
assert sys.__stdout__ is not None
if not sys.__stdout__.isatty():
return False
termenv = os.environ.get('TERM')
# If TERM is unset, don't attempt color (this is currently the case
# in xcode).
if termenv is None:
return False
# A common way to say the terminal can't do fancy stuff like color.
if termenv == 'dumb':
return False
# On windows, try to enable ANSI color mode.
if platform.system() == 'Windows':
return _windows_enable_color()
# We seem to be a terminal with color support; let's do it!
return True
# noinspection PyPep8Naming
def _windows_enable_color() -> bool:
"""Attempt to enable ANSI color on windows terminal; return success."""
# pylint: disable=invalid-name, import-error, undefined-variable
# Pulled from: https://bugs.python.org/issue30075
import msvcrt
import ctypes
from ctypes import wintypes
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) # type: ignore
ERROR_INVALID_PARAMETER = 0x0057
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004
def _check_bool(result: Any, _func: Any, args: Any) -> Any:
if not result:
raise ctypes.WinError(ctypes.get_last_error()) # type: ignore
return args
LPDWORD = ctypes.POINTER(wintypes.DWORD)
kernel32.GetConsoleMode.errcheck = _check_bool
kernel32.GetConsoleMode.argtypes = (wintypes.HANDLE, LPDWORD)
kernel32.SetConsoleMode.errcheck = _check_bool
kernel32.SetConsoleMode.argtypes = (wintypes.HANDLE, wintypes.DWORD)
def set_conout_mode(new_mode: int, mask: int = 0xFFFFFFFF) -> int:
# don't assume StandardOutput is a console.
# open CONOUT$ instead
fdout = os.open('CONOUT$', os.O_RDWR)
try:
hout = msvcrt.get_osfhandle(fdout) # type: ignore
# pylint: disable=useless-suppression
# pylint: disable=no-value-for-parameter
old_mode = wintypes.DWORD()
# pylint: enable=useless-suppression
kernel32.GetConsoleMode(hout, ctypes.byref(old_mode))
mode = (new_mode & mask) | (old_mode.value & ~mask)
kernel32.SetConsoleMode(hout, mode)
return old_mode.value
finally:
os.close(fdout)
def enable_vt_mode() -> int:
mode = mask = ENABLE_VIRTUAL_TERMINAL_PROCESSING
try:
return set_conout_mode(mode, mask)
except WindowsError as exc: # type: ignore
if exc.winerror == ERROR_INVALID_PARAMETER:
raise NotImplementedError from exc
raise
try:
enable_vt_mode()
return True
except NotImplementedError:
return False
class ClrBase:
"""Base class for color convenience class."""
RST: ClassVar[str]
BLD: ClassVar[str]
UND: ClassVar[str]
INV: ClassVar[str]
# Normal foreground colors
BLK: ClassVar[str]
RED: ClassVar[str]
GRN: ClassVar[str]
YLW: ClassVar[str]
BLU: ClassVar[str]
MAG: ClassVar[str]
CYN: ClassVar[str]
WHT: ClassVar[str]
# Normal background colors.
BBLK: ClassVar[str]
BRED: ClassVar[str]
BGRN: ClassVar[str]
BYLW: ClassVar[str]
BBLU: ClassVar[str]
BMAG: ClassVar[str]
BCYN: ClassVar[str]
BWHT: ClassVar[str]
# Strong foreground colors
SBLK: ClassVar[str]
SRED: ClassVar[str]
SGRN: ClassVar[str]
SYLW: ClassVar[str]
SBLU: ClassVar[str]
SMAG: ClassVar[str]
SCYN: ClassVar[str]
SWHT: ClassVar[str]
# Strong background colors.
SBBLK: ClassVar[str]
SBRED: ClassVar[str]
SBGRN: ClassVar[str]
SBYLW: ClassVar[str]
SBBLU: ClassVar[str]
SBMAG: ClassVar[str]
SBCYN: ClassVar[str]
SBWHT: ClassVar[str]
class ClrAlways(ClrBase):
"""Convenience class for color terminal output.
This version has colors always enabled. Generally you should use Clr which
points to the correct enabled/disabled class depending on the environment.
"""
color_enabled = True
# Styles
RST = TerminalColor.RESET.value
BLD = TerminalColor.BOLD.value
UND = TerminalColor.UNDERLINE.value
INV = TerminalColor.INVERSE.value
# Normal foreground colors
BLK = TerminalColor.BLACK.value
RED = TerminalColor.RED.value
GRN = TerminalColor.GREEN.value
YLW = TerminalColor.YELLOW.value
BLU = TerminalColor.BLUE.value
MAG = TerminalColor.MAGENTA.value
CYN = TerminalColor.CYAN.value
WHT = TerminalColor.WHITE.value
# Normal background colors.
BBLK = TerminalColor.BG_BLACK.value
BRED = TerminalColor.BG_RED.value
BGRN = TerminalColor.BG_GREEN.value
BYLW = TerminalColor.BG_YELLOW.value
BBLU = TerminalColor.BG_BLUE.value
BMAG = TerminalColor.BG_MAGENTA.value
BCYN = TerminalColor.BG_CYAN.value
BWHT = TerminalColor.BG_WHITE.value
# Strong foreground colors
SBLK = TerminalColor.STRONG_BLACK.value
SRED = TerminalColor.STRONG_RED.value
SGRN = TerminalColor.STRONG_GREEN.value
SYLW = TerminalColor.STRONG_YELLOW.value
SBLU = TerminalColor.STRONG_BLUE.value
SMAG = TerminalColor.STRONG_MAGENTA.value
SCYN = TerminalColor.STRONG_CYAN.value
SWHT = TerminalColor.STRONG_WHITE.value
# Strong background colors.
SBBLK = TerminalColor.STRONG_BG_BLACK.value
SBRED = TerminalColor.STRONG_BG_RED.value
SBGRN = TerminalColor.STRONG_BG_GREEN.value
SBYLW = TerminalColor.STRONG_BG_YELLOW.value
SBBLU = TerminalColor.STRONG_BG_BLUE.value
SBMAG = TerminalColor.STRONG_BG_MAGENTA.value
SBCYN = TerminalColor.STRONG_BG_CYAN.value
SBWHT = TerminalColor.STRONG_BG_WHITE.value
class ClrNever(ClrBase):
"""Convenience class for color terminal output.
This version has colors disabled. Generally you should use Clr which
points to the correct enabled/disabled class depending on the environment.
"""
color_enabled = False
# Styles
RST = ''
BLD = ''
UND = ''
INV = ''
# Normal foreground colors
BLK = ''
RED = ''
GRN = ''
YLW = ''
BLU = ''
MAG = ''
CYN = ''
WHT = ''
# Normal background colors.
BBLK = ''
BRED = ''
BGRN = ''
BYLW = ''
BBLU = ''
BMAG = ''
BCYN = ''
BWHT = ''
# Strong foreground colors
SBLK = ''
SRED = ''
SGRN = ''
SYLW = ''
SBLU = ''
SMAG = ''
SCYN = ''
SWHT = ''
# Strong background colors.
SBBLK = ''
SBRED = ''
SBGRN = ''
SBYLW = ''
SBBLU = ''
SBMAG = ''
SBCYN = ''
SBWHT = ''
_envval = os.environ.get('EFRO_TERMCOLORS')
color_enabled: bool = (
True
if _envval == '1'
else False if _envval == '0' else _default_color_enabled()
)
Clr: type[ClrBase] = ClrAlways if color_enabled else ClrNever