Files
dtourolle fa4dca327f Give the desktop executable a version flag and a Windows identity
Three things the Windows build showed the entry point was missing, and
that a Linux build never asks for.

`--version`, answered before the logger and the crash hook install: a
binary built on a machine that cannot run the application — the Linux
CI producing the Windows executable, checked under Wine — needs an
exit that proves it starts without opening a window or touching the
user's directories. It is the smoke test in docs/windows.md §6.

A GUI-subsystem executable in release, or Windows keeps a console
window open behind the application for the life of the process. Debug
builds keep the console, which is where their log goes.

A resource block, or Explorer, the Start Menu and the taskbar show the
generic executable icon and the Details tab is empty. build.rs wraps
the PNG every other platform uses into an .ico at build time — an ICO
entry may be a PNG, so the wrapper is a 22-byte header — and hands it
to winresource with the version cargo already knows. The crate is an
unconditional build-dependency because a cfg(windows) on one is
evaluated against the host, which here is Linux; the script itself
returns before touching it on any other target.
2026-09-12 00:54:11 +02:00

59 lines
2.6 KiB
Rust

//! TRACES: FR-PLAT-WIN-2
//! The Windows resource block: icon and version, compiled into the executable.
//!
//! Windows takes an application's icon and its "Details" tab from a resource
//! inside the `.exe`, not from a `.desktop` file, so without this the installed
//! program shows the generic executable icon in Explorer, the Start Menu and
//! the taskbar, and reports no version. Nothing here runs for any other
//! target: the whole body is behind the target-OS check, and the crate that
//! does the work is a build-dependency only.
//!
//! The icon is the same PNG every other platform uses, wrapped into an `.ico`
//! in `OUT_DIR` rather than committed: an ICO entry may *be* a PNG (Vista and
//! later read them directly), so the wrapper is a 22-byte header and the
//! file's bytes, and a generated binary stays out of the tree.
use std::io::Write as _;
use std::path::PathBuf;
fn main() {
println!("cargo:rerun-if-changed=build.rs");
if std::env::var("CARGO_CFG_TARGET_OS").as_deref() != Ok("windows") {
return;
}
let png = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../ui/dr-ui/ui/app-icon.png");
println!("cargo:rerun-if-changed={}", png.display());
let bytes = std::fs::read(&png).expect("read app-icon.png");
let ico = PathBuf::from(std::env::var("OUT_DIR").unwrap()).join("darkroom.ico");
write_png_ico(&ico, &bytes, 256).expect("write darkroom.ico");
let mut res = winresource::WindowsResource::new();
res.set_icon(ico.to_str().unwrap());
res.set("ProductName", "DarkRoom");
res.set("FileDescription", "DarkRoom");
res.set("LegalCopyright", "GPL-3.0-or-later");
// Cross-compiling: `winresource` looks for a `windres` for the target and
// the Windows image names it explicitly, for the same reason the Android
// image names its linkers.
if let Ok(windres) = std::env::var("WINDRES") {
res.set_windres_path(&windres);
}
res.compile().expect("compile the Windows resource block");
}
/// One PNG image as an `.ico`. `edge` is the PNG's width and height; 256 is
/// written as 0 per the format.
fn write_png_ico(path: &std::path::Path, png: &[u8], edge: u32) -> std::io::Result<()> {
let mut f = std::fs::File::create(path)?;
let dim = if edge >= 256 { 0u8 } else { edge as u8 };
// ICONDIR: reserved, type 1 (icon), one image.
f.write_all(&[0, 0, 1, 0, 1, 0])?;
// ICONDIRENTRY: width, height, palette 0, reserved, planes 1, bpp 32,
// byte length, offset (6 + 16).
f.write_all(&[dim, dim, 0, 0, 1, 0, 32, 0])?;
f.write_all(&(png.len() as u32).to_le_bytes())?;
f.write_all(&22u32.to_le_bytes())?;
f.write_all(png)
}