diff --git a/apps/lightcraft/src/main.rs b/apps/lightcraft/src/main.rs index 97aa338b..e0335e3f 100644 --- a/apps/lightcraft/src/main.rs +++ b/apps/lightcraft/src/main.rs @@ -523,11 +523,15 @@ fn main() -> eframe::Result { } // The log file: opened after the arguments, so `--version`, `--help` and a usage error leave // no file behind. Records logged until now are written to it first. + let mut log_file = None; if let Some(logger) = logger { // a --memory session writes nothing (issue #164), a log file included match log_dir().filter(|_| !in_memory) { Some(dir) => match logger.attach_dir(&dir) { - Ok(path) => log::info!("LightCraft {}, log file {}", env!("CARGO_PKG_VERSION"), path.display()), + Ok(path) => { + log::info!("LightCraft {}, log file {}", env!("CARGO_PKG_VERSION"), path.display()); + log_file = Some(path); + } // The file sink has given up by now, so this goes to standard error only. Err(e) => log::warn!("no log file: {e}"), }, @@ -535,6 +539,10 @@ fn main() -> eframe::Result { } } let (prefs, prefs_warning, keep_prefs_file) = load_prefs(in_memory); + // the saved language from the start, so a failed start is reported in it too + if let Some(ui) = &prefs { + lightcraft_ui_egui::i18n::set_language(ui.language); + } // --library, else the library last opened from Settings, else the default location let library_dir = library_dir.or_else(|| { prefs @@ -564,7 +572,7 @@ fn main() -> eframe::Result { wgpu_options: window_wgpu_options(), ..Default::default() }; - eframe::run_native( + let started = eframe::run_native( "LightCraft", options, Box::new(move |cc| { @@ -618,7 +626,28 @@ fn main() -> eframe::Result { menu, ))) }), - ) + ); + if let Err(e) = &started { + startup_failed(&e.to_string(), log_file.as_deref()); + } + started +} + +/// The window could not start (e.g. no usable graphics device): say so in the log and in a native +/// message box naming the log file, instead of exiting without a trace — the release builds have +/// no console on Windows, and an app started from Finder shows no standard error (issue #260). +fn startup_failed(error: &str, log_file: Option<&std::path::Path>) { + log::error!("LightCraft could not open its window: {error}"); + if std::env::var_os("LIGHTCRAFT_NO_PREFS").is_some() { + return; // tests and scripts: no dialog to click away + } + let (title, text) = lightcraft_ui_egui::i18n::startup_failed_message(error, log_file.map(|p| p.display().to_string()).as_deref()); + let _ = rfd::MessageDialog::new() + .set_level(rfd::MessageLevel::Error) + .set_title(title) + .set_description(text) + .set_buttons(rfd::MessageButtons::Ok) + .show(); } #[cfg(test)] diff --git a/crates/ui-egui/locales/de-formats.json b/crates/ui-egui/locales/de-formats.json index 7afca56e..adc0343d 100644 --- a/crates/ui-egui/locales/de-formats.json +++ b/crates/ui-egui/locales/de-formats.json @@ -67,6 +67,8 @@ "Imported {} photo{}": "{} Foto{} importiert", "Insert at the cursor (also written {})": "An Cursorposition einfügen (auch als {} geschrieben)", "Label Set: {name}": "Farbmarkierungssatz: {name}", + "LightCraft could not open its window: {e}": "LightCraft konnte sein Fenster nicht öffnen: {e}", + "LightCraft could not open its window: {e}\n\nThe log file has the details: {path}": "LightCraft konnte sein Fenster nicht öffnen: {e}\n\nEinzelheiten stehen in der Protokolldatei: {path}", "MIT OR Apache-2.0. Fonts: {} (OFL). Icons: original.": "MIT ODER Apache-2.0. Schriften: {} (OFL). Symbole: eigene Gestaltung.", "Matches: {}": "Treffer: {}", "Merging… {stage} {:.0}%": "Wird zusammengefügt… {stage} {:.0}%", diff --git a/crates/ui-egui/locales/de.json b/crates/ui-egui/locales/de.json index db33e0de..84a033c2 100644 --- a/crates/ui-egui/locales/de.json +++ b/crates/ui-egui/locales/de.json @@ -753,6 +753,7 @@ "LightCraft Preset": "LightCraft-Vorgabe", "LightCraft Presets": "LightCraft-Vorgaben", "LightCraft Website": "LightCraft-Website", + "LightCraft could not start": "LightCraft konnte nicht starten", "LightCraft couldn't find where to keep your library.": "LightCraft konnte keinen Speicherort für deine Bibliothek finden.", "LightCraft couldn't open the library at": "LightCraft konnte die Bibliothek nicht öffnen unter", "LightCraft no longer crashes at launch on Windows PCs whose Vulkan driver is broken (issue #136, e.g. some Intel UHD": "LightCraft stürzt beim Start auf Windows-PCs mit defektem Vulkan-Treiber nicht mehr ab (Issue #136, etwa bestimmte Intel-UHD-630-Treiber).", diff --git a/crates/ui-egui/locales/ja-formats.json b/crates/ui-egui/locales/ja-formats.json index 696426db..81e27cb0 100644 --- a/crates/ui-egui/locales/ja-formats.json +++ b/crates/ui-egui/locales/ja-formats.json @@ -35,6 +35,8 @@ "Imported {n} preset{}": "{n}件のプリセットを読み込みました{:.0}", "Imported {n} preset{}, {f} file{} not readable": "{n}件のプリセットを読み込みました{:.0}。{f}件は読み込めません{:.0}", "Imported {} photo{}": "{}枚を読み込みました{:.0}", + "LightCraft could not open its window: {e}": "LightCraftのウィンドウを開けませんでした: {e}", + "LightCraft could not open its window: {e}\n\nThe log file has the details: {path}": "LightCraftのウィンドウを開けませんでした: {e}\n\n詳しくはログファイルをご覧ください: {path}", "Matches: {}": "一致条件:{}", "Merging… {stage} {:.0}%": "合成中… {stage} {:.0}%", "Move {what} to Recently Deleted?": "{what}を「最近削除した写真」に移動しますか?", diff --git a/crates/ui-egui/locales/ja.json b/crates/ui-egui/locales/ja.json index 263cc742..d3e39f27 100644 --- a/crates/ui-egui/locales/ja.json +++ b/crates/ui-egui/locales/ja.json @@ -531,6 +531,7 @@ "Library State": "ライブラリの状態", "Library saved": "ライブラリを保存しました", "Licence:": "ライセンス:", + "LightCraft could not start": "LightCraftを起動できませんでした", "Line delta": "行数の増減", "Linear": "リニア", "Lines +/−": "行数 +/−", diff --git a/crates/ui-egui/locales/pt-br-formats.json b/crates/ui-egui/locales/pt-br-formats.json index 7e819683..14ae2f65 100644 --- a/crates/ui-egui/locales/pt-br-formats.json +++ b/crates/ui-egui/locales/pt-br-formats.json @@ -46,6 +46,8 @@ "Imported {n} preset{}, {f} file{} not readable": "Importadas {n} predefinição{:.0}, {f} arquivo{:.0} não legível", "Imported {} photo{}": "Importadas {} foto{:.0}", "Label Set: {name}": "Conjunto de rótulos: {name}", + "LightCraft could not open its window: {e}": "O LightCraft não conseguiu abrir sua janela: {e}", + "LightCraft could not open its window: {e}\n\nThe log file has the details: {path}": "O LightCraft não conseguiu abrir sua janela: {e}\n\nO arquivo de log tem os detalhes: {path}", "Matches: {}": "Correspondências: {}", "Merging… {stage} {:.0}%": "Mesclando… {stage} {:.0}%", "Move {what} to Recently Deleted?": "Mover {what} para Excluídos recentemente?", diff --git a/crates/ui-egui/locales/pt-br.json b/crates/ui-egui/locales/pt-br.json index c28c1948..6e7045bf 100644 --- a/crates/ui-egui/locales/pt-br.json +++ b/crates/ui-egui/locales/pt-br.json @@ -721,6 +721,7 @@ "LightCraft Preset": "Predefinição do LightCraft", "LightCraft Presets": "Predefinições do LightCraft", "LightCraft Website": "Site do LightCraft", + "LightCraft could not start": "Não foi possível iniciar o LightCraft", "LightCraft couldn't find where to keep your library.": "O LightCraft não conseguiu encontrar onde guardar sua biblioteca.", "LightCraft couldn't open the library at": "O LightCraft não conseguiu abrir a biblioteca em", "LightCraft on GitHub": "LightCraft no GitHub", diff --git a/crates/ui-egui/locales/ru-formats.json b/crates/ui-egui/locales/ru-formats.json index 40c87ee3..b74a1ac8 100644 --- a/crates/ui-egui/locales/ru-formats.json +++ b/crates/ui-egui/locales/ru-formats.json @@ -71,6 +71,8 @@ "Insert at the cursor (also written {})": "Вставить в позицию курсора (также пишется как {})", "Label Set: {name}": "Набор меток: {name}", "LightCraft can't decode this raw variant yet ({}). You're editing the camera's embedded JPEG preview, which already includes the camera's picture style (e.g. Monochrome) and white balance.": "LightCraft пока не умеет декодировать этот вариант RAW ({}). Вы редактируете встроенный в файл JPEG-просмотр камеры, в котором уже применены стиль изображения камеры (например, «Монохром») и баланс белого.", + "LightCraft could not open its window: {e}": "LightCraft не удалось открыть окно: {e}", + "LightCraft could not open its window: {e}\n\nThe log file has the details: {path}": "LightCraft не удалось открыть окно: {e}\n\nПодробности в файле журнала: {path}", "MIT OR Apache-2.0. Fonts: {} (OFL). Icons: original.": "MIT OR Apache-2.0. Шрифты: {} (OFL). Значки: оригинальные.", "Matches: {}": "Совпадения: {}", "Merging… {stage} {:.0}%": "Слияние… {stage} {:.0}%", diff --git a/crates/ui-egui/locales/ru.json b/crates/ui-egui/locales/ru.json index 913c85ad..6cb076db 100644 --- a/crates/ui-egui/locales/ru.json +++ b/crates/ui-egui/locales/ru.json @@ -590,6 +590,7 @@ "LightCraft Preset": "Пресет LightCraft", "LightCraft Presets": "Пресеты LightCraft", "LightCraft Website": "Сайт LightCraft", + "LightCraft could not start": "Не удалось запустить LightCraft", "LightCraft couldn't find where to keep your library.": "LightCraft не удалось найти место для хранения вашей библиотеки.", "LightCraft couldn't open the library at": "LightCraft не удалось открыть библиотеку в", "LightCraft on GitHub": "LightCraft на GitHub", diff --git a/crates/ui-egui/locales/zh-hans-formats.json b/crates/ui-egui/locales/zh-hans-formats.json index 9a27c6b2..d342e30f 100644 --- a/crates/ui-egui/locales/zh-hans-formats.json +++ b/crates/ui-egui/locales/zh-hans-formats.json @@ -35,6 +35,8 @@ "Imported {n} preset{}": "已导入 {n} 个预设{0:.0}", "Imported {n} preset{}, {f} file{} not readable": "已导入 {n} 个预设,{f} 个文件无法读取{0:.0}{1:.0}", "Imported {} photo{}": "已导入 {0} 张照片{1:.0}", + "LightCraft could not open its window: {e}": "LightCraft 无法打开窗口:{e}", + "LightCraft could not open its window: {e}\n\nThe log file has the details: {path}": "LightCraft 无法打开窗口:{e}\n\n详细信息见日志文件:{path}", "Matches: {}": "匹配项:{0}", "Merging… {stage} {:.0}%": "合成中… {stage} {0:.0}%", "Move {what} to Recently Deleted?": "将 {what} 移到最近删除?", diff --git a/crates/ui-egui/locales/zh-hans.json b/crates/ui-egui/locales/zh-hans.json index 75013731..c42e2abd 100644 --- a/crates/ui-egui/locales/zh-hans.json +++ b/crates/ui-egui/locales/zh-hans.json @@ -523,6 +523,7 @@ "Lens Blur": "镜头模糊", "Library saved": "图库已保存", "Licence:": "许可证:", + "LightCraft could not start": "LightCraft 无法启动", "Line delta": "行数差", "Linear": "线性", "Lines +/−": "行数 +/−", diff --git a/crates/ui-egui/locales/zh-hant-formats.json b/crates/ui-egui/locales/zh-hant-formats.json index b9d0b808..674a1810 100644 --- a/crates/ui-egui/locales/zh-hant-formats.json +++ b/crates/ui-egui/locales/zh-hant-formats.json @@ -43,6 +43,8 @@ "Imported {n} preset{}, {f} file{} not readable": "已匯入 {n} 個預設集{:.0},{f} 個檔案無法讀取{:.0}", "Imported {} photo{}": "已匯入 {} 張照片{:.0}", "Label Set: {name}": "標籤集:{name}", + "LightCraft could not open its window: {e}": "LightCraft 無法開啟視窗:{e}", + "LightCraft could not open its window: {e}\n\nThe log file has the details: {path}": "LightCraft 無法開啟視窗:{e}\n\n詳細資訊請見記錄檔:{path}", "Matches: {}": "符合條件:{}", "Merging… {stage} {:.0}%": "正在合併… {stage} {:.0}%", "Move {what} to Recently Deleted?": "要將{what}移至「最近刪除」嗎?", diff --git a/crates/ui-egui/locales/zh-hant.json b/crates/ui-egui/locales/zh-hant.json index 278255b4..c097f4f6 100644 --- a/crates/ui-egui/locales/zh-hant.json +++ b/crates/ui-egui/locales/zh-hant.json @@ -523,6 +523,7 @@ "Lens Blur": "鏡頭模糊", "Library saved": "照片庫已儲存", "Licence:": "授權:", + "LightCraft could not start": "LightCraft 無法啟動", "Line delta": "行數差", "Linear": "線性", "Lines +/−": "行數 +/−", diff --git a/crates/ui-egui/src/i18n.rs b/crates/ui-egui/src/i18n.rs index cba28585..76999b25 100644 --- a/crates/ui-egui/src/i18n.rs +++ b/crates/ui-egui/src/i18n.rs @@ -189,6 +189,16 @@ include!(concat!(env!("OUT_DIR"), "/tr-formats.rs")); /// A stock preset's or profile's name in the UI language; an imported or user-edited name stays /// exactly as its owner wrote it. +/// The title and text of the message box shown when the desktop app's window can't start +/// (issue #260), in the UI language: the error and, when there is one, the log file to look in. +pub fn startup_failed_message(error: &str, log_file: Option<&str>) -> (String, String) { + let text = match log_file { + Some(path) => tr_format!("LightCraft could not open its window: {e}\n\nThe log file has the details: {path}", e = error, path = path), + None => tr_format!("LightCraft could not open its window: {e}", e = error), + }; + (tr("LightCraft could not start").to_string(), text) +} + pub fn builtin_label(source: &str, builtin: bool) -> &str { if builtin { tr(source) } else { source } } @@ -404,6 +414,16 @@ mod tests { } /// Locale tags are matched leniently, so a system locale reaches a shipped language. + /// The message box for a desktop start that failed (issue #260) names the error and the log + /// file, in the UI language. + #[test] + fn startup_failure_message_names_the_error_and_the_log() { + let (title, text) = startup_failed_message("no adapter", Some("/home/a/.config/lightcraft/logs/lightcraft.log")); + assert_eq!(title, "LightCraft could not start"); + assert!(text.contains("no adapter") && text.ends_with("lightcraft.log"), "{text}"); + assert!(!startup_failed_message("no adapter", None).1.contains("log file")); + } + #[test] fn locale_tags_normalize() { assert_eq!(Locale::parse_tag("en"), Some(Locale::En)); diff --git a/docs/whats-new.md b/docs/whats-new.md index 05f8dbfb..9569853a 100644 --- a/docs/whats-new.md +++ b/docs/whats-new.md @@ -63,6 +63,8 @@ promise, instead of filtering whatever album or folder was open, which often showed nothing (issue #341). ### Reliability +- If the desktop app can't open its window (for example when no graphics device can be used), it now says so in a + message box that names the log file, instead of quitting without a trace (issue #260). - On macOS, single-key shortcuts that appear in the menu bar now work: E, C, H, M, ⇧P, I, K, D, ratings 0–5, labels 6–9, P / U and the rest did nothing, because macOS only passes ⌘ / ⌃ combinations and function keys to the menu bar and the app ignored those keys, assuming the menu bar would handle them. Keys outside the menus