Bug from Grok round-1 #5. spawn_with_sock returned Ok(_) and dropped
the LiveOverlayHandle, so the JoinHandle was never joined or signalled.
The thread detached; OverlayHub could only clear its slot map, never
stop the actual Wayland thread. With env-gated rendering (Bug #8), the
operator's overlays would accumulate as ghost threads.
Changes:
- wayland_layer: LiveOverlayHandle gains a stop: Arc<AtomicBool>.
spawn() allocates it, threads a copy into run(), stores a copy on the
returned handle.
- wayland_layer: run() polls stop in addition to state.exited; flipping
the bit causes the next roundtrip to exit instead of waiting on the
compositor's Closed event.
- overlay: OverlayHandle gains stop: Option<Arc<AtomicBool>> and a
kill() method that flips the bit.
- overlay: spawn_with_sock now puts the same stop Arc on the returned
OverlayHandle (was previously throwing the live handle away).
- overlay: OverlayHub.sync() and kill_all() call kill() on every
removed handle, so slot changes actually tear the threads down.
cargo test 96+/0; clippy clean.
- set_margin (wayland_layer.rs:234): correct arg order to
(top, right, bottom, left). The previous code passed rect.x as
the right margin, a no-op under TOP+LEFT anchoring, so the
overlay's x offset was silently dropped. Comment updated to
document the protocol's true shape.
- click handler (wayland_layer.rs:466): drop 'state.exited = true'
after send_swap. Flipping exited destroyed the badge but left
OverlayHub.by_slot still holding the slot, so the hub refused to
respawn it. The compositor's zwlr_layer_surface::Closed event
is the only path that drives thread teardown — the click now
just fires the swap IPC and returns.
- CHANGELOG entries.
cargo test 96+/0; clippy clean.