oxwm

https://git.tonybtw.com/oxwm.git git://git.tonybtw.com/oxwm.git

Removed libc as a dep, and used a double-fork strategy to handle zombie processes instead of the dwm method due to shell being required as a hanging process for the bar.

Commit
d90e50dfcb3f6a12e59e0d221bf03147d43ec458
Parent
d5f7b65
Author
tonybtw <tonybtw@tonybtw.com>
Date
2025-12-12 08:31:53

Diff

diff --git a/Cargo.lock b/Cargo.lock
index 6b613df..2566f7f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -295,7 +295,6 @@ version = "0.8.1"
 dependencies = [
  "chrono",
  "dirs",
- "libc",
  "mlua",
  "serde",
  "x11",
diff --git a/Cargo.toml b/Cargo.toml
index fb1078a..6a13c55 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -18,4 +18,3 @@ chrono = "0.4"
 dirs = "5.0"
 serde = { version = "1.0", features = ["derive"] }
 mlua = { version = "0.10", features = ["lua54", "vendored"] }
-libc = "0.2"
diff --git a/src/bin/main.rs b/src/bin/main.rs
index 9eb3692..f6de012 100644
--- a/src/bin/main.rs
+++ b/src/bin/main.rs
@@ -1,8 +1,6 @@
 use std::path::PathBuf;
 
 fn main() -> Result<(), Box<dyn std::error::Error>> {
-    oxwm::signal::prevent_zombie_processes();
-
     let arguments: Vec<String> = std::env::args().collect();
 
     let mut custom_config_path: Option<PathBuf> = None;
diff --git a/src/config/lua_api.rs b/src/config/lua_api.rs
index 089baec..ebb11ec 100644
--- a/src/config/lua_api.rs
+++ b/src/config/lua_api.rs
@@ -647,10 +647,6 @@ fn register_misc(lua: &Lua, parent: &Table, builder: SharedBuilder) -> Result<()
         create_action_table(lua, "Restart", Value::Nil)
     })?;
 
-    let recompile = lua.create_function(|lua, ()| {
-        create_action_table(lua, "Recompile", Value::Nil)
-    })?;
-
     let toggle_gaps = lua.create_function(|lua, ()| {
         create_action_table(lua, "ToggleGaps", Value::Nil)
     })?;
@@ -693,7 +689,6 @@ fn register_misc(lua: &Lua, parent: &Table, builder: SharedBuilder) -> Result<()
     parent.set("autostart", autostart)?;
     parent.set("quit", quit)?;
     parent.set("restart", restart)?;
-    parent.set("recompile", recompile)?;
     parent.set("toggle_gaps", toggle_gaps)?;
     parent.set("set_master_factor", set_master_factor)?;
     parent.set("inc_num_master", inc_num_master)?;
@@ -781,7 +776,6 @@ fn string_to_action(s: &str) -> mlua::Result<KeyAction> {
         "MoveStack" => Ok(KeyAction::MoveStack),
         "Quit" => Ok(KeyAction::Quit),
         "Restart" => Ok(KeyAction::Restart),
-        "Recompile" => Ok(KeyAction::Recompile),
         "ViewTag" => Ok(KeyAction::ViewTag),
         "ToggleView" => Ok(KeyAction::ToggleView),
         "MoveToTag" => Ok(KeyAction::MoveToTag),
diff --git a/src/keyboard/handlers.rs b/src/keyboard/handlers.rs
index f527819..6683816 100644
--- a/src/keyboard/handlers.rs
+++ b/src/keyboard/handlers.rs
@@ -1,5 +1,4 @@
-use std::io::{ErrorKind, Result};
-use std::process::Command;
+use std::io::Result;
 
 use serde::Deserialize;
 use x11rb::connection::Connection;
@@ -17,7 +16,6 @@ pub enum KeyAction {
     MoveStack,
     Quit,
     Restart,
-    Recompile,
     ViewTag,
     ToggleView,
     MoveToTag,
@@ -315,16 +313,9 @@ fn handle_next_key(
 pub fn handle_spawn_action(action: KeyAction, arg: &Arg, selected_monitor: usize) -> Result<()> {
     if let KeyAction::Spawn = action {
         match arg {
-            Arg::Str(command) => match Command::new(command.as_str()).spawn() {
-                Err(error) if error.kind() == ErrorKind::NotFound => {
-                    eprintln!(
-                        "KeyAction::Spawn failed: could not spawn \"{}\", command not found",
-                        command
-                    );
-                }
-                Err(error) => Err(error)?,
-                _ => (),
-            },
+            Arg::Str(command) => {
+                crate::signal::spawn_detached(command);
+            }
             Arg::Array(command) => {
                 let Some((cmd, args)) = command.split_first() else {
                     return Ok(());
@@ -341,16 +332,7 @@ pub fn handle_spawn_action(action: KeyAction, arg: &Arg, selected_monitor: usize
                 }
 
                 let args_str: Vec<&str> = args_vec.iter().map(|s| s.as_str()).collect();
-                match Command::new(cmd.as_str()).args(&args_str).spawn() {
-                    Err(error) if error.kind() == ErrorKind::NotFound => {
-                        eprintln!(
-                            "KeyAction::Spawn failed: could not spawn \"{}\", command not found",
-                            cmd
-                        );
-                    }
-                    Err(error) => Err(error)?,
-                    _ => (),
-                }
+                crate::signal::spawn_detached_with_args(cmd, &args_str);
             }
             _ => {}
         }
diff --git a/src/overlay/keybind.rs b/src/overlay/keybind.rs
index 59d3113..8dc1eee 100644
--- a/src/overlay/keybind.rs
+++ b/src/overlay/keybind.rs
@@ -198,7 +198,6 @@ impl KeybindOverlay {
             KeyAction::ShowKeybindOverlay => "Show This Keybind Help".to_string(),
             KeyAction::Quit => "Quit Window Manager".to_string(),
             KeyAction::Restart => "Restart Window Manager".to_string(),
-            KeyAction::Recompile => "Recompile Window Manager".to_string(),
             KeyAction::KillClient => "Close Focused Window".to_string(),
             KeyAction::Spawn => match &binding.arg {
                 Arg::Str(cmd) => format!("Launch: {}", cmd),
diff --git a/src/signal.rs b/src/signal.rs
index 408fbd8..c5cec0b 100644
--- a/src/signal.rs
+++ b/src/signal.rs
@@ -1,13 +1,32 @@
-use std::ptr;
+use std::process::{Command, Stdio};
 
-pub fn prevent_zombie_processes() {
-    unsafe {
-        let mut sa: libc::sigaction = std::mem::zeroed();
-        libc::sigemptyset(&mut sa.sa_mask);
-        sa.sa_flags = libc::SA_NOCLDSTOP | libc::SA_NOCLDWAIT | libc::SA_RESTART;
-        sa.sa_sigaction = libc::SIG_IGN;
-        libc::sigaction(libc::SIGCHLD, &sa, ptr::null_mut());
+pub fn spawn_detached(cmd: &str) {
+    if let Ok(mut child) = Command::new("sh")
+        .arg("-c")
+        .arg(format!("({}) &", cmd))
+        .stdin(Stdio::null())
+        .stdout(Stdio::null())
+        .stderr(Stdio::null())
+        .spawn()
+    {
+        let _ = child.wait();
+    }
+}
+
+pub fn spawn_detached_with_args(program: &str, args: &[&str]) {
+    let escaped_args: Vec<String> = args.iter().map(|a| shell_escape(a)).collect();
+    let full_cmd = if escaped_args.is_empty() {
+        program.to_string()
+    } else {
+        format!("{} {}", program, escaped_args.join(" "))
+    };
+    spawn_detached(&full_cmd)
+}
 
-        while libc::waitpid(-1, ptr::null_mut(), libc::WNOHANG) > 0 {}
+fn shell_escape(s: &str) -> String {
+    if s.contains(|c: char| c.is_whitespace() || c == '\'' || c == '"' || c == '\\') {
+        format!("'{}'", s.replace('\'', "'\\''"))
+    } else {
+        s.to_string()
     }
 }
diff --git a/src/window_manager.rs b/src/window_manager.rs
index 0beac56..d79e163 100644
--- a/src/window_manager.rs
+++ b/src/window_manager.rs
@@ -9,7 +9,6 @@ use crate::layout::{Layout, LayoutBox, LayoutType, layout_from_str, next_layout}
 use crate::monitor::{Monitor, detect_monitors};
 use crate::overlay::{ErrorOverlay, KeybindOverlay, Overlay};
 use std::collections::{HashMap, HashSet};
-use std::process::Command;
 use x11rb::cursor::Handle as CursorHandle;
 
 use x11rb::connection::Connection;
@@ -299,7 +298,7 @@ impl WindowManager {
 
         window_manager.scan_existing_windows()?;
         window_manager.update_bar()?;
-        window_manager.run_autostart_commands()?;
+        window_manager.run_autostart_commands();
 
         Ok(window_manager)
     }
@@ -718,11 +717,7 @@ impl WindowManager {
         match action {
             KeyAction::Spawn => handlers::handle_spawn_action(action, arg, self.selected_monitor)?,
             KeyAction::SpawnTerminal => {
-                use std::process::Command;
-                let terminal = &self.config.terminal;
-                if let Err(error) = Command::new(terminal).spawn() {
-                    eprintln!("Failed to spawn terminal {}: {:?}", terminal, error);
-                }
+                crate::signal::spawn_detached(&self.config.terminal);
             }
             KeyAction::KillClient => {
                 if let Some(focused) = self
@@ -787,15 +782,6 @@ impl WindowManager {
             KeyAction::Quit | KeyAction::Restart => {
                 // Handled in handle_event
             }
-            KeyAction::Recompile => {
-                match std::process::Command::new("oxwm")
-                    .arg("--recompile")
-                    .spawn()
-                {
-                    Ok(_) => eprintln!("Recompiling in background"),
-                    Err(e) => eprintln!("Failed to spawn recompile: {}", e),
-                }
-            }
             KeyAction::ViewTag => {
                 if let Arg::Int(tag_index) = arg {
                     self.view_tag(*tag_index as usize)?;
@@ -3808,15 +3794,10 @@ impl WindowManager {
         Ok(())
     }
 
-    fn run_autostart_commands(&self) -> Result<(), WmError> {
+    fn run_autostart_commands(&self) {
         for command in &self.config.autostart {
-            Command::new("sh")
-                .arg("-c")
-                .arg(command)
-                .spawn()
-                .map_err(|e| WmError::Autostart(command.clone(), e))?;
+            crate::signal::spawn_detached(command);
             eprintln!("[autostart] Spawned: {}", command);
         }
-        Ok(())
     }
 }