Pressing Esc left the desktop and started the shell, but nothing appeared in the
emulator window -- the session was only visible on the host terminal. Once the
VBE adapter is switched into graphics mode the VGA text buffer at 0xB8000 stops
being scanned out, so every printk after that point reached serial and the
dmesg ring but never the display.
Disabling the DISPI interface does not bring the text console back; QEMU keeps
presenting the last framebuffer surface. So rather than clawing the adapter back
to text mode, the console is now rendered into the framebuffer:
drivers/video/fbcon.c draws the console with the shared 8x8 font and carries a
VT100 subset -- SGR colours, cursor addressing, relative moves, erase-display,
erase-line and save/restore. It presents a fixed 80x25 grid, because that is
the geometry the userland assumes (nano pads its bars with %-80s, the shell
wraps at TTY_WIDTH), with the glyph cell scaled to fill the active resolution.
The TTY driver became the console dispatcher: tty_write, tty_clear and
tty_set_cursor route to fbcon while it owns the display. That last one is what
makes nano work -- it positions with tty_set_cursor() rather than escapes, so
its full-screen repaint was landing as an append-only stream.
Also in this change:
- `video=WIDTHxHEIGHT` on the kernel command line selects the display mode,
defaulting to 1280x720 so a 16:9 host panel is filled rather than pillarboxed
when QEMU goes fullscreen. Rejects modes the heap cannot back.
- `make run-fullscreen`, plus zoom-to-fit on the windowed target, so scaling
preserves the guest aspect ratio instead of stretching it.
- QEMU targets raise vgamem_mb, since a 1280x720 32-bit surface exceeds the
default VGA memory.
- bochs_vbe_disable()/fb_set_active() let the framebuffer be marked dormant so
drawing routines no-op after a mode teardown.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01L5yKJT4Wo2cV3rfPrwY5MP
Boot logo (init/bootlogo.c, init/logo_data.c, scripts/mklogo.py):
The logo appears in two forms because the framebuffer does not exist for the
first third of boot. An ASCII rendition goes to the text console and serial
line from the first moments; once the video driver is up at step [5/14] the
real bitmap takes over as a graphical splash with a progress bar tied to the
actual boot stages.
The asset is 4-bit palette indexed, so 160x160 pixels cost 12.5 KB instead of
100 KB, and carries a transparency mask so the mark composites onto the
gradient rather than stamping a flat rectangle over it. scripts/mklogo.py
regenerates init/logo_data.c from the source PNG.
Boot reaches the desktop in well under a second, so the splash is held for a
minimum duration and the bar sweeps to 100% during the hold; without that the
logo would flash past unseen. `splashtime=<ms>` tunes it and `nosplash`,
`nogui` or `text` skip it.
Bootloader (boot/stage2.asm):
stage2 staged the whole kernel in conventional RAM before copying it to 1 MB,
which capped the kernel at roughly 576 KB before the load ran into video
memory at 0xA0000. The logo pushed it over that line and the kernel stopped
booting. Each 32 KB chunk now goes into one fixed low buffer and is copied
straight to its final address, using unreal mode for the 32-bit reach. The
ceiling is now the image itself, 1.375 MB.
Console font (lib/font8x8.c):
fb_draw_char was a stub that ignored its character argument and drew a filled
rectangle, which is why the splash rendered its text as solid blocks. It now
renders real glyphs, and the table it uses is shared: the framebuffer console,
the canvas rasterizer and the desktop compositor had three divergent private
copies, two of which still carried the `// \` line-continuation defect.
printk console sink (kernel/printk.c):
printk output can be redirected to a callback, so a caller can run a subsystem
that reports via printk and capture what it printed. Serial and the dmesg ring
still see everything, which keeps a captured run debuggable.
Shell builtins (userland/lazybox/shell.c):
The builtin chain is extracted into shell_execute_builtin(), so the kernel TTY
and the desktop terminal accept the same vocabulary instead of the GUI seeing
only the applet table. Callers that cannot be suspended pass allow_blocking =
false to refuse the animating ones.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01L5yKJT4Wo2cV3rfPrwY5MP