hyprshell_exec_lib/
run.rs

1use core_lib::{TERMINALS, Warn};
2use std::os::unix::prelude::CommandExt;
3use std::path::Path;
4use std::process::{Command, Stdio};
5use std::{env, io, thread};
6use tracing::{debug, error, info, trace};
7
8pub fn run_program(
9    run: &str,
10    path: Option<Box<Path>>,
11    terminal: bool,
12    default_terminal: &Option<Box<str>>,
13) {
14    debug!("Running: {run}");
15    if terminal {
16        if let Some(term) = default_terminal {
17            let command = format!("{term} -e {run}");
18            run_command(&command, &path).warn("Failed to run command");
19        } else {
20            let path_env = env::var_os("PATH").unwrap_or_default();
21            info!(
22                "No default terminal found, searching common terminals in PATH. (Set default_terminal in config to avoid this search)"
23            );
24            trace!("PATH: {}", path_env.to_string_lossy());
25            let paths: Vec<_> = env::split_paths(&path_env).collect();
26            let mut found_terminal = false;
27            for term in TERMINALS {
28                if paths.iter().any(|p| p.join(term).exists()) {
29                    let command = format!("{term} -e {run}");
30                    if run_command(&command, &path).is_ok() {
31                        trace!("Found and launched terminal: {term}");
32                        found_terminal = true;
33                        break;
34                    }
35                }
36            }
37            if !found_terminal {
38                error!("Failed to find a terminal to run the command");
39            }
40        }
41    } else {
42        run_command(run, &path).warn("Failed to run command");
43    }
44}
45
46fn get_command(command: &str) -> Command {
47    // replace common exec placeholders
48    let mut command = command.to_string();
49    for replacement in ["%f", "%F", "%u", "%U"] {
50        command = command.replace(replacement, "");
51    }
52    // if run as systemd unit all programs exit when not run outside the units cgroup
53    if env::var_os("INVOCATION_ID").is_some() {
54        let mut cmd = Command::new("systemd-run");
55        cmd.args(["--user", "--scope", "--collect", "sh", "-c", &command]);
56        cmd
57    } else {
58        let mut cmd = Command::new("sh");
59        cmd.args(["-c", &command]);
60        cmd
61    }
62}
63
64fn run_command(run: &str, path: &Option<Box<Path>>) -> io::Result<()> {
65    trace!("Original command: {:?}", run);
66    let mut cmd = get_command(run);
67    cmd.process_group(0);
68    if let Some(path) = path {
69        cmd.current_dir(path.as_ref());
70    }
71
72    debug!("Running command: {:?}", cmd);
73    let _out = cmd.stdout(Stdio::piped()).stderr(Stdio::piped()).spawn()?;
74
75    thread::spawn(move || {
76        let output = _out.wait_with_output();
77        if env::var_os("HYPRSHELL_SHOW_OUTPUT").is_some() {
78            if let Ok(output) = output {
79                if !output.stdout.is_empty() || !output.stderr.is_empty() {
80                    debug!("Output: {:?}", output);
81                }
82            }
83        }
84    });
85    Ok(())
86}