#!/usr/bin/env python3 # Copyright (c) 2018, Nordic Semiconductor ASA and Ulf Magnusson # SPDX-License-Identifier: ISC """ Overview ======== A curses-based menuconfig implementation. The interface should feel familiar to people used to mconf ('make menuconfig'). Supports the same keys as mconf, and also supports a set of keybindings inspired by Vi: J/K : Down/Up L : Enter menu/Toggle item H : Leave menu Ctrl-D/U: Page Down/Page Down G/End : Jump to end of list g/Home : Jump to beginning of list The mconf feature where pressing a key jumps to a menu entry with that character in it in the current menu isn't supported. A jump-to feature for jumping directly to any symbol (including invisible symbols) is available instead. Space and Enter are "smart" and try to do what you'd expect for the given menu entry. Running ======= menuconfig.py can be run either as a standalone executable or by calling the menu.menuconfig() function with an existing Kconfig instance. The second option is a bit inflexible in that it will still load and save .config, etc. When run in standalone mode, the top-level Kconfig file to load can be passed as a command-line argument. With no argument, it defaults to "Kconfig". The KCONFIG_CONFIG environment variable specifies the .config file to load (if it exists) and save. If KCONFIG_CONFIG is unset, ".config" is used. $srctree is supported through Kconfiglib. Color schemes ============= It is possible to customize the color scheme by setting the MENUCONFIG_STYLE environment variable. For example, setting it to 'aquatic' will enable an alternative, less yellow, more 'make menuconfig'-like color scheme, contributed by Mitja Horvat (pinkfluid). This is the current list of built-in styles: - default classic Kconfiglib theme with a yellow accent - monochrome colorless theme (uses only bold and standout) attributes, this style is used if the terminal doesn't support colors - aquatic blue tinted style loosely resembling the lxdialog theme It is possible to customize the current style by changing colors of UI elements on the screen. This is the list of elements that can be stylized: - path Top row in the main display, with the menu path - separator Separator lines between windows. Also used for the top line in the symbol - list List of items, e.g. the main display - selection Style for the selected item - inv-list: Like list, but for invisible items. Used in show-all mode. - inv-selection Like selection, but for invisible items. Used in show-all mode. - help Help text windows at the bottom of various fullscreen dialogs - frame Frame around dialog boxes - body Body of dialog boxes - edit Edit box in pop-up dialogs - jump-edit Edit box in jump-to dialog - text Symbol information text The color definition is a comma separated list of attributes: - fg:COLOR Set the foreground/background colors. COLOR can be one of * or * the basic 16 colors (black, red, green, yellow, blue, - bg:COLOR magenta,cyan, white and brighter versions, for example, brightred). On terminals that support more than 8 colors, you can also directly put in a color number, e.g. fg:123 (hexadecimal and octal constants are accepted as well). Colors outside the range -1..curses.COLORS-1 (which is terminal-dependent) are ignored (with a warning). If the background or foreground color of an element is not specified, it defaults to -1, representing the default terminal foreground or background color. Note: On some terminals a bright version of the color implies bold. - bold Use bold text - underline Use underline text - standout Standout text attribute (reverse color) A keyword without the '=' is assumed to be a style template. The template name is looked up in the built-in styles list and the style definition is expanded in-place. With this, built-in styles can be used as basis for new styles. For example, take the aquatic theme and give it a red selection bar: MENUCONFIG_STYLE="aquatic selection=fg:white,bg:red" If there's an error in the style definition or if a missing style is assigned to, the assignment will be ignored, along with a warning being printed on stderr. The 'default' theme is always implicitly parsed first (or the 'monochrome' theme if the terminal lacks colors), so the following two settings have the same effect: MENUCONFIG_STYLE="selection=fg:white,bg:red" MENUCONFIG_STYLE="default selection=fg:white,bg:red" Other features ============== - Seamless terminal resizing - No dependencies on *nix, as the 'curses' module is in the Python standard library - Unicode text entry - Improved information screen compared to mconf: * Expressions are split up by their top-level &&/|| operands to improve readability * Undefined symbols in expressions are pointed out * Menus and comments have information displays * Kconfig definitions are printed * The include path is shown, listing the locations of the 'source' statements that included the Kconfig file of the symbol (or other item) Limitations =========== - Python 3 only This is mostly due to Python 2 not having curses.get_wch(), which is needed for Unicode support. - Doesn't work out of the box on Windows Has been tested to work with the wheels provided at https://www.lfd.uci.edu/~gohlke/pythonlibs/#curses though. """ import curses import errno import locale import os import platform import re import sys import textwrap from kconfiglib import Symbol, Choice, MENU, COMMENT, MenuNode, \ BOOL, TRISTATE, STRING, INT, HEX, UNKNOWN, \ AND, OR, NOT, \ expr_str, expr_value, split_expr, \ standard_sc_expr_str, \ TRI_TO_STR, TYPE_TO_STR, \ standard_kconfig, standard_config_filename # # Configuration variables # # If True, try to convert LC_CTYPE to a UTF-8 locale if it is set to the C # locale (which implies ASCII). This fixes curses Unicode I/O issues on systems # with bad defaults. ncurses configures itself from the locale settings. # # Related PEP: https://www.python.org/dev/peps/pep-0538/ _CONVERT_C_LC_CTYPE_TO_UTF8 = True # How many steps an implicit submenu will be indented. Implicit submenus are # created when an item depends on the symbol before it. Note that symbols # defined with 'menuconfig' create a separate menu instead of indenting. _SUBMENU_INDENT = 4 # Number of steps for Page Up/Down to jump _PG_JUMP = 6 # How far the cursor needs to be from the edge of the window before it starts # to scroll. Used for the main menu display, the information display, the # search display, and for text boxes. _SCROLL_OFFSET = 5 # Minimum width of dialogs that ask for text input _INPUT_DIALOG_MIN_WIDTH = 30 # Number of arrows pointing up/down to draw when a window is scrolled _N_SCROLL_ARROWS = 14 # Lines of help text shown at the bottom of the "main" display _MAIN_HELP_LINES = """ [Space/Enter] Toggle/enter [ESC] Leave menu [S] Save [O] Load [?] Symbol info [/] Jump to symbol [A] Toggle show-all mode [C] Toggle show-name mode [Q] Quit (prompts for save) [D] Save minimal config (advanced) """[1:-1].split("\n") # Lines of help text shown at the bottom of the information dialog _INFO_HELP_LINES = """ [ESC/q] Return to menu """[1:-1].split("\n") # Lines of help text shown at the bottom of the search dialog _JUMP_TO_HELP_LINES = """ Type text to narrow the search. Regexes are supported (via Python's 're' module). The up/down cursor keys step in the list. [Enter] jumps to the selected symbol. [ESC] aborts the search. Type multiple space-separated strings/regexes to find entries that match all of them. Type Ctrl-F to view the help of the selected item without leaving the dialog. """[1:-1].split("\n") _STYLES = { "default": """ path=fg:black,bg:white,bold separator=fg:black,bg:yellow,bold list=fg:black,bg:white selection=fg:white,bg:blue,bold inv-list=fg:red,bg:white inv-selection=fg:red,bg:blue help=fg:black,bg:white,bold frame=fg:black,bg:yellow,bold body=fg:white,bg:black edit=fg:white,bg:blue jump-edit=fg:white,bg:blue text=fg:black,bg:white """, # This style is forced on terminals that do no support colors "monochrome": """ path=bold separator=bold,standout list= selection=bold,standout inv-list=bold inv-selection=bold,standout help=bold frame=bold,standout body= edit=standout jump-edit= text= """, # Blue tinted style loosely resembling lxdialog "aquatic": """ path=fg:cyan,bg:blue,bold separator=fg:white,bg:cyan,bold help=fg:cyan,bg:blue,bold frame=fg:white,bg:cyan,bold body=fg:brightwhite,bg:blue edit=fg:black,bg:white """ } # Standard colors definition _STYLE_STD_COLORS = { # Basic colors "black": curses.COLOR_BLACK, "red": curses.COLOR_RED, "green": curses.COLOR_GREEN, "yellow": curses.COLOR_YELLOW, "blue": curses.COLOR_BLUE, "magenta": curses.COLOR_MAGENTA, "cyan": curses.COLOR_CYAN, "white": curses.COLOR_WHITE, # Bright versions "brightblack": curses.COLOR_BLACK + 8, "brightred": curses.COLOR_RED + 8, "brightgreen": curses.COLOR_GREEN + 8, "brightyellow": curses.COLOR_YELLOW + 8, "brightblue": curses.COLOR_BLUE + 8, "brightmagenta":curses.COLOR_MAGENTA + 8, "brightcyan": curses.COLOR_CYAN + 8, "brightwhite": curses.COLOR_WHITE + 8, # Aliases "purple": curses.COLOR_MAGENTA, "brightpurple": curses.COLOR_MAGENTA + 8, } # Dictionary mapping element types to the curses attributes used to display # them _style = {} def _parse_style(style_str, parsing_default): # Parses a string with '=