From 858b6e9dee496d701a60b2415bcfd8f01ab38534 Mon Sep 17 00:00:00 2001 From: jyviko <2501648+jyviko@users.noreply.github.com> Date: Wed, 14 Oct 2026 07:45:24 -0400 Subject: [PATCH] app: reverse-DNS app id and the Windows executable icon The window's Wayland app_id / X11 class is io.github.jyviko.fieldcraft, the id the release packages use for the .desktop entry and the macOS bundle (#72). Windows builds embed assets/app-icon/icon.ico in fieldcraft.exe through embed-resource (MIT, a build dependency only). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm --- Cargo.lock | 75 +++++++++++++++++++++++++++++++++++++ apps/fieldcraft/Cargo.toml | 4 ++ apps/fieldcraft/build.rs | 20 ++++++++++ apps/fieldcraft/src/main.rs | 4 ++ 4 files changed, 103 insertions(+) create mode 100644 apps/fieldcraft/build.rs diff --git a/Cargo.lock b/Cargo.lock index f1a4ecd..9974cf3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1035,6 +1035,20 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "embed-resource" +version = "3.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6168487b5e7eb2673e6efe1200b1848016e436a6ca0b8031bf04bd9d493a249f" +dependencies = [ + "cc", + "memchr", + "rustc_version", + "toml", + "vswhom", + "winreg", +] + [[package]] name = "endi" version = "1.1.1" @@ -1177,6 +1191,7 @@ version = "0.1.0" dependencies = [ "eframe", "egui", + "embed-resource", "fieldcraft-engine", "fieldcraft-io", "fieldcraft-ui-egui", @@ -3206,6 +3221,15 @@ dependencies = [ "syn 3.0.9", ] +[[package]] +name = "serde_spanned" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7523beb55eece201a2356bee0bbca0d1ab466c14c07703b2e0ee6d42cb0c2c" +dependencies = [ + "serde_core", +] + [[package]] name = "sha2" version = "0.10.9" @@ -3658,6 +3682,21 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "toml" +version = "1.1.8+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20489e00e4d8741d6be680764cc12e270655e375a20d1011e844a9c3379e678d" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow", +] + [[package]] name = "toml_datetime" version = "1.1.2+spec-1.1.0" @@ -3688,6 +3727,12 @@ dependencies = [ "winnow", ] +[[package]] +name = "toml_writer" +version = "1.1.3+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06bdbd8cfc056b8d2e2e85f29b56a3bdbecb527cef81eb39e3e7b98af4652770" + [[package]] name = "tracing" version = "0.1.44" @@ -3981,6 +4026,26 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "vswhom" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" +dependencies = [ + "libc", + "vswhom-sys", +] + +[[package]] +name = "vswhom-sys" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb067e4cbd1ff067d1df46c9194b5de0e98efd2810bbc95c5d5e5f25a3231150" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "walkdir" version = "2.5.0" @@ -4750,6 +4815,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d6f32a0ff4a9f6f01231eb2059cc85479330739333e0e58cadf03b6af2cca10" +dependencies = [ + "cfg-if", + "windows-sys 0.61.2", +] + [[package]] name = "wit-bindgen" version = "0.57.1" diff --git a/apps/fieldcraft/Cargo.toml b/apps/fieldcraft/Cargo.toml index 5fa82c5..edc30f6 100644 --- a/apps/fieldcraft/Cargo.toml +++ b/apps/fieldcraft/Cargo.toml @@ -18,3 +18,7 @@ eframe = { workspace = true, features = ["default_fonts", "wgpu", "accesskit", " egui = { workspace = true } serde_json = { workspace = true } log = { workspace = true } + +[build-dependencies] +# The Windows executable's icon resource (build.rs). +embed-resource = "3.0.12" diff --git a/apps/fieldcraft/build.rs b/apps/fieldcraft/build.rs new file mode 100644 index 0000000..12ebbaa --- /dev/null +++ b/apps/fieldcraft/build.rs @@ -0,0 +1,20 @@ +//! Windows builds embed the application icon (assets/app-icon/icon.ico) in `fieldcraft.exe`, so +//! Explorer, the taskbar and shortcuts show it. Other targets need nothing here. +#![forbid(unsafe_code)] + +use std::path::PathBuf; + +fn main() -> Result<(), Box> { + let icon = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR")?).join("../../assets/app-icon/icon.ico"); + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed={}", icon.display()); + if std::env::var("CARGO_CFG_TARGET_OS")? != "windows" { + return Ok(()); + } + let out = PathBuf::from(std::env::var("OUT_DIR")?); + let rc = out.join("fieldcraft.rc"); + std::fs::write(&rc, format!("1 ICON \"{}\"\n", icon.display().to_string().replace('\\', "/")))?; + // Missing resource tools are a build error, rather than silently shipping a generic icon. + embed_resource::compile_for(&rc, ["fieldcraft"], embed_resource::NONE).manifest_required()?; + Ok(()) +} diff --git a/apps/fieldcraft/src/main.rs b/apps/fieldcraft/src/main.rs index cacfe2e..794842e 100644 --- a/apps/fieldcraft/src/main.rs +++ b/apps/fieldcraft/src/main.rs @@ -22,6 +22,9 @@ const DEPTH_BITS: u8 = 24; /// The window draws its own caption buttons where the OS ones would sit apart from the app bar. const CUSTOM_TITLEBAR: bool = !cfg!(target_os = "macos"); const MSAA: u16 = 4; +/// The reverse-DNS application id: the Wayland app_id / X11 class, matched to the packaged +/// `.desktop` entry (packaging/linux), and the macOS bundle id (packaging/macos/Info.plist.in). +const APP_ID: &str = "io.github.jyviko.fieldcraft"; /// The window and Dock icon: the 256 px export of the application icon (assets/app-icon). const ICON_PNG: &[u8] = include_bytes!("../../../assets/app-icon/icon.iconset/icon_128x128@2x.png"); @@ -148,6 +151,7 @@ fn main() -> eframe::Result { let options = eframe::NativeOptions { viewport: egui::ViewportBuilder::default() .with_title("FieldCraft") + .with_app_id(APP_ID) .with_icon(app_icon()) .with_inner_size([1400.0, 900.0]) .with_min_inner_size([960.0, 600.0])