Fix libei mousemove double-scale at fractional DPI - #55
Conversation
KWin advertises its EIS region in logical pixels with a scale field (physical-per-logical, 1.25 for 125% display scale), and libei takes motion_absolute / motion_relative coords in those logical pixels. wdotool's user-facing coordinate convention is device pixels (matching xdotool, which inherits from X11). So on hron's 4K @ 125% KDE box, wdotool was sending 1920 thinking "device pixel halfway", KWin was interpreting 1920 as a logical-pixel position within its 3072x1728 region, and the cursor was landing at physical (2400, 0) instead of (1920, 0). Divide incoming coords by the region's scale before calling motion_absolute / motion_relative. Multi-region setups pick the region whose device-pixel extent contains the target point and use its scale (uniform-scale dual-monitor works; mixed-scale multi-monitor falls back to the first region's scale and is best-effort). Seven new unit tests cover the math. End-to-end verification still needs hron's KDE box; the logic falls out of the libei spec cleanly. Also logs the announced regions at debug level on PointerAbsolute device add so the next debugger has visibility. Closes: #53 Refs: WDOTOOL-14
|
@hron, fix candidate for the fractional-scale row. If you have a minute on the 4K @ 125% box: Cursor should land at physical (1920, 0) instead of the (2400, 0) you were seeing. Same divide-by-scale fix applies to relative motion. I can't reproduce locally (Hyprland, no fractional scale), so a real-hardware confirmation before tagging v0.6.0 would close the loop. |
|
Heads up: I'm rebuilding wflow now against current wdotool-core, which pulls in the v0.5.2 libei flush + two-stage seat.bind fix and the v0.5.3 KWin scripting fix. That should fix your wflow 5-step workflow row from #50 (which failed on the pre-v0.5.2 libei timeout signature). I'll ping again with the wflow release version when it's out, so you can re-test both rows in one pass. |
|
it's out. update wflow and it should work fine now |
|
@cushycush, it works great! |
The bug
@hron reported during the KDE Plasma 6 verification walk in #50: on a 4K display at 125% scale,
wdotool mousemove 1920 0was landing the cursor at physical (2400, 0) instead of (1920, 0). Filed as #53.Root cause
KWin advertises its EIS region in logical pixels with a
scalefield that's physical-per-logical (1.25 for 125% scale, 1.75 for 175%), and the libei spec saysmotion_absoluteandmotion_relativecoordinates are in those logical pixels. wdotool's user-facing coordinate convention is device pixels, matching xdotool (which inherits from X11, where there's no compositor scale). So when hron asked for the cursor at device-pixel 1920, wdotool sent 1920 raw, KWin interpreted it as a logical-pixel position within its 3072x1728 logical region, and the cursor landed at physical (2400, 0) because KWin re-applied the scale.The fix
In
wdotool-core/src/backend/libei.rs, divide incoming coordinates by the active region's scale before sending. For absolute motion,convert_absolute_to_logicalpicks the region whose device-pixel extent contains the target point and divides by its scale, which lands the cursor on the right monitor in mixed-scale setups and does the obvious thing for uniform-scale ones. Relative motion has no positional context to pick a region from, soconvert_relative_to_logicaldivides by the first region's scale; uniform-scale multi-monitor still works, and mixed-scale stays best-effort. While I was in there I also dropped a debug log of the announced regions on PointerAbsolute device add, so the next person debugging coord issues can see what the compositor advertised. The--backend wlrootspath is untouched (wlroots'zwlr_virtual_pointer_v1uses per-output mode dimensions directly, no scale to undo), and xdotool's device-pixel convention is preserved end-to-end; the fix is internal to libei.Verification
Seven new unit tests in
libei::testscover the math: identity for no-regions and unit scale, the 125% case from hron's report, the multi-region containment pick, the out-of-bounds fallback, and both flavors ofmotion_relative. End-to-end verification still needs KDE Plasma 6 hardware (I'm on Hyprland), but the logic falls out of the libei spec cleanly and matches what KWin docs and reis'sRegionstruct say about logical-pixel coordinates. If @hron is able to spin this up on his box and check thatmousemove 1920 0lands at physical (1920, 0) instead of (2400, 0), that would close the loop.Closes #53.