From 2d8ccf2a4f7b71ab6a384d400a5887374fafbccb Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Wed, 12 Nov 2025 23:49:06 +0300 Subject: [PATCH 1/2] multicall: go back to forking solution Signed-off-by: NotAShelf Change-Id: I2a24a3c7efc41fc45c675fd98e08782e6a6a6964 --- Cargo.lock | 1 + Cargo.toml | 1 + src/multicall/wl_copy.rs | 60 ++++++++++++++++++++++++++-------------- 3 files changed, 42 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4c7670e..a0a22c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1664,6 +1664,7 @@ dependencies = [ "env_logger", "imagesize", "inquire", + "libc", "log", "notify-rust", "ratatui", diff --git a/Cargo.toml b/Cargo.toml index 721afd9..dc491e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ imagesize = "0.14.0" inquire = { default-features = false, version = "0.9.1", features = [ "crossterm", ] } +libc = "0.2.177" log = "0.4.28" env_logger = "0.11.8" thiserror = "2.0.17" diff --git a/src/multicall/wl_copy.rs b/src/multicall/wl_copy.rs index cab79f5..3794420 100644 --- a/src/multicall/wl_copy.rs +++ b/src/multicall/wl_copy.rs @@ -106,7 +106,7 @@ fn handle_check_primary() { std::process::exit(exit_code); } -fn get_clipboard_type(primary: bool) -> CopyClipboardType { +const fn get_clipboard_type(primary: bool) -> CopyClipboardType { if primary { CopyClipboardType::Primary } else { @@ -213,21 +213,32 @@ fn handle_clear_clipboard( } fn fork_and_serve(prepared_copy: wl_clipboard_rs::copy::PreparedCopy) { - // Use a simpler approach: serve in background thread instead of forking - // This avoids all the complexity and safety issues with fork() - let handle = std::thread::spawn(move || { - if let Err(e) = prepared_copy.serve() { - log::error!("background clipboard service failed: {e}"); + // Use proper Unix fork() to create a child process that continues + // serving clipboard content after parent exits. + // XXX: I wanted to choose and approach without fork, but we could not + // ensure persistence after the thread dies. Alas, we gotta fork. + unsafe { + match libc::fork() { + 0 => { + // Child process - serve clipboard content + if let Err(e) = prepared_copy.serve() { + log::error!("background clipboard service failed: {e}"); + std::process::exit(1); + } + std::process::exit(0); + }, + -1 => { + // Fork failed + log::error!("failed to fork background process"); + std::process::exit(1); + }, + _ => { + // Parent process - exit immediately + log::debug!("forked background process to serve clipboard content"); + std::process::exit(0); + }, } - }); - - // Give the background thread a moment to start - std::thread::sleep(std::time::Duration::from_millis(50)); - log::debug!("clipboard service started in background thread"); - - // Detach the thread to allow it to run independently - // The thread will be cleaned up when it completes or when the process exits - std::mem::forget(handle); + } } pub fn wl_copy_main() -> Result<()> { @@ -255,14 +266,23 @@ pub fn wl_copy_main() -> Result<()> { // Handle foreground vs background mode if args.foreground { - // Foreground mode: copy and serve in current process - opts - .copy(Source::Bytes(input.into()), mime_type) - .context("failed to copy to clipboard")?; + // Foreground mode: copy content and serve in current process + // Use prepare_copy + serve to ensure proper clipboard registration + let mut opts_fg = opts; + opts_fg.foreground(true); + + let prepared_copy = opts_fg + .prepare_copy(Source::Bytes(input.into()), mime_type) + .context("failed to prepare copy")?; + + // Serve in foreground - blocks until interrupted (Ctrl+C, etc.) + prepared_copy + .serve() + .context("failed to serve clipboard content")?; } else { // Background mode: spawn child process to serve requests // First prepare to copy to validate before spawning - let mut opts_fg = opts.clone(); + let mut opts_fg = opts; opts_fg.foreground(true); let prepared_copy = opts_fg From a68946d54dfdf0f9aeeab2e82543f65674021ebd Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Thu, 13 Nov 2025 00:05:20 +0300 Subject: [PATCH 2/2] various: fix clippy lints Signed-off-by: NotAShelf Change-Id: I4bb649a5161460d8794dc5c93baa6cc46a6a6964 --- src/commands/query.rs | 2 +- src/db/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/commands/query.rs b/src/commands/query.rs index c5b5851..e1bd465 100644 --- a/src/commands/query.rs +++ b/src/commands/query.rs @@ -6,6 +6,6 @@ pub trait QueryCommand { impl QueryCommand for SqliteClipboardDb { fn query_delete(&self, query: &str) -> Result { - ::delete_query(self, query) + ::delete_query(self, query) } } diff --git a/src/db/mod.rs b/src/db/mod.rs index fa27cce..0b906d5 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -266,7 +266,7 @@ impl ClipboardDb for SqliteClipboardDb { .execute( "INSERT INTO clipboard (contents, mime, content_hash) VALUES (?1, ?2, \ ?3)", - params![buf, mime.map(|s| s.to_string()), content_hash], + params![buf, mime, content_hash], ) .map_err(|e| StashError::Store(e.to_string().into()))?;