diff --git a/qt/launcher/src/main.rs b/qt/launcher/src/main.rs index d8903140d..e6da056cf 100644 --- a/qt/launcher/src/main.rs +++ b/qt/launcher/src/main.rs @@ -6,7 +6,6 @@ use std::io::stdin; use std::io::stdout; use std::io::Write; -use std::os::unix::process::CommandExt; use std::process::Command; use std::time::SystemTime; use std::time::UNIX_EPOCH; @@ -25,8 +24,8 @@ use anyhow::Result; use crate::platform::ensure_terminal_shown; use crate::platform::get_exe_and_resources_dirs; use crate::platform::get_uv_binary_name; -use crate::platform::launch_anki_after_update; use crate::platform::launch_anki_normally; +use crate::platform::respawn_launcher; mod platform; @@ -148,8 +147,8 @@ fn run() -> Result<()> { println!("\x1B[1mYou can close this window.\x1B[0m\n"); } - let cmd = build_python_command(&state.uv_install_root, &[])?; - launch_anki_after_update(cmd)?; + // respawn the launcher as a disconnected subprocess for normal startup + respawn_launcher()?; Ok(()) } @@ -493,7 +492,7 @@ fn build_python_command(uv_install_root: &std::path::Path, args: &[String]) -> R }; let mut cmd = Command::new(python_exe); - cmd.args(["-c", "import aqt; aqt.run()"]); + cmd.args(["-c", "import aqt, sys; sys.argv[0] = 'Anki'; aqt.run()"]); cmd.args(args); // tell the Python code it was invoked by the launcher, and updating is // available diff --git a/qt/launcher/src/platform/mod.rs b/qt/launcher/src/platform/mod.rs index 9dc74f8e9..c94f1d1ac 100644 --- a/qt/launcher/src/platform/mod.rs +++ b/qt/launcher/src/platform/mod.rs @@ -49,10 +49,32 @@ pub fn get_uv_binary_name() -> &'static str { } } -pub fn launch_anki_after_update(mut cmd: std::process::Command) -> Result<()> { +pub fn respawn_launcher() -> Result<()> { use std::process::Stdio; - cmd.stdin(Stdio::null()) + let mut launcher_cmd = if cfg!(target_os = "macos") { + // On macOS, we need to launch the .app bundle, not the executable directly + let current_exe = + std::env::current_exe().context("Failed to get current executable path")?; + + // Navigate from Contents/MacOS/launcher to the .app bundle + let app_bundle = current_exe + .parent() // MacOS + .and_then(|p| p.parent()) // Contents + .and_then(|p| p.parent()) // .app + .context("Failed to find .app bundle")?; + + let mut cmd = std::process::Command::new("open"); + cmd.arg(app_bundle); + cmd + } else { + let current_exe = + std::env::current_exe().context("Failed to get current executable path")?; + std::process::Command::new(current_exe) + }; + + launcher_cmd + .stdin(Stdio::null()) .stdout(Stdio::null()) .stderr(Stdio::null()); @@ -61,16 +83,16 @@ pub fn launch_anki_after_update(mut cmd: std::process::Command) -> Result<()> { use std::os::windows::process::CommandExt; const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200; const DETACHED_PROCESS: u32 = 0x00000008; - cmd.creation_flags(CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS); + launcher_cmd.creation_flags(CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS); } - #[cfg(unix)] + #[cfg(all(unix, not(target_os = "macos")))] { use std::os::unix::process::CommandExt; - cmd.process_group(0); + launcher_cmd.process_group(0); } - let child = cmd.ensure_spawn()?; + let child = launcher_cmd.ensure_spawn()?; std::mem::forget(child); Ok(())