Schcom is gone; the desktop path is Xorg now, Wayland groundwork next
SchOS will not write its own compositor. projects/schcom, its test and its make targets are deleted. roadmap.md section 5 is now the road to Wayland (udev/eudev, a /sys/devices tree and uevent netlink, libinput and libseat, a DRM device via the GPU plan) and 5b is the boot manager rework (Niveus/skeuomorphic UEFI GUI). XLibre, Xorg or a Wayland compositor from Alpine, whichever suits; Xorg for now. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EVRhxi9zpdW2uDUN4FVcao
This commit is contained in:
parent
6e78bb0d5e
commit
ed81f9a07a
21 changed files with 62 additions and 1356 deletions
|
|
@ -1,154 +0,0 @@
|
|||
#!/bin/sh
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#
|
||||
# Run Schcom and a client inside SchOS, then check the actual pixels from the
|
||||
# host with QEMU's screendump. The host has no connection to the compositor, so
|
||||
# it is a real oracle: if the block is the right colour in the right place, the
|
||||
# buffer really was shared, composited and presented.
|
||||
#
|
||||
# One display device on purpose (-vga std, no virtio-gpu): with two, screendump
|
||||
# captures whichever QEMU considers active, which need not be the one /dev/fb0
|
||||
# maps.
|
||||
set -e
|
||||
# Run from anywhere: resolve the repo root from this script's own location
|
||||
# rather than trusting the caller's working directory. Everything below is
|
||||
# relative to it, and running from tools/posix/ used to fail with "missing
|
||||
# build/kernel/kernel -- run make first" on a tree that was perfectly built.
|
||||
script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
||||
repo=$(CDPATH= cd -- "$script_dir/../.." && pwd)
|
||||
cd "$repo"
|
||||
|
||||
BUILD=${BUILD:-build}
|
||||
mkdir -p "$BUILD"
|
||||
|
||||
LOG=$BUILD/schcom-test.log
|
||||
PPM=${1:-$BUILD/schcom.ppm}
|
||||
rm -f "$LOG" "$PPM"
|
||||
|
||||
# The firmware varstore is written by OVMF, so this run gets its own copy
|
||||
# rather than sharing the pristine one every other run starts from.
|
||||
cp -f build/OVMF_VARS.fd build/schcom-OVMF_VARS.fd
|
||||
|
||||
PORT=$(python3 - <<'EOF'
|
||||
import socket
|
||||
s=socket.socket(); s.bind(('127.0.0.1',0)); print(s.getsockname()[1]); s.close()
|
||||
EOF
|
||||
)
|
||||
|
||||
(
|
||||
sleep 26
|
||||
printf 'schcom --frames 24 &\n'; sleep 6
|
||||
printf 'schcom-demo --colour 0x00C04020 --at 100 80 --size 240 160 --wait 40 &\n'; sleep 10
|
||||
printf 'schcom-demo --colour 0x002060C0 --at 260 180 --size 240 160 --wait 30 &\n'; sleep 12
|
||||
printf '\n'
|
||||
sleep 40
|
||||
) | timeout -k 5 120 qemu-system-x86_64 \
|
||||
-machine pc-i440fx-9.2 -cpu qemu64,+rdtscp \
|
||||
-drive if=pflash,format=raw,unit=0,readonly=on,file=build/OVMF_CODE.fd \
|
||||
-drive if=pflash,format=raw,unit=1,file=build/schcom-OVMF_VARS.fd \
|
||||
-drive file=fat:rw:build/esp,index=0,media=disk,format=raw \
|
||||
-drive file=build/schfs.img,format=raw,index=1,media=disk \
|
||||
-m 1G -smp 1 \
|
||||
-display none -vga std \
|
||||
-monitor tcp:127.0.0.1:$PORT,server,nowait \
|
||||
-chardev stdio,id=c0,logfile="$LOG" -serial chardev:c0 \
|
||||
>/dev/null 2>&1 &
|
||||
QPID=$!
|
||||
|
||||
# Wait until *both* clients have committed, then photograph. Breaking on the
|
||||
# first one screenshots a desktop the second client has not reached yet, which
|
||||
# looks exactly like a compositor that cannot handle two clients.
|
||||
for i in $(seq 1 90); do
|
||||
[ "$(grep -ac 'committed' "$LOG" 2>/dev/null)" -ge 2 ] && break
|
||||
sleep 1
|
||||
done
|
||||
sleep 2
|
||||
|
||||
python3 - "$PORT" "$PPM" <<'EOF'
|
||||
import socket, sys, time
|
||||
port, ppm = int(sys.argv[1]), sys.argv[2]
|
||||
s = socket.create_connection(('127.0.0.1', port), timeout=10)
|
||||
time.sleep(0.5)
|
||||
s.sendall(b'screendump ' + ppm.encode() + b'\n')
|
||||
time.sleep(2.0)
|
||||
s.close()
|
||||
EOF
|
||||
|
||||
sleep 1
|
||||
kill -9 $QPID 2>/dev/null || true
|
||||
wait 2>/dev/null || true
|
||||
|
||||
echo "=== guest output ==="
|
||||
sed 's/\x1b\[[0-9;=]*[A-Za-z]//g' "$LOG" | tr -d '\r' | grep -aE 'schcom|surface|committed|listening' | head -12
|
||||
|
||||
echo "=== pixel check (host oracle) ==="
|
||||
python3 - "$PPM" <<'EOF'
|
||||
import sys
|
||||
path = sys.argv[1]
|
||||
try:
|
||||
data = open(path, 'rb').read()
|
||||
except FileNotFoundError:
|
||||
print("FAIL: no screendump produced"); sys.exit(1)
|
||||
|
||||
# Binary PPM: P6, whitespace-separated width height maxval, then RGB triples.
|
||||
def token(buf, i):
|
||||
while buf[i:i+1].isspace(): i += 1
|
||||
j = i
|
||||
while not buf[j:j+1].isspace(): j += 1
|
||||
return buf[i:j], j
|
||||
|
||||
if data[:2] != b'P6':
|
||||
print("FAIL: not a P6 PPM"); sys.exit(1)
|
||||
i = 2
|
||||
w, i = token(data, i); h, i = token(data, i); mx, i = token(data, i)
|
||||
w, h = int(w), int(h)
|
||||
px = data[i+1:]
|
||||
print(f" screendump {w}x{h}")
|
||||
|
||||
def at(x, y):
|
||||
o = (y*w + x)*3
|
||||
return px[o], px[o+1], px[o+2]
|
||||
|
||||
def near(got, want, tol=8):
|
||||
return all(abs(a-b) <= tol for a, b in zip(got, want))
|
||||
|
||||
fails = 0
|
||||
inside = at(150, 120) # middle of the 240x160 block at 100,80
|
||||
if near(inside, (0xC0, 0x40, 0x20)):
|
||||
print(f" [ok] surface pixel at 150,120 is {inside} -- the client's colour")
|
||||
else:
|
||||
print(f" FAIL: 150,120 is {inside}, expected (192, 64, 32)"); fails += 1
|
||||
|
||||
desktop = at(20, 20) # compositor background 0x203040
|
||||
if near(desktop, (0x20, 0x30, 0x40)):
|
||||
print(f" [ok] desktop pixel at 20,20 is {desktop} -- compositor background")
|
||||
else:
|
||||
print(f" FAIL: 20,20 is {desktop}, expected (32, 48, 64)"); fails += 1
|
||||
|
||||
border = at(100, 80) # top-left of the first surface
|
||||
if near(border, (0xFF, 0xFF, 0xFF)):
|
||||
print(f" [ok] border pixel at 100,80 is {border} -- surface landed exactly")
|
||||
else:
|
||||
print(f" FAIL: 100,80 is {border}, expected white border"); fails += 1
|
||||
|
||||
# Second client, created later, so it is above the first where they overlap.
|
||||
second = at(460, 320) # inside surface 2, clear of the pointer
|
||||
if near(second, (0x20, 0x60, 0xC0)):
|
||||
print(f" [ok] second surface at 460,320 is {second} -- two clients composited")
|
||||
else:
|
||||
print(f" FAIL: 460,320 is {second}, expected (32, 96, 192)"); fails += 1
|
||||
|
||||
# 260,180..340,240 is where the two overlap. The later surface must win: if
|
||||
# stacking were ignored the first client's colour would show through here, and
|
||||
# a compositor that cannot order two windows cannot host a window manager.
|
||||
overlap = at(300, 220)
|
||||
if near(overlap, (0x20, 0x60, 0xC0)):
|
||||
print(f" [ok] overlap at 300,220 is {overlap} -- newer surface on top")
|
||||
elif near(overlap, (0xC0, 0x40, 0x20)):
|
||||
print(f" FAIL: overlap shows the older surface -- stacking order ignored"); fails += 1
|
||||
else:
|
||||
print(f" FAIL: overlap at 300,220 is {overlap}, expected the newer surface"); fails += 1
|
||||
|
||||
print("schcom end-to-end: PASSED" if not fails else f"schcom end-to-end: {fails} FAILED")
|
||||
sys.exit(1 if fails else 0)
|
||||
EOF
|
||||
Loading…
Reference in a new issue