From 97f9c2fc5de430730c24b954736901f2a85a9f69 Mon Sep 17 00:00:00 2001 From: Barzabel Date: Wed, 14 Oct 2026 17:37:59 +0300 Subject: [PATCH] M1: say why when the window can't open (#261) Release builds on Windows have no console, so an eframe::run_native error (no usable graphics adapter, a device the driver refuses) went to an unseen standard error and the app quit silently. The error is now logged and shown in a message box with a hint: unset WGPU_BACKEND when it is set, otherwise update the driver or try another graphics API (OpenGL on Windows), and where to report it. With --control the box is skipped so agents aren't blocked. --- apps/figcraft/src/main.rs | 12 ++++++-- apps/figcraft/src/startup.rs | 44 ++++++++++++++++++++++++++++++ apps/figcraft/src/startup_tests.rs | 35 ++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 apps/figcraft/src/startup.rs create mode 100644 apps/figcraft/src/startup_tests.rs diff --git a/apps/figcraft/src/main.rs b/apps/figcraft/src/main.rs index 7b14d8e2..1babfd5b 100644 --- a/apps/figcraft/src/main.rs +++ b/apps/figcraft/src/main.rs @@ -17,6 +17,7 @@ mod gamepad; mod graphics; mod recovery; mod services; +mod startup; #[cfg(all(test, target_os = "macos"))] mod apple_events_tests; @@ -30,6 +31,8 @@ mod recovery_online_tests; mod recovery_tests; #[cfg(test)] mod services_tests; +#[cfg(test)] +mod startup_tests; use std::sync::{Arc, Mutex}; @@ -203,7 +206,7 @@ fn main() -> eframe::Result { let apple_events = apple_events::AppleEvents::install(); #[cfg(target_os = "macos")] let apple_events = &apple_events; - eframe::run_native( + let result = eframe::run_native( "FigCraft", options, Box::new(move |_cc| { @@ -216,5 +219,10 @@ fn main() -> eframe::Result { apple_events: apple_events.connect(&_cc.egui_ctx), })) }), - ) + ); + // Say why instead of quitting silently: release builds on Windows have no console. + if let Err(e) = &result { + startup::failed(e, port.is_some()); + } + result } diff --git a/apps/figcraft/src/startup.rs b/apps/figcraft/src/startup.rs new file mode 100644 index 00000000..8a50d7a5 --- /dev/null +++ b/apps/figcraft/src/startup.rs @@ -0,0 +1,44 @@ +//! When the window can't open. +//! +//! Release builds on Windows have no console, so an error from `eframe::run_native` (no usable +//! graphics adapter, a surface or device the driver refuses) went to a standard error nobody sees +//! and the app quit without a word (#261). Now it is logged and shown in a message box. + +/// Where a report goes. +const ISSUES: &str = "https://github.com/storytold/figcraft/issues"; + +/// The text shown when the window couldn't open with `error`. `backend_set`: `WGPU_BACKEND` named +/// the graphics API, so the hint is to unset it rather than to try another one. +pub fn message(error: &str, backend_set: bool) -> String { + let hint = if backend_set { + "WGPU_BACKEND is set and names the graphics API to use; unsetting it lets FigCraft choose." + } else if cfg!(windows) { + // Direct3D 12 and OpenGL are both offered (`graphics.rs`), but wgpu keeps the Direct3D 12 + // adapter it picked even when its device then fails, so naming OpenGL alone can still help. + "Updating the graphics driver often helps. To use OpenGL only, start FigCraft from a Command Prompt with:\n\nset WGPU_BACKEND=gl\nfigcraft.exe" + } else { + "Updating the graphics driver often helps. WGPU_BACKEND (vulkan, gl, metal) names another graphics API to try." + }; + format!("FigCraft couldn't open its window: {error}\n\n{hint}\n\nIf it keeps happening, please report it at {ISSUES} with this message.") +} + +/// Log the failure and, unless `quiet` (started for an agent with `--control`: a modal box would +/// keep the caller waiting, and it reads standard error), show it in a message box. +pub fn failed(error: &eframe::Error, quiet: bool) { + let text = message(&error.to_string(), std::env::var_os("WGPU_BACKEND").is_some()); + // Standard error even when RUST_LOG turned errors off. + if log::log_enabled!(log::Level::Error) { + log::error!("{text}"); + } else { + eprintln!("figcraft: {text}"); + } + if quiet { + return; + } + let _ = rfd::MessageDialog::new() + .set_level(rfd::MessageLevel::Error) + .set_title("FigCraft") + .set_description(text) + .set_buttons(rfd::MessageButtons::Ok) + .show(); +} diff --git a/apps/figcraft/src/startup_tests.rs b/apps/figcraft/src/startup_tests.rs new file mode 100644 index 00000000..6818ae77 --- /dev/null +++ b/apps/figcraft/src/startup_tests.rs @@ -0,0 +1,35 @@ +//! The message shown when the window can't open: it carries the error, a next step and where to +//! report it, instead of the app quitting silently (#261). + +use crate::startup::message; + +const ERROR: &str = "WGPU error: Failed to create surface for any enabled backend: {}"; + +#[test] +fn the_message_carries_the_error_and_where_to_report_it() { + let text = message(ERROR, false); + assert!(text.contains(ERROR), "the error itself: {text}"); + assert!(text.contains("https://github.com/storytold/figcraft/issues"), "where to report it: {text}"); +} + +#[test] +fn without_wgpu_backend_it_suggests_the_driver_and_another_graphics_api() { + let text = message(ERROR, false); + assert!(text.contains("graphics driver"), "{text}"); + assert!(text.contains("WGPU_BACKEND"), "another graphics API to try: {text}"); + assert!(!text.contains("unsetting"), "{text}"); +} + +#[cfg(windows)] +#[test] +fn on_windows_it_shows_how_to_start_with_opengl() { + let text = message(ERROR, false); + assert!(text.contains("set WGPU_BACKEND=gl"), "a release build has no console to look this up from: {text}"); +} + +#[test] +fn with_wgpu_backend_set_it_suggests_unsetting_it() { + let text = message(ERROR, true); + assert!(text.contains("unsetting"), "{text}"); + assert!(!text.contains("graphics driver"), "the chosen backend is the likelier cause: {text}"); +}