From: Lennart Poettering Date: Fri, 19 Sep 2025 12:54:08 +0000 (+0200) Subject: boot: work around ansi color issues between sd-boot, uefi and terminals X-Git-Tag: v259-rc1~456^2~2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=8d36341cef4c3ab9c58a3923faa80f4d6aa17bca;p=thirdparty%2Fsystemd.git boot: work around ansi color issues between sd-boot, uefi and terminals So, UEFI's color texting is a bit weird. It translates everything to ANSI sequences, but unlike ANSI sequences it has no understanding of a distinct "default" bg/fg color, it assumes the ansi color "0" is always equal to white on black, but that's of course not really true, most terminal emulators at the very least support white background too. tianocore then also tries to be smart and suppresses ANSI color changes from a color to itself. But if the understanding of the color is wrong in the first place, then any color change suppression like this hurts more than it helps. Then in addition there are certain terminal tools that will reset the bg color on every line break ("less" for example) to the default. Let's deal with that and improve the situation on all fronts: 1. force out color changes by doing two color changes whenever we really want it. 2. on every newline force out the color change again. with this in place, using sd-boot on a terminal emulator is a lot nicer. --- diff --git a/src/boot/efi-string.c b/src/boot/efi-string.c index 8e2c73d84d6..f051b5dd887 100644 --- a/src/boot/efi-string.c +++ b/src/boot/efi-string.c @@ -917,6 +917,37 @@ static bool handle_format_specifier(FormatContext *ctx, SpecifierContext *sp) { } } +#if SD_BOOT +static void output_string_safe(EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *this, const char16_t *s) { + assert(this); + assert(s); + + /* This is a color-conscious version of ST->ConOut->OutputString(). Whenever it encounters a newline + * character, it will reset the color to our default, because some UEFI implementations/terminals + * reset the color in that case, and we want our default color to remain in effect */ + + int32_t saved_attribute = ST->ConOut->Mode->Attribute; + for (;;) { + const char16_t *nl = strchr16(s, '\n'); + if (!nl) /* No further newline */ + return (void) ST->ConOut->OutputString(ST->ConOut, (char16_t*) s); + + if (nl[1] == 0) { /* Newline is at the end of the string */ + (void) ST->ConOut->OutputString(ST->ConOut, (char16_t*) s); + set_attribute_safe(saved_attribute); + return; + } + + /* newline is in the middle of the string */ + _cleanup_free_ char16_t *x = xstrndup16(s, nl - s + 1); + (void) ST->ConOut->OutputString(ST->ConOut, x); + set_attribute_safe(saved_attribute); + + s = nl + 1; + } +} +#endif + /* printf_internal is largely compatible to userspace vasprintf. Any features omitted should trigger asserts. * * Supported: @@ -983,7 +1014,7 @@ _printf_(2, 0) static char16_t *printf_internal(EFI_STATUS status, const char *f } #if SD_BOOT - ST->ConOut->OutputString(ST->ConOut, ctx.buf); + output_string_safe(ST->ConOut, ctx.buf); #endif return mfree(ctx.dyn_buf); diff --git a/src/boot/util.c b/src/boot/util.c index 5d439430b8d..92da63fc0ff 100644 --- a/src/boot/util.c +++ b/src/boot/util.c @@ -195,16 +195,27 @@ EFI_STATUS file_read( return file_handle_read(handle, offset, size, ret, ret_size); } +void set_attribute_safe(size_t attr) { + /* Various UEFI implementations suppress color changes from a color to the same color. Often, we want + * to force out the color change though, hence change the color here once, and then back. We simply + * mark the color as bright for a moment, and then revert that. */ + + attr ^= 0x08; + ST->ConOut->SetAttribute(ST->ConOut, attr); + attr ^= 0x08; + ST->ConOut->SetAttribute(ST->ConOut, attr); +} + void print_at(size_t x, size_t y, size_t attr, const char16_t *str) { assert(str); ST->ConOut->SetCursorPosition(ST->ConOut, x, y); - ST->ConOut->SetAttribute(ST->ConOut, attr); + set_attribute_safe(attr); ST->ConOut->OutputString(ST->ConOut, (char16_t *) str); } void clear_screen(size_t attr) { log_wait(); - ST->ConOut->SetAttribute(ST->ConOut, attr); + set_attribute_safe(attr); ST->ConOut->ClearScreen(ST->ConOut); } diff --git a/src/boot/util.h b/src/boot/util.h index e7a3ba03033..ef017f45fa0 100644 --- a/src/boot/util.h +++ b/src/boot/util.h @@ -152,6 +152,7 @@ static inline void unload_imagep(EFI_HANDLE *image) { #define GUID_FORMAT_VAL(g) (g).Data1, (g).Data2, (g).Data3, (g).Data4[0], (g).Data4[1], \ (g).Data4[2], (g).Data4[3], (g).Data4[4], (g).Data4[5], (g).Data4[6], (g).Data4[7] +void set_attribute_safe(size_t attr); void print_at(size_t x, size_t y, size_t attr, const char16_t *str); void clear_screen(size_t attr);