Skip to content

Fix: check the system clipboard off the UI thread - #510

Closed
omellado-gh wants to merge 2 commits into
storytold:mainfrom
omellado-gh:main
Closed

omellado-gh wants to merge 2 commits into
storytold:mainfrom
omellado-gh:main

Conversation

@omellado-gh

Copy link
Copy Markdown

What and why

The app checks the system clipboard every 0.25 s to decide whether the Paste menu
items are enabled. On X11, arboard's clipboard read waits up to 4 s for a
SelectionNotify; when the clipboard owner never answers (a hung or foreign app,
a clipboard manager, XWayland), that call ran on the UI thread and froze the whole
frame loop — the window stopped drawing and could not be closed.

Reproduced with the real failure: seed the clipboard and stop its owner
(printf x | xclip -selection clipboard; kill -STOP $(pgrep -x xclip)), then open
a document. The app became unresponsive and a control-channel ui.inspect timed out
while the main thread sat in arboard's wait loop.

This change moves the check off the UI thread: a clipboard_probe worker owns a
second clipboard handle, publishes whether Paste has something to take
(has(PASTE_ORDER)) over a channel, and wakes the UI only when the verdict changes.
logic_frame reads the latest verdict with try_recv and never blocks. Without a
factory (the web, and hosts that don't install one) the check stays in line,
throttled, exactly as before. The factory is installed on Linux only for now;
macOS and Windows keep the in-line check, so their behaviour is unchanged.

How it was tested

  • New tests in crates/ui-egui/src/tests_sysclip_probe.rs (5): a fake clipboard
    whose has() blocks proves the headless UI frame does not wait on it (measured
    elapsed time) and that the check runs on the vectorcraft-clip probe thread; the
    probe's verdict enables the Paste menu; a probe that reports nothing leaves it
    disabled; dropping the app stops the probe; and the in-line path (no factory) still
    works. cargo test -p vectorcraft-ui-egui --lib → 717 passed.
  • End to end with the real X11 hang: with the clipboard owner stopped with SIGSTOP
    and a document open, the app stays responsive (ui.inspect answers in ~0 ms, main
    thread in its normal epoll wait), where before it froze.
  • cargo check -p vectorcraft, cargo fmt --all --check, and clippy on the touched
    crates.

Note: locally, workspace clippy / cargo xtask ci fails on a pre-existing lint in
crates/eps/src/import/data.rs:746 (chunks_exact_to_as_chunks, new in clippy 1.99)
that this PR does not touch; the touched crates are clippy-clean with that lint
allowed.

Checklist

  • cargo xtask ci passes (fmt, clippy, tests, assets, brands, layers, wasm).
  • No panics in shipped code: no unwrap(), expect(), panic!, unreachable!, todo! or
    unimplemented! outside tests, and no unchecked indexing on data from files, parameters or
    messages. Errors are returned as Result and shown to the user
    (why and how).
  • Clean-room: nothing copied from Adobe products or GPL/AGPL code (AGENTS.md).
  • Every new asset has a row in ASSETS.md.

An X11 clipboard owner that never answers `get_text()` blocks the thread
that reads it for up to seconds. The app checks the clipboard every
0.25 s to enable Paste, so a stuck owner froze the whole frame loop (the
window stopped drawing and could not be closed).

These tests pin the regression before the fix: a fake clipboard whose
`has()` blocks on a gate, a probe factory sharing a recorder, and a
headless frame whose elapsed time is measured. They assert the frame
does not wait on the clipboard and that the check runs on the probe
thread, that the probe's verdict reaches `system_paste` and the Paste
menu, that dropping the app stops the probe, and that the in-line check
(no factory: the web, older hosts) still works.

Two fail today: the frame waits on the clipboard (1 s) and the probe's
verdict never arrives, because the probe is not wired yet.
`has()` on an X11 clipboard owner that never answers can block for
seconds; the app called it every 0.25 s on the UI thread to decide
whether Paste is enabled, so a stuck owner froze the frame loop.

`clipboard_probe` owns a second clipboard handle on a worker thread,
publishes its verdict over a channel and wakes the UI only when it
changes; the frame reads the latest verdict without ever blocking.
Without a factory the check stays in line, throttled, exactly as before
(the web shows SVG text only; hosts that don't install one keep the old
path).

The factory is installed on Linux only for now (the environment the bug
reproduces in); other platforms keep the in-line check, so this cannot
change their behaviour.

The five tests added previously now pass.
bflatastic added a commit that referenced this pull request Oct 8, 2026
Linux: check the system clipboard for Paste off the UI thread (#510)
@bflatastic

Copy link
Copy Markdown
Member

Thanks a lot for this, @omellado-gh, and welcome! A great first PR: you tracked the freeze down to arboard's X11 read on the UI thread, reproduced it with a stopped xclip, and wrote tests that catch it first. Because this PR comes from your fork's main branch, we landed it from our own branch, with your commit (squashed into one, authored by you) plus a follow-up on top, instead of force-pushing over your main:

  • the background check only reads the clipboard while Paste depends on it (a document is open and nothing has been copied in the app), and wakes the UI only when the answer changes;
  • Windows, macOS and the web keep their in-line check exactly as before, including its short-circuit;
  • if the worker thread dies, the check moves back to the UI thread, and quitting waits a moment for the worker to drop its clipboard handle, so X11 still hands what you copied to the clipboard manager;
  • we trimmed the tests to three that share the existing clipboard test helpers, and noted the change in ROADMAP.md and docs/development.md.

If you'd like to chat about Linux and the rest of VectorCraft, come join us on Discord: https://discord.gg/artcraft

@bflatastic

Copy link
Copy Markdown
Member

Landed via #534 (your commit, authored by you, plus our review commit). Thanks again!

@bflatastic bflatastic closed this Oct 8, 2026
Haven-King pushed a commit to Haven-King/vectorcraft that referenced this pull request Oct 8, 2026
For users, since 0.6.0:
- Linux: no more startup crash on desktops whose compositor runs on another
  GPU (storytold#502, storytold#551); the window renders on the desktop's GPU and retries
  the next one instead of crashing.
- Windows: rendering through DirectX 12 first, fixing the black flicker on
  Intel GPUs (storytold#545).
- Italian interface (storytold#514).
- macOS: the menus live in the system menu bar.
- Panels, panel groups and the Tools panel undock into floating panels (storytold#495).
- Live Corners on any path's corners: stars, polygons, pen paths (storytold#511).
- Drawing tools snap to Smart Guides as the Selection tool does (storytold#506).
- Pen: Cmd/Ctrl edits the path being drawn, Alt reshapes segments, drags
  out of the first anchor always close (storytold#494, storytold#501); Convert buttons for
  whole paths (storytold#504).
- Right-click a ruler to change the document's units; the Control bar's
  Transform link opens the Transform panel.
- Object > Image Trace > Make traces with the Default preset (storytold#543).
- Edit menu Select All, Cut, Copy and Paste act on a focused field (storytold#516).
- Japanese UI: new type starts top-to-top with em box centre alignment (storytold#432).
- PDF/.ai import keeps type strokes, wrapped lines and layer order (storytold#508);
  EPS import fixes (storytold#498, storytold#505).
- macOS 27: only file URLs are opened (storytold#433); Linux: the Paste menu no
  longer freezes on an unresponsive clipboard owner (storytold#510).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

2 participants