schcom: prove two clients and stacking order

The compositor already walked a surface table, but nothing had ever run
two clients at once, so "it composites" rested on a single rectangle.

schcom-test now starts two overlapping clients and checks the overlap
region from the host: the later surface has to win there. A compositor
that cannot order two surfaces cannot host a window manager, and that is
the property SchWM will be built on.

Two bugs in the test itself, both of which would have read as compositor
faults:

  - it waited for the first "committed" line and screenshotted then, so
    the second client had not drawn yet. It waits for both now.
  - one probe point sat at 400,300 -- dead centre of an 800x600 screen,
    which is exactly where the pointer is drawn. Moved clear of it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Nexoniarz 2026-08-10 04:19:09 +02:00
commit 181c420f21
2 changed files with 31 additions and 7 deletions

View file

@ -16,7 +16,9 @@ and Schcom exposes a protocol for it rather than implementing policy itself.
commits; the result is composited and presented. Verified end to end by commits; the result is composited and presented. Verified end to end by
`make schcom-test`, which boots SchOS, runs the compositor and a client, then `make schcom-test`, which boots SchOS, runs the compositor and a client, then
screendumps the framebuffer and asserts the pixels **from the host** — the screendumps the framebuffer and asserts the pixels **from the host** — the
compositor's own report that it composited something proves nothing. compositor's own report that it composited something proves nothing. It runs
two overlapping clients and checks that the newer one wins in the overlap: a
compositor that cannot order two surfaces cannot host a window manager.
What exists: What exists:
@ -26,6 +28,7 @@ What exists:
- surface / attach / **commit** semantics: nothing is shown mid-draw - surface / attach / **commit** semantics: nothing is shown mid-draw
- input from `/dev/input/eventN`, routed to the topmost surface - input from `/dev/input/eventN`, routed to the topmost surface
- clients that die are cleaned up, including their shm segments - clients that die are cleaned up, including their shm segments
- **multiple clients**, composited bottom-to-top in creation order
What does not exist yet: damage tracking beyond "repaint everything", multiple What does not exist yet: damage tracking beyond "repaint everything", multiple
outputs, subsurfaces, and any focus model beyond "topmost". Those are additions outputs, subsurfaces, and any focus model beyond "topmost". Those are additions

View file

@ -25,8 +25,9 @@ EOF
( (
sleep 26 sleep 26
printf 'schcom --frames 8 &\n'; sleep 6 printf 'schcom --frames 24 &\n'; sleep 6
printf 'schcom-demo --colour 0x00C04020 --at 100 80 --size 240 160 --wait 25\n'; sleep 14 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' printf '\n'
sleep 40 sleep 40
) | timeout -k 5 120 qemu-system-x86_64 \ ) | timeout -k 5 120 qemu-system-x86_64 \
@ -38,9 +39,11 @@ EOF
>/dev/null 2>&1 & >/dev/null 2>&1 &
QPID=$! QPID=$!
# Wait until the client says it committed, then photograph. # Wait until *both* clients have committed, then photograph. Breaking on the
for i in $(seq 1 60); do # first one screenshots a desktop the second client has not reached yet, which
grep -aq 'committed' "$LOG" 2>/dev/null && break # 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 sleep 1
done done
sleep 2 sleep 2
@ -106,12 +109,30 @@ if near(desktop, (0x20, 0x30, 0x40)):
else: else:
print(f" FAIL: 20,20 is {desktop}, expected (32, 48, 64)"); fails += 1 print(f" FAIL: 20,20 is {desktop}, expected (32, 48, 64)"); fails += 1
border = at(100, 80) # top-left of the surface: white border border = at(100, 80) # top-left of the first surface
if near(border, (0xFF, 0xFF, 0xFF)): if near(border, (0xFF, 0xFF, 0xFF)):
print(f" [ok] border pixel at 100,80 is {border} -- surface landed exactly") print(f" [ok] border pixel at 100,80 is {border} -- surface landed exactly")
else: else:
print(f" FAIL: 100,80 is {border}, expected white border"); fails += 1 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") print("schcom end-to-end: PASSED" if not fails else f"schcom end-to-end: {fails} FAILED")
sys.exit(1 if fails else 0) sys.exit(1 if fails else 0)
EOF EOF