The BIOS loader drew its own splash and the UEFI loader drew a different
one, which is how two implementations of a single screen end up looking
nothing alike: only one of them ever got a change. stage2main.c's
fourteen drawing functions are deleted and it calls boot/common/ui.c,
which is what the UEFI loader already used. There is one splash now.
What changed in the look, in order of how much it mattered:
- No product name under the mark. The name is *in* the mark, and
setting it again underneath read as a placeholder nobody removed.
- The mark is scaled to the bar's width and sits directly above it, so
the two are one object rather than a large logo with an unrelated
line beneath. Box-filtered down from the source mask, never up:
point-sampling a coverage mask drops half the edge pixels and puts
holes in the thin strokes.
- Metal rather than flat grey. A vertical gradient with a knee near the
top where a highlight falls, applied to the mark, the bar fill and
panel headings. A single colour on a dark ground looks printed; the
knee is what makes it look lit. No texture -- at this size noise
reads as dirt.
- The boot manager is a panel with a lit selected row, replacing a
screen laid out in literals with a magic y of 340 that only ever
looked right at 800x600.
Three defects found by reading pixels rather than code:
- the UEFI loader kept calling print() after the splash, and ConOut on
a graphical firmware renders into the same framebuffer -- a
screenshot showed firmware text across our mark from the left edge.
Diagnostics go to COM1 directly now, which is where they were being
read from anyway, and the console is cleared before drawing so the
firmware has nothing to put back.
- SPLASH_UP was declared and read but never assigned, so that redirect
was dead code and the "serial works" reading was OVMF's own console.
- stage2 overflowed its allocation -- 25440 bytes against 24576 -- and
the build did not stop. The kernel is written at sector
1 + STAGE2_SECTORS, so a stage2 past its allocation is overwritten by
the kernel it is loading. STAGE2_SECTORS is 64 now, and the padding
tool's check does fire; it had simply not been re-run.
Verified from the host on both: BIOS 800x600 and UEFI 1280x800 both draw
a centred group with zero stray pixels, UEFI's spanning x 576-703 which
is exactly the mark width centred. bootcheck 108 [ok], iso builds.
Still text-mode: the resolution menu. On BIOS it necessarily is -- it
runs in real mode before any framebuffer exists, which is the whole
reason it can set one. On UEFI it could be graphical and is not yet.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
91 lines
3.7 KiB
C
91 lines
3.7 KiB
C
/* SPDX-License-Identifier: Apache-2.0 */
|
|
/*
|
|
* The loader's user interface, shared by both firmwares.
|
|
*
|
|
* This is the first piece of the two loaders that is genuinely common code
|
|
* rather than a contract they both honour. It can be, because the difference
|
|
* between BIOS and UEFI is entirely in how you *obtain* a framebuffer -- VBE in
|
|
* real mode, or GOP -- and not in what you do with one afterwards. By the time
|
|
* there is a base address, a size and a stride, there is nothing firmware-
|
|
* specific left to say.
|
|
*
|
|
* Everything here draws and nothing here decides. No disk, no memory map, no
|
|
* mode setting: a caller hands over a framebuffer and asks for pixels. That is
|
|
* what makes it compilable into a 16/32/64-bit BIOS loader and into a PE/COFF
|
|
* UEFI application without either of them leaking into it.
|
|
*
|
|
* Types are spelled out rather than taken from a header, because the two
|
|
* callers do not agree on one: the BIOS side has uchar/uint32 from the kernel
|
|
* tree, the UEFI side has UINT8/UINT32 from the EFI headers, and this file has
|
|
* to compile inside both.
|
|
*/
|
|
#ifndef SCHOS_BOOT_UI_H
|
|
#define SCHOS_BOOT_UI_H
|
|
|
|
typedef unsigned char ui_u8;
|
|
typedef unsigned short ui_u16;
|
|
typedef unsigned int ui_u32;
|
|
typedef unsigned long long ui_u64;
|
|
|
|
/* A linear framebuffer, 32 bits per pixel. Both firmwares can produce one and
|
|
* neither can produce anything this code could use if they could not. */
|
|
struct ui_fb {
|
|
ui_u8 *base;
|
|
ui_u32 width;
|
|
ui_u32 height;
|
|
ui_u32 stride; /* bytes per row, from the driver -- never width*4 */
|
|
};
|
|
|
|
/* Hand over the framebuffer. Passing a null base, or a zero size, leaves the
|
|
* UI inert: every call below then does nothing, so a caller with no display
|
|
* does not need to guard each one. */
|
|
void ui_init(const struct ui_fb *fb);
|
|
int ui_active(void);
|
|
|
|
ui_u32 ui_width(void);
|
|
ui_u32 ui_height(void);
|
|
|
|
void ui_clear(void);
|
|
void ui_fill_rect(int x, int y, int w, int h, ui_u32 colour);
|
|
void ui_text(int x, int y, const char *s, ui_u32 colour, ui_u32 bg);
|
|
void ui_text_centered(int y, const char *s, ui_u32 colour);
|
|
|
|
/* The logo, centred horizontally, top-left at the given y, scaled to `w`
|
|
* pixels wide. Downscaled from the source mask -- never up, which would only
|
|
* make it soft. */
|
|
void ui_logo(int y, int w);
|
|
|
|
/* The mark's width, which is also the progress bar's. One number, because the
|
|
* two being the same width is the whole arrangement. */
|
|
int ui_mark_width(void);
|
|
|
|
/* The splash: the mark, and the bar beneath it.
|
|
*
|
|
* No product name: the name is *in* the mark, and setting it again underneath
|
|
* is the kind of thing that reads as a placeholder nobody removed.
|
|
*
|
|
* Laid out from the centre outwards rather than from the top down, so it looks
|
|
* the same at 800x600 and at 1600x1200 instead of drifting up the screen as the
|
|
* mode grows.
|
|
*/
|
|
void ui_splash(const char *subtitle);
|
|
|
|
/* A menu, drawn as a panel. The caller owns the keyboard: this draws a state
|
|
* and returns, so the same function serves a firmware with INT 16h and one with
|
|
* a UEFI text-input protocol without either appearing in here.
|
|
*/
|
|
void ui_menu(const char *title, const char *const *items, int n, int selected,
|
|
const char *hint);
|
|
|
|
/* A panel of label/value rows -- the system information screen. */
|
|
void ui_panel(const char *title, const char *const *labels,
|
|
const char *const *values, int n, const char *hint);
|
|
|
|
/* The progress bar. `total` is whatever unit the caller counts in; ticks are
|
|
* cumulative, and the bar only ever moves forward. */
|
|
void ui_progress_begin(const char *label, ui_u32 total);
|
|
void ui_progress_label(const char *label);
|
|
void ui_progress_tick(ui_u32 done);
|
|
void ui_progress_end(void);
|
|
|
|
#endif /* SCHOS_BOOT_UI_H */
|