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
@@ -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",@@ -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" }new file mode 100644@@ -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"));+}