mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-31 13:17:19 -06:00
Linux clipboard: copy text through the core Wayland protocol
Where arboard can't open (GNOME has no data-control protocol, and the Flatpak on a Wayland session has no X11), Copy and Paste use the compositor's core data-device protocol on the window's own connection through smithay-clipboard, already in the build through egui-winit: text only, a copy's text or its SVG markup. A bitmap-only copy reports that this clipboard carries text only and leaves it as it was. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
committed by
bflatastic
co-authored by
Claude Opus 5.5
parent
33d15bec56
commit
dd934dfd4e
Generated
+1
@@ -6062,6 +6062,7 @@ dependencies = [
|
||||
"raw-window-handle",
|
||||
"rfd",
|
||||
"serde_json",
|
||||
"smithay-clipboard",
|
||||
"socket2",
|
||||
"vectorcraft-engine",
|
||||
"vectorcraft-text",
|
||||
|
||||
@@ -144,6 +144,7 @@ Ranked; detail in [docs/roadmap.md](docs/roadmap.md) and [docs/gaps.md](docs/gap
|
||||
|
||||
| Date | What landed |
|
||||
|---|---|
|
||||
| 2026-10-29 | Linux: the Flatpak on GNOME copies and pastes text with other apps through the compositor's core Wayland protocol (smithay-clipboard on the window's connection), where arboard has neither data-control nor X11. Scores and estimates unchanged. |
|
||||
| 2026-10-29 | Linux: a clipboard that can't be opened (the Flatpak on GNOME, where arboard has neither data-control nor X11) is retried after 1 s, doubling to a minute, and logged once, where the background Paste check used to reconnect and log a warning every 250 ms (267k warnings in 18 h). Scores and estimates unchanged. |
|
||||
| 2026-10-21 | 3D lighting: four original lighting presets shared by Revolve, Extrude & Bevel, Inflate and Rotate, with command access and manual Custom settings; scores and estimates unchanged. |
|
||||
| 2026-10-21 | Print: the Print dialog's Media list has the Japanese papers — JIS B4 (257 × 364 mm), JIS B5 (182 × 257 mm) and JIS B6 next to the ISO B4/B5 (which keep their ISO 216 dimensions for settings saved before them), はがき 100 × 148 mm, 往復はがき 148 × 200 mm, 封筒 長形3号 120 × 235 mm / 長形4号 90 × 225 mm / 角形2号 240 × 332 mm / 角形3号 216 × 277 mm and 四六判 127 × 188 mm. A paper is named by its JSON id or its dialog label, any case and spacing (`B5 (JIS)` or `b5jis`), and every added size prints as its own PDF page size. Headline numbers unchanged. |
|
||||
@@ -199,6 +200,7 @@ The full pre-standard record of what landed is kept in [docs/roadmap.md](docs/ro
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-29 | minor | Progress log row for the Linux Flatpak clipboard's core Wayland protocol text fallback; headline numbers unchanged. |
|
||||
| 2026-10-29 | minor | Progress log row for the Linux clipboard retry backoff; headline numbers unchanged. |
|
||||
| 2026-10-23 | minor | Progress log row for Pathfinder › Trap; Live vector effects hours 6–10 → 4–8; headline numbers unchanged. |
|
||||
| 2026-10-23 | minor | Progress log row for OpenType SVG colour fonts; headline numbers unchanged. |
|
||||
|
||||
@@ -92,6 +92,10 @@ x11rb = { version = "0.13", default-features = false, features = ["xinput"] }
|
||||
# Asks the Wayland compositor which GPU it draws with (linux-dmabuf feedback, src/compositor_gpu.rs),
|
||||
# so Automatic draws on that one. Both are already in the build through winit.
|
||||
wayland-client = "0.31"
|
||||
# Copy and Paste of text through the compositor's core data-device protocol on the window's
|
||||
# connection (src/clipboard.rs), where arboard has neither data-control (GNOME) nor X11 (a Flatpak
|
||||
# on a Wayland session). MIT, already in the build through egui-winit.
|
||||
smithay-clipboard = "0.7"
|
||||
wayland-protocols = { version = "0.32", features = ["client", "unstable"] }
|
||||
# The save dialog's parent window on Wayland (src/portal_save.rs): an xdg-foreign handle (in
|
||||
# wayland-protocols' `unstable` set) made on the window's own connection. MIT, already in the build
|
||||
|
||||
@@ -9,8 +9,10 @@
|
||||
//! and EPS as vector art (#1499) and its PNG or TIFF as they are, as screenshots copy them (on
|
||||
//! Linux arboard reads no other formats). On Wayland, arboard's data-control
|
||||
//! backend reads the compositor's clipboard (X11 through XWayland only sees what X11 apps
|
||||
//! copied), falling back to X11 where the compositor lacks the protocol. A clipboard that can't
|
||||
//! be opened is tried again after a wait that grows to a minute, not each time Paste asks.
|
||||
//! copied), falling back to X11 where the compositor lacks the protocol, and where there is no
|
||||
//! X11 either (a Flatpak on GNOME) to the compositor's core protocol on the window's connection,
|
||||
//! text only. A clipboard that can't be opened is tried again after a wait that grows to a
|
||||
//! minute, not each time Paste asks.
|
||||
//! - Everywhere: files copied in a file manager paste as the first one that is art (SVG, PDF, EPS,
|
||||
//! a metafile or a bitmap), before the clipboard's other formats (the files' paths as text).
|
||||
|
||||
@@ -20,12 +22,16 @@ use std::path::PathBuf;
|
||||
use vectorcraft_engine::cmd::clipboard::{BITMAP, EPS, FILE_HEAD, Flavour, PDF, PNG, SVG, TEXT, file_flavour};
|
||||
use vectorcraft_ui_egui::SystemClipboard;
|
||||
|
||||
/// This platform's system clipboard.
|
||||
pub fn system_clipboard() -> Box<dyn SystemClipboard> {
|
||||
/// This platform's system clipboard. On Linux, `window`'s Wayland connection carries text where
|
||||
/// arboard can't open (GNOME has no data-control protocol, a Flatpak there no X11).
|
||||
pub fn system_clipboard(window: Option<std::sync::Arc<winit::window::Window>>) -> Box<dyn SystemClipboard> {
|
||||
#[cfg(windows)]
|
||||
let cb = Box::<win::Clipboard>::default();
|
||||
let cb = {
|
||||
let _ = window;
|
||||
Box::<win::Clipboard>::default()
|
||||
};
|
||||
#[cfg(not(windows))]
|
||||
let cb = Box::<portable::Clipboard>::default();
|
||||
let cb = Box::new(portable::Clipboard::new(window));
|
||||
cb
|
||||
}
|
||||
|
||||
@@ -307,10 +313,12 @@ mod win {
|
||||
}
|
||||
}
|
||||
|
||||
/// arboard: one format at a time. Built everywhere so it is checked everywhere.
|
||||
/// arboard, or on a Wayland compositor without its data-control protocol the core protocol's text
|
||||
/// clipboard: one format at a time. Built everywhere so it is checked everywhere.
|
||||
#[cfg_attr(windows, allow(dead_code))]
|
||||
mod portable {
|
||||
use std::borrow::Cow;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
@@ -322,6 +330,108 @@ mod portable {
|
||||
Image(usize, usize),
|
||||
}
|
||||
|
||||
/// An open clipboard connection.
|
||||
enum Backend {
|
||||
Arboard(arboard::Clipboard),
|
||||
/// The compositor's core data-device protocol on the window's connection (text only).
|
||||
#[cfg(target_os = "linux")]
|
||||
Wayland(super::wayland::TextClipboard),
|
||||
}
|
||||
|
||||
/// What a write of a picture says where the clipboard carries text only.
|
||||
const TEXT_ONLY: &str = "this clipboard carries text only";
|
||||
|
||||
impl Backend {
|
||||
/// arboard (Wayland data-control, else X11), else the core protocol on `window`'s
|
||||
/// Wayland connection. Err: why each failed.
|
||||
fn open(window: Option<&Arc<winit::window::Window>>) -> Result<Self, String> {
|
||||
first_open(
|
||||
|| arboard::Clipboard::new().map(Self::Arboard).map_err(|e| e.to_string()),
|
||||
|| {
|
||||
#[cfg(target_os = "linux")]
|
||||
return window.and_then(|w| super::wayland::TextClipboard::new(w.clone())).map(Self::Wayland).ok_or("not a Wayland window");
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
{
|
||||
let _ = window;
|
||||
Err("not on Linux")
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Arboard(_) => "arboard",
|
||||
#[cfg(target_os = "linux")]
|
||||
Self::Wayland(_) => "the Wayland core protocol (text only)",
|
||||
}
|
||||
}
|
||||
|
||||
fn get_text(&mut self) -> Result<String, String> {
|
||||
match self {
|
||||
Self::Arboard(cb) => cb.get_text().map_err(|e| e.to_string()),
|
||||
#[cfg(target_os = "linux")]
|
||||
Self::Wayland(cb) => cb.get_text(),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_text(&mut self, text: &str) -> Result<(), String> {
|
||||
match self {
|
||||
Self::Arboard(cb) => cb.set_text(text).map_err(|e| e.to_string()),
|
||||
#[cfg(target_os = "linux")]
|
||||
Self::Wayland(cb) => {
|
||||
cb.set_text(text);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_image(&mut self) -> Result<arboard::ImageData<'static>, String> {
|
||||
match self {
|
||||
Self::Arboard(cb) => cb.get_image().map_err(|e| e.to_string()),
|
||||
#[cfg(target_os = "linux")]
|
||||
Self::Wayland(_) => Err(TEXT_ONLY.into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_image(&mut self, img: arboard::ImageData<'_>) -> Result<(), String> {
|
||||
match self {
|
||||
Self::Arboard(cb) => cb.set_image(img).map_err(|e| e.to_string()),
|
||||
#[cfg(target_os = "linux")]
|
||||
Self::Wayland(_) => Err(TEXT_ONLY.into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&mut self) -> Result<(), String> {
|
||||
match self {
|
||||
Self::Arboard(cb) => cb.clear().map_err(|e| e.to_string()),
|
||||
#[cfg(target_os = "linux")]
|
||||
Self::Wayland(cb) => {
|
||||
cb.set_text("");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Files copied in a file manager (none where the backend can't read a file list).
|
||||
fn file_list(&mut self) -> Vec<std::path::PathBuf> {
|
||||
match self {
|
||||
Self::Arboard(cb) => cb.get().file_list().unwrap_or_default(),
|
||||
#[cfg(target_os = "linux")]
|
||||
Self::Wayland(_) => vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The first of `arboard` and `wayland` that opens, tried in that order. Err: why each failed.
|
||||
fn first_open<B>(arboard: impl FnOnce() -> Result<B, String>, wayland: impl FnOnce() -> Result<B, &'static str>) -> Result<B, String> {
|
||||
let arboard = match arboard() {
|
||||
Ok(cb) => return Ok(cb),
|
||||
Err(e) => e,
|
||||
};
|
||||
wayland().map_err(|wayland| format!("arboard: {arboard}; Wayland core protocol: {wayland}"))
|
||||
}
|
||||
|
||||
/// Who asks for the clipboard: the background paste check waits out the whole wait after a
|
||||
/// failure, a Copy or Paste of the user's [`USER_RETRY`] at most.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
@@ -348,7 +458,7 @@ mod portable {
|
||||
}
|
||||
|
||||
/// A handle opened on first use and kept, or after a failure opened again only once its wait is
|
||||
/// over: where the clipboard can't be opened, asking a few times a second must not reconnect each time.
|
||||
/// over: where neither backend opens, asking a few times a second must not reconnect each time.
|
||||
pub(super) struct Reopen<B> {
|
||||
handle: Option<B>,
|
||||
failed: Option<Failed>,
|
||||
@@ -415,20 +525,20 @@ mod portable {
|
||||
|
||||
pub struct Clipboard {
|
||||
/// Kept open: on Linux the copied data lives as long as the handle does.
|
||||
cb: Reopen<arboard::Clipboard>,
|
||||
cb: Reopen<Backend>,
|
||||
/// The app window, for the Wayland core protocol (on its connection).
|
||||
window: Option<Arc<winit::window::Window>>,
|
||||
ours: Option<Ours>,
|
||||
}
|
||||
|
||||
impl Default for Clipboard {
|
||||
fn default() -> Self {
|
||||
Self { cb: Reopen::new(&UNAVAILABLE_LOGGED), ours: None }
|
||||
}
|
||||
}
|
||||
|
||||
impl Clipboard {
|
||||
fn cb(&mut self, asker: Asker) -> Result<&mut arboard::Clipboard, String> {
|
||||
let open = || arboard::Clipboard::new().map_err(|e| e.to_string());
|
||||
self.cb.get(asker, Instant::now(), open, |_| "arboard").ok_or_else(|| "no system clipboard".to_string())
|
||||
pub fn new(window: Option<Arc<winit::window::Window>>) -> Self {
|
||||
Self { cb: Reopen::new(&UNAVAILABLE_LOGGED), window, ours: None }
|
||||
}
|
||||
|
||||
fn cb(&mut self, asker: Asker) -> Result<&mut Backend, String> {
|
||||
let window = self.window.as_ref();
|
||||
self.cb.get(asker, Instant::now(), || Backend::open(window), Backend::name).ok_or_else(|| "no system clipboard".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -442,16 +552,16 @@ mod portable {
|
||||
let cb = self.cb(Asker::User)?;
|
||||
let ours = match (text, image) {
|
||||
(Some(t), _) => {
|
||||
cb.set_text(t.as_str()).map_err(|e| e.to_string())?;
|
||||
cb.set_text(t.as_str())?;
|
||||
Some(Ours::Text(t))
|
||||
}
|
||||
(None, Some(img)) => {
|
||||
let (width, height) = (img.width() as usize, img.height() as usize);
|
||||
cb.set_image(arboard::ImageData { width, height, bytes: Cow::Owned(img.into_raw()) }).map_err(|e| e.to_string())?;
|
||||
cb.set_image(arboard::ImageData { width, height, bytes: Cow::Owned(img.into_raw()) })?;
|
||||
Some(Ours::Image(width, height))
|
||||
}
|
||||
(None, None) => {
|
||||
cb.clear().map_err(|e| e.to_string())?;
|
||||
cb.clear()?;
|
||||
None
|
||||
}
|
||||
};
|
||||
@@ -470,7 +580,7 @@ mod portable {
|
||||
|
||||
fn read(&mut self, mimes: &[&'static str]) -> Option<Flavour> {
|
||||
let cb = self.cb(Asker::User).ok()?;
|
||||
if let Some(f) = cb.get().file_list().ok().and_then(|paths| copied_file(paths, mimes)) {
|
||||
if let Some(f) = copied_file(cb.file_list(), mimes) {
|
||||
return Some(f);
|
||||
}
|
||||
mimes.iter().find_map(|m| {
|
||||
@@ -608,6 +718,26 @@ mod portable {
|
||||
assert_eq!(cb.opened().copied(), Some(7));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn arboard_is_tried_before_the_wayland_core_protocol() {
|
||||
let wayland_tried = Cell::new(false);
|
||||
let wayland = |ok| {
|
||||
wayland_tried.set(true);
|
||||
if ok { Ok("wayland") } else { Err("not a Wayland window") }
|
||||
};
|
||||
// Where arboard opens (data-control, X11) the core protocol isn't tried.
|
||||
assert_eq!(first_open(|| Ok("arboard"), || wayland(true)), Ok("arboard"));
|
||||
assert!(!wayland_tried.get());
|
||||
// The Flatpak on GNOME: no data-control, no X11.
|
||||
assert_eq!(first_open(|| Err("no X11".into()), || wayland(true)), Ok("wayland"));
|
||||
assert!(wayland_tried.get());
|
||||
// Neither: every reason is kept for the log.
|
||||
assert_eq!(
|
||||
first_open(|| Err("no X11".into()), || wayland(false)),
|
||||
Err::<&str, _>("arboard: no X11; Wayland core protocol: not a Wayland window".into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn one_outage_is_logged_once_across_handles() {
|
||||
static LOGGED: AtomicBool = AtomicBool::new(false);
|
||||
@@ -623,6 +753,43 @@ mod portable {
|
||||
}
|
||||
}
|
||||
|
||||
/// The compositor's clipboard through the core data-device protocol (`wl_data_device`), on the
|
||||
/// window's own Wayland connection: what Copy and Paste use where the compositor lacks the
|
||||
/// data-control protocol arboard needs (GNOME) and there is no X11 to fall back on (the Flatpak on
|
||||
/// a Wayland session). Text only (smithay-clipboard), and only while the window has focus, as the
|
||||
/// protocol has it.
|
||||
#[cfg(target_os = "linux")]
|
||||
mod wayland {
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct TextClipboard {
|
||||
cb: smithay_clipboard::Clipboard,
|
||||
/// Held as long as the clipboard: its connection is the window's.
|
||||
_window: Arc<winit::window::Window>,
|
||||
}
|
||||
|
||||
impl TextClipboard {
|
||||
/// None when `window` isn't a Wayland window.
|
||||
#[allow(unsafe_code)]
|
||||
pub fn new(window: Arc<winit::window::Window>) -> Option<Self> {
|
||||
use winit::raw_window_handle::{HasDisplayHandle as _, RawDisplayHandle};
|
||||
let RawDisplayHandle::Wayland(d) = window.display_handle().ok()?.as_raw() else { return None };
|
||||
// SAFETY: the display is winit's open `wl_display`, which outlives `window` (held beside
|
||||
// the clipboard); smithay-clipboard makes its own event queue on it and doesn't close it.
|
||||
let cb = unsafe { smithay_clipboard::Clipboard::new(d.display.as_ptr()) };
|
||||
Some(Self { cb, _window: window })
|
||||
}
|
||||
|
||||
pub fn get_text(&self) -> Result<String, String> {
|
||||
self.cb.load().map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
pub fn set_text(&self, text: &str) {
|
||||
self.cb.store(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The macOS pasteboard's vector art and bitmaps as they are (arboard reads text, and of the
|
||||
/// pictures TIFF only, decoded).
|
||||
#[cfg(target_os = "macos")]
|
||||
|
||||
@@ -255,6 +255,7 @@ pub(crate) fn services(parent: Parent, ui: egui::Context) -> Services {
|
||||
.as_ref()
|
||||
.and_then(|w| w.window_handle().ok())
|
||||
.is_some_and(|h| matches!(h.as_raw(), RawWindowHandle::Xlib(_) | RawWindowHandle::Xcb(_)));
|
||||
let (clip, clip_probe) = (parent.clone(), parent.clone());
|
||||
let (p1, p2, p3, p4, p5) = (parent.clone(), parent.clone(), parent.clone(), parent.clone(), parent);
|
||||
#[cfg(target_os = "macos")]
|
||||
let sampled_ui = ui.clone();
|
||||
@@ -286,11 +287,12 @@ pub(crate) fn services(parent: Parent, ui: egui::Context) -> Services {
|
||||
fileio::write_file_with(p, fill).map_err(|e| e.to_string())
|
||||
})),
|
||||
// Every format Copy offers and Paste reads (menu-bar Paste never sees egui's Paste event).
|
||||
system_clipboard: Some(clipboard::system_clipboard()),
|
||||
system_clipboard: Some(clipboard::system_clipboard(clip)),
|
||||
// Linux checks whether Paste has something to take on a background thread: an X11 clipboard
|
||||
// owner that never answers holds a read for up to 4 s. Windows only asks which formats the
|
||||
// clipboard holds and macOS asks the pasteboard server, so they check in line.
|
||||
clipboard_probe: cfg!(target_os = "linux").then(|| Box::new(clipboard::system_clipboard) as ClipboardProbeFactory),
|
||||
// The probe's handle is made on its own thread, from the window (`Send`), not its raw display.
|
||||
clipboard_probe: cfg!(target_os = "linux").then(|| Box::new(move || clipboard::system_clipboard(clip_probe)) as ClipboardProbeFactory),
|
||||
// Help → Discord / website / GitHub, the Discord button, About and Home links.
|
||||
open_url: Some(Box::new(|url: &str| {
|
||||
let _ = webbrowser::open(url);
|
||||
|
||||
+3
-1
@@ -1,5 +1,6 @@
|
||||
# Development
|
||||
|
||||
> **Last updated:** 2026-10-29 · **Change:** Linux clipboard: the Flatpak on GNOME copies and pastes text through the core Wayland protocol.
|
||||
> **Last updated:** 2026-10-29 · **Change:** Linux clipboard: a clipboard that can't be opened is retried with a growing wait and logged once.
|
||||
> **Last updated:** 2026-10-25 · **Change:** Linux Wayland drag-and-drop note names eframe 0.37 (still on winit 0.30).
|
||||
> **Last updated:** 2026-10-23 · **Change:** Tests: cargo xtask test, one integration-test binary per crate, the dev profile's debug info.
|
||||
@@ -114,7 +115,7 @@ On GNOME/Wayland, enable **Preferences › User Interface › System Title Bar**
|
||||
|
||||
The window runs natively on Wayland (eframe's `wayland` feature) and on X11. On machines with two GPUs it renders on the one the desktop runs on unless Preferences say otherwise; see [Desktop graphics processor](#desktop-graphics-processor) (#502). The system clipboard (`apps/vectorcraft/src/clipboard.rs`) is arboard with its `wayland-data-control` feature:
|
||||
|
||||
- **Clipboard:** under Wayland arboard talks to the compositor through the data-control protocol (wlroots compositors such as Sway and Hyprland, KDE Plasma), so bitmaps (`image/png`), SVG markup and text copied in any app paste (#398). Where the compositor lacks the protocol (GNOME's Mutter, [arboard#223](https://github.com/1Password/arboard/issues/223)), arboard falls back to the X11 clipboard through XWayland, which only holds what X11 apps copied. A clipboard arboard can't open at all (the Flatpak on a Wayland session, where `--socket=fallback-x11` gives it no X11) is tried again after 1 s, doubling to a minute (a Copy or Paste of the user's at most once a second), and logged once when it goes and once when it comes back: before, the background Paste check reconnected and logged a warning every 250 ms. egui's own text paste into fields goes through smithay-clipboard and works either way.
|
||||
- **Clipboard:** under Wayland arboard talks to the compositor through the data-control protocol (wlroots compositors such as Sway and Hyprland, KDE Plasma), so bitmaps (`image/png`), SVG markup and text copied in any app paste (#398). Where the compositor lacks the protocol (GNOME's Mutter, [arboard#223](https://github.com/1Password/arboard/issues/223)), arboard falls back to the X11 clipboard through XWayland, which only holds what X11 apps copied. Where there is no X11 either (the Flatpak on a Wayland session: `--socket=fallback-x11` gives it none), Copy and Paste use the compositor's core data-device protocol on the window's own connection through smithay-clipboard: text only (a copy's text or its SVG markup), and only while the window has focus. A clipboard neither backend opens is tried again after 1 s, doubling to a minute (a Copy or Paste of the user's at most once a second), and logged once when it goes and once when it comes back: before, the background Paste check reconnected and logged a warning every 250 ms. egui's own text paste into fields goes through smithay-clipboard and works either way.
|
||||
- **Clipboard owners that never answer (#510):** reading the clipboard waits on the app that owns it: arboard gives up on an X11 owner after 4 s, and a Wayland read waits for the owner to send its data. So on Linux the check that enables the Paste menu items runs on a background thread with a second clipboard handle (`crates/ui-egui/src/clipboard_probe.rs`, installed as `Services::clipboard_probe`), only while nothing is copied in the app and a document is open, and the window keeps drawing. A paste itself still reads on the UI thread, so pasting from such an owner waits for it once. Windows and macOS keep the check on the UI thread: Windows only asks which formats the clipboard holds, macOS asks the pasteboard server.
|
||||
- **Copied files:** files copied in a file manager (`text/uri-list`; `CF_HDROP` on Windows, file URLs on macOS) paste as the first one that is art (SVG, PDF, EPS, EMF/WMF or a bitmap; a `.ai` or `.ait` file from the editing data it holds, else as the PDF or PostScript it holds), before the clipboard's other formats (which include the files' paths as text).
|
||||
- **Dropping files on the window does not work under Wayland:** winit 0.30, which eframe 0.37 runs on, has no Wayland drag and drop ([winit#1881](https://github.com/rust-windowing/winit/issues/1881), added in winit 0.31; [egui#1563](https://github.com/emilk/egui/issues/1563)), so no `dropped_files` arrive. Until eframe moves to winit 0.31: copy the files in the file manager and paste them, use File › Place or Open, or open the window through XWayland, where drops work: set **Preferences › Performance › Linux Display Server** to **X11 (XWayland)** and restart (#1151), or start the app with `WAYLAND_DISPLAY= vectorcraft`. The preference (`linuxDisplayServer`, read at startup in `apps/vectorcraft/src/main.rs`, shown on Linux only) has winit open the window on X11 (`EventLoopBuilderExtX11::with_x11`) when the session is Wayland, `DISPLAY` is set and the X11 libraries winit loads are installed (`apps/vectorcraft/src/xwayland.rs` reads `ldconfig -p` and the usual library directories); otherwise the window opens on Wayland and the log says why. Unlike clearing `WAYLAND_DISPLAY`, it leaves the clipboard on the compositor's own.
|
||||
@@ -493,6 +494,7 @@ Scores and hour estimates are unchanged.
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-29 | minor | Linux clipboard: core Wayland protocol text fallback where arboard has neither data-control nor X11 (the Flatpak on GNOME). |
|
||||
| 2026-10-29 | minor | Linux clipboard: a clipboard arboard can't open is retried after 1 s doubling to 60 s and logged once, instead of every 250 ms. |
|
||||
| 2026-10-28 | minor | Localisation: Catalan (`ca`) catalog and registry entry (every `ca-*` locale); complete under the catalog tests. |
|
||||
| 2026-10-25 | minor | Linux Wayland drag-and-drop note names eframe 0.37 (still on winit 0.30); behavior unchanged. |
|
||||
|
||||
+3
-2
@@ -178,8 +178,8 @@ from G14 and the budget run of G15: **~170–270 h** one agent, ~50–80 h wall
|
||||
|
||||
- **Unconfirmed (2026-10-14):** Tool cursors are painted into the window on Wayland (#1652; not run on Linux, #1645 stays open until confirmed). On Windows the custom title bar's window stays truly borderless, so the pointer lines up with what is drawn on systems where the hidden caption offset it (#1791, #1758; awaiting the reporter's confirmation).
|
||||
- **Done:** RPM required dependencies use 64-bit library capabilities and declare glibc 2.35, libm and libgcc_s (#1275); generated metadata and isolated synthetic-provider checks cover x86_64/aarch64. Real-distribution install/start-up QA is still missing.
|
||||
- **Done (2026-10-29):** the Linux Flatpak on GNOME no longer reconnects to the compositor and logs a warning every 250 ms when arboard has neither data-control nor X11 (267k warnings in 18 h filled the 16 MB log in 3 h): a clipboard that can't be opened is retried with a wait growing to a minute and logged once. Copy and Paste with other apps still don't work there.
|
||||
- **Missing:** the app not starting or staying in the background on some Windows 11 machines (#858, #964 fixed
|
||||
- **Done (2026-10-29):** the Linux Flatpak on GNOME no longer reconnects to the compositor and logs a warning every 250 ms when arboard has neither data-control nor X11 (267k warnings in 18 h filled the 16 MB log in 3 h): a clipboard that can't be opened is retried with a wait growing to a minute and logged once. Copy and Paste of text with other apps work there through the core Wayland protocol.
|
||||
- **Missing:** pictures (`image/png`) and SVG as `image/svg+xml` on the clipboard of the Flatpak on GNOME, where only text travels (smithay-clipboard 0.7 carries text only); the app not starting or staying in the background on some Windows 11 machines (#858, #964 fixed
|
||||
2026-10-10, #620, #818), slow pointer on a Linux AppImage (#575), macOS paste from other apps (#954), a
|
||||
web-free crash/hang reporter, a field record.
|
||||
- **Evidence:** open issues; the never-crash lints and `guard` stop panics, not driver, windowing or packaging
|
||||
@@ -655,6 +655,7 @@ trap, and the defaults (0.25 pt, 100%, 40%) are VectorCraft's own, the pages giv
|
||||
|---|---|---|
|
||||
| 2026-10-30 | minor | G1: Object › Path › Average & Join… (`path.averageJoin`, Cmd+Alt+Shift+J) moves two open path ends to their midpoint and joins them there, the joined anchor a corner or a smooth point as its dialog's Points choice asks, in one undo step; the "Average and Join in one step" entry is done. Estimate unchanged |
|
||||
| 2026-10-30 | minor | G12: Preferences audit (#394): 5 options are stored but not acted on (Open Documents As Tabs, Units › East Asian Type, Hyphenation › Default Language, Scratch Disks, Legacy vector clipboard), each waiting on a subsystem; Highlight Alternate Glyphs, Real-time Drawing and the Devices options act. Estimate unchanged. |
|
||||
| 2026-10-29 | minor | G10: the Linux Flatpak on GNOME copies and pastes text through the core Wayland protocol; pictures and `image/svg+xml` there listed as missing. Estimate unchanged. |
|
||||
| 2026-10-29 | minor | G10: the Linux Flatpak on GNOME stops the clipboard retry storm (retried with a growing wait, logged once). Estimate unchanged. |
|
||||
| 2026-10-29 | minor | G12: Pathfinder: live compound shapes (#4522): the panel menu's Make, Release and Expand Compound Shape, Alt-clicking a Shape Mode, a selected member's Shape Mode and the Expand button act. No entry closed, estimates unchanged. |
|
||||
| 2026-10-29 | minor | G8: a blend makes at most 100,000 steps in all, the steps of blends nested in its keys and steps included; past it each pair of keys keeps the same share of its steps (#3579). No entry closed, estimates unchanged. |
|
||||
|
||||
Reference in New Issue
Block a user