diff --git a/glances/main.py b/glances/main.py index b4cf05e3..698c6ee6 100644 --- a/glances/main.py +++ b/glances/main.py @@ -720,6 +720,10 @@ Examples of use: args.time = 1 args.disable_history = True + # Unicode => No separator + if args.disable_unicode: + args.enable_separator = False + def parse_args(self): """Parse command line arguments.""" args = self.init_args().parse_args() diff --git a/glances/outputs/glances_colors.py b/glances/outputs/glances_colors.py new file mode 100644 index 00000000..f006268b --- /dev/null +++ b/glances/outputs/glances_colors.py @@ -0,0 +1,161 @@ +# +# This file is part of Glances. +# +# SPDX-FileCopyrightText: 2024 Nicolas Hennion +# +# SPDX-License-Identifier: LGPL-3.0-only +# + +"""Glances colors.""" + +import sys + +from glances.logger import logger + +try: + import curses +except ImportError: + logger.critical("Curses module not found. Glances cannot start in standalone mode.") + sys.exit(1) + + +class GlancesColors: + """Class to manage colors in Glances UI + For the moment limited to Curses interface. + But will be used in the WebUI through the issue #2048""" + + def __init__(self, args) -> None: + self.args = args + + # Define "home made" bold + self.A_BOLD = 0 if args.disable_bold else curses.A_BOLD + + # Set defaults curses colors + try: + if hasattr(curses, 'start_color'): + curses.start_color() + logger.debug(f'Curses interface compatible with {curses.COLORS} colors') + if hasattr(curses, 'use_default_colors'): + # Use -1 to use the default foregound/background color + curses.use_default_colors() + if hasattr(curses, 'assume_default_colors'): + # Define the color index 0 with -1 and -1 for foregound/background + # = curses.init_pair(0, -1, -1) + curses.assume_default_colors(-1, -1) + except Exception as e: + logger.warning(f'Error initializing terminal color ({e})') + + if curses.has_colors(): + # The screen is compatible with a colored design + # ex: export TERM=xterm-256color + # export TERM=xterm-color + self.__define_colors() + else: + # The screen is NOT compatible with a colored design + # switch to B&W text styles + # ex: export TERM=xterm-mono + self.__define_bw() + + def __repr__(self) -> dict: + return self.get() + + def __define_colors(self) -> None: + curses.init_pair(1, -1, -1) + if self.args.disable_bg: + curses.init_pair(2, curses.COLOR_RED, -1) + curses.init_pair(3, curses.COLOR_GREEN, -1) + curses.init_pair(5, curses.COLOR_MAGENTA, -1) + else: + curses.init_pair(2, -1, curses.COLOR_RED) + curses.init_pair(3, curses.COLOR_BLACK, curses.COLOR_GREEN) + curses.init_pair(5, -1, curses.COLOR_MAGENTA) + curses.init_pair(4, curses.COLOR_BLUE, -1) + curses.init_pair(6, curses.COLOR_RED, -1) + curses.init_pair(7, curses.COLOR_GREEN, -1) + curses.init_pair(8, curses.COLOR_MAGENTA, -1) + + # Colors text styles + self.DEFAULT = curses.color_pair(1) + self.OK_LOG = curses.color_pair(3) | self.A_BOLD + self.NICE = curses.color_pair(8) + self.CPU_TIME = curses.color_pair(8) + self.CAREFUL_LOG = curses.color_pair(4) | self.A_BOLD + self.WARNING_LOG = curses.color_pair(5) | self.A_BOLD + self.CRITICAL_LOG = curses.color_pair(2) | self.A_BOLD + self.OK = curses.color_pair(7) + self.CAREFUL = curses.color_pair(4) + self.WARNING = curses.color_pair(8) | self.A_BOLD + self.CRITICAL = curses.color_pair(6) | self.A_BOLD + self.INFO = curses.color_pair(4) + self.FILTER = self.A_BOLD + self.SELECTED = self.A_BOLD + self.SEPARATOR = curses.color_pair(1) + + if curses.COLORS > 8: + # ex: export TERM=xterm-256color + try: + curses.init_pair(9, curses.COLOR_CYAN, -1) + curses.init_pair(10, curses.COLOR_YELLOW, -1) + except Exception: + curses.init_pair(9, -1, -1) + curses.init_pair(10, -1, -1) + self.FILTER = curses.color_pair(9) | self.A_BOLD + self.SELECTED = curses.color_pair(10) | self.A_BOLD + + # Define separator line style + try: + curses.init_color(11, 500, 500, 500) + curses.init_pair(11, curses.COLOR_BLACK, -1) + self.SEPARATOR = curses.color_pair(11) + except Exception: + # Catch exception in TMUX + pass + + def __define_bw(self) -> None: + # The screen is NOT compatible with a colored design + # switch to B&W text styles + # ex: export TERM=xterm-mono + self.DEFAULT = -1 + self.OK_LOG = -1 + self.NICE = self.A_BOLD + self.CPU_TIME = self.A_BOLD + self.CAREFUL_LOG = self.A_BOLD + self.WARNING_LOG = curses.A_UNDERLINE + self.CRITICAL_LOG = curses.A_REVERSE + self.OK = -1 + self.CAREFUL = self.A_BOLD + self.WARNING = curses.A_UNDERLINE + self.CRITICAL = curses.A_REVERSE + self.INFO = self.A_BOLD + self.FILTER = self.A_BOLD + self.SELECTED = self.A_BOLD + self.SEPARATOR = -1 + + def get(self) -> dict: + return { + 'DEFAULT': self.DEFAULT, + 'UNDERLINE': curses.A_UNDERLINE, + 'BOLD': self.A_BOLD, + 'SORT': curses.A_UNDERLINE | self.A_BOLD, + 'OK': self.OK, + 'MAX': self.OK | self.A_BOLD, + 'FILTER': self.FILTER, + 'TITLE': self.A_BOLD, + 'PROCESS': self.OK, + 'PROCESS_SELECTED': self.OK | curses.A_UNDERLINE, + 'STATUS': self.OK, + 'NICE': self.NICE, + 'CPU_TIME': self.CPU_TIME, + 'CAREFUL': self.CAREFUL, + 'WARNING': self.WARNING, + 'CRITICAL': self.CRITICAL, + 'OK_LOG': self.OK_LOG, + 'CAREFUL_LOG': self.CAREFUL_LOG, + 'WARNING_LOG': self.WARNING_LOG, + 'CRITICAL_LOG': self.CRITICAL_LOG, + 'PASSWORD': curses.A_PROTECT, + 'SELECTED': self.SELECTED, + 'INFO': self.INFO, + 'ERROR': self.SELECTED, + 'SEPARATOR': self.SEPARATOR, + } diff --git a/glances/outputs/glances_curses.py b/glances/outputs/glances_curses.py index a4f8c877..b201ea95 100644 --- a/glances/outputs/glances_curses.py +++ b/glances/outputs/glances_curses.py @@ -14,6 +14,7 @@ import sys from glances.events_list import glances_events from glances.globals import MACOS, WINDOWS, disable, enable, itervalues, nativestr, u from glances.logger import logger +from glances.outputs.glances_colors import GlancesColors from glances.outputs.glances_unicode import unicode_message from glances.processes import glances_processes, sort_processes_key_list from glances.timer import Timer @@ -162,7 +163,7 @@ class _GlancesCurses: self._init_cursor() # Init the colors - self.colors_list = build_colors_list(args) + self.colors_list = GlancesColors(args).get() # Init main window self.term_window = self.screen.subwin(0, 0) @@ -1179,128 +1180,3 @@ class GlancesTextboxYesNo(Textbox): def do_command(self, ch): return super().do_command(ch) - - -def build_colors_list(args): - """Init the Curses color layout.""" - # Set curses options - try: - if hasattr(curses, 'start_color'): - curses.start_color() - logger.debug(f'Curses interface compatible with {curses.COLORS} colors') - if hasattr(curses, 'use_default_colors'): - curses.use_default_colors() - except Exception as e: - logger.warning(f'Error initializing terminal color ({e})') - - # Init colors - if args.disable_bold: - A_BOLD = 0 - args.disable_bg = True - else: - A_BOLD = curses.A_BOLD - - title_color = A_BOLD - - if curses.has_colors(): - # The screen is compatible with a colored design - # ex: export TERM=xterm-256color - # export TERM=xterm-color - - curses.init_pair(1, -1, -1) - if args.disable_bg: - curses.init_pair(2, curses.COLOR_RED, -1) - curses.init_pair(3, curses.COLOR_GREEN, -1) - curses.init_pair(5, curses.COLOR_MAGENTA, -1) - else: - curses.init_pair(2, -1, curses.COLOR_RED) - curses.init_pair(3, 0, curses.COLOR_GREEN) - curses.init_pair(5, -1, curses.COLOR_MAGENTA) - curses.init_pair(4, curses.COLOR_BLUE, -1) - curses.init_pair(6, curses.COLOR_RED, -1) - curses.init_pair(7, curses.COLOR_GREEN, -1) - curses.init_pair(8, curses.COLOR_MAGENTA, -1) - - # Colors text styles - no_color = curses.color_pair(1) - default_color = curses.color_pair(3) | A_BOLD - nice_color = curses.color_pair(8) - cpu_time_color = curses.color_pair(8) - ifCAREFUL_color = curses.color_pair(4) | A_BOLD - ifWARNING_color = curses.color_pair(5) | A_BOLD - ifCRITICAL_color = curses.color_pair(2) | A_BOLD - default_color2 = curses.color_pair(7) - ifCAREFUL_color2 = curses.color_pair(4) - ifWARNING_color2 = curses.color_pair(8) | A_BOLD - ifCRITICAL_color2 = curses.color_pair(6) | A_BOLD - ifINFO_color = curses.color_pair(4) - filter_color = A_BOLD - selected_color = A_BOLD - separator = curses.color_pair(1) - - if curses.COLORS > 8: - # ex: export TERM=xterm-256color - colors_list = [curses.COLOR_CYAN, curses.COLOR_YELLOW] - for i in range(0, 3): - try: - curses.init_pair(i + 9, colors_list[i], -1) - except Exception: - curses.init_pair(i + 9, -1, -1) - filter_color = curses.color_pair(9) | A_BOLD - selected_color = curses.color_pair(10) | A_BOLD - # Define separator line style - try: - curses.init_color(11, 500, 500, 500) - curses.init_pair(11, curses.COLOR_BLACK, -1) - separator = curses.color_pair(11) - except Exception: - # Catch exception in TMUX - pass - else: - # The screen is NOT compatible with a colored design - # switch to B&W text styles - # ex: export TERM=xterm-mono - no_color = -1 - default_color = -1 - nice_color = A_BOLD - cpu_time_color = A_BOLD - ifCAREFUL_color = A_BOLD - ifWARNING_color = curses.A_UNDERLINE - ifCRITICAL_color = curses.A_REVERSE - default_color2 = -1 - ifCAREFUL_color2 = A_BOLD - ifWARNING_color2 = curses.A_UNDERLINE - ifCRITICAL_color2 = curses.A_REVERSE - ifINFO_color = A_BOLD - filter_color = A_BOLD - selected_color = A_BOLD - separator = -1 - - # Define the colors list (hash table) for stats - return { - 'DEFAULT': no_color, - 'UNDERLINE': curses.A_UNDERLINE, - 'BOLD': A_BOLD, - 'SORT': curses.A_UNDERLINE | A_BOLD, - 'OK': default_color2, - 'MAX': default_color2 | A_BOLD, - 'FILTER': filter_color, - 'TITLE': title_color, - 'PROCESS': default_color2, - 'PROCESS_SELECTED': default_color2 | curses.A_UNDERLINE, - 'STATUS': default_color2, - 'NICE': nice_color, - 'CPU_TIME': cpu_time_color, - 'CAREFUL': ifCAREFUL_color2, - 'WARNING': ifWARNING_color2, - 'CRITICAL': ifCRITICAL_color2, - 'OK_LOG': default_color, - 'CAREFUL_LOG': ifCAREFUL_color, - 'WARNING_LOG': ifWARNING_color, - 'CRITICAL_LOG': ifCRITICAL_color, - 'PASSWORD': curses.A_PROTECT, - 'SELECTED': selected_color, - 'INFO': ifINFO_color, - 'ERROR': selected_color, - 'SEPARATOR': separator, - }