diff --git a/internal/qemu/args.go b/internal/qemu/args.go index 489d008..550b48d 100644 --- a/internal/qemu/args.go +++ b/internal/qemu/args.go @@ -113,6 +113,14 @@ func Args(v *config.VM, graphical bool) []string { a = append(a, "-display", "none", "-vnc", "unix:"+v.VNCPath()) } + // Absolute pointer for the framebuffer above. qemu's default is a relative + // PS/2 mouse; a desktop guest cannot align its cursor to the host cursor + // from relative deltas, so the pointer drifts and clicks land off target. + // usb-tablet reports absolute coordinates, so the guest cursor tracks the + // host cursor in the gtk window and in a VNC client alike. qemu-xhci gives + // it a USB bus, which the default pc machine has none of. + a = append(a, "-device", "qemu-xhci,id=xhci", "-device", "usb-tablet") + // Two exports (core-api.md ยง10.2): the user's dir read-only, stoat's // per-VM scratch writable. // diff --git a/internal/qemu/args_test.go b/internal/qemu/args_test.go index 9140d53..cd298cd 100644 --- a/internal/qemu/args_test.go +++ b/internal/qemu/args_test.go @@ -29,11 +29,16 @@ func TestArgsLive(t *testing.T) { "-virtfs local,path=/home/u/vms,mount_tag=host,security_model=mapped-xattr,readonly=on", "-cdrom /data/isos/alpine.iso", "-drive file=fat:rw:/data/live1/ovl,format=raw,if=virtio", + "-device qemu-xhci,id=xhci", "-device usb-tablet", } { if !strings.Contains(got, want) { t.Errorf("missing %q in:\n%s", want, got) } } + // The tablet must ride the VNC path too, or a VNC client's clicks miss. + if vnc := joined(Args(v, false)); !strings.Contains(vnc, "-device usb-tablet") { + t.Errorf("headless VM lacks usb-tablet, so VNC clicks would miss:\n%s", vnc) + } if strings.Contains(got, "disk.qcow2") { t.Error("live mode must not attach a disk") } diff --git a/internal/recipes/bundled/xfce/install-alpine.sh b/internal/recipes/bundled/xfce/install-alpine.sh index 2eef04c..abfd281 100644 --- a/internal/recipes/bundled/xfce/install-alpine.sh +++ b/internal/recipes/bundled/xfce/install-alpine.sh @@ -12,6 +12,19 @@ setup-apkrepos -c -1 setup-xorg-base apk add xfce4 xfce4-terminal dbus-x11 +# Xorg reads its input devices from udev. Alpine boots mdev by default, which +# never feeds Xorg, so libinput finds no mouse or keyboard and the desktop +# ignores every click. setup-devd switches the device manager to eudev and +# adds udev, udev-trigger, and udev-settle to sysinit for the next boot. +# Ubuntu, Debian, and Arch already run udev under systemd, so only Alpine +# needs this. +setup-devd udev +# Cold-plug the devices that already exist so Xorg finds the tablet in this +# boot, not only after a reboot. The live case below starts X now with a HUP +# to init, before udev-trigger would run at the next sysinit. +udevadm trigger 2>/dev/null || true +udevadm settle 2>/dev/null || true + rc-update add dbus rc-service dbus start || true