feat(v4): the screen and the keyboards

The stick, increment 1, task 2 (plan 2026-10-26-stick-1-screen-and-keyboard;
docs/v4.0.0/STICK.md 2, 5.3, 5.4). On the v4 path:
- the framebuffer is brought up after POST, cleared and dark: console text
  goes to serial only ("splash only, log on serial") until Zuse logs in;
  then the screen is the console (TTF, a blinking cursor, as v3's), and
  when she logs out it is cleared and dark again (console_fb_mirror(),
  console_fb_clear(); the mirror is on by default, so v3 is unchanged);
- the line editor and the node's KEY read every keyboard -- PS/2, USB, the
  virtio keyboard -- through the shared bridge (sk_input_getc()); the
  headless wait drops keyboard input as well as serial bytes;
- an EMERGENCY_CONSOLE_ENABLED build has the screen from the start.
The framebuffer setup is one helper both paths call (kernel_main.c).

Red (before): screen_dark_until_login 153051 (10835 lit), console/keys
153309, logout 153609, kbd_typeahead 153918, virtio_kbd aarch64 154245
and riscv64 154718; no_framebuffer 155546 passed before and after.
Green: 155926 (0 lit), 160156, 160409 (0 lit after logout), 160630,
virtio_kbd 161414 and 161820, no_framebuffer 161206; usb_kbd with the USB
keyboard the only one (-machine i8042=off) 164548, v3 control 164757;
the identity gate's sessions 162245-163802 all pass; v3 typed by keyboard
164009, 164115. (usb_kbd 160857 and 164313 failed in the harness:
input-send-event's device names a display, not a keyboard.)
Accepted: suite EXIT 0; three clean qemu boots, POST 538/538, dict_hash
0xfec8c47cfc6f437f unchanged (174704, 174911, 175315); test-32, 39
modules, 0 failures.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rajames
2026-10-26 18:02:27 -04:00
co-authored by Claude Opus 5.5
parent 0dbc98c049
commit cab0ffb487
76 changed files with 40541 additions and 26 deletions
+20 -5
View File
@@ -249,6 +249,21 @@ void console_describe_serial(void)
static char g_active_vm_name_buf[CONSOLE_VM_NAME_BUF];
static const char *g_active_vm_name = (void *)0;
static int g_line_start = 1;
/* Whether console text is drawn on the framebuffer as well as sent to the
* serial line. On unless a boot turns it off: v3's console mirrors from
* console_fb_init() on, as always; the v4 boot keeps the screen dark until
* Zuse logs in ("splash only, log on serial", docs/v4.0.0/STICK.md 2). */
static int g_fb_mirror = 1;
static int fb_mirrored(void) { return g_fb_mirror && fb_is_available(); }
void console_fb_mirror(int on) { g_fb_mirror = on ? 1 : 0; }
void console_fb_clear(void)
{
if (fb_is_available()) vt100_puts("\x1b[2J\x1b[H");
}
static uint64_t g_console_tx_count; /* monotonic console_putc() counter (BSS) */
void console_set_vm_name(const char *name) {
@@ -307,7 +322,7 @@ static void raw_putc(char c) {
* function. */
static void emit_prefix(void) {
const char *p;
int fb = fb_is_available();
int fb = fb_mirrored();
static const char *color_on = "\x1b[38;2;255;165;0m";
static const char *color_off = "\x1b[39m";
const char *user = g_user_prefix_fn ? g_user_prefix_fn() : (void *)0;
@@ -383,8 +398,8 @@ static void console_putc_inner(char c)
g_line_start = 1;
}
/* --- framebuffer VT100 path (when available) --- */
if (fb_is_available()) {
/* --- framebuffer VT100 path (when available, and mirrored) --- */
if (fb_mirrored()) {
if (c == '\n') vt100_putc('\r');
vt100_putc(c);
}
@@ -479,14 +494,14 @@ void console_fb_toggle_graphics(void)
void console_fb_draw_cursor(void)
{
if (fb_is_available()) {
if (fb_mirrored()) {
vt100_draw_cursor();
}
}
void console_fb_erase_cursor(void)
{
if (fb_is_available()) {
if (fb_mirrored()) {
vt100_erase_cursor();
}
}