name: mjdev-qemu-test
Goal: the deb and the ISO must make the mjdev wayland desktop usable on any machine.
Iterate without rebuilding the ISO each time (a full makeIso is ~10 min). Only rebuild
for a final check. Reflect every working change back into the project.
mjdevc = the wayland compositor (Kotlin/Native, links libwlroots-0.18 + the wayland stack).mjdev-session (session/mjdev-session) = the session launcher: sets env, exec mjdevc --session --shell-cmd /opt/mjdev-desktop/bin/mjdev-desktop, logs to ~/.cache/mjdev-desktop/session.log./opt/mjdev-desktop). It is AWT-based → needs an X display,
which mjdevc provides via its built-in XWayland (shim.c:1351, setenv DISPLAY at 1368-1374).
This is not xorg; "pure wayland" still needs XWayland for this one legacy app.mjdev → mjdev-session. If it crashes, agetty relogs → crash-loop → black screen.libwlroots-0.18.so: cannot open shared object file — the runtime wayland stack mjdevc
links is not installed. The jpackage deb does NOT pull it. FIX: install/depend on
libwlroots-0.18 xwayland libegl1 libgles2 libgbm1 libinput10 libseat1 libxkbcommon0
(libwlroots-0.18 pulls libdrm/libwayland/libdisplay-info/libliftoff/libpixman/... as its deps).Software rendering detected ... WLR_RENDERER_ALLOW_SOFTWARE — in a VM (no GPU) mesa is
llvmpipe and wlroots refuses software GL. FIX: export WLR_RENDERER_ALLOW_SOFTWARE=1 in
session/mjdev-session (already there; ignored on real GPUs). Needs libgl1-mesa-dri.java.awt.HeadlessException: No X11 DISPLAY — only when the shell is run with no compositor
(e.g. a bare serial shell). Under mjdevc with XWayland+DISPLAY it is fine. Not a real bug by itself.Map mjdevc's deps to packages on the (Debian trixie) host:
ldd compositor/build/session-install/mjdevc | awk '/=>/{print $3}' | while read f; do dpkg -S "$(readlink -f "$f")"; done
mjdev-session redirects mjdevc+shell to ~/.cache/mjdev-desktop/session.log (and .log.1 = previous
crash). Without that the tty1 session dies to a black screen with no trace. Keep this.
qmp_drive.pyBoot QEMU headless with a framebuffer + QMP + a USB tablet, then drive it:
ISO=releases/mjdev-desktop-1.0.3.iso
# direct-kernel boot is faster than -boot d; extract once:
xorriso -osirrox on -indev "$ISO" -extract /live/vmlinuz /tmp/iso-test/vmlinuz -extract /live/initrd.img /tmp/iso-test/initrd.img
qemu-system-x86_64 -enable-kvm -cpu host -m 4096 -smp 4 \
-kernel /tmp/iso-test/vmlinuz -initrd /tmp/iso-test/initrd.img -append "boot=live console=ttyS0,115200" \
-cdrom "$ISO" -device virtio-vga -usb -device usb-tablet \
-qmp unix:/tmp/iso-test/qmp.sock,server,nowait -serial file:/tmp/iso-test/vm-serial.log -display none &
python3 .claude/skills/mjdev-qemu-test/qmp_drive.py /tmp/iso-test/qmp.sock # writes /tmp/iso-test/*.png
qmp_drive.py screendumps boot frames, then moves the pointer to the bottom edge (reveals the
bottom bar) and the right edge (reveals the control center) and dumps each — then quits the VM.
PPM→PNG is built in (no deps). Read the PNGs with the Read tool. var=10 ≈ black; higher ≈ content.
The serial getty prompt timing is fragile — wait ~80s, send a wake newline, then login + cat:
{ sleep 80; printf '\n\nmjdev\n'; sleep 5; \
printf 'cat ~/.cache/mjdev-desktop/session.log.1; echo ---; cat ~/.cache/mjdev-desktop/session.log\n'; sleep 8; } \
| timeout 130 qemu-system-x86_64 -enable-kvm -cpu host -m 4096 -smp 4 \
-kernel /tmp/iso-test/vmlinuz -initrd /tmp/iso-test/initrd.img -append "boot=live console=ttyS0,115200" \
-cdrom "$ISO" -nographic -serial mon:stdio 2>&1 | tr -d '\r' | sed 's/\x1b\[[0-9;?]*[a-zA-Z]//g'
Once an ISO with the runtime deps exists, debug session/compositor changes live:
-virtfs local,path=$PWD,mount_tag=host,security_model=none,readonly=on
and in the guest mkdir -p /mnt/host && mount -t 9p -o trans=virtio host /mnt/host.mjdev is in group video → it can get a seatd seat and run the compositor without root.
Copy a freshly-built mjdevc/mjdev-session from /mnt/host/... into ~/bin, point MJDEVC at it,
run it, capture via QMP. No rebuild.passwd -d root in provisioning so serial root login works.apt/dep changes or kernel/initrd changes truly need a rebuild — almost never.make-iso.sh provision (apt-get install ...) and the deb Depends
(deb packaging) so a clean console install pulls them via apt.session/mjdev-session.compositor/native/shim.c / compositor/src/... (do NOT touch the desktop app code).qemu-system, mksquashfs (root, via pkexec), and the gradle daemon.makeIso escalates via pkexec; the script tees to /tmp/mjdev-iso-build.log (pkexec hides stdio).