fix(share): copy links through QClipboard, not arboard (#684)
Copy link failed on GNOME + Flatpak: arboard's Wayland path needs the
wlr/ext-data-control protocol, which Mutter does not implement, so it fell
back to X11 — reachable natively through XWayland, unreachable inside the
sandbox ("clipboard unavailable: X11 server connection timed out"). KWin
has the protocol, which is why the failure never reproduced on Plasma.
Replace it with a C++ shim (cxx/clipboard.cpp) that queues the write onto
the GUI thread and hands it to QClipboard: the base wl_data_device protocol
every compositor implements, no extra sandbox permission, and the offer
lives as long as the application (the #514 lifetime rule, without the
long-lived arboard instance). arboard is dropped from the crate.
Verified on Plasma/Wayland and on an Ubuntu GNOME/Wayland VM with DISPLAY
unset (the sandbox condition), where the copy now lands.
cc
This commit is contained in:
5 files changed
+66
-313
No files matched your search
Generated
+6
-251
@@ -154,27 +154,6 @@ dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arboard"
|
||||
version = "3.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf"
|
||||
dependencies = [
|
||||
"clipboard-win",
|
||||
"image",
|
||||
"log",
|
||||
"objc2 0.6.4",
|
||||
"objc2-app-kit 0.3.2",
|
||||
"objc2-core-foundation",
|
||||
"objc2-core-graphics",
|
||||
"objc2-foundation 0.3.2",
|
||||
"parking_lot",
|
||||
"percent-encoding",
|
||||
"windows-sys 0.60.2",
|
||||
"wl-clipboard-rs",
|
||||
"x11rb",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.8"
|
||||
@@ -678,15 +657,6 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
|
||||
|
||||
[[package]]
|
||||
name = "clipboard-win"
|
||||
version = "5.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4"
|
||||
dependencies = [
|
||||
"error-code",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cmake"
|
||||
version = "0.1.58"
|
||||
@@ -1079,7 +1049,7 @@ dependencies = [
|
||||
"cxxbridge-cmd",
|
||||
"cxxbridge-flags",
|
||||
"cxxbridge-macro",
|
||||
"foldhash 0.2.0",
|
||||
"foldhash",
|
||||
"link-cplusplus",
|
||||
]
|
||||
|
||||
@@ -1487,12 +1457,6 @@ dependencies = [
|
||||
"litrs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "downcast-rs"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
|
||||
|
||||
[[package]]
|
||||
name = "dst-decoder"
|
||||
version = "0.1.2"
|
||||
@@ -1601,12 +1565,6 @@ dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "error-code"
|
||||
version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b5343afd4a8365a643ac588dab4cf234a190c7f6c88c9f6dd6ffe00837661b7"
|
||||
|
||||
[[package]]
|
||||
name = "euclid"
|
||||
version = "0.22.14"
|
||||
@@ -1724,12 +1682,6 @@ version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
version = "0.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
|
||||
|
||||
[[package]]
|
||||
name = "flacenc"
|
||||
version = "0.5.1"
|
||||
@@ -1785,12 +1737,6 @@ version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.2.0"
|
||||
@@ -1936,16 +1882,6 @@ dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gethostname"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8"
|
||||
dependencies = [
|
||||
"rustix 1.1.4",
|
||||
"windows-link 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.17"
|
||||
@@ -2017,15 +1953,6 @@ dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||
dependencies = [
|
||||
"foldhash 0.1.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.16.1"
|
||||
@@ -2034,7 +1961,7 @@ checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
"foldhash 0.2.0",
|
||||
"foldhash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2045,7 +1972,7 @@ checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
"foldhash 0.2.0",
|
||||
"foldhash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3125,15 +3052,6 @@ dependencies = [
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "8.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "notify"
|
||||
version = "8.2.0"
|
||||
@@ -3355,7 +3273,6 @@ dependencies = [
|
||||
"bitflags 2.13.1",
|
||||
"block2 0.6.2",
|
||||
"objc2 0.6.4",
|
||||
"objc2-core-graphics",
|
||||
"objc2-foundation 0.3.2",
|
||||
]
|
||||
|
||||
@@ -3434,19 +3351,6 @@ dependencies = [
|
||||
"objc2 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-graphics"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807"
|
||||
dependencies = [
|
||||
"bitflags 2.13.1",
|
||||
"dispatch2",
|
||||
"objc2 0.6.4",
|
||||
"objc2-core-foundation",
|
||||
"objc2-io-surface",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-image"
|
||||
version = "0.2.2"
|
||||
@@ -3490,17 +3394,6 @@ dependencies = [
|
||||
"objc2-core-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-io-surface"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d"
|
||||
dependencies = [
|
||||
"bitflags 2.13.1",
|
||||
"objc2 0.6.4",
|
||||
"objc2-core-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-metal"
|
||||
version = "0.2.2"
|
||||
@@ -3594,16 +3487,6 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "os_pipe"
|
||||
version = "1.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "palette"
|
||||
version = "0.7.7"
|
||||
@@ -3726,17 +3609,6 @@ dependencies = [
|
||||
"pest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "petgraph"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455"
|
||||
dependencies = [
|
||||
"fixedbitset 0.5.7",
|
||||
"hashbrown 0.15.5",
|
||||
"indexmap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.11.3"
|
||||
@@ -4524,7 +4396,6 @@ dependencies = [
|
||||
name = "qbz-qt"
|
||||
version = "2.1.1"
|
||||
dependencies = [
|
||||
"arboard",
|
||||
"async-io",
|
||||
"async-trait",
|
||||
"cc",
|
||||
@@ -6161,7 +6032,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4ea810f0692f9f51b382fff5893887bb4580f5fa246fde546e0b13e7fcee662"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"nom 7.1.3",
|
||||
"nom",
|
||||
"phf",
|
||||
"phf_codegen",
|
||||
]
|
||||
@@ -6187,7 +6058,7 @@ dependencies = [
|
||||
"fancy-regex",
|
||||
"filedescriptor",
|
||||
"finl_unicode",
|
||||
"fixedbitset 0.4.2",
|
||||
"fixedbitset",
|
||||
"hex",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
@@ -6577,17 +6448,6 @@ dependencies = [
|
||||
"strength_reduce",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tree_magic_mini"
|
||||
version = "3.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8765b90061cba6c22b5831f675da109ae5561588290f9fa2317adab2714d5a6"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"nom 8.0.0",
|
||||
"petgraph",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "try-lock"
|
||||
version = "0.2.5"
|
||||
@@ -6757,7 +6617,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f25d498b63d1fdb376b4250f39ab3a5ee8d103957346abacd911e2d8b612c139"
|
||||
dependencies = [
|
||||
"itertools 0.13.0",
|
||||
"nom 7.1.3",
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6884,76 +6744,6 @@ dependencies = [
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-backend"
|
||||
version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38a91b4eaddff87b1cd1074985e3713da4af2c49742d1b356b2c01670a67a078"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"downcast-rs",
|
||||
"rustix 1.1.4",
|
||||
"smallvec",
|
||||
"wayland-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-client"
|
||||
version = "0.31.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3c36a0f861ad76d0901f2800b46321410d9f73f2ea88aac0650d86c32688073"
|
||||
dependencies = [
|
||||
"bitflags 2.13.1",
|
||||
"rustix 1.1.4",
|
||||
"wayland-backend",
|
||||
"wayland-scanner",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-protocols"
|
||||
version = "0.32.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23d0c813de3daa2ed6520af85a3bd49b0e722a3078506899aa9686fea58dc4b6"
|
||||
dependencies = [
|
||||
"bitflags 2.13.1",
|
||||
"wayland-backend",
|
||||
"wayland-client",
|
||||
"wayland-scanner",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-protocols-wlr"
|
||||
version = "0.3.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234"
|
||||
dependencies = [
|
||||
"bitflags 2.13.1",
|
||||
"wayland-backend",
|
||||
"wayland-client",
|
||||
"wayland-protocols",
|
||||
"wayland-scanner",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-scanner"
|
||||
version = "0.31.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "338e30461b3a2b67d70eb30a6d89f8e0c93a833e07d2ae89085cd070c4a00ac0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quick-xml",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-sys"
|
||||
version = "0.31.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be"
|
||||
dependencies = [
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.104"
|
||||
@@ -7657,47 +7447,12 @@ version = "0.57.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
|
||||
|
||||
[[package]]
|
||||
name = "wl-clipboard-rs"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e9651471a32e87d96ef3a127715382b2d11cc7c8bb9822ded8a7cc94072eb0a3"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"os_pipe",
|
||||
"rustix 1.1.4",
|
||||
"thiserror 2.0.20",
|
||||
"tree_magic_mini",
|
||||
"wayland-backend",
|
||||
"wayland-client",
|
||||
"wayland-protocols",
|
||||
"wayland-protocols-wlr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "writeable"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc"
|
||||
|
||||
[[package]]
|
||||
name = "x11rb"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414"
|
||||
dependencies = [
|
||||
"gethostname",
|
||||
"rustix 1.1.4",
|
||||
"x11rb-protocol",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "x11rb-protocol"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd"
|
||||
|
||||
[[package]]
|
||||
name = "xdg-home"
|
||||
version = "1.3.0"
|
||||
|
||||
@@ -37,17 +37,10 @@ url = "2"
|
||||
open = "5"
|
||||
dirs = "5"
|
||||
|
||||
# Share links (src/share_qt.rs).
|
||||
# Rust owns the clipboard on this side because the toast does: cxx-qt-lib 0.7
|
||||
# ships no QClipboard binding, and QML cannot publish QbzShell.toastJson, so
|
||||
# splitting the copy and its confirmation across the boundary would buy
|
||||
# nothing.
|
||||
#
|
||||
# wayland-data-control: Wayland-native clipboard via the data-control
|
||||
# protocols (GNOME/KDE/sway/niri/Hyprland), falling back to X11 when Wayland
|
||||
# is absent (#514 — without it arboard is X11-only and every copy fails on
|
||||
# Wayland-only sessions).
|
||||
arboard = { version = "3", features = ["wayland-data-control"] }
|
||||
# Share links (src/share_qt.rs) copy through Qt's own QClipboard
|
||||
# (cxx/clipboard.cpp) since #684: arboard's Wayland path needs the
|
||||
# wlr/ext-data-control protocol, which Mutter lacks, and its X11 fallback is
|
||||
# unreachable inside the Flatpak sandbox.
|
||||
|
||||
qbz-core = { path = "../qbz-core" }
|
||||
qbz-app = { path = "../qbz-app" }
|
||||
|
||||
@@ -401,6 +401,8 @@ fn build_rhi_items() {
|
||||
cc.file("cxx/win_shell.cpp");
|
||||
println!("cargo:rerun-if-changed=cxx/app_lifecycle.cpp");
|
||||
cc.file("cxx/app_lifecycle.cpp");
|
||||
println!("cargo:rerun-if-changed=cxx/clipboard.cpp");
|
||||
cc.file("cxx/clipboard.cpp");
|
||||
// Shell_NotifyIconW tray. No Q_OBJECT, so no moc; the body is inside
|
||||
// `#ifdef _WIN32` and compiles to nothing on Linux and macOS.
|
||||
println!("cargo:rerun-if-changed=cxx/win_tray.cpp");
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
// System clipboard through Qt (#684).
|
||||
//
|
||||
// The Rust side used arboard, whose Wayland path needs the wlr/ext-data-control
|
||||
// protocol. KWin has it; Mutter (GNOME) does not, so arboard fell back to X11
|
||||
// there — which works natively through XWayland and FAILS inside the Flatpak
|
||||
// sandbox (no X11 socket): "clipboard unavailable: X11 server connection timed
|
||||
// out". QClipboard speaks the base wl_data_device protocol every compositor
|
||||
// implements, needs no extra sandbox permission, and the offer stays alive for
|
||||
// as long as the application does (the #514 lifetime rule, for free).
|
||||
//
|
||||
// The clipboard is a GUI-thread object; the call is queued onto the
|
||||
// application's thread so any caller (a cxx-qt invokable already on it, or a
|
||||
// tokio worker) is fine.
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QMetaObject>
|
||||
#include <QtCore/QString>
|
||||
#include <QtGui/QClipboard>
|
||||
#include <QtGui/QGuiApplication>
|
||||
|
||||
extern "C" void qbz_clipboard_set_text(const char *utf8, int len) {
|
||||
QCoreApplication *core = QCoreApplication::instance();
|
||||
if (!core || !qobject_cast<QGuiApplication *>(core)) {
|
||||
return;
|
||||
}
|
||||
const QString text = QString::fromUtf8(utf8, len);
|
||||
QMetaObject::invokeMethod(
|
||||
core,
|
||||
[text]() {
|
||||
if (QClipboard *clipboard = QGuiApplication::clipboard()) {
|
||||
clipboard->setText(text, QClipboard::Clipboard);
|
||||
}
|
||||
},
|
||||
Qt::QueuedConnection);
|
||||
}
|
||||
@@ -58,63 +58,31 @@ pub(crate) fn qobuz_label_url(label_id: &str) -> String {
|
||||
format!("https://play.qobuz.com/label/{label_id}")
|
||||
}
|
||||
|
||||
/// Long-lived clipboard instance. arboard ties the offer's lifetime to the
|
||||
/// LAST live `Clipboard` object: dropping it destroys the X11 selection
|
||||
/// window (contents survive only when a clipboard MANAGER accepts the
|
||||
/// handoff — KDE ships one, stock GNOME/XFCE/Cinnamon do not) and ends the
|
||||
/// Wayland offer with the same rule. The old create-per-copy pattern
|
||||
/// therefore worked on KDE and silently lost the text everywhere else
|
||||
/// (HiFi-wizard copy report, #514). One instance kept alive for the whole
|
||||
/// process serves the offer like any normal app.
|
||||
///
|
||||
/// This is the #514 fix, verbatim from `share.rs:32-41` — it is NOT
|
||||
/// boilerplate to be simplified into a local. A create-per-copy port would
|
||||
/// pass every test run on KDE and lose the text everywhere else.
|
||||
static CLIPBOARD: std::sync::OnceLock<std::sync::Mutex<Option<arboard::Clipboard>>> =
|
||||
std::sync::OnceLock::new();
|
||||
unsafe extern "C" {
|
||||
fn qbz_clipboard_set_text(utf8: *const std::os::raw::c_char, len: i32);
|
||||
}
|
||||
|
||||
/// Copy `text` to the system clipboard. Runs on a blocking thread —
|
||||
/// clipboard backends (X11/Wayland) can block. `share.rs:43-70`.
|
||||
/// Copy `text` to the system clipboard through Qt (`cxx/clipboard.cpp`).
|
||||
///
|
||||
/// The one adaptation vs the reference: in the Slint app the arm already runs
|
||||
/// inside a tokio context, whereas a cxx-qt invokable runs on the **Qt event
|
||||
/// loop thread**, where a bare `tokio::task::spawn_blocking` panics ("must be
|
||||
/// called from the context of a Tokio runtime"). It therefore goes through
|
||||
/// `crate::spawn` (main.rs) onto the process-global runtime first — the same
|
||||
/// `spawn` + `spawn_blocking` sandwich `local_ephemeral.rs`, `browse_qt.rs`
|
||||
/// and `ambient_qt.rs` already use.
|
||||
/// #684: the arboard path before this needed `wlr/ext-data-control`, which
|
||||
/// KWin has and Mutter does not; on GNOME it fell back to X11, which works
|
||||
/// natively through XWayland and is unreachable inside the Flatpak sandbox —
|
||||
/// every Copy link on GNOME + Flatpak logged "clipboard unavailable" and
|
||||
/// copied nothing. QClipboard uses the base Wayland data-device protocol,
|
||||
/// needs no sandbox permission, and QGuiApplication keeps the offer alive
|
||||
/// for the life of the process (the #514 lifetime rule, without the
|
||||
/// long-lived instance the arboard port had to carry).
|
||||
///
|
||||
/// Fire-and-forget by design: the caller never learns whether the copy
|
||||
/// worked, which is why the toast at the call site is unconditional
|
||||
/// (`main.rs:12758-12761` does the same).
|
||||
/// (`main.rs:12758-12761` does the same). The C++ side queues the write onto
|
||||
/// the GUI thread, so this is safe from a cxx-qt invokable or a worker.
|
||||
pub(crate) fn copy_to_clipboard(text: String) {
|
||||
crate::spawn(async move {
|
||||
let _ = tokio::task::spawn_blocking(move || {
|
||||
let cell = CLIPBOARD.get_or_init(|| std::sync::Mutex::new(None));
|
||||
let mut guard = match cell.lock() {
|
||||
Ok(g) => g,
|
||||
Err(poisoned) => poisoned.into_inner(),
|
||||
};
|
||||
if guard.is_none() {
|
||||
match arboard::Clipboard::new() {
|
||||
Ok(c) => *guard = Some(c),
|
||||
Err(e) => {
|
||||
log::warn!("[qbz-qt] clipboard unavailable: {e}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(clipboard) = guard.as_mut() {
|
||||
if let Err(e) = clipboard.set_text(text) {
|
||||
log::warn!("[qbz-qt] clipboard set failed: {e}");
|
||||
// Drop the instance so the next copy reconnects — the
|
||||
// display connection may have gone away.
|
||||
*guard = None;
|
||||
}
|
||||
}
|
||||
})
|
||||
.await;
|
||||
});
|
||||
let len = i32::try_from(text.len()).unwrap_or(i32::MAX);
|
||||
// SAFETY: the pointer/length pair is read synchronously inside the call
|
||||
// (the C++ side copies into a QString before queueing), so `text` may be
|
||||
// dropped as soon as the call returns.
|
||||
unsafe { qbz_clipboard_set_text(text.as_ptr().cast(), len) };
|
||||
}
|
||||
|
||||
/// Artist header ⋯ → Share. `artist/ArtistPageView.slint:530-538` fires
|
||||
|
||||
Reference in new issue
Block a user