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:
co-authored by
Claude Opus 5.5
parent
0dbc98c049
commit
cab0ffb487
+90
-4
@@ -22,10 +22,19 @@ the stick's, plan 2026-10-26-stick-1-screen-and-keyboard.md):
|
||||
typeahead serial bytes typed while headless do not run once Zuse attaches
|
||||
emergency an EMERGENCY_CONSOLE_ENABLED=1 build: ok> with no drive
|
||||
v3_keys system=v3: `3 4 + .` typed on QEMU's keyboard (send-key) -> 7 ok
|
||||
screen_dark_until_login no drive: 30 s after POST the screen is all black
|
||||
screen_console_after_login Zuse added: the screen shows the console; keys typed
|
||||
on QEMU's keyboard answer 7 ok and change the screen
|
||||
screen_dark_after_logout ... and when her drive is pulled, the screen is black again
|
||||
kbd_typeahead keys pressed while headless do not run once Zuse attaches
|
||||
usb_kbd the only keyboard a USB one (--make "QEMU_EXTRA='-machine
|
||||
i8042=off -device usb-kbd,bus=xhci0.0,port=3'"): it types
|
||||
no_framebuffer no display (-vga none): serial alone, 7 ok
|
||||
virtio_kbd aarch64/riscv64: QEMU's virtio keyboard types 7 ok
|
||||
|
||||
Needs only Python 3. One QEMU at a time (.claude/CLAUDE.md).
|
||||
"""
|
||||
import argparse, glob, json, os, re, shutil, socket, subprocess, sys, threading, time
|
||||
import argparse, glob, json, os, re, shlex, shutil, socket, subprocess, sys, threading, time
|
||||
|
||||
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
||||
PROMPT = r"\[zuse@Hera\][^\n]*ok> "
|
||||
@@ -39,7 +48,9 @@ ap.add_argument("--make", default="")
|
||||
ap.add_argument("--no-zuse", action="store_true")
|
||||
a = ap.parse_args()
|
||||
|
||||
NO_ZUSE_MODES = {"headless", "hotplug", "midpull", "waitpull", "typeahead", "emergency"}
|
||||
NO_ZUSE_MODES = {"headless", "hotplug", "midpull", "waitpull", "typeahead", "emergency",
|
||||
"screen_dark_until_login", "screen_console_after_login", "screen_dark_after_logout",
|
||||
"kbd_typeahead"}
|
||||
label = a.label or f"{a.mode}-{a.arch}"
|
||||
work = os.path.join(ROOT, "build", "qemu-session", label)
|
||||
shutil.rmtree(work, ignore_errors=True)
|
||||
@@ -50,8 +61,8 @@ art = os.path.join(work, "artemis.img")
|
||||
zimg = os.path.join(work, "zuse-thumb-ident.img")
|
||||
attach_zuse = not (a.no_zuse or a.mode in NO_ZUSE_MODES)
|
||||
|
||||
make_args = ["make", f"ARCH={a.arch}", "clean", "qemu", f"ARTDISK={art}",
|
||||
f"ZUSEDISK={zimg if attach_zuse else ''}"] + a.make.split()
|
||||
make_args = ["make", "-f", "kernel/Makefile", f"ARCH={a.arch}", "clean", "qemu", f"ARTDISK={art}",
|
||||
f"ZUSEDISK={zimg if attach_zuse else ''}"] + shlex.split(a.make)
|
||||
if a.mode == "v3_keys":
|
||||
make_args.append("KERNEL_ARGS=system=v3")
|
||||
start = time.time()
|
||||
@@ -165,6 +176,28 @@ def key_line(line):
|
||||
time.sleep(0.1)
|
||||
|
||||
|
||||
SHOTS = 0
|
||||
|
||||
|
||||
def screen():
|
||||
"""A screendump of QEMU's display: (width, height, number of non-black pixels, bytes)."""
|
||||
global SHOTS
|
||||
SHOTS += 1
|
||||
path = os.path.join(work, f"screen-{SHOTS}.ppm")
|
||||
qcmd("screendump", filename=path)
|
||||
for _ in range(20):
|
||||
if os.path.exists(path) and os.path.getsize(path) > 0:
|
||||
break
|
||||
time.sleep(0.5)
|
||||
with open(path, "rb") as f:
|
||||
data = f.read()
|
||||
parts = data.split(b"\n", 3) # P6, "w h", maxval, pixels
|
||||
w, h = (int(x) for x in parts[1].split())
|
||||
px = parts[3]
|
||||
lit = sum(1 for i in range(0, len(px) - 2, 3) if px[i] or px[i + 1] or px[i + 2])
|
||||
return w, h, lit, px
|
||||
|
||||
|
||||
def add_zuse(n):
|
||||
qcmd("blockdev-add", driver="raw", **{"node-name": f"zuse{n}"},
|
||||
file={"driver": "file", "filename": zimg})
|
||||
@@ -262,6 +295,59 @@ elif m == "v3_keys":
|
||||
key_line("3 4 + .")
|
||||
check("7 ok typed on the keyboard", wait_for(PROMPT, 60, n) and re.search(r"7\s+ok", text()) is not None)
|
||||
|
||||
elif m == "screen_dark_until_login":
|
||||
time.sleep(30)
|
||||
w, h, lit, _ = screen()
|
||||
check(f"the screen ({w}x{h}) is all black before login ({lit} lit)", lit == 0)
|
||||
|
||||
elif m in ("screen_console_after_login", "screen_dark_after_logout"):
|
||||
add_zuse(1)
|
||||
check("[zuse@Hera] ok> after attach", wait_for(PROMPT, 120))
|
||||
time.sleep(3)
|
||||
w, h, lit1, px1 = screen()
|
||||
check(f"the screen shows the console ({lit1} lit pixels)", lit1 >= 500)
|
||||
n = count(PROMPT)
|
||||
key_line("3 4 + .")
|
||||
check("7 ok typed on the keyboard", wait_for(PROMPT, 60, n) and re.search(r"7\s+ok", text()) is not None)
|
||||
time.sleep(3)
|
||||
_, _, lit2, px2 = screen()
|
||||
check("the screen changed with what was typed", px2 != px1)
|
||||
if m == "screen_dark_after_logout":
|
||||
del_zuse(1)
|
||||
check("session ended on detach", wait_for(r"Zuse: session ended -- reattach to re-authenticate", 60))
|
||||
time.sleep(5)
|
||||
_, _, lit3, _ = screen()
|
||||
check(f"the screen is black after logout ({lit3} lit)", lit3 == 0)
|
||||
|
||||
elif m == "kbd_typeahead":
|
||||
check("no ok> for 10 s with no drive", not wait_for(ANYPROMPT, 10))
|
||||
key_line("3 4 + .")
|
||||
time.sleep(5)
|
||||
add_zuse(1)
|
||||
check("[zuse@Hera] ok> after attach", wait_for(PROMPT, 120))
|
||||
time.sleep(5)
|
||||
check("keys pressed while headless did not run (no 7)", re.search(r"\b7\s+ok", text()) is None)
|
||||
n = count(PROMPT)
|
||||
key_line("1 2 + .")
|
||||
check("3 ok typed on the keyboard", wait_for(PROMPT, 60, n) and re.search(r"\b3\s+ok", text()) is not None)
|
||||
|
||||
elif m == "usb_kbd":
|
||||
check("[zuse@Hera] ok>", wait_for(PROMPT, 300))
|
||||
time.sleep(2)
|
||||
n = count(PROMPT)
|
||||
key_line("3 4 + .") # the USB keyboard is the only one (-machine i8042=off)
|
||||
check("7 ok typed on the USB keyboard", wait_for(PROMPT, 60, n) and re.search(r"7\s+ok", text()) is not None)
|
||||
|
||||
elif m in ("no_framebuffer", "virtio_kbd"):
|
||||
check("[zuse@Hera] ok>", wait_for(PROMPT, 300))
|
||||
time.sleep(2)
|
||||
n = count(PROMPT)
|
||||
if m == "no_framebuffer":
|
||||
type_line("3 4 + .")
|
||||
else:
|
||||
key_line("3 4 + .")
|
||||
check("7 ok", wait_for(PROMPT, 60, n) and re.search(r"7\s+ok", text()) is not None)
|
||||
|
||||
else:
|
||||
check(f"known mode {m}", False)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user