Repository navigation
native: restart a WSLg clipboard read when focus leaves its helper - #1849
Merged
Merged
Conversation
A Wayland connect failure, or a registry with no seat or no clipboard protocol, left the connection `unsupported` for the life of the service. Every later operation fell back to X11, or returned `unsupported` without a display. Under WSLg the X11 fallback gives a 0-byte text for an image, so a socket that appeared after the first paste never helped. The idle drain now closes an unsupported connection like any other idle one, so the next operation connects again. The retry costs at most one connect per operation: an operation that sees `unsupported` moves to X11 or settles, and the drain closes the connection only when no pending operation still needs Wayland. On a nested compositor whose socket appears after the first read, the next two reads return `image/png` instead of `unsupported`. Issue: 383
WSLg serves its compositor at `/mnt/wslg/runtime-dir/wayland-0`. When `XDG_RUNTIME_DIR` points elsewhere (a systemd user runtime directory, for example), libwayland cannot find the socket, the clipboard falls back to the WSLg X11 bridge, and an image paste gives a 0-byte text. Reporters worked around it with `WAYLAND_DISPLAY=/mnt/wslg/runtime-dir/wayland-0`. On WSL only, a failed connect now tries `/mnt/wslg/runtime-dir/$WAYLAND_DISPLAY` before it reports `unsupported`. Off WSL nothing changes. A connection made this way is not the inherited `WAYLAND_SOCKET`, so the idle drain may close it. The source grows by 13 lines for this branch, which the user asked for. `allow_core_data_device` becomes `is_wsl`, because it now gates both WSL paths. Issue: 383
The setup table did not notice two plausible breaks. Trying the WSLg socket before the default one on WSL passed, and so did forgetting a failed connect so that every later drive connects again. The table now counts connect calls and drives each connection three more times. A failed connect must stay at one attempt (two on WSL) until the host closes the connection. A new WSL row with a working default socket must not touch the WSLg path. Issue: 383
With `XDG_RUNTIME_DIR` unset and a relative `WAYLAND_DISPLAY` (the anomalyco/opencode#57724 environment), libwayland refuses the connect and logs "XDG_RUNTIME_DIR is invalid" to stderr, which is the terminal. Since unsupported results are retried per operation, that line appeared on every paste instead of once per process. The default connect now runs only when libwayland can resolve it: an inherited `WAYLAND_SOCKET`, an absolute `XDG_RUNTIME_DIR`, or an absolute `WAYLAND_DISPLAY`. Otherwise it goes straight to the WSLg socket on WSL, or reports unsupported. Five reads in that environment print 0 lines, down from 5 (1 on the base). The source grows by five lines net for the check and its comment. Issue: 383
The WSLg fallback and the core data device both depend on the host copying `libraries.is_wsl` into the connection, but no test went through that assignment. Dropping it, or setting it to true, passed the native suite while a real socket probe showed the wrong result on and off WSL. A host test now swaps in fake libwayland symbols, drives a connection from the service for both flag values, and checks the flag the connection received. Issue: 383
A Wayland core data-device read needs a focused helper surface. When keyboard focus left that surface while the read waited, for example because another window took focus on WSLg, the read ended as `failed` with "Wayland clipboard protocol failed". The read now destroys the helper and maps a new one, which takes focus again and receives the current selection. It does so at most twice per acquisition, the bound the stale-image retry uses, so a compositor that keeps taking focus still ends the read as `failed`. On WSLg with a second clipboard client taking focus, failed reads went from 3-11 of 48 to 0 of 48. The source grows by 8 lines: the restart, its counter and its comment. The host test drives a core read through replayed compositor events for the first time, so the tests grow by its table. Issue: 385
Losing the bound seat's keyboard fails the connection in the same branch, and both readers of `core_focus_lost` return on a failed connection first, so setting the flag there had no effect. Now only a `leave` for a live helper sets it. Issue: 385
The restart lives in the connection, so a connection test that replays focus losses shows it as well as a host read did: the restarted read takes the fresh offer, and the third loss fails it. That replaces the host test, and the listener functions are private again. A host row now checks that a data-control read takes no helper and stays on Wayland, which no test caught before. Issue: 385
Base automatically changed from
harden/383-wayland-unsupported-cached-forever
to
main
October 29, 2026 10:18
simonklee
enabled auto-merge (squash)
October 29, 2026 10:18
…fails-on-focus-churn
Dicklesworthstone
added a commit
to Dicklesworthstone/opentui
that referenced
this pull request
Oct 29, 2026
Local main 4c316d1 is 52 ahead and 9 behind origin/main. The merge-tree is one clean tree 592461c, with 0 path deletions. 38 files changed, 1416 insertions(+), 638 deletions(-). Incoming: 738067f Release v0.8.0 25ab5fb native: drop the sync envelope for remote terminals that pay for it (anomalyco#1851) 366000f native: settle a running clipboard operation at its deadline (anomalyco#1847) 0718aa9 native: reconnect the X11 clipboard after a connection failure (anomalyco#1844) 4c12eed fix(core): restyle markdown lists on style changes in top-level block mode (anomalyco#1841) 7c34b99 native: restart a WSLg clipboard read when focus leaves its helper (anomalyco#1849) 87562e7 native: retry an unsupported Wayland clipboard connection (anomalyco#1845) de63ba8 core: measure extmarks edits without moving the cursor (anomalyco#1848) 9a4e8cc native: marshal Wayland requests without libwayland 1.20 (anomalyco#1846) Files in the merge result versus local main (38): api/0.8.0.txt bun.lock packages/core/package.json packages/core/src/edit-buffer.test.ts packages/core/src/edit-buffer.ts packages/core/src/index.ts packages/core/src/lib/extmarks-multiwidth.test.ts packages/core/src/lib/extmarks.ts packages/core/src/lib/terminal-capability-detection.test.ts packages/core/src/native-abi.generated.ts packages/core/src/renderables/Markdown.ts packages/core/src/renderables/__tests__/Markdown.test.ts packages/core/src/renderer.ts packages/core/src/zig.ts packages/examples/package.json packages/keymap/package.json packages/native/include/opentui.h packages/native/package.json packages/native/src/clipboard/host.zig packages/native/src/clipboard/linux.zig packages/native/src/clipboard/wayland.zig packages/native/src/clipboard/x11.zig packages/native/src/context-abi.zig packages/native/src/context-editor-abi.zig packages/native/src/edit-buffer.zig packages/native/src/renderer.zig packages/native/src/session.zig packages/native/src/terminal.zig packages/native/src/tests/renderer_test.zig packages/qrcode/package.json packages/react/package.json packages/solid/package.json packages/ssh/package.json packages/three/package.json packages/web/package.json packages/web/src/content/docs/core-concepts/clipboard.mdx packages/web/src/content/docs/core-concepts/renderer.mdx packages/web/src/content/docs/releases/0.8.0.md The OpenAI File Downloader user-agent line stays at AGENTS.md:64. Local commits stay on main. Local master is fast-forwarded only by git push . HEAD:master, which refuses a non-fast-forward. Pushed only to the Dicklesworthstone/opentui fork (main, and master only when fork/master is a strict ancestor). anomalyco/opentui is not pushed. Experiment refs on origin stay unfolded. Co-Authored-By: Claude <[email protected]>
simonklee
added a commit
that referenced
this pull request
Oct 30, 2026
Follow-up to #1845 and #1849 with three small Wayland clipboard fixes from their review. A queued `wl_keyboard.enter` for a destroyed core helper reaches OpenTUI with a nil surface. The leave handler ignored nil, but the enter handler matched it against the missing helper and marked focus as entered. The flag outlived the helper, so the next WSLg read could report ready before its new helper had focus and read an unfocused offer. Enter now ignores nil like leave, and an acquisition asserts that it starts without focus. Two test gaps are closed. The Wayland setup table now covers a relative `XDG_RUNTIME_DIR` and an absolute `WAYLAND_DISPLAY`, which pins the checks that skip libwayland's default connect. The focus-loss restart test now checks every poll, so a restart that settles the read before the new helper has focus fails it. The Wayland setup test also no longer unsets `XDG_RUNTIME_DIR`. Unsetting it corrupted the environment that Zig keeps from startup, so in a Wayland session a failing native test hung `zig build test` with no output; such a failure now reports. Each new row fails under the break it guards; native and core suites pass. Source +0 lines, tests +1.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
On WSLg the clipboard reads through the core Wayland data device, which only sends the selection to a focused surface, so OpenTUI maps a 1×1 helper surface for each read. When keyboard focus left that surface while the read waited, for example because another window took focus, the read failed with "Wayland clipboard protocol failed". The user sees an image paste that does nothing (part of anomalyco/opencode#49895).
The read now maps a new helper, which takes focus again and gets the current selection, and reads that offer. It does this at most twice per acquisition, the same bound as the stale-image retry, so a compositor that keeps taking focus still ends the read as
failed. A seat that loses its keyboard still fails the connection; the next read reconnects.Stacked on #1845, which retries an unsupported Wayland connection and tries the WSLg socket.
A native test replays focus losses on the connection and checks the fresh offer and the bound. On WSLg, with a second clipboard client taking focus during reads, failed reads went from 3–11 of 48 to 0 of 48. Source +7 lines, tests +51.