display: confirm a resolution change, and size the console font to the screen

On the Ryzen 4600G desktop and the ASUS TUF A15, AMD's GOP "sets" the
smaller modes without leaving the panel's native timing and reports the
mode's own row length. Every check the loader made passed, and the screen
showed three or four squashed copies side by side (1920/640, 2560/640).
Nothing the firmware reports can tell such a mode from a real one.

So a mode change now asks "Keep this resolution?" in the new mode and keeps
it only on Enter within ten seconds. On a mode that does not really work the
question cannot be read, and the old mode comes back by itself.

And the reason to want a smaller mode was the text: the console font was a
fixed 16 px, unreadable on a 1440p monitor. It now scales to about 45 rows,
16 to 40 px per em, on whatever mode the firmware booted in.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
This commit is contained in:
Nexoniarz 2026-10-24 23:11:22 +02:00
commit d2c70c9129
2 changed files with 50 additions and 8 deletions

View file

@ -1069,6 +1069,41 @@ res_read_number(const char *prompt)
* cannot prove a mode is right, but they can prove one is wrong, and a wrong * cannot prove a mode is right, but they can prove one is wrong, and a wrong
* one is refused here, in the menu, where there is still a screen to say so on. * one is refused here, in the menu, where there is still a screen to say so on.
*/ */
/* "Keep this resolution?", drawn in the new mode, and Enter within ten
* seconds to keep it -- the question every desktop asks after a mode change,
* for the same reason. The numbers the firmware reports for a mode are no
* proof the screen shows it: AMD's GOP on a Ryzen APU and on the ASUS TUF
* "sets" the smaller modes without leaving the panel's native timing, and
* reports a row length that is the mode's rather than the panel's. Every
* check above passes and the picture comes out as three or four squashed
* copies side by side. Nobody can read the question on a screen like that,
* so nobody presses Enter, and the old mode comes back on its own. */
static int
mode_confirmed(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop)
{
struct ui_fb uifb;
EFI_INPUT_KEY key;
const char *msg[2] = { "Keep this resolution?",
"If you can read this, press Enter" };
uifb.base = (ui_u8 *)(UINTN)gop->Mode->FrameBufferBase;
uifb.width = gop->Mode->Info->HorizontalResolution;
uifb.height = gop->Mode->Info->VerticalResolution;
uifb.stride = gop->Mode->Info->PixelsPerScanLine * 4;
ui_init(&uifb);
ui_menu("Resolution", msg, 2, 0, "going back in 10 seconds");
if (ST->ConIn)
while (!EFI_ERROR(ST->ConIn->ReadKeyStroke(ST->ConIn, &key)))
; /* the key that chose the mode is not an answer */
for (UINTN waited = 0; waited < 10000; waited += 10) {
if (ST->ConIn && !EFI_ERROR(ST->ConIn->ReadKeyStroke(ST->ConIn, &key)))
return key.UnicodeChar == L'\r' || key.UnicodeChar == L'\n';
BS->Stall(10000);
}
return 0;
}
static int static int
mode_set_checked(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop, UINT32 mode) mode_set_checked(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop, UINT32 mode)
{ {
@ -1087,10 +1122,11 @@ mode_set_checked(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop, UINT32 mode)
got->VerticalResolution == want->VerticalResolution && got->VerticalResolution == want->VerticalResolution &&
got->PixelsPerScanLine >= got->HorizontalResolution && got->PixelsPerScanLine >= got->HorizontalResolution &&
gop->Mode->FrameBufferBase && gop->Mode->FrameBufferBase &&
gop->Mode->FrameBufferSize >= need) gop->Mode->FrameBufferSize >= need &&
mode_confirmed(gop))
return 1; return 1;
describe_gop(gop, L"schos: mode does not add up, going back:"); describe_gop(gop, L"schos: mode not kept, going back:");
gop->SetMode(gop, was); gop->SetMode(gop, was);
return 0; return 0;
} }
@ -1101,7 +1137,7 @@ res_refused(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop)
{ {
struct ui_fb uifb; struct ui_fb uifb;
EFI_INPUT_KEY key; EFI_INPUT_KEY key;
const char *msg[1] = { "The display refused that size -- keeping this one" }; const char *msg[1] = { "Not kept -- back to the size before" };
uifb.base = (ui_u8 *)(UINTN)gop->Mode->FrameBufferBase; uifb.base = (ui_u8 *)(UINTN)gop->Mode->FrameBufferBase;
uifb.width = gop->Mode->Info->HorizontalResolution; uifb.width = gop->Mode->Info->HorizontalResolution;

View file

@ -473,10 +473,16 @@ gfx_font_init(void)
} }
} }
// 16 pixels per em. The bitmap it replaces is 16 tall, so the terminal keeps // The size follows the screen: about 45 rows whatever the resolution, and
// the number of rows it had and nothing that assumed a screenful has to // never under 16 pixels per em, the bitmap's height. A fixed 16 was right
// change. // on 1024x768 and unreadable on a 1440p monitor -- and asking for a smaller
if (ttf_open(&consttf, data, len, 16) != 0) { // mode to get bigger text is no answer, because firmware on real machines
// often "sets" a smaller mode by leaving the panel at its native size and
// misreporting the row length, which is the sheared, repeated screen.
int em = scanout.height / 45;
if (em < 16) em = 16;
if (em > 40) em = 40;
if (ttf_open(&consttf, data, len, em) != 0) {
// A bad file on the ESP must not cost the console its font. Nothing has // A bad file on the ESP must not cost the console its font. Nothing has
// been changed yet at this point, so the built-in one is still there to // been changed yet at this point, so the built-in one is still there to
// fall back to -- and it is worth saying, because a font that silently // fall back to -- and it is worth saying, because a font that silently
@ -487,7 +493,7 @@ gfx_font_init(void)
"using the built-in one\n"); "using the built-in one\n");
data = _binary_consfont_start; data = _binary_consfont_start;
len = (uint32)(_binary_consfont_end - _binary_consfont_start); len = (uint32)(_binary_consfont_end - _binary_consfont_start);
if (ttf_open(&consttf, data, len, 16) != 0) if (ttf_open(&consttf, data, len, em) != 0)
return; return;
} }