]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
boot: Draw unicode separator line for status messages
authorJan Janssen <medhefgo@web.de>
Tue, 25 Jan 2022 12:48:27 +0000 (13:48 +0100)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Fri, 18 Mar 2022 18:33:13 +0000 (19:33 +0100)
src/boot/efi/boot.c

index ae252845274c6774503ce22a61dacc695d04174d..88cfd91a8ced8db23ba42239e1181947670c1b7a 100644 (file)
@@ -429,6 +429,17 @@ static CHAR16 *update_timeout_efivar(UINT32 *t, BOOLEAN inc) {
         }
 }
 
+static BOOLEAN unicode_supported(void) {
+        static INTN cache = -1;
+
+        if (cache < 0)
+                /* Basic unicode box drawing support is mandated by the spec, but it does
+                 * not hurt to make sure it works. */
+                cache = !EFI_ERROR(ST->ConOut->TestString(ST->ConOut, (CHAR16 *) L"─"));
+
+        return cache;
+}
+
 static void ps_string(const CHAR16 *fmt, const void *value) {
         assert(fmt);
         if (value)
@@ -444,7 +455,11 @@ static BOOLEAN ps_continue(void) {
         UINT64 key;
         EFI_STATUS err;
 
-        Print(L"\n--- Press any key to continue, ESC or q to quit. ---\n\n");
+        if (unicode_supported())
+                Print(L"\n─── Press any key to continue, ESC or q to quit. ───\n\n");
+        else
+                Print(L"\n--- Press any key to continue, ESC or q to quit. ---\n\n");
+
         err = console_key_read(&key, UINT64_MAX);
         return !EFI_ERROR(err) && !IN_SET(key, KEYPRESS(0, SCAN_ESC, 0), KEYPRESS(0, 0, 'q'), KEYPRESS(0, 0, 'Q'));
 }
@@ -600,7 +615,7 @@ static BOOLEAN menu_run(
         UINTN x_start = 0, y_start = 0, y_status = 0;
         UINTN x_max, y_max;
         _cleanup_(strv_freep) CHAR16 **lines = NULL;
-        _cleanup_freepool_ CHAR16 *clearline = NULL, *status = NULL;
+        _cleanup_freepool_ CHAR16 *clearline = NULL, *separator = NULL, *status = NULL;
         UINT32 timeout_efivar_saved = config->timeout_sec_efivar;
         UINT32 timeout_remain = config->timeout_sec == TIMEOUT_MENU_FORCE ? 0 : config->timeout_sec;
         BOOLEAN exit = FALSE, run = TRUE, firmware_setup = FALSE;
@@ -621,12 +636,11 @@ static BOOLEAN menu_run(
                 log_error_stall(L"Error switching console mode: %r", err);
         }
 
+        UINTN line_width = 0, entry_padding = 3;
         while (!exit) {
                 UINT64 key;
 
                 if (new_mode) {
-                        UINTN line_width = 0, entry_padding = 3;
-
                         console_query_mode(&x_max, &y_max);
 
                         /* account for padding+status */
@@ -645,6 +659,7 @@ static BOOLEAN menu_run(
                         idx_last = idx_first + visible_max - 1;
 
                         /* length of the longest entry */
+                        line_width = 0;
                         for (UINTN i = 0; i < config->entry_count; i++)
                                 line_width = MAX(line_width, StrLen(config->entries[i]->title_show));
                         line_width = MIN(line_width + 2 * entry_padding, x_max);
@@ -657,10 +672,11 @@ static BOOLEAN menu_run(
                                 y_start = 0;
 
                         /* Put status line after the entry list, but give it some breathing room. */
-                        y_status = MIN(y_start + MIN(visible_max, config->entry_count) + 4, y_max - 1);
+                        y_status = MIN(y_start + MIN(visible_max, config->entry_count) + 1, y_max - 1);
 
                         lines = strv_free(lines);
                         clearline = mfree(clearline);
+                        separator = mfree(separator);
 
                         /* menu entries title lines */
                         lines = xnew(CHAR16*, config->entry_count + 1);
@@ -684,9 +700,13 @@ static BOOLEAN menu_run(
                         lines[config->entry_count] = NULL;
 
                         clearline = xnew(CHAR16, x_max + 1);
-                        for (UINTN i = 0; i < x_max; i++)
+                        separator = xnew(CHAR16, x_max + 1);
+                        for (UINTN i = 0; i < x_max; i++) {
                                 clearline[i] = ' ';
+                                separator[i] = unicode_supported() ? L'─' : L'-';
+                        }
                         clearline[x_max] = 0;
+                        separator[x_max] = 0;
 
                         new_mode = FALSE;
                         clear = TRUE;
@@ -706,16 +726,16 @@ static BOOLEAN menu_run(
                                 if (i == config->idx_default_efivar)
                                         print_at(x_start, y_start + i - idx_first,
                                                  (i == idx_highlight) ? COLOR_HIGHLIGHT : COLOR_ENTRY,
-                                                 (CHAR16*) L"=>");
+                                                 (CHAR16*) (unicode_supported() ? L" ►" : L"=>"));
                         }
                         refresh = FALSE;
                 } else if (highlight) {
                         print_at(x_start, y_start + idx_highlight_prev - idx_first, COLOR_ENTRY, lines[idx_highlight_prev]);
                         print_at(x_start, y_start + idx_highlight - idx_first, COLOR_HIGHLIGHT, lines[idx_highlight]);
                         if (idx_highlight_prev == config->idx_default_efivar)
-                                print_at(x_start , y_start + idx_highlight_prev - idx_first, COLOR_ENTRY, (CHAR16*) L"=>");
+                                print_at(x_start , y_start + idx_highlight_prev - idx_first, COLOR_ENTRY, (CHAR16*) (unicode_supported() ? L" ►" : L"=>"));
                         if (idx_highlight == config->idx_default_efivar)
-                                print_at(x_start, y_start + idx_highlight - idx_first, COLOR_HIGHLIGHT, (CHAR16*) L"=>");
+                                print_at(x_start, y_start + idx_highlight - idx_first, COLOR_HIGHLIGHT, (CHAR16*) (unicode_supported() ? L" ►" : L"=>"));
                         highlight = FALSE;
                 }
 
@@ -724,20 +744,24 @@ static BOOLEAN menu_run(
                         status = xpool_print(L"Boot in %u s.", timeout_remain);
                 }
 
-                /* print status at last line of screen */
                 if (status) {
-                        UINTN len;
-                        UINTN x;
-
-                        /* center line */
-                        len = StrLen(status);
-                        if (len < x_max)
-                                x = (x_max - len) / 2;
-                        else
-                                x = 0;
-                        print_at(0, y_status, COLOR_NORMAL, clearline + (x_max - x));
+                        /* If we draw the last char of the last line, the screen will scroll and break our
+                         * input. Therefore, draw one less character then we could for the status message.
+                         * Note that the same does not apply for the separator line as it will never be drawn
+                         * on the last line. */
+                        UINTN len = StrnLen(status, x_max - 1);
+                        UINTN x = (x_max - len) / 2;
+                        status[len] = '\0';
+                        print_at(0, y_status, COLOR_NORMAL, clearline + x_max - x);
                         ST->ConOut->OutputString(ST->ConOut, status);
                         ST->ConOut->OutputString(ST->ConOut, clearline + 1 + x + len);
+
+                        len = MIN(MAX(len, line_width) + 2 * entry_padding, x_max);
+                        x = (x_max - len) / 2;
+                        print_at(x, y_status - 1, COLOR_NORMAL, separator + x_max - len);
+                } else {
+                        print_at(0, y_status - 1, COLOR_NORMAL, clearline);
+                        print_at(0, y_status, COLOR_NORMAL, clearline + 1); /* See comment above. */
                 }
 
                 /* Beep several times so that the selected entry can be distinguished. */
@@ -768,11 +792,7 @@ static BOOLEAN menu_run(
                 timeout_remain = 0;
 
                 /* clear status after keystroke */
-                if (status) {
-                        FreePool(status);
-                        status = NULL;
-                        print_at(0, y_status, COLOR_NORMAL, clearline + 1);
-                }
+                status = mfree(status);
 
                 idx_highlight_prev = idx_highlight;