git.lucas.co / cce-system-interface
system settings
git clone https://git.lucas.co/cce-system-interface.git

commit69246a264cadcb2a19b78130c012b52aa7960f8d
parentef824b64c9
authorLucas Galante <lsgalante12@gmail.com>
date2026-09-28 19:18
fix: reap xdg-open locally; cce-ui dropped process::spawn_detached

cce-ui@4e94236 removed its process module saying nothing called it, but
the accounts and system-info pages did, four sites in all, and the crate
stopped compiling. A crate-local spawn_detached in lib.rs does what the
removed helper did in its no-runtime branch: spawn, then wait from a
background thread so the child never lingers as a zombie.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

 src/lib.rs               | 12 ++++++++++++
 src/pages/accounts.rs    |  5 ++---
 src/pages/system_info.rs |  4 ++--
 3 files changed, 16 insertions(+), 5 deletions(-)

diff --git a/src/lib.rs b/src/lib.rs
index c4ed311..24b6c57 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -5,3 +5,15 @@ pub mod power_plan;
 pub use cce_ui::widget::scroll_region;
 pub mod widgets;
 pub mod watchers;
+
+/// Spawn a command and reap it from a background thread, so a finished
+/// xdg-open never lingers as a zombie. cce-ui's `process::spawn_detached`
+/// did exactly this until cce-ui@4e94236 removed the module; the accounts and
+/// system-info pages were its remaining callers.
+pub(crate) fn spawn_detached(mut cmd: std::process::Command) -> std::io::Result<()> {
+    let mut child = cmd.spawn()?;
+    std::thread::spawn(move || {
+        let _ = child.wait();
+    });
+    Ok(())
+}
diff --git a/src/pages/accounts.rs b/src/pages/accounts.rs
index 34f2fbb..614a92a 100644
--- a/src/pages/accounts.rs
+++ b/src/pages/accounts.rs
@@ -326,7 +326,7 @@ async fn google_login_flow(sender: &calloop::channel::Sender<AppAction>) {
     );
     let mut cmd = std::process::Command::new("xdg-open");
     cmd.arg(&auth_url);
-    let _ = cce_ui::process::spawn_detached(cmd);
+    let _ = crate::spawn_detached(cmd);
 
     let accepted = match tokio::time::timeout(OAUTH_WAIT, listener.accept()).await {
         Ok(res) => res,
@@ -1013,7 +1013,7 @@ pub fn update(state: &mut AccountsState, msg: AccountsMessage) {
         AccountsMessage::ICloudLoginHelp => {
             let mut cmd = std::process::Command::new("xdg-open");
             cmd.arg("https://appleid.apple.com/");
-            let _ = cce_ui::process::spawn_detached(cmd);
+            let _ = crate::spawn_detached(cmd);
             state.status_msg = Some("Generate iCloud App Password...".to_string());
         }
     }
@@ -1325,4 +1325,3 @@ mod tests {
         assert!(!state.oauth_listener_running, "a finished flow frees the button");
     }
 }
-
diff --git a/src/pages/system_info.rs b/src/pages/system_info.rs
index 1616db0..0e9cde8 100644
--- a/src/pages/system_info.rs
+++ b/src/pages/system_info.rs
@@ -272,7 +272,7 @@ fn button_need(label: &str) -> f32 {
 fn spawn_systemctl(action: &str) {
     let mut cmd = std::process::Command::new("systemctl");
     cmd.arg(action);
-    let _ = cce_ui::process::spawn_detached(cmd);
+    let _ = crate::spawn_detached(cmd);
 }
 
 fn spawn_systemctl_force(action: &str) {
@@ -280,7 +280,7 @@ fn spawn_systemctl_force(action: &str) {
     cmd.arg(action);
     cmd.arg("-f");
     cmd.arg("-f");
-    let _ = cce_ui::process::spawn_detached(cmd);
+    let _ = crate::spawn_detached(cmd);
 }
 
 #[derive(Debug, Clone, Default)]