From 8fcd85768beef5382fb4cb238148889c0b7eee4b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 5 Feb 2025 09:35:51 +0100 Subject: [PATCH] terminal-util: tweak any_key_to_proceed() a bit 1. Make the message a bit more visible, by adding ANSI color. This matters in particular during boot, where the message otherwise might be overprinted by other output 2. Let's turn off terminal echo so that whatever key is entered is not made visible on screen, and we can handle newline and other keys reasonably uniformly. --- src/basic/terminal-util.c | 43 +++++++++++++++++++---------------- src/basic/terminal-util.h | 2 +- src/cgtop/cgtop.c | 2 +- src/firstboot/firstboot.c | 2 +- src/home/homectl.c | 7 +++++- src/journal/bsod.c | 4 ++-- src/test/test-terminal-util.c | 8 +++---- 7 files changed, 39 insertions(+), 29 deletions(-) diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c index 678cd4ed4d..b2cbadb579 100644 --- a/src/basic/terminal-util.c +++ b/src/basic/terminal-util.c @@ -103,22 +103,25 @@ int chvt(int vt) { return RET_NERRNO(ioctl(fd, VT_ACTIVATE, vt)); } -int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) { +int read_one_char(FILE *f, char *ret, usec_t t, bool echo, bool *need_nl) { _cleanup_free_ char *line = NULL; struct termios old_termios; int r, fd; - assert(f); assert(ret); + if (!f) + f = stdin; + /* If this is a terminal, then switch canonical mode off, so that we can read a single * character. (Note that fmemopen() streams do not have an fd associated with them, let's handle that - * nicely.) */ + * nicely.) If 'echo' is false we'll also disable ECHO mode so that the pressed key is not made + * visible to the user. */ fd = fileno(f); if (fd >= 0 && tcgetattr(fd, &old_termios) >= 0) { struct termios new_termios = old_termios; - new_termios.c_lflag &= ~ICANON; + new_termios.c_lflag &= ~(ICANON|(echo ? 0 : ECHO)); new_termios.c_cc[VMIN] = 1; new_termios.c_cc[VTIME] = 0; @@ -201,7 +204,7 @@ int ask_char(char *ret, const char *replies, const char *fmt, ...) { fflush(stdout); - r = read_one_char(stdin, &c, DEFAULT_ASK_REFRESH_USEC, &need_nl); + r = read_one_char(stdin, &c, DEFAULT_ASK_REFRESH_USEC, /* echo= */ true, &need_nl); if (r < 0) { if (r == -ETIMEDOUT) @@ -257,20 +260,23 @@ int ask_string(char **ret, const char *text, ...) { } bool any_key_to_proceed(void) { + + /* Insert a new line here as well as to when the user inputs, as this is also used during the boot up + * sequence when status messages may be interleaved with the current program output. This ensures + * that the status messages aren't appended on the same line as this message. */ + + fputc('\n', stdout); + fputs(ansi_highlight_magenta(), stdout); + fputs("-- Press any key to proceed --", stdout); + fputs(ansi_normal(), stdout); + fflush(stdout); + char key = 0; - bool need_nl = true; + (void) read_one_char(stdin, &key, USEC_INFINITY, /* echo= */ false, /* need_nl= */ NULL); - /* - * Insert a new line here as well as to when the user inputs, as this is also used during the - * boot up sequence when status messages may be interleaved with the current program output. - * This ensures that the status messages aren't appended on the same line as this message. - */ - puts("-- Press any key to proceed --"); - - (void) read_one_char(stdin, &key, USEC_INFINITY, &need_nl); - - if (need_nl) - putchar('\n'); + fputc('\n', stdout); + fputc('\n', stdout); + fflush(stdout); return key != 'q'; } @@ -312,10 +318,9 @@ int show_menu(char **x, unsigned n_columns, unsigned width, unsigned percentage) putchar('\n'); /* on the first screen we reserve 2 extra lines for the title */ - if (i % break_lines == break_modulo) { + if (i % break_lines == break_modulo) if (!any_key_to_proceed()) return 0; - } } return 0; diff --git a/src/basic/terminal-util.h b/src/basic/terminal-util.h index d6dd394bcf..d11daefb56 100644 --- a/src/basic/terminal-util.h +++ b/src/basic/terminal-util.h @@ -80,7 +80,7 @@ int proc_cmdline_tty_size(const char *tty, unsigned *ret_rows, unsigned *ret_col int chvt(int vt); -int read_one_char(FILE *f, char *ret, usec_t timeout, bool *need_nl); +int read_one_char(FILE *f, char *ret, usec_t timeout, bool echo, bool *need_nl); int ask_char(char *ret, const char *replies, const char *text, ...) _printf_(3, 4); int ask_string(char **ret, const char *text, ...) _printf_(2, 3); bool any_key_to_proceed(void); diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c index 08eae5988b..ce73599d90 100644 --- a/src/cgtop/cgtop.c +++ b/src/cgtop/cgtop.c @@ -954,7 +954,7 @@ static int loop(const char *root) { if (arg_batch) (void) usleep_safe(usec_add(usec_sub_unsigned(last_refresh, t), arg_delay)); else { - r = read_one_char(stdin, &key, usec_add(usec_sub_unsigned(last_refresh, t), arg_delay), NULL); + r = read_one_char(stdin, &key, usec_add(usec_sub_unsigned(last_refresh, t), arg_delay), /* echo= */ false, /* need_nl= */ NULL); if (r == -ETIMEDOUT) continue; if (r < 0) diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index d00886fb80..aaff8a8e88 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -128,7 +128,7 @@ static void print_welcome(int rfd) { else printf("\nWelcome to your new installation of %s!\n", pn); - printf("\nPlease configure your system!\n\n"); + printf("\nPlease configure your system!\n"); any_key_to_proceed(); diff --git a/src/home/homectl.c b/src/home/homectl.c index bfd4b3b574..89e9c8b82b 100644 --- a/src/home/homectl.c +++ b/src/home/homectl.c @@ -2485,7 +2485,12 @@ static int create_interactively(void) { return 0; } - any_key_to_proceed(); + printf("\nPlease create your user account!\n"); + + if (!any_key_to_proceed()) { + log_notice("Skipping."); + return 0; + } (void) terminal_reset_defensive_locked(STDOUT_FILENO, /* switch_to_text= */ false); diff --git a/src/journal/bsod.c b/src/journal/bsod.c index 2f06808cd4..68361b084a 100644 --- a/src/journal/bsod.c +++ b/src/journal/bsod.c @@ -228,9 +228,9 @@ static int display_emergency_message_fullscreen(const char *message) { goto cleanup; } - r = read_one_char(f, &read_character_buffer, USEC_INFINITY, NULL); + r = read_one_char(f, &read_character_buffer, USEC_INFINITY, /* echo= */ true, /* need_nl= */ NULL); if (r < 0 && r != -EINTR) - log_error_errno(r, "Failed to read character: %m"); + log_warning_errno(r, "Failed to read character, ignoring: %m"); r = 0; diff --git a/src/test/test-terminal-util.c b/src/test/test-terminal-util.c index ac7eacb01a..87304346e9 100644 --- a/src/test/test-terminal-util.c +++ b/src/test/test-terminal-util.c @@ -55,20 +55,20 @@ TEST(read_one_char) { assert_se(fputs("c\n", file) >= 0); rewind(file); - assert_se(read_one_char(file, &r, 1000000, &need_nl) >= 0); + assert_se(read_one_char(file, &r, 1000000, /* echo= */ true, &need_nl) >= 0); assert_se(!need_nl); assert_se(r == 'c'); - assert_se(read_one_char(file, &r, 1000000, &need_nl) < 0); + assert_se(read_one_char(file, &r, 1000000, /* echo= */ true, &need_nl) < 0); rewind(file); assert_se(fputs("foobar\n", file) >= 0); rewind(file); - assert_se(read_one_char(file, &r, 1000000, &need_nl) < 0); + assert_se(read_one_char(file, &r, 1000000, /* echo= */ true, &need_nl) < 0); rewind(file); assert_se(fputs("\n", file) >= 0); rewind(file); - assert_se(read_one_char(file, &r, 1000000, &need_nl) < 0); + assert_se(read_one_char(file, &r, 1000000, /* echo= */ true, &need_nl) < 0); } TEST(getttyname_malloc) {