1use std::path::PathBuf;
8use std::sync::{Arc, Mutex};
9use std::time::Duration;
10
11use crate::launch::{DEFAULT_READY_TIMEOUT, LaunchFail, launch, launch_workdir, unique_marker};
12use crate::pick::write_pick_in;
13
14#[derive(Default)]
16pub struct RunShared {
17 pub handle: Mutex<Option<std::thread::JoinHandle<()>>>,
19 pub outcome: Mutex<Option<Result<(), LaunchFail>>>,
21}
22
23pub fn run_pick_launcher() -> Result<(), String> {
25 let program = std::env::current_exe().map_err(|e| format!("current_exe: {e:?}"))?;
26 let shared = Arc::new(RunShared::default());
27 let app = crate::launcher_ui::PickApp::new(program.clone(), shared.clone());
28
29 let opts = eframe::NativeOptions {
30 viewport: egui::ViewportBuilder::default()
31 .with_inner_size([460.0, 460.0])
32 .with_title("ek 启动选择"),
33 ..Default::default()
34 };
35 let window = eframe::run_native(
36 "ek-launch",
37 opts,
38 Box::new(|cc| {
39 crate::fonts::apply_cjk_fonts(&cc.egui_ctx); Ok(Box::new(app) as Box<dyn eframe::App>)
41 }),
42 );
43
44 if let Some(h) = shared.handle.lock().unwrap().take() {
46 let _ = h.join();
47 }
48 window.map_err(|e| format!("pick window: {e:?}"))?;
49
50 match shared.outcome.lock().unwrap().take() {
51 None => Ok(()), Some(Ok(())) => Ok(()), Some(Err(e)) => Err(e.to_string()), }
55}
56
57pub fn launch_worker(shared: &RunShared, names: &[String], program: PathBuf) {
60 let dir = launch_workdir();
61 let names_ref: Vec<&str> = names.iter().map(String::as_str).collect();
62 let outcome = (|| -> Result<(), LaunchFail> {
63 let pick_path = write_pick_in(&dir, &names_ref)?;
64 let marker = unique_marker(&dir);
65 let args = vec![
66 "--from".to_string(),
67 pick_path.to_string_lossy().into_owned(),
68 "--ready-file".to_string(),
69 marker.to_string_lossy().into_owned(),
70 ];
71 launch(&program, &args, &marker, DEFAULT_READY_TIMEOUT)
72 })();
73 *shared.outcome.lock().unwrap() = Some(outcome);
74}
75
76pub const POLL: Duration = Duration::from_millis(200);