#!/usr/bin/env bash # Boot the normal (non-headless) kernel with no window, "type" some keys into # it through the QEMU monitor's `sendkey` command, then dump the VGA screen to # a PNG. Doubles as an automated keyboard-interrupt test: the typed text is # echoed on the serial port too, so we can grep for it. # # Usage: ./scripts/screenshot.sh [out.png] [text-to-type] set -euo pipefail cd "$(dirname "$0")/.." cargo build # Force bootimage to repack the disk image (see scripts/runner.sh for why). touch target/x86_64-rust_kernel/debug/rust_kernel cargo bootimage IMG=target/x86_64-rust_kernel/debug/bootimage-rust_kernel.bin OUT="${1:-$PWD/boot.png}" TEXT="${2:-Hello, kernel!}" SOCK=$(mktemp -u /tmp/qemu-mon.XXXXXX) SERIAL_LOG=$(mktemp /tmp/serial.XXXXXX) qemu-system-x86_64 -drive format=raw,file="$IMG" \ -serial file:"$SERIAL_LOG" -display none -monitor unix:"$SOCK",server,nowait & QPID=$! sleep 3 # give the BIOS + bootloader + kernel time to boot # Talk to the QEMU monitor over its UNIX socket (python avoids needing socat). python3 - "$SOCK" "$OUT" "$TEXT" <<'PY' import socket, sys, time sock, out, text = sys.argv[1:] # QEMU key names for the characters we might type. names = {' ': 'spc', ',': 'comma', '.': 'dot', '!': 'shift-1', '?': 'shift-slash', '-': 'minus', '\n': 'ret'} def key(c): if c in names: return names[c] if c.isupper(): return 'shift-' + c.lower() return c s = socket.socket(socket.AF_UNIX); s.connect(sock) for c in text: s.sendall(f"sendkey {key(c)}\n".encode()); time.sleep(0.1) time.sleep(0.5) s.sendall(f"screendump {out} -f png\n".encode()); time.sleep(1) s.sendall(b"quit\n"); time.sleep(0.5) PY wait $QPID || true echo "---- serial output ----" cat "$SERIAL_LOG"; echo echo "-----------------------" if grep -qF -- "$TEXT" "$SERIAL_LOG"; then echo "[screenshot] keyboard echo OK: '$TEXT'" else echo "[screenshot] keyboard echo NOT found" fi rm -f "$SERIAL_LOG" echo "[screenshot] saved $OUT"