fix #67
This commit is contained in:
parent
f1aceb18eb
commit
2983ddb49e
6 changed files with 93 additions and 60 deletions
|
@ -2,9 +2,7 @@ use crate::Error;
|
|||
use clap::{Parser, ValueEnum};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use std::os::unix::process::CommandExt;
|
||||
use std::path::PathBuf;
|
||||
use std::process::{Command, Stdio};
|
||||
use std::str::FromStr;
|
||||
use std::{env, fs};
|
||||
use thiserror::Error;
|
||||
|
@ -804,35 +802,6 @@ fn merge_json(a: &mut Value, b: &Value) {
|
|||
}
|
||||
}
|
||||
|
||||
/// Fork into background if configured
|
||||
/// # Panics
|
||||
/// Panics if preexec and or setsid do not work
|
||||
pub fn fork_if_configured(config: &Config) {
|
||||
let fork_env_var = "WORF_PROCESS_IS_FORKED";
|
||||
if config.fork() && env::var(fork_env_var).is_err() {
|
||||
let mut cmd = Command::new(env::current_exe().expect("Failed to get current executable"));
|
||||
|
||||
for arg in env::args().skip(1) {
|
||||
cmd.arg(arg);
|
||||
}
|
||||
|
||||
cmd.env(fork_env_var, "1");
|
||||
cmd.stdin(Stdio::null())
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null());
|
||||
|
||||
unsafe {
|
||||
cmd.pre_exec(|| {
|
||||
libc::setsid();
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
cmd.spawn().expect("Failed to fork to background");
|
||||
std::process::exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
use freedesktop_file_parser::DesktopFile;
|
||||
use notify_rust::Notification;
|
||||
use rayon::prelude::*;
|
||||
use regex::Regex;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::OsStr;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::os::unix::prelude::CommandExt;
|
||||
use std::path::Path;
|
||||
|
@ -6,15 +11,10 @@ use std::path::PathBuf;
|
|||
use std::process::{Command, Stdio};
|
||||
use std::time::Instant;
|
||||
use std::{env, fs, io};
|
||||
|
||||
use freedesktop_file_parser::DesktopFile;
|
||||
use notify_rust::Notification;
|
||||
use rayon::prelude::*;
|
||||
use regex::Regex;
|
||||
use wl_clipboard_rs::copy::{ClipboardType, MimeType, ServeRequests, Source};
|
||||
|
||||
use crate::Error;
|
||||
use crate::config::expand_path;
|
||||
use crate::config::{Config, expand_path};
|
||||
|
||||
/// Returns a regex with supported image extensions
|
||||
/// # Panics
|
||||
|
@ -132,6 +132,23 @@ pub fn lookup_name_with_locale(
|
|||
.or_else(|| Some(fallback.to_owned()))
|
||||
}
|
||||
|
||||
/// Fork into background if configured
|
||||
/// # Panics
|
||||
/// Panics if preexec and or setsid do not work
|
||||
pub fn fork_if_configured(config: &Config) {
|
||||
let fork_env_var = "WORF_PROCESS_IS_FORKED";
|
||||
if config.fork() && env::var(fork_env_var).is_err() {
|
||||
let mut cmd = Command::new(env::current_exe().expect("Failed to get current executable"));
|
||||
|
||||
for arg in env::args().skip(1) {
|
||||
cmd.arg(arg);
|
||||
}
|
||||
|
||||
start_forked_cmd(cmd).expect("Failed to fork to background");
|
||||
std::process::exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
/// Spawn a new process and forks it away from the current worf process
|
||||
/// # Errors
|
||||
/// * No action in menu item
|
||||
|
@ -169,18 +186,32 @@ pub fn spawn_fork(cmd: &str, working_dir: Option<&String>) -> Result<(), Error>
|
|||
.map(|arg| expand_path(arg))
|
||||
.collect();
|
||||
|
||||
start_forked(&exec, args)
|
||||
}
|
||||
|
||||
fn start_forked<I, S>(exec: &str, args: I) -> Result<(), Error>
|
||||
where
|
||||
I: IntoIterator<Item = S>,
|
||||
S: AsRef<OsStr>,
|
||||
{
|
||||
let mut cmd = Command::new(exec);
|
||||
cmd.args(args);
|
||||
start_forked_cmd(cmd)
|
||||
}
|
||||
|
||||
fn start_forked_cmd(mut cmd: Command) -> Result<(), Error> {
|
||||
cmd.stdin(Stdio::null())
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null());
|
||||
|
||||
unsafe {
|
||||
let _ = Command::new(exec)
|
||||
.args(args)
|
||||
.stdin(Stdio::null())
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.pre_exec(|| {
|
||||
libc::setsid();
|
||||
Ok(())
|
||||
})
|
||||
.spawn();
|
||||
cmd.pre_exec(|| {
|
||||
libc::setsid();
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
cmd.spawn().map_err(|e| Error::Io(e.to_string()))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,12 @@ struct AutoItemProvider {
|
|||
impl AutoItemProvider {
|
||||
fn new(config: &Config) -> Self {
|
||||
AutoItemProvider {
|
||||
drun: DRunProvider::new(AutoRunType::DRun, config.no_actions(), config.sort_order()),
|
||||
drun: DRunProvider::new(
|
||||
AutoRunType::DRun,
|
||||
config.no_actions(),
|
||||
config.sort_order(),
|
||||
config.term(),
|
||||
),
|
||||
file: FileItemProvider::new(AutoRunType::File, config.sort_order()),
|
||||
math: MathProvider::new(AutoRunType::Math),
|
||||
ssh: SshProvider::new(AutoRunType::Ssh, &config.sort_order()),
|
||||
|
|
|
@ -26,6 +26,7 @@ pub(crate) struct DRunProvider<T: Clone> {
|
|||
data: T,
|
||||
no_actions: bool,
|
||||
sort_order: SortOrder,
|
||||
terminal: Option<String>,
|
||||
}
|
||||
|
||||
impl<T: Clone + Send + Sync> ItemProvider<T> for DRunProvider<T> {
|
||||
|
@ -42,7 +43,12 @@ impl<T: Clone + Send + Sync> ItemProvider<T> for DRunProvider<T> {
|
|||
}
|
||||
|
||||
impl<T: Clone + Send + Sync> DRunProvider<T> {
|
||||
pub(crate) fn new(menu_item_data: T, no_actions: bool, sort_order: SortOrder) -> Self {
|
||||
pub(crate) fn new(
|
||||
menu_item_data: T,
|
||||
no_actions: bool,
|
||||
sort_order: SortOrder,
|
||||
terminal: Option<String>,
|
||||
) -> Self {
|
||||
let (cache_path, d_run_cache) = load_d_run_cache();
|
||||
DRunProvider {
|
||||
items: None,
|
||||
|
@ -51,6 +57,7 @@ impl<T: Clone + Send + Sync> DRunProvider<T> {
|
|||
data: menu_item_data,
|
||||
no_actions,
|
||||
sort_order,
|
||||
terminal,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -74,8 +81,8 @@ impl<T: Clone + Send + Sync> DRunProvider<T> {
|
|||
&file.entry.name.default,
|
||||
)?;
|
||||
|
||||
let (action, working_dir) = match &file.entry.entry_type {
|
||||
EntryType::Application(app) => (app.exec.clone(), app.path.clone()),
|
||||
let (action, working_dir, in_terminal) = match &file.entry.entry_type {
|
||||
EntryType::Application(app) => (app.exec.clone(), app.path.clone(), app.terminal.unwrap_or(false)),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
|
@ -106,12 +113,13 @@ impl<T: Clone + Send + Sync> DRunProvider<T> {
|
|||
let mut entry = MenuItem::new(
|
||||
name.clone(),
|
||||
icon.clone(),
|
||||
action.clone(),
|
||||
self.get_action(in_terminal, action, &name),
|
||||
Vec::new(),
|
||||
working_dir.clone(),
|
||||
sort_score,
|
||||
Some(self.data.clone()),
|
||||
);
|
||||
|
||||
if !self.no_actions {
|
||||
for action in file.actions.values() {
|
||||
if let Some(action_name) = lookup_name_with_locale(
|
||||
|
@ -127,10 +135,12 @@ impl<T: Clone + Send + Sync> DRunProvider<T> {
|
|||
.unwrap_or("application-x-executable".to_string());
|
||||
|
||||
|
||||
let action = self.get_action(in_terminal, action.exec.clone(), &action_name);
|
||||
|
||||
entry.sub_elements.push(MenuItem::new(
|
||||
action_name,
|
||||
Some(action_icon),
|
||||
action.exec.clone(),
|
||||
action,
|
||||
Vec::new(),
|
||||
working_dir.clone(),
|
||||
0.0,
|
||||
|
@ -157,6 +167,25 @@ impl<T: Clone + Send + Sync> DRunProvider<T> {
|
|||
gui::apply_sort(&mut entries, &self.sort_order);
|
||||
entries
|
||||
}
|
||||
|
||||
fn get_action(
|
||||
&self,
|
||||
in_terminal: bool,
|
||||
action: Option<String>,
|
||||
action_name: &String,
|
||||
) -> Option<String> {
|
||||
if in_terminal {
|
||||
match self.terminal.as_ref() {
|
||||
None => {
|
||||
log::warn!("No terminal configured for terminal app {action_name}");
|
||||
None
|
||||
}
|
||||
Some(terminal) => action.map(|cmd| format!("{terminal} {cmd}")),
|
||||
}
|
||||
} else {
|
||||
action
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn load_d_run_cache() -> (Option<PathBuf>, HashMap<String, i64>) {
|
||||
|
@ -188,7 +217,7 @@ pub(crate) fn update_drun_cache_and_run<T: Clone>(
|
|||
///
|
||||
/// Will return `Err` if it was not able to spawn the process
|
||||
pub fn show(config: &Config) -> Result<(), Error> {
|
||||
let provider = DRunProvider::new(0, config.no_actions(), config.sort_order());
|
||||
let provider = DRunProvider::new(0, config.no_actions(), config.sort_order(), config.term());
|
||||
let cache_path = provider.cache_path.clone();
|
||||
let mut cache = provider.cache.clone();
|
||||
|
||||
|
|
|
@ -34,9 +34,7 @@ impl EmojiProvider {
|
|||
.collect::<Vec<_>>();
|
||||
gui::apply_sort(&mut menus, sort_order);
|
||||
|
||||
Self {
|
||||
elements: menus,
|
||||
}
|
||||
Self { elements: menus }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,7 +53,7 @@ impl ItemProvider<String> for EmojiProvider {
|
|||
///
|
||||
/// Forwards errors from the gui. See `gui::show` for details.
|
||||
pub fn show(config: &Config) -> Result<(), Error> {
|
||||
let provider = EmojiProvider::new( &config.sort_order(), config.emoji_hide_label());
|
||||
let provider = EmojiProvider::new(&config.sort_order(), config.emoji_hide_label());
|
||||
let selection_result = gui::show(config.clone(), provider, true, None, None)?;
|
||||
match selection_result.menu.data {
|
||||
None => Err(Error::MissingAction),
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use std::env;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use worf_lib::config::{Mode, fork_if_configured};
|
||||
use worf_lib::config::Mode;
|
||||
use worf_lib::desktop::fork_if_configured;
|
||||
use worf_lib::{Error, config, modes};
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
|
@ -26,7 +27,7 @@ fn main() -> anyhow::Result<()> {
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
fork_if_configured(&config);
|
||||
fork_if_configured(&config); // may exit the program
|
||||
|
||||
if let Some(show) = &config.show() {
|
||||
let result = match show {
|
||||
|
|
Loading…
Add table
Reference in a new issue