Skip to content

Preserve cross-edge cursor position when switching machines - #494

Open
salvarecuero wants to merge 2 commits into
feschber:mainfrom
salvarecuero:main
Open

Preserve cross-edge cursor position when switching machines#494
salvarecuero wants to merge 2 commits into
feschber:mainfrom
salvarecuero:main

Conversation

@salvarecuero

Copy link
Copy Markdown

Addresses #230.

When moving the cursor from one machine to another, Lan Mouse currently only tells the receiving peer which edge was crossed.

It does not tell it where along that edge the cursor crossed, so the receiver can keep its previous cursor position instead. For example, crossing the right edge near the top of one screen may make the cursor appear at a completely different height on the other machine.

This PR makes the cursor enter the destination at the corresponding position along the edge.

How it works

The source sends a normalized position (Option<f32>, 0.0..=1.0) along the crossed edge:

  • left/right crossings preserve the vertical position
  • top/bottom crossings preserve the horizontal position

For a single monitor this is straightforward.

Multi-monitor layouts need a little more care. Using the percentage of the whole virtual desktop does not work well when monitors have different sizes, are stacked or offset, or have gaps between them.

Instead, Lan Mouse builds the parts of each desktop edge that are actually exposed to the outside and maps the cursor position across those edges. Internal borders between monitors and empty gaps are not counted.

This keeps the mapping based on the physical edge the cursor can actually cross.

Protocol compatibility

The new Capabilities event lets peers advertise support for positioned transitions.

When both peers support it, the sender uses EnterWithPosition, which includes the edge, the optional cross-axis position, and transition identifiers: epoch (set once at startup, so the receiver can tell transitions from a previous process lifetime apart) plus serial (per-transition).

When the other peer does not support it, Lan Mouse keeps using the existing Enter event, so mixed old/new setups keep the previous behavior.

The existing protocol event IDs are unchanged; the new event types are appended after them. Unknown events are already ignored by the receive loop instead of closing the connection.

EnterWithPosition is also retried while waiting for its ACK. Its epoch and serial let the receiver recognize retries and stale transitions, so the same handoff is not applied multiple times.

Backend support

Capture backends that currently provide the crossing position:

  • libei (InputCapturePortal)
  • layer-shell
  • Windows

macOS and dummy capture currently send no position (None); macOS only for lack of a Mac to test against.

On the receiving side, cursor positioning is implemented for:

  • Windows
  • libei
  • wlroots

Other emulation backends (X11, macOS, xdg-desktop-portal) keep their existing behavior.

macOS cursor positioning is left as a follow-up: the CoreGraphics pieces (display bounds, warp) are all available in the existing backend, but I have no Mac available to test against.

Testing

The shared screen geometry is tested with:

  • single and multi-monitor layouts
  • different monitor sizes
  • stacked and stepped layouts
  • gaps and internal monitor edges
  • negative coordinates
  • normalization/denormalization round trips

The edge calculations are also checked against a separate grid-based model rather than only testing the implementation against itself. Generated coverage exercises 1,296 two-monitor layouts.

Protocol tests cover positioned transitions both with and without a cursor position, plus capability serialization.

Local verification on Linux:

  • cargo fmt --check
  • cargo test -p input-event — 25/25 passed
  • cargo test --workspace
  • cargo clippy --workspace --all-targets -- -D warnings

Runtime test on a real two-machine setup (Linux host on the left, Windows 11 client on the right): crossing at various heights lands at the corresponding height on the peer.

Note: the Windows build used for testing was compiled with cargo build --no-default-features (no GTK frontend), which is sufficient for daemon mode.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant