Skip to content

Cross-window drops resolve by the drag's source window - #19

Merged
kingb merged 1 commit into
mainfrom
fix/drag-release-routing
Sep 25, 2026
Merged

kingb merged 1 commit into
mainfrom
fix/drag-release-routing

Conversation

@kingb

@kingb kingb commented Sep 24, 2026

Copy link
Copy Markdown
Owner

Fixes a cross-window drag bug reported on Ubuntu 24.04: dropping a wisp-carried surface onto another window could make the pane disappear, with its shell still running invisibly.

Fixes #18

Mechanism. The mouse-release handler resolved a drag against whichever window the OS delivered the release event to, assuming that is always the drag's source window. Wayland provides no implicit pointer grab during such a drag, so the release can be delivered to the window under the cursor - the drop target - instead. The drag then ended half-resolved, and the source window's hidden-while-carried bookkeeping could be left set, leaving the carried surface excluded from layout indefinitely.

Fix, two layers:

  1. Routing: a release while a drag is live is always resolved against the drag's recorded source window, regardless of which window received the event; when the release arrived at a different window, the drop target is recomputed from that window's cursor position through the same hover logic a correctly-delivered release uses, so drop semantics (new tab vs split vs miss) are unchanged.
  2. Backstop invariant: every drag-end path (drop, cancel, rejected move, misrouted release) now sweeps all windows and clears any carried-surface exclusion that no longer corresponds to a live drag - so this class of "alive but invisible" outcome is structurally prevented even under pointer-delivery behaviors not modeled here.

Testing. 8 new unit tests on the routing decision and the exclusion-clearing predicate; full workspace suite green; the X11 headless two-window reproduction (sole-tab window carried onto a single-pane window) re-run and passing: source window closes, target gains the tab, both shell sessions preserved. The Wayland misdelivery itself cannot be synthesized headlessly - verification there is by the traced mechanism plus the backstop invariant; field confirmation on the issue is welcome.

🤖 Generated with Claude Code

MouseInput::Released trusted whichever window the OS delivered the
release to as a live drag's owner, even though the drag's true source
is recorded separately. On Wayland, winit has no implicit pointer
grab, so a cross-window carry's release can land on the target window
instead of the source that tore the surface off. Resolving the drop
against the wrong WindowState skipped the source's carried-exclusion
cleanup, leaving it hidden with its OS window never shown again.

Route every release through the drag's recorded source window instead
of the event's window, recomputing the drop hover fresh against
whichever window actually received the release when the two differ.
Add an invariant backstop that sweeps every window after any drag
resolution (drop, cancel, or misrouted release) and force-clears a
carried exclusion left armed, so a hidden window can never survive
past the drag that hid it, independent of the routing fix itself.

Covered by new unit tests for the pure routing decision and the
backstop's clearing predicate. cargo test --workspace, clippy, and
fmt are clean.

Fixes #18

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@kingb
kingb merged commit 429fcc8 into main Sep 25, 2026
13 checks passed
@kingb
kingb deleted the fix/drag-release-routing branch September 25, 2026 19:48
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.

Linux: dragging a single-pane window's wisp onto another window makes the pane disappear

1 participant