mirror of
https://github.com/RhapsodiOS/RhapsodiOS.git
synced 2026-11-03 22:01:40 -07:00
[GH-ISSUE #25] i386: PS/2 mouse is erratic under QEMU (backwards jumps, corner snaps, phantom clicks) #20
Labels
No labels
bug
documentation
enhancement
enhancement
good first issue
help wanted
help wanted
pull-request
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
github-starred/RhapsodiOS#20
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @raynorpat on GitHub (Sep 25, 2026).
Original GitHub issue: https://github.com/RhapsodiOS/RhapsodiOS/issues/25
Summary
On i386 under QEMU, the PS/2 mouse is erratic. Fast moves jump the cursor backwards or to the far corner of the screen, and it clicks when no button was pressed. It happens with our reconstructed
drvPS2Mouseand with Apple's original driver, so the cause is shared code, not only the reconstruction.Environment
-M pc -cpu pentium -accel tcg, emulated i8042 PS/2 mousevm/work/eide-ne2k.imgwith our kernel and the EIDE and NE2K driversSymptoms
Measurements
These came from a headless QEMU copy driven over QMP with
input-send-event. Screenshots located the cursor, and PS/2 tracing was on (-trace pckbd_kbd_read_data -trace ps2_mouse_send_packet).18 81 7Fand the guest read exactly those 36 bytes, in order. By this point the PS/2 layer was losing nothing, so the wrong-way jump happens after decoding.Causes found
Several separate defects add up to the symptoms above. All of them are inherited from Apple's code.
drvPS2Mouse: the running delta total wraps. Packets that arrive while an event is still being dispatched are added intosummedEventas raw bytes.PCPointerEvent'sdx/dyare 8-bit signed bitfields, so a total past +127 wraps and reverses direction. Three packets of +60 arrive as -76. QEMU sends packets back to back, so they pile up on almost every fast move. Apple'skernel-7/bsd/dev/i386/PS2Mouse.mdoes the same adds.drvPS2Mouse: no packet framing check. Byte 0 of every packet has bit 3 set, and the driver never checks it. It only resyncs after 250 ms of silence, so one lost byte misframes every later packet. Movement bytes then land in the button byte, which gives the phantom clicks.drvPS2Keyboard(PS2Controller): keyboard paths read mouse bytes.keyboardDataPresent(),reallyGetKeyboardData()and the IRQ 1 handler test only output-buffer-full, never the 8042's auxiliary-data bit (0x20). The keyboard I/O-thread drain loop,NewStealKeyboardEvent()andsetLEDs:can each consume a mouse byte as a scancode. That byte is lost to the mouse driver and the packets go out of step.EventInput.m: the pointer location wraps.-relativePointerEvent:adds each accelerated delta intopointerLoc, which is{ short x, y; }. When the WindowServer holds the cursor lock,_setCursorPosition:puts off clipping, and later deltas keep adding intopointerLocunclipped. A burst of heavily accelerated deltas passes ±32767 and wraps, so the cursor ends up on the far side or mid-screen on an edge. This explains the corner snaps in the clean trace.EventSrcPCPointer: bursts get maximum acceleration. Acceleration is computed from the time between events. Back-to-back packets have a near-zero interval, so each one gets the top scaling factor. This is the lurching, and it is not a decoding bug.Real hardware rarely triggers 1, 4 or 5, because a physical mouse reports at about 100 Hz with small deltas. QEMU splits large host motion into back-to-back packets of up to ±127, and TCG slows the guest's I/O thread and WindowServer, so all three fire routinely.
@raynorpat commented on GitHub (Sep 25, 2026):
Proposed fix
Branch
claude/ps2-mouse-qemu-erratic-35b897. Each fix is a labelled divergence from Apple's code, and the reconstruction fixes are recorded in the drivers'reconstruction/divergences.md.1.
drvPS2Mouse: saturate summed deltas (8ec72be4a)A new
addDelta()adds two delta bytes assigned charand clamps the result to ±127. All six adds inPS2MouseIntHandleruse it, so a summed burst stops at the cap instead of reversing. Very fast motion can now fall short of the full distance, but it never goes the wrong way.2.
drvPS2Mouse: drop bytes that can't start a packet (40c0c767c)When the handler expects byte 0 of a packet and the byte doesn't have bit 3 set, it drops it. A misframed stream then realigns within a packet or two instead of waiting for 250 ms of silence. Linux's psmouse makes the same check. It can't catch every slip, because a data byte can have bit 3 set too, which is why fix 3 matters.
3.
drvPS2Keyboard: leave mouse bytes for the mouse (a4640a130)PS2Controller.mgets a newPS2_STATUS_AUX_DATA(0x20):keyboardDataPresent()reports hardware data only when bit 0 is set and bit 5 is clear.reallyGetKeyboardData()reads and discards a mouse byte while it waits. Its callers hold the controller lock at spl 6, so the mouse handler can't take the byte, and waiting would hang. Fix 2 resyncs the mouse afterwards.4. driverkit: saturate
pointerLoc(4e58aa4c1)-relativePointerEvent:inlibDriver/Kernel/EventInput.mnow adds inintand clamps each axis to theshortrange before storing intopointerLoc. A deferred burst then parks at the extreme, and_setCursorPosition:pins it to the screen edge in the right direction. This needs a kernel rebuild, since libDriver is linked intomach_kernel.5. Burst acceleration: open, pending testing
Not changed yet. Options, if it still feels bad once 1–4 are in:
EventSrcPCPointer's acceleration a minimum time between events (for example about 8 ms, one report at 125 Hz), so back-to-back packets aren't treated as extreme velocity.The first is simpler and also covers any other pointer driver.
Status
drvPS2KeyboardanddrvPS2Mousebuild on the i386 guest, and both register at boot oneide-ne2k.img.@raynorpat commented on GitHub (Sep 25, 2026):
Update: remaining fixes, and testing
Testing the first four fixes under QEMU turned up two more problems, plus a build-profile gap.
5. Burst acceleration: kernel
EventSrcPCPointer(fee0ad761)Adds a minimum time between events for pointer acceleration:
MIN_EVENT_DELTA, 153 scaled ticks, about 10 ms (one report at the PS/2 default of 100 Hz). Packets that queue up and arrive back to back no longer each get the top scaling factor. Real hardware at 100 Hz or slower is unaffected.6.
drvPS2Mouse: resync only on a forward time gap (1a80dece9)After fixes 1–5, some snapping remained. A QEMU trace of 602 packets showed the guest reading every byte exactly once, in order, so nothing was lost at the 8042. A diagnostic build of the driver then logged eight mid-packet resyncs over one set of sweeps:
IOGetTimestamp()stepped backwards between two bytes of one packet. The unsignedns_time_tdifference wrapped to an enormous value and tripped the 250 ms timeout.Each resync restarted framing on a movement byte. That byte could pass the bit-3 check (0x08 has bit 3 set), so horizontal motion came out vertical. The timeout now fires only when
newStamp > lastTimeStampand the forward gap is over 250 ms.The backwards clock itself (
clock_get_counter(System)on i386) is a separate kernel bug and is being handled separately.7. rbuild toolchain profiles:
/usr/local/binon the path (87cd2d6aa)Kernel builds on the i386 QEMU build guest failed with
relpath: not found.relpathcomes frombootstrap-cmdsat/usr/local/bininside the build chroot, but the profiles'path=didn't include that directory. All three profiles now do.Testing
The test image is
eide-ne2k.imgwith the rebuilt kernel (driverkit fix 4 and kernel fix 5) and both rebuilt PS/2 drivers. Mouse input was driven over QMP at about 125 Hz in slow, medium and fast sweeps in all four directions, and the cursor was located from screendumps.@raynorpat commented on GitHub (Sep 25, 2026):
Fixed on master in
04a699b5a(merge of the PS/2 mouse fixes above).