Skip to content

Commit

Permalink
Merge pull request #431 from xrelkd/feat/primary
Browse files Browse the repository at this point in the history
Add threshold for primary selection
  • Loading branch information
xrelkd authored May 26, 2024
2 parents a509c4d + 828c144 commit f9fb742
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 4 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,11 @@ history_file_path = "/home/<username>/.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/<user-id>/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.
Expand Down
10 changes: 10 additions & 0 deletions clipcatd/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,

Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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 { 5000 }

#[inline]
pub const fn default_max_history() -> usize { 50 }

Expand Down Expand Up @@ -186,6 +193,7 @@ impl From<Config> for clipcat_server::Config {
fn from(
Config {
grpc,
primary_threshold_ms,
max_history,
synchronize_selection_with_clipboard,
history_file_path,
Expand All @@ -197,6 +205,7 @@ impl From<Config> for clipcat_server::Config {
..
}: Config,
) -> Self {
let primary_threshold = time::Duration::milliseconds(primary_threshold_ms);
let grpc_listen_address = grpc.enable_http.then_some(grpc.socket_address());
let grpc_local_socket = grpc.enable_local_socket.then_some(grpc.local_socket);
let grpc_access_token = if let Some(file_path) = grpc.access_token_file_path {
Expand All @@ -220,6 +229,7 @@ impl From<Config> for clipcat_server::Config {
grpc_listen_address,
grpc_local_socket,
grpc_access_token,
primary_threshold,
max_history,
synchronize_selection_with_clipboard,
history_file_path,
Expand Down
2 changes: 2 additions & 0 deletions crates/server/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ pub struct Config {

pub grpc_access_token: Option<String>,

pub primary_threshold: time::Duration,

pub max_history: usize,

pub synchronize_selection_with_clipboard: bool,
Expand Down
2 changes: 2 additions & 0 deletions crates/server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ pub async fn serve_with_shutdown(
grpc_listen_address,
grpc_local_socket,
grpc_access_token,
primary_threshold,
max_history,
history_file_path,
synchronize_selection_with_clipboard,
Expand Down Expand Up @@ -116,6 +117,7 @@ pub async fn serve_with_shutdown(
let mut clipboard_manager = ClipboardManager::with_capacity(
clipboard_backend.clone(),
max_history,
primary_threshold,
desktop_notification.clone(),
);

Expand Down
48 changes: 44 additions & 4 deletions crates/server/src/manager/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ const DEFAULT_CAPACITY: usize = 40;
pub struct ClipboardManager<Notification> {
backend: Arc<dyn ClipboardBackend>,

primary_threshold: time::Duration,

capacity: usize,

// use id of ClipEntry as the key
Expand All @@ -40,11 +42,13 @@ where
pub fn with_capacity(
backend: Arc<dyn ClipboardBackend>,
capacity: usize,
primary_threshold: time::Duration,
notification: Notification,
) -> Self {
let capacity = if capacity == 0 { DEFAULT_CAPACITY } else { capacity };
Self {
backend,
primary_threshold,
capacity,
clips: HashMap::new(),
current_clips: [None; ClipboardKind::MAX_LENGTH],
Expand All @@ -57,7 +61,12 @@ where
#[cfg(test)]
#[inline]
pub fn new(backend: Arc<dyn ClipboardBackend>, notification: Notification) -> Self {
Self::with_capacity(backend, DEFAULT_CAPACITY, notification)
Self::with_capacity(
backend,
DEFAULT_CAPACITY,
time::Duration::milliseconds(0),
notification,
)
}

#[inline]
Expand Down Expand Up @@ -128,6 +137,22 @@ 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(current_clip) = self.clips.get(&id) {
if entry.timestamp() - current_clip.timestamp() < self.primary_threshold {
if let ClipboardContent::Plaintext(current_text) = current_clip.as_ref()
{
let len = text.len().min(current_text.len());
if text[..len] == current_text[..len] {
if let Some(clip) = self.clips.remove(&id) {
let _id = self.timestamp_to_id.remove(&clip.timestamp());
}
}
}
}
}
}
}
}

Expand Down Expand Up @@ -267,7 +292,12 @@ mod tests {

let cap = 20;
let backend = Arc::new(LocalClipboardBackend::new());
let mgr = ClipboardManager::with_capacity(backend, cap, notification);
let mgr = ClipboardManager::with_capacity(
backend,
cap,
time::Duration::milliseconds(0),
notification,
);
assert!(mgr.is_empty());
assert_eq!(mgr.len(), 0);
assert_eq!(mgr.capacity(), cap);
Expand All @@ -280,7 +310,12 @@ mod tests {
let backend = Arc::new(LocalClipboardBackend::new());
let notification = DummyNotification::default();
let cap = 10;
let mut mgr = ClipboardManager::with_capacity(backend, cap, notification);
let mut mgr = ClipboardManager::with_capacity(
backend,
cap,
time::Duration::milliseconds(0),
notification,
);
assert_eq!(mgr.len(), 0);
assert_eq!(mgr.capacity(), cap);

Expand Down Expand Up @@ -336,7 +371,12 @@ mod tests {
let mut clips = create_clips(n);
let backend = Arc::new(LocalClipboardBackend::new());
let notification = DummyNotification::default();
let mut mgr = ClipboardManager::with_capacity(backend, 20, notification);
let mut mgr = ClipboardManager::with_capacity(
backend,
20,
time::Duration::milliseconds(0),
notification,
);

mgr.import(&clips);
assert_eq!(mgr.len(), n);
Expand Down

0 comments on commit f9fb742

Please sign in to comment.