summaryrefslogtreecommitdiffstats
path: root/scripts/test-mouse.sh
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-08-01 19:34:40 +0200
committersalmonik <salmonik@tuta.io>2026-08-01 19:34:40 +0200
commit9b9758e5ee2564b04685eadf2b9ce23dd1060b86 (patch)
tree57fce9e715510ec70df70dc399b4440f3480fd77 /scripts/test-mouse.sh
parentfix pi-lens warnings: unwraps, shell SC2046, dup code (fmtbuf/scripts/hexdump... (diff)
downloadrs-dos-9b9758e5ee2564b04685eadf2b9ce23dd1060b86.tar.gz
rs-dos-9b9758e5ee2564b04685eadf2b9ce23dd1060b86.zip
rsnix v0.4.0: GUI input foundation — PS/2 mouse + scheduler message queues
Mouse (IRQ12, stream mode): ByteRing generalized for both PS/2 devices, ISR routes data-port bytes by the controller's aux bit; mouse_init with bounded-ACK handshake (boots without a mouse); packet decoder with bit-3 resync, screen-relative deltas, buttons. Exposed as /dev/mouse, the `mouse` stream command, and `make test-mouse`. Message queues (ADR 0003's GUI-era primitive): every task gets a static 16-slot TaskMsg ring; send/recv/recv_blocking with direct wake + tick scan safety net for the lost-wakeup race; `msgrecv`/`send` commands, `mouse <id>` routes real IRQ12 events into a task's queue, jobs shows q=. `make test-msg` + `make test-disk` regression pass.
Diffstat (limited to 'scripts/test-mouse.sh')
-rwxr-xr-xscripts/test-mouse.sh130
1 files changed, 130 insertions, 0 deletions
diff --git a/scripts/test-mouse.sh b/scripts/test-mouse.sh
new file mode 100755
index 0000000..ed4df44
--- /dev/null
+++ b/scripts/test-mouse.sh
@@ -0,0 +1,130 @@
1#!/usr/bin/env bash
2#
3# scripts/test-mouse.sh — automated PS/2 mouse round trip.
4#
5# Builds the kernel, boots QEMU headless, injects mouse movement and
6# button presses through the monitor's `mouse_move` / `mouse_button`
7# commands, and verifies the guest decoded them via IRQ12 → ring →
8# packet decoder. Covers both surfaces:
9# - `cat /dev/mouse` (VFS snapshot of pending events)
10# - `mouse` (live streaming command, stopped by ^C)
11#
12# Usage:
13# ./scripts/test-mouse.sh
14#
15# Environment:
16# OVMF firmware (default: /usr/share/edk2/x64/OVMF.4m.fd)
17# QEMU qemu binary (default: qemu-system-x86_64)
18#
19set -euo pipefail
20
21SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
22PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
23cd "$PROJECT_DIR"
24
25OVMF="${OVMF:-/usr/share/edk2/x64/OVMF.4m.fd}"
26QEMU="${QEMU:-qemu-system-x86_64}"
27ESP_DIR="$PROJECT_DIR/esp"
28QPID=/tmp/rsdos-mouse-qpid
29SOCK=/tmp/rsdos-mouse-qmon.sock
30LOG=/tmp/rsdos-mouse-serial.log
31QLOG=/tmp/rsdos-mouse-qemu.log
32
33# Never leave a QEMU behind.
34trap 'kill "$(cat "$QPID" 2>/dev/null)" 2>/dev/null || true' EXIT
35
36fail() {
37 echo "FAIL: $1"
38 [ -f "$LOG" ] && tail -20 "$LOG"
39 exit 1
40}
41
42# wait_log FILE PATTERN TIMEOUT-SECONDS
43wait_log() {
44 local i=0
45 while [ "$i" -lt $((${3:-60} * 10)) ]; do
46 grep -q "$2" "$1" 2>/dev/null && return 0
47 sleep 0.1
48 i=$((i + 1))
49 done
50 return 1
51}
52
53# wait_qemu_exit TIMEOUT-SECONDS
54wait_qemu_exit() {
55 local i=0
56 while [ "$i" -lt $((${1:-30} * 10)) ]; do
57 kill -0 "$(cat "$QPID" 2>/dev/null)" 2>/dev/null || return 0
58 sleep 0.1
59 i=$((i + 1))
60 done
61 return 1
62}
63
64# qemu_hmp — send one raw HMP command to the monitor socket.
65qemu_hmp() {
66 python3 - "$SOCK" "$1" <<'EOF'
67import socket, sys, time
68s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
69s.connect(sys.argv[1])
70s.sendall((sys.argv[2] + "\n").encode())
71time.sleep(0.4)
72EOF
73}
74
75echo "==> 1/5 build kernel (debug)"
76cargo build --package rsdos --target x86_64-unknown-uefi
77
78echo "==> 2/5 deploy to ESP"
79mkdir -p "$ESP_DIR/EFI/BOOT"
80cp target/x86_64-unknown-uefi/debug/rsdos.efi "$ESP_DIR/EFI/BOOT/BOOTX64.EFI"
81
82echo "==> 3/5 boot QEMU (headless)"
83kill "$(cat "$QPID" 2>/dev/null)" 2>/dev/null || true
84sleep 1
85rm -f "$SOCK" "$LOG"
86"$QEMU" -bios "$OVMF" \
87 -drive file=fat:rw:"$ESP_DIR",format=raw \
88 -serial file:"$LOG" \
89 -monitor unix:"$SOCK",server,nowait \
90 -m 256M -machine q35 -cpu qemu64 -no-reboot -display none \
91 >"$QLOG" 2>&1 &
92echo $! >"$QPID"
93
94wait_log "$LOG" "boot: entering shell" 90 || fail "kernel never reached the shell"
95grep -aq "ps2: mouse ready" "$LOG" || fail "mouse driver did not come up at boot"
96
97# Seed a move *before* any consumer runs, so `cat /dev/mouse` has a
98# queued event to drain (exercises the VFS snapshot path).
99qemu_hmp "mouse_move 8 4"
100
101echo "==> 4/5 drive the shell (cat /dev/mouse, mouse stream, poweroff)"
102python3 "$SCRIPT_DIR/sendkeys.py" --sock "$SOCK" \
103 "cat /dev/mouse" \
104 "mouse"
105
106# While the `mouse` stream is live: a move, a left-press, a move with the
107# button held, then release — the button state must ride along on the
108# movement packet.
109qemu_hmp "mouse_move 20 7"
110qemu_hmp "mouse_button 1"
111qemu_hmp "mouse_move -5 -3"
112qemu_hmp "mouse_button 0"
113qemu_hmp "sendkey ctrl-c"
114
115python3 "$SCRIPT_DIR/sendkeys.py" --sock "$SOCK" \
116 "poweroff"
117
118echo "==> 5/5 wait for shutdown + verify"
119wait_qemu_exit 30 || fail "QEMU did not exit after poweroff"
120
121grep -aq "ps2: mouse ready (IRQ12, stream mode)" "$LOG" || fail "no mouse-ready boot line"
122grep -aq "dx=+8 dy=+4 buttons=---" "$LOG" || fail "/dev/mouse did not drain the queued move (dx=+8 dy=+4)"
123grep -aq "dx=+20 dy=+7 buttons=---" "$LOG" || fail "mouse stream missed mouse_move 20 7"
124grep -aq "dx=+0 dy=+0 buttons=L--" "$LOG" || fail "left-button press did not decode"
125grep -aq "dx=-5 dy=-3 buttons=L--" "$LOG" || fail "held-button movement wrong (dx=-5 dy=-3 L)"
126grep -aq "mouse: stopped" "$LOG" || fail "^C did not stop the mouse stream"
127
128echo ""
129echo "PASS — PS/2 mouse round trip OK (IRQ12, /dev/mouse, live stream, buttons)"
130echo " events: 8,4 · 20,7 · L-press · -5,-3 held · release"