diff --git a/man/os-release.xml b/man/os-release.xml index 4bbb87b1bf..0c9b3de493 100644 --- a/man/os-release.xml +++ b/man/os-release.xml @@ -439,15 +439,26 @@ ANSI_COLOR= - A suggested presentation color when showing the OS name on the console. This should - be specified as string suitable for inclusion in the ESC [ m ANSI/ECMA-48 escape code for setting - graphical rendition. This field is optional. + A suggested presentation (foreground) text color when showing the OS name on the + console. This should be specified as string suitable for inclusion in the ESC [ m ANSI/ECMA-48 + escape code for setting graphical rendition. This field is optional. Examples: ANSI_COLOR="0;31" for red, ANSI_COLOR="1;34" for light blue, or ANSI_COLOR="0;38;2;60;110;180" for Fedora blue. + + ANSI_COLOR_REVERSE= + + Similar to ANSI_COLOR=, but should encode the desired + presentation color as background color, along with a suitable foreground color. This is may be used + by console applications to set off "chrome" UI elements from the main terminal contents. This field + is optional. + + + + VENDOR_NAME= diff --git a/src/shared/prompt-util.c b/src/shared/prompt-util.c index 42ddf19189..5c3fa98a92 100644 --- a/src/shared/prompt-util.c +++ b/src/shared/prompt-util.c @@ -1,10 +1,14 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include + #include "alloc-util.h" #include "glyph-util.h" #include "log.h" #include "macro.h" +#include "os-util.h" #include "parse-util.h" +#include "pretty-print.h" #include "prompt-util.h" #include "string-util.h" #include "strv.h" @@ -189,3 +193,140 @@ int prompt_loop( } } } + +/* Default: bright white on blue background */ +#define ANSI_COLOR_CHROME "\x1B[0;44;1;37m" + +static unsigned chrome_visible = 0; /* if non-zero chrome is visible and value is saved number of lines */ + +int chrome_show( + const char *top, + const char *bottom) { + int r; + + assert(top); + + /* Shows our "chrome", i.e. a blue bar at top and bottom. Reduces the scrolling area to the area in + * between */ + + if (terminal_is_dumb()) + return 0; + + unsigned n = lines(); + if (n < 12) /* Do not bother with the chrom on tiny screens */ + return 0; + + _cleanup_free_ char *b = NULL, *ansi_color_reverse = NULL; + if (!bottom) { + _cleanup_free_ char *pretty_name = NULL, *os_name = NULL, *ansi_color = NULL, *documentation_url = NULL; + + r = parse_os_release( + /* root= */ NULL, + "PRETTY_NAME", &pretty_name, + "NAME", &os_name, + "ANSI_COLOR", &ansi_color, + "ANSI_COLOR_REVERSE", &ansi_color_reverse, + "DOCUMENTATION_URL", &documentation_url); + if (r < 0) + log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r, + "Failed to read os-release file, ignoring: %m"); + + const char *m = os_release_pretty_name(pretty_name, os_name); + const char *c = ansi_color ?: "0"; + + if (ansi_color_reverse) { + _cleanup_free_ char *j = strjoin("\x1B[0;", ansi_color_reverse, "m"); + if (!j) + return log_oom_debug(); + + free_and_replace(ansi_color_reverse, j); + } + + if (asprintf(&b, "\x1B[0;%sm %s %s", c, m, ansi_color_reverse ?: ANSI_COLOR_CHROME) < 0) + return log_oom_debug(); + + if (documentation_url) { + _cleanup_free_ char *u = NULL; + if (terminal_urlify(documentation_url, "documentation", &u) < 0) + return log_oom_debug(); + + if (!strextend(&b, " - See ", u, " for more information.")) + return log_oom_debug(); + } + + bottom = b; + } + + const char *chrome_color = ansi_color_reverse ?: ANSI_COLOR_CHROME; + + WITH_BUFFERED_STDOUT; + + fputs("\033[H" /* move home */ + "\033[2J", /* clear screen */ + stdout); + + /* Blue bar on top (followed by one empty regular one) */ + printf("\x1B[1;1H" /* jump to top left */ + "%1$s" ANSI_ERASE_TO_END_OF_LINE "\n" + "%1$s %2$s" ANSI_ERASE_TO_END_OF_LINE "\n" + "%1$s" ANSI_ERASE_TO_END_OF_LINE "\n" + ANSI_NORMAL ANSI_ERASE_TO_END_OF_LINE, + chrome_color, + top); + + /* Blue bar on bottom (with one empty regular one before) */ + printf("\x1B[%1$u;1H" /* jump to bottom left, above the blue bar */ + ANSI_NORMAL ANSI_ERASE_TO_END_OF_LINE "\n" + "%2$s" ANSI_ERASE_TO_END_OF_LINE "\n" + "%2$s %3$s" ANSI_ERASE_TO_END_OF_LINE "\n" + "%2$s" ANSI_ERASE_TO_END_OF_LINE ANSI_NORMAL, + n - 3, + chrome_color, + bottom); + + /* Reduce scrolling area (DECSTBM), cutting off top and bottom bars */ + printf("\x1B[5;%ur", n - 4); + + /* Position cursor in fifth line */ + fputs("\x1B[5;1H", stdout); + + fflush(stdout); + + chrome_visible = n; + return 1; +} + +void chrome_hide(void) { + int r; + + if (chrome_visible == 0) + return; + + unsigned n = chrome_visible; + chrome_visible = 0; + + unsigned saved_row = 0; + r = terminal_get_cursor_position(STDIN_FILENO, STDOUT_FILENO, &saved_row, /* ret_column= */ NULL); + if (r < 0) + return (void) log_debug_errno(r, "Failed to get terminal cursor position, skipping chrome hiding: %m"); + + WITH_BUFFERED_STDOUT; + + /* Erase Blue bar on bottom */ + assert(n >= 2); + printf("\x1B[%u;1H" + ANSI_NORMAL ANSI_ERASE_TO_END_OF_LINE "\n" + ANSI_NORMAL ANSI_ERASE_TO_END_OF_LINE "\n" + ANSI_NORMAL ANSI_ERASE_TO_END_OF_LINE ANSI_NORMAL, + n - 2); + + /* Reset scrolling area (DECSTBM) */ + fputs("\x1B[r\n", stdout); + + /* Place the cursor where it was again, but not in the former blue bars */ + assert(n >= 9); + unsigned k = CLAMP(saved_row, 5U, n - 4); + printf("\x1B[%u;1H", k); + + fflush(stdout); +} diff --git a/src/shared/prompt-util.h b/src/shared/prompt-util.h index 179f056b53..06dd2c7f0c 100644 --- a/src/shared/prompt-util.h +++ b/src/shared/prompt-util.h @@ -26,3 +26,6 @@ int prompt_loop(const char *text, void *userdata, PromptFlags flags, char **ret); + +int chrome_show(const char *top, const char *bottom); +void chrome_hide(void);