From 7f9688980310693a1c03c5faa23416314195a6b3 Mon Sep 17 00:00:00 2001 From: Alexander Mohr Date: Sat, 24 May 2025 21:35:53 +0200 Subject: [PATCH] support forking --- worf/src/lib/config.rs | 81 ++++++++++++++++++++++++++++++------------ worf/src/main.rs | 4 ++- 2 files changed, 62 insertions(+), 23 deletions(-) diff --git a/worf/src/lib/config.rs b/worf/src/lib/config.rs index 196336f..c3b49c6 100644 --- a/worf/src/lib/config.rs +++ b/worf/src/lib/config.rs @@ -1,11 +1,12 @@ -use std::path::PathBuf; -use std::str::FromStr; -use std::{env, fs}; - 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; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Serialize, Deserialize)] @@ -177,10 +178,11 @@ impl FromStr for KeyDetectionType { #[derive(Debug, Deserialize, Serialize, Clone, Parser)] #[clap(about = "Worf is a wofi clone written in rust, it aims to be a drop-in replacement")] #[derive(Default)] +#[allow(clippy::struct_excessive_bools)] // it's fine for config pub struct Config { /// Forks the menu so you can close the terminal - #[clap(short = 'f', long = "fork")] - fork: Option, // todo support fork + #[clap(long = "fork")] + fork: bool, /// Selects a config file to use #[clap(short = 'c', long = "conf")] @@ -225,7 +227,8 @@ pub struct Config { /// Set to 'false' to disable images, defaults to true #[clap(short = 'I', long = "allow-images")] - allow_images: Option, + #[serde(default = "default_true")] + allow_images: bool, /// If `true` pango markup is parsed #[clap(short = 'm', long = "allow-markup")] @@ -335,16 +338,16 @@ pub struct Config { // todo re-add this // #[serde(flatten)] // key_custom: Option>, - global_coords: Option, // todo support this + global_coords: bool, // todo support this - /// If set to `true` the search field will be hidden. + /// If set to `true` the search field willOption<> be hidden. #[clap(long = "hide-search")] - hide_search: Option, - dynamic_lines: Option, // todo support this - layer: Option, // todo support this - copy_exec: Option, // todo support this - single_click: Option, // todo support this - pre_display_exec: Option, // todo support this + hide_search: bool, + dynamic_lines: bool, // todo support this + layer: Option, // todo support this + copy_exec: Option, // todo support this + single_click: bool, // todo support this + pre_display_exec: bool, // todo support this /// Minimum score for a fuzzy search to be shown #[clap(long = "fuzzy-min-score")] @@ -359,13 +362,18 @@ pub struct Config { /// Display only icon in emoji mode #[clap(long = "emoji-hide-string")] - emoji_hide_label: Option, + emoji_hide_label: bool, #[clap(long = "keyboard-detection-type")] key_detection_type: Option, } impl Config { + #[must_use] + pub fn fork(&self) -> bool { + self.fork + } + #[must_use] pub fn image_size(&self) -> u16 { self.image_size.unwrap_or(32) @@ -469,7 +477,7 @@ impl Config { #[must_use] pub fn allow_images(&self) -> bool { - self.allow_images.unwrap_or(true) + self.allow_images } #[must_use] @@ -513,7 +521,7 @@ impl Config { #[must_use] pub fn hide_search(&self) -> bool { - self.hide_search.unwrap_or(false) + self.hide_search } #[must_use] @@ -543,7 +551,7 @@ impl Config { #[must_use] pub fn emoji_hide_label(&self) -> bool { - self.emoji_hide_label.unwrap_or(false) + self.emoji_hide_label } #[must_use] @@ -558,9 +566,9 @@ fn default_false() -> bool { false } -// fn default_true() -> bool { -// true -// } +fn default_true() -> bool { + true +} // // // TODO @@ -774,3 +782,32 @@ 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); + } +} diff --git a/worf/src/main.rs b/worf/src/main.rs index 7a43ae3..ee0142d 100644 --- a/worf/src/main.rs +++ b/worf/src/main.rs @@ -1,7 +1,7 @@ use std::env; use anyhow::anyhow; -use worf_lib::config::Mode; +use worf_lib::config::{Mode, fork_if_configured}; use worf_lib::{Error, config, modes}; fn main() -> anyhow::Result<()> { @@ -13,6 +13,8 @@ fn main() -> anyhow::Result<()> { let args = config::parse_args(); let config = config::load_config(Some(&args)).unwrap_or(args); + fork_if_configured(&config); + if let Some(show) = &config.show() { let result = match show { Mode::Run => modes::run::show(&config),