boot/uefi: an F8 menu, with the resolution chooser inside it

UEFI had no boot menu at all, and asked about resolution in a prompt of
its own before anything else. Two questions at two moments through two
mechanisms is what made the firmwares feel like different programs.

There is one window now: press F8 (or 'o' -- a serial console sends no
scan codes, and that is how this is usually driven) and the same panel
the BIOS loader shows appears, with resolution as a row in it. Selecting
it steps to the next 32bpp mode at or above 800x600 and redraws; the
framebuffer is re-read every pass, because changing mode moves it and
drawing where it used to be is a black screen with no explanation.

Two layout bugs, both visible and both arithmetic:

  - TITLE_H was 34 while the heading alone occupies PANEL_PAD + GLYPH_H
    = 34, so the rule at TITLE_H - 6 was drawn *through* the lettering
    and the first row started on the heading's baseline. TITLE_H is
    derived from its parts now and cannot drift from them again.
  - the selected row's highlight was offset by a fixed 3px against a
    24px row, so it sat on the text rather than around it. It is centred
    on the glyph box: (ROW_H - GLYPH_H) / 2 above and below.

Verified from the host: the panel is centred at 640 on a 1280 screen, the
highlight is exactly ROW_H tall and clear of the rule, and Enter boots
through to cpu0. bootcheck 108 [ok], iso builds.

The BIOS resolution chooser is still a separate text prompt, and that is
not an oversight to fix later in the same way. VBE mode setting is an
INT 10h call, so it can only happen in real mode -- before there is any
framebuffer to draw a menu in. Offering it from the graphical menu means
either returning to real mode from long mode, or entering unreal mode to
draw the menu before the switch. Both are real mode-switching work in the
riskiest code in the tree, and neither is a tidy-up.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Nexoniarz 2026-08-11 09:04:57 +02:00
commit 55a585792e
2 changed files with 156 additions and 76 deletions

View file

@ -316,9 +316,19 @@ metal_text_centered(int y, const char *s)
}
}
#define ROW_H 22
/* Panel metrics.
*
* TITLE_H is the whole title band -- the padding above the heading, the
* heading itself, and the air beneath it before the rule. It was 34 while the
* heading alone needs 18 + 16 = 34, so the rule at TITLE_H - 6 was drawn
* *through* the lettering and the first row started on the heading's baseline.
* Derived from its parts now, so it cannot drift from them again.
*/
#define ROW_H 24
#define PANEL_PAD 18
#define TITLE_H 34
#define TITLE_GAP 14 /* heading to the rule */
#define TITLE_H (PANEL_PAD + GLYPH_H + TITLE_GAP) /* = 48 */
#define ROW_LIFT ((ROW_H - GLYPH_H) / 2) /* highlight overhang */
static void
panel_frame(const char *title, int rows, const char *hint,
@ -342,7 +352,8 @@ panel_frame(const char *title, int rows, const char *hint,
panel(x, y, w, h);
metal_text_centered(y + PANEL_PAD, title);
ui_fill_rect(x + PANEL_PAD, y + TITLE_H - 6, w - PANEL_PAD * 2, 1, METAL_EDGE);
ui_fill_rect(x + PANEL_PAD, y + PANEL_PAD + GLYPH_H + TITLE_GAP / 2,
w - PANEL_PAD * 2, 1, METAL_EDGE);
if (hint)
ui_text_centered(y + h + 16, hint, SCH_DIM);
@ -367,7 +378,10 @@ ui_menu(const char *title, const char *const *items, int n, int selected,
/* The selected row is the only lit thing on the plate. Filling it
* with the accent and putting the label in the ground colour reads
* at a glance; an outline does not, at this size. */
ui_fill_rect(x + PANEL_PAD / 2, ry - 3, w - PANEL_PAD, ROW_H,
/* Centred on the glyph box: (ROW_H - GLYPH_H) / 2 above it, the
* same below. Off-by-a-few here is what makes a highlight look
* like it is sitting on the text rather than around it. */
ui_fill_rect(x + PANEL_PAD / 2, ry - ROW_LIFT, w - PANEL_PAD, ROW_H,
SCH_ACCENT);
ui_text(x + PANEL_PAD, ry, items[i], PANEL_BG, SCH_BG_NONE);
} else {