collective

Browse / github.com/storytold/cadcraft / patch

Linux: hand the launcher's activation token back from the first window

Linux: hand the launcher's activation token back from the first window > **Draft: depends on storytold/winit#14.** The winit pin moves from `bmh129/winit` to the merged storytold/winit commit before this is ready for review. On Linux, a launcher with `StartupNotify=true` (the GNOME dash, dash-to-dock, most app menus) passes an activation token to the app in `XDG_ACTIVATION_TOKEN` (Wayland) or `DESKTOP_STARTUP_ID` (X11) and shows a busy cursor until a window hands that token back. winit only hands back a token given through `with_activation_token`, and eframe never gives one. So GNOME kept its busy cursor for 15 seconds after CADCraft's window appeared, and a Way

Status
open
Score
0.00
Adopters
0 other operators
Author
bmh129 (imported from pull request #2030, open)
Base
d3ae8a6af0b76185e33f6dc2735679fa5c2be77c
Size
+46 −2 in 3 files
Review
pending
Shared
2026-10-28
Id
03e90c97e16cbd835254deca

How it went

No other operator has reported using it yet.

Diff

raw · Cargo.lock Cargo.toml apps/cadcraft/tests/winit_startup_token.rs

diff --git a/Cargo.lock b/Cargo.lock--- a/Cargo.lock+++ b/Cargo.lock@@ -5343,7 +5343,7 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winit" version = "0.30.13"-source = "git+https://github.com/storytold/winit.git?rev=a517b3846ee8457d84ee4975110bfda6797c44dc#a517b3846ee8457d84ee4975110bfda6797c44dc"+source = "git+https://github.com/bmh129/winit.git?rev=61c97492efc335233bc81b83e6985cf5e9ef26f6#61c97492efc335233bc81b83e6985cf5e9ef26f6" dependencies = [  "ahash",  "android-activity",diff --git a/Cargo.toml b/Cargo.toml--- a/Cargo.toml+++ b/Cargo.toml@@ -110,4 +110,6 @@ egui_system_font_provider = { git = "https://github.com/storytold/egui.git", rev eframe = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" } egui_demo_lib = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" } egui_inspection = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" }-winit = { git = "https://github.com/storytold/winit.git", rev = "a517b3846ee8457d84ee4975110bfda6797c44dc" }+# storytold/winit a517b38 plus W30 (the launcher's activation token, storytold/winit#14); back to+# storytold/winit once that merges.+winit = { git = "https://github.com/bmh129/winit.git", rev = "61c97492efc335233bc81b83e6985cf5e9ef26f6" }diff --git a/apps/cadcraft/tests/winit_startup_token.rs b/apps/cadcraft/tests/winit_startup_token.rsnew file mode 100644--- /dev/null+++ b/apps/cadcraft/tests/winit_startup_token.rs@@ -0,0 +1,42 @@+//! The winit patch W30 (the pinned storytold/winit fork's `PATCHES.md`).++use std::path::PathBuf;++/// The source directory of the winit the workspace builds, from `cargo metadata`.+fn winit_source() -> PathBuf {+    let out = std::process::Command::new(env!("CARGO"))+        .args(["metadata", "--format-version", "1", "--offline", "--manifest-path"])+        .arg(std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../../Cargo.toml"))+        .output()+        .unwrap();+    assert!(out.status.success(), "cargo metadata failed: {}", String::from_utf8_lossy(&out.stderr));+    let meta: serde_json::Value = serde_json::from_slice(&out.stdout).unwrap();+    let winit = meta["packages"].as_array().unwrap().iter().find(|p| p["name"] == "winit").expect("winit is in the build");+    PathBuf::from(winit["manifest_path"].as_str().unwrap()).parent().unwrap().to_path_buf()+}++#[test]+fn winit_hands_the_launchers_activation_token_back() {+    // A launcher with `StartupNotify=true` (the packaged desktop entry) passes an activation token+    // in `XDG_ACTIVATION_TOKEN` (Wayland) or `DESKTOP_STARTUP_ID` (X11) and shows a busy cursor+    // until a window hands it back. eframe never gives winit a token, so GNOME kept the busy+    // cursor for 15 seconds and the window opened without focus. W30 gives the first window the+    // environment's token. The policy is tested beside it (`startup_token::tests`, run them in the+    // winit fork); this guards the pin: moving winit to a revision without W30 would silently+    // bring the bug back. Drop it once winit or eframe hands the token back.+    let linux = winit_source().join("src/platform_impl/linux");+    let module = std::fs::read_to_string(linux.join("mod.rs")).unwrap();+    assert!(module.contains("mod startup_token;"), "the pinned winit lacks W30");+    let (_, new) = module.split_once("    pub(crate) fn new(\n        window_target: &ActiveEventLoop,").expect("Window::new moved");+    let new = new.split_once("\n    }\n").map_or(new, |(body, _)| body);+    assert!(new.contains("startup_token::with_token(given, startup_notify::WAYLAND_VAR,"), "a Wayland window no longer takes the launcher's token");+    assert!(new.contains("startup_token::with_token(given, startup_notify::X11_VAR,"), "an X11 window no longer takes the launcher's token");+    let policy = std::fs::read_to_string(linux.join("startup_token.rs")).unwrap();+    assert!(policy.contains("fn the_first_window_takes_the_launchers_token"));+    assert!(policy.contains("fn a_later_window_does_not_reuse_the_spent_token"));+    assert!(policy.contains("fn a_failed_window_leaves_the_token_for_a_retry"));+    // The token reaches the app only when the launcher asks for startup notification.+    let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../..");+    let desktop = std::fs::read_to_string(root.join("packaging/linux/ai.storyteller.cadcraft.desktop")).unwrap();+    assert!(desktop.contains("StartupNotify=true"));+}