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 clap::{Parser, ValueEnum};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::os::unix::process::CommandExt;
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process::{Command, Stdio};
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::{env, fs};
|
use std::{env, fs};
|
||||||
use thiserror::Error;
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
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::collections::HashMap;
|
||||||
|
use std::ffi::OsStr;
|
||||||
use std::os::unix::fs::PermissionsExt;
|
use std::os::unix::fs::PermissionsExt;
|
||||||
use std::os::unix::prelude::CommandExt;
|
use std::os::unix::prelude::CommandExt;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
@ -6,15 +11,10 @@ use std::path::PathBuf;
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use std::{env, fs, io};
|
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 wl_clipboard_rs::copy::{ClipboardType, MimeType, ServeRequests, Source};
|
||||||
|
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
use crate::config::expand_path;
|
use crate::config::{Config, expand_path};
|
||||||
|
|
||||||
/// Returns a regex with supported image extensions
|
/// Returns a regex with supported image extensions
|
||||||
/// # Panics
|
/// # Panics
|
||||||
|
@ -132,6 +132,23 @@ pub fn lookup_name_with_locale(
|
||||||
.or_else(|| Some(fallback.to_owned()))
|
.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
|
/// Spawn a new process and forks it away from the current worf process
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// * No action in menu item
|
/// * 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))
|
.map(|arg| expand_path(arg))
|
||||||
.collect();
|
.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 {
|
unsafe {
|
||||||
let _ = Command::new(exec)
|
cmd.pre_exec(|| {
|
||||||
.args(args)
|
libc::setsid();
|
||||||
.stdin(Stdio::null())
|
Ok(())
|
||||||
.stdout(Stdio::null())
|
});
|
||||||
.stderr(Stdio::null())
|
|
||||||
.pre_exec(|| {
|
|
||||||
libc::setsid();
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.spawn();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmd.spawn().map_err(|e| Error::Io(e.to_string()))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,12 @@ struct AutoItemProvider {
|
||||||
impl AutoItemProvider {
|
impl AutoItemProvider {
|
||||||
fn new(config: &Config) -> Self {
|
fn new(config: &Config) -> Self {
|
||||||
AutoItemProvider {
|
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()),
|
file: FileItemProvider::new(AutoRunType::File, config.sort_order()),
|
||||||
math: MathProvider::new(AutoRunType::Math),
|
math: MathProvider::new(AutoRunType::Math),
|
||||||
ssh: SshProvider::new(AutoRunType::Ssh, &config.sort_order()),
|
ssh: SshProvider::new(AutoRunType::Ssh, &config.sort_order()),
|
||||||
|
|
|
@ -26,6 +26,7 @@ pub(crate) struct DRunProvider<T: Clone> {
|
||||||
data: T,
|
data: T,
|
||||||
no_actions: bool,
|
no_actions: bool,
|
||||||
sort_order: SortOrder,
|
sort_order: SortOrder,
|
||||||
|
terminal: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Clone + Send + Sync> ItemProvider<T> for DRunProvider<T> {
|
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> {
|
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();
|
let (cache_path, d_run_cache) = load_d_run_cache();
|
||||||
DRunProvider {
|
DRunProvider {
|
||||||
items: None,
|
items: None,
|
||||||
|
@ -51,6 +57,7 @@ impl<T: Clone + Send + Sync> DRunProvider<T> {
|
||||||
data: menu_item_data,
|
data: menu_item_data,
|
||||||
no_actions,
|
no_actions,
|
||||||
sort_order,
|
sort_order,
|
||||||
|
terminal,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,8 +81,8 @@ impl<T: Clone + Send + Sync> DRunProvider<T> {
|
||||||
&file.entry.name.default,
|
&file.entry.name.default,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let (action, working_dir) = match &file.entry.entry_type {
|
let (action, working_dir, in_terminal) = match &file.entry.entry_type {
|
||||||
EntryType::Application(app) => (app.exec.clone(), app.path.clone()),
|
EntryType::Application(app) => (app.exec.clone(), app.path.clone(), app.terminal.unwrap_or(false)),
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -106,12 +113,13 @@ impl<T: Clone + Send + Sync> DRunProvider<T> {
|
||||||
let mut entry = MenuItem::new(
|
let mut entry = MenuItem::new(
|
||||||
name.clone(),
|
name.clone(),
|
||||||
icon.clone(),
|
icon.clone(),
|
||||||
action.clone(),
|
self.get_action(in_terminal, action, &name),
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
working_dir.clone(),
|
working_dir.clone(),
|
||||||
sort_score,
|
sort_score,
|
||||||
Some(self.data.clone()),
|
Some(self.data.clone()),
|
||||||
);
|
);
|
||||||
|
|
||||||
if !self.no_actions {
|
if !self.no_actions {
|
||||||
for action in file.actions.values() {
|
for action in file.actions.values() {
|
||||||
if let Some(action_name) = lookup_name_with_locale(
|
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());
|
.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(
|
entry.sub_elements.push(MenuItem::new(
|
||||||
action_name,
|
action_name,
|
||||||
Some(action_icon),
|
Some(action_icon),
|
||||||
action.exec.clone(),
|
action,
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
working_dir.clone(),
|
working_dir.clone(),
|
||||||
0.0,
|
0.0,
|
||||||
|
@ -157,6 +167,25 @@ impl<T: Clone + Send + Sync> DRunProvider<T> {
|
||||||
gui::apply_sort(&mut entries, &self.sort_order);
|
gui::apply_sort(&mut entries, &self.sort_order);
|
||||||
entries
|
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>) {
|
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
|
/// Will return `Err` if it was not able to spawn the process
|
||||||
pub fn show(config: &Config) -> Result<(), Error> {
|
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 cache_path = provider.cache_path.clone();
|
||||||
let mut cache = provider.cache.clone();
|
let mut cache = provider.cache.clone();
|
||||||
|
|
||||||
|
|
|
@ -34,9 +34,7 @@ impl EmojiProvider {
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
gui::apply_sort(&mut menus, sort_order);
|
gui::apply_sort(&mut menus, sort_order);
|
||||||
|
|
||||||
Self {
|
Self { elements: menus }
|
||||||
elements: menus,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +53,7 @@ impl ItemProvider<String> for EmojiProvider {
|
||||||
///
|
///
|
||||||
/// Forwards errors from the gui. See `gui::show` for details.
|
/// Forwards errors from the gui. See `gui::show` for details.
|
||||||
pub fn show(config: &Config) -> Result<(), Error> {
|
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)?;
|
let selection_result = gui::show(config.clone(), provider, true, None, None)?;
|
||||||
match selection_result.menu.data {
|
match selection_result.menu.data {
|
||||||
None => Err(Error::MissingAction),
|
None => Err(Error::MissingAction),
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
use anyhow::anyhow;
|
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};
|
use worf_lib::{Error, config, modes};
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
fn main() -> anyhow::Result<()> {
|
||||||
|
@ -26,7 +27,7 @@ fn main() -> anyhow::Result<()> {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
fork_if_configured(&config);
|
fork_if_configured(&config); // may exit the program
|
||||||
|
|
||||||
if let Some(show) = &config.show() {
|
if let Some(show) = &config.show() {
|
||||||
let result = match show {
|
let result = match show {
|
||||||
|
|
Loading…
Add table
Reference in a new issue