From 40adfaf81026507aa157528d3a742d6187d95d56 Mon Sep 17 00:00:00 2001 From: Jake Stanger Date: Mon, 14 Jul 2025 21:42:11 +0100 Subject: [PATCH] fix: opening programs via `launcher` and `menu` leaving zombie processes Fixes #1020 --- src/desktop_file.rs | 13 +++++++++---- src/modules/launcher/mod.rs | 2 +- src/modules/menu/ui.rs | 8 +++++++- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/desktop_file.rs b/src/desktop_file.rs index ce54730..f5c285b 100644 --- a/src/desktop_file.rs +++ b/src/desktop_file.rs @@ -3,9 +3,10 @@ use color_eyre::{Help, Report, Result}; use std::collections::HashMap; use std::env; use std::path::{Path, PathBuf}; -use std::process::{Command, Stdio}; +use std::process::Stdio; use std::sync::Arc; use tokio::io::{AsyncBufReadExt, BufReader}; +use tokio::process::Command; use tokio::sync::Mutex; use tracing::{debug, error}; use walkdir::{DirEntry, WalkDir}; @@ -325,20 +326,24 @@ fn files(dir: &Path) -> Vec { } /// Starts a `.desktop` file with the provided formatted command. -pub fn open_program(file_name: &str, str: &str) { - let expanded = str.replace("{app_name}", file_name); +pub async fn open_program(file_name: &str, launch_command: &str) { + let expanded = launch_command.replace("{app_name}", file_name); let launch_command_parts: Vec<&str> = expanded.split_whitespace().collect(); + + debug!("running {launch_command_parts:?}"); if let Err(err) = Command::new(launch_command_parts[0]) .args(&launch_command_parts[1..]) + .stdin(Stdio::null()) .stdout(Stdio::null()) .stderr(Stdio::null()) + .kill_on_drop(true) .spawn() { error!( "{:?}", Report::new(err) .wrap_err("Failed to run launch command.") - .suggestion("Perhaps the applications file is invalid?") + .suggestion("Perhaps the desktop file is invalid or orphaned?") ); } } diff --git a/src/modules/launcher/mod.rs b/src/modules/launcher/mod.rs index 0bdfbb2..4420f40 100644 --- a/src/modules/launcher/mod.rs +++ b/src/modules/launcher/mod.rs @@ -388,7 +388,7 @@ impl Module for LauncherModule { if let ItemEvent::OpenItem(app_id) = event { match desktop_files.find(&app_id).await { Ok(Some(file)) => { - open_program(&file.file_name, &launch_command_str); + open_program(&file.file_name, &launch_command_str).await; } Ok(None) => warn!("Could not find applications file for {}", app_id), Err(err) => error!("Failed to find parse file for {}: {}", app_id, err), diff --git a/src/modules/menu/ui.rs b/src/modules/menu/ui.rs index 7c7cb1e..7f73871 100644 --- a/src/modules/menu/ui.rs +++ b/src/modules/menu/ui.rs @@ -100,7 +100,13 @@ where let tx = tx.clone(); button.connect_clicked(move |_button| { - open_program(&file_name, &command); + // TODO: this needs refactoring to call open from the controller + let file_name = file_name.clone(); + let command = command.clone(); + + glib::spawn_future_local(async move { + open_program(&file_name, &command).await + }); sub_menu.hide(); tx.send_spawn(ModuleUpdateEvent::ClosePopup);