diff --git a/README.md b/README.md index f3251d4..36620e6 100644 --- a/README.md +++ b/README.md @@ -180,6 +180,11 @@ history_file_path = "/home//.cache/clipcat/clipcatd-history" # File path of PID file, # if you omit this value, clipcatd places the PID file on `$XDG_RUNTIME_DIR/clipcatd.pid`. pid_file = "/run/user//clipcatd.pid" +# Controls how often the program updates its stored value of the Linux +# primary selection. In the Linux environment, the primary selection is a +# mechanism that automatically updates to reflect the current highlighted text or +# object, typically updating with every mouse movement. +primary_threshold_ms = 5000 [log] # Emit log message to a log file. diff --git a/clipcatd/src/config/mod.rs b/clipcatd/src/config/mod.rs index bde4399..0d88311 100644 --- a/clipcatd/src/config/mod.rs +++ b/clipcatd/src/config/mod.rs @@ -26,6 +26,9 @@ pub struct Config { #[serde(default = "Config::default_pid_file_path")] pub pid_file: PathBuf, + #[serde(default = "Config::default_primary_threshold_ms")] + pub primary_threshold_ms: i64, + #[serde(default = "Config::default_max_history")] pub max_history: usize, @@ -62,6 +65,7 @@ impl Default for Config { Self { daemonize: true, pid_file: Self::default_pid_file_path(), + primary_threshold_ms: Self::default_primary_threshold_ms(), max_history: Self::default_max_history(), history_file_path: Self::default_history_file_path(), synchronize_selection_with_clipboard: @@ -124,6 +128,9 @@ impl Config { #[inline] pub const fn default_synchronize_selection_with_clipboard() -> bool { true } + #[inline] + pub const fn default_primary_threshold_ms() -> i64 { 0 } + #[inline] pub const fn default_max_history() -> usize { 50 } @@ -186,6 +193,7 @@ impl From for clipcat_server::Config { fn from( Config { grpc, + primary_threshold_ms, max_history, synchronize_selection_with_clipboard, history_file_path, @@ -220,6 +228,7 @@ impl From for clipcat_server::Config { grpc_listen_address, grpc_local_socket, grpc_access_token, + primary_threshold_ms, max_history, synchronize_selection_with_clipboard, history_file_path, diff --git a/crates/server/src/config.rs b/crates/server/src/config.rs index 7e7f482..d54a366 100644 --- a/crates/server/src/config.rs +++ b/crates/server/src/config.rs @@ -10,6 +10,8 @@ pub struct Config { pub grpc_access_token: Option, + pub primary_threshold_ms: i64, + pub max_history: usize, pub synchronize_selection_with_clipboard: bool, diff --git a/crates/server/src/lib.rs b/crates/server/src/lib.rs index 9f70a20..dbe1b86 100644 --- a/crates/server/src/lib.rs +++ b/crates/server/src/lib.rs @@ -56,6 +56,7 @@ pub async fn serve_with_shutdown( grpc_listen_address, grpc_local_socket, grpc_access_token, + primary_threshold_ms, max_history, history_file_path, synchronize_selection_with_clipboard, @@ -115,6 +116,7 @@ pub async fn serve_with_shutdown( tracing::info!("Initialize ClipboardManager with capacity {max_history}"); let mut clipboard_manager = ClipboardManager::with_capacity( clipboard_backend.clone(), + primary_threshold_ms, max_history, desktop_notification.clone(), ); diff --git a/crates/server/src/manager/mod.rs b/crates/server/src/manager/mod.rs index 4bf7b60..144a54f 100644 --- a/crates/server/src/manager/mod.rs +++ b/crates/server/src/manager/mod.rs @@ -17,6 +17,8 @@ const DEFAULT_CAPACITY: usize = 40; pub struct ClipboardManager { backend: Arc, + primary_threshold: time::Duration, + capacity: usize, // use id of ClipEntry as the key @@ -39,12 +41,14 @@ where { pub fn with_capacity( backend: Arc, + primary_threshold_ms: i64, capacity: usize, notification: Notification, ) -> Self { let capacity = if capacity == 0 { DEFAULT_CAPACITY } else { capacity }; Self { backend, + primary_threshold: time::Duration::milliseconds(primary_threshold_ms), capacity, clips: HashMap::new(), current_clips: [None; ClipboardKind::MAX_LENGTH], @@ -57,7 +61,7 @@ where #[cfg(test)] #[inline] pub fn new(backend: Arc, notification: Notification) -> Self { - Self::with_capacity(backend, DEFAULT_CAPACITY, notification) + Self::with_capacity(backend, 0, DEFAULT_CAPACITY, notification) } #[inline] @@ -128,6 +132,20 @@ where } ClipboardContent::Plaintext(text) => { self.notification.on_plaintext_fetched(text.chars().count()); + if let Some(id) = self.current_clips[usize::from(entry.kind())] { + if let Some(cur_clip) = self.clips.get(&id) { + if entry.timestamp() - cur_clip.timestamp() < self.primary_threshold { + if let ClipboardContent::Plaintext(cur_text) = cur_clip.as_ref() { + let len = std::cmp::min(text.len(), cur_text.len()); + if text[..len] == cur_text[..len] { + if let Some(clip) = self.clips.remove(&id) { + let _id = self.timestamp_to_id.remove(&clip.timestamp()); + } + } + } + } + } + } } }