From 3704ce34272b5ae0fc36a6bd63d24db00cece33f Mon Sep 17 00:00:00 2001 From: Chris Down Date: Thu, 8 Feb 2018 01:04:47 +0000 Subject: [PATCH 01/23] Fall back to polling if clipnotify fails --- clipmenud | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/clipmenud b/clipmenud index 5ee6e00..e516b52 100755 --- a/clipmenud +++ b/clipmenud @@ -96,13 +96,16 @@ fi exec {lock_fd}> "$lock_file" +sleep_cmd=(sleep "${CM_SLEEP:-0.5}") + while true; do if ! (( CM_ONESHOT )); then if (( has_clipnotify )); then - clipnotify + # Fall back to polling if clipnotify fails + clipnotify || "${sleep_cmd[@]}" else # Use old polling method - sleep "${CM_SLEEP:-0.5}" + "${sleep_cmd[@]}" fi fi From b5e8543b3ee254b179ebd4dd4b7f31858c8f0e03 Mon Sep 17 00:00:00 2001 From: Chris Down Date: Mon, 19 Feb 2018 14:43:44 +0000 Subject: [PATCH 02/23] Use `xsel -k`, instead of `-o` -> `-i` --- clipmenud | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clipmenud b/clipmenud index e516b52..f25e7f8 100755 --- a/clipmenud +++ b/clipmenud @@ -173,7 +173,7 @@ while true; do # We can't colocate this with the above copying code because # https://github.com/cdown/clipmenu/issues/34 requires knowing if # we would skip first. - _xsel -o --"$selection" | _xsel -i --"$selection" + _xsel -k --"$selection" fi if (( CM_MAX_CLIPS )); then From 55407ba3f622c99b1efafbe6b6371680f6abd8f6 Mon Sep 17 00:00:00 2001 From: Chris Down Date: Mon, 19 Feb 2018 14:51:00 +0000 Subject: [PATCH 03/23] Remove last_data checks Nowadays everything should be using clipnotify, which avoids constantly copying the same data. --- clipmenud | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/clipmenud b/clipmenud index f25e7f8..6de7704 100755 --- a/clipmenud +++ b/clipmenud @@ -85,8 +85,6 @@ fi # shellcheck disable=SC2174 mkdir -p -m0700 "$cache_dir" -declare -A last_data - command -v clipnotify >/dev/null 2>&1 && has_clipnotify=1 if ! (( has_clipnotify )); then @@ -137,29 +135,16 @@ while true; do continue fi - if [[ ${last_data[$selection]} == "$data" ]]; then - debug 'Skipping as last selection is the same as this one' - continue - fi - - last_data[$selection]=$data - first_line=$(get_first_line "$data") debug "New clipboard entry on $selection selection: \"$first_line\"" - # Without checking ${last_data[any]}, we often double write since both - # selections get the same content - if [[ ${last_data[any]} != "$data" ]]; then - filename="$cache_dir/$(cksum <<< "$first_line")" - debug "Writing $data to $filename" - printf '%s' "$data" > "$filename" + filename="$cache_dir/$(cksum <<< "$first_line")" + debug "Writing $data to $filename" + printf '%s' "$data" > "$filename" - debug "Writing $first_line to $cache_file" - printf '%s\n' "$first_line" >> "$cache_file" - fi - - last_data[any]=$data + debug "Writing $first_line to $cache_file" + printf '%s\n' "$first_line" >> "$cache_file" if (( CM_OWN_CLIPBOARD )) && [[ $selection != primary ]]; then # Take ownership of the clipboard, in case the original application From eb7d2b94817e8b66f62dc372a204a78a12e3ef0f Mon Sep 17 00:00:00 2001 From: Chris Down Date: Mon, 19 Feb 2018 15:01:21 +0000 Subject: [PATCH 04/23] Take clipboard ownership prior to clipnotify This avoids infinite loop if we make the work block async --- clipmenud | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/clipmenud b/clipmenud index 6de7704..cd40b34 100755 --- a/clipmenud +++ b/clipmenud @@ -97,6 +97,19 @@ exec {lock_fd}> "$lock_file" sleep_cmd=(sleep "${CM_SLEEP:-0.5}") while true; do + # We need to take ownership synchronously before we run `clipnotify` as + # otherwise we could enter an infinite loop. + if (( CM_OWN_CLIPBOARD )); then + # Take ownership of the clipboard, in case the original application + # is unable to serve the clipboard request (due to being suspended, + # etc). + # + # Primary is excluded from the change of ownership as applications + # sometimes act up if clipboard focus is taken away from them -- + # for example, urxvt will unhilight text, which is undesirable. + _xsel -k --clipboard + fi + if ! (( CM_ONESHOT )); then if (( has_clipnotify )); then # Fall back to polling if clipnotify fails @@ -146,21 +159,6 @@ while true; do debug "Writing $first_line to $cache_file" printf '%s\n' "$first_line" >> "$cache_file" - if (( CM_OWN_CLIPBOARD )) && [[ $selection != primary ]]; then - # Take ownership of the clipboard, in case the original application - # is unable to serve the clipboard request (due to being suspended, - # etc). - # - # Primary is excluded from the change of ownership as applications - # sometimes act up if clipboard focus is taken away from them -- - # for example, urxvt will unhilight text, which is undesirable. - # - # We can't colocate this with the above copying code because - # https://github.com/cdown/clipmenu/issues/34 requires knowing if - # we would skip first. - _xsel -k --"$selection" - fi - if (( CM_MAX_CLIPS )); then mapfile -t to_remove < <( head -n -"$CM_MAX_CLIPS" "$cache_file" | From 11304d3a60dfc469759c4662743f792cd5d4ca80 Mon Sep 17 00:00:00 2001 From: Chris Down Date: Mon, 19 Feb 2018 15:04:52 +0000 Subject: [PATCH 05/23] Make main work block async Closes #59. --- clipmenud | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/clipmenud b/clipmenud index cd40b34..0a92dd0 100755 --- a/clipmenud +++ b/clipmenud @@ -10,7 +10,7 @@ major_version=4 cache_dir=$CM_DIR/clipmenu.$major_version.$USER/ cache_file=$cache_dir/line_cache lock_file=$cache_dir/lock -lock_timeout=2 +lock_timeout=5 has_clipnotify=0 xsel_log=/dev/null @@ -120,6 +120,7 @@ while true; do fi fi + { if ! flock -x -w "$lock_timeout" "$lock_fd"; then if (( CM_ONESHOT )); then printf 'ERROR: %s\n' 'Timed out waiting for lock' >&2 @@ -176,8 +177,10 @@ while true; do done flock -u "$lock_fd" + } & if (( CM_ONESHOT )); then + wait debug 'Oneshot mode enabled, exiting' break fi From 47eb3d182c22e4ab2584562ef9dd215df43796dd Mon Sep 17 00:00:00 2001 From: Chris Down Date: Mon, 19 Feb 2018 15:07:26 +0000 Subject: [PATCH 06/23] Use xsel -o/-i instead of -k -k is not selection based, so it also takes PRIMARY. --- clipmenud | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clipmenud b/clipmenud index 0a92dd0..994c6fb 100755 --- a/clipmenud +++ b/clipmenud @@ -107,7 +107,7 @@ while true; do # Primary is excluded from the change of ownership as applications # sometimes act up if clipboard focus is taken away from them -- # for example, urxvt will unhilight text, which is undesirable. - _xsel -k --clipboard + _xsel -o --clipboard | _xsel -i --clipboard fi if ! (( CM_ONESHOT )); then From fe6986110197aa73f22fdcbc4f4991f8350ee8b3 Mon Sep 17 00:00:00 2001 From: Chris Down Date: Mon, 19 Feb 2018 15:11:35 +0000 Subject: [PATCH 07/23] Make sure we don't break #34 in new pre-ownership --- clipmenud | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/clipmenud b/clipmenud index 994c6fb..1a0b779 100755 --- a/clipmenud +++ b/clipmenud @@ -107,7 +107,10 @@ while true; do # Primary is excluded from the change of ownership as applications # sometimes act up if clipboard focus is taken away from them -- # for example, urxvt will unhilight text, which is undesirable. - _xsel -o --clipboard | _xsel -i --clipboard + # + # We need to check if the clipboard is empty to mitigate #34. + data=$(_xsel -o --clipboard; printf x) + [[ $data != x ]] && _xsel -i --clipboard <<< "${data%x}" fi if ! (( CM_ONESHOT )); then From 728d242d3c196a3fb2ed99c388581eab8edf3b6e Mon Sep 17 00:00:00 2001 From: Chris Down Date: Mon, 19 Feb 2018 15:41:11 +0000 Subject: [PATCH 08/23] Remove CM_ONESHOT-specific logic in flock failure checks Now that we're in a subshell and acting asynchronously, this doesn't do anything different. --- clipmenud | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/clipmenud b/clipmenud index 1a0b779..473e5e1 100755 --- a/clipmenud +++ b/clipmenud @@ -125,14 +125,8 @@ while true; do { if ! flock -x -w "$lock_timeout" "$lock_fd"; then - if (( CM_ONESHOT )); then - printf 'ERROR: %s\n' 'Timed out waiting for lock' >&2 - exit 1 - else - printf 'ERROR: %s\n' \ - 'Timed out waiting for lock, skipping this run' >&2 - continue - fi + printf 'ERROR: %s\n' 'Timed out waiting for lock' >&2 + exit 1 fi for selection in clipboard primary; do From caab3f6a62af041305b2c6a68ff68e67d0fdd2e4 Mon Sep 17 00:00:00 2001 From: Chris Down Date: Mon, 19 Feb 2018 18:21:44 +0000 Subject: [PATCH 09/23] Add $CM_SELECTIONS to limit which selections we copy Closes #60. --- clipmenud | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/clipmenud b/clipmenud index 473e5e1..84d6972 100755 --- a/clipmenud +++ b/clipmenud @@ -5,6 +5,7 @@ : "${CM_DEBUG=0}" : "${CM_DIR="${XDG_RUNTIME_DIR-"${TMPDIR-/tmp}"}"}" : "${CM_MAX_CLIPS=1000}" +: "${CM_SELECTIONS=clipboard primary}" major_version=4 cache_dir=$CM_DIR/clipmenu.$major_version.$USER/ @@ -13,6 +14,11 @@ lock_file=$cache_dir/lock lock_timeout=5 has_clipnotify=0 +# This comes from the environment, so we rely on word splitting. +# shellcheck disable=SC2206 +cm_selections=( $CM_SELECTIONS ) + + xsel_log=/dev/null for file in /proc/self/fd/2 /dev/stderr; do [[ -f "$file" ]] || continue @@ -64,6 +70,17 @@ debug() { fi } +element_in() { + local item element + item="$1" + for element in "${@:2}"; do + if [[ "$item" == "$element" ]]; then + return 0 + fi + done + return 1 +} + if [[ $1 == --help ]] || [[ $1 == -h ]]; then cat << 'EOF' clipmenud is the daemon that collects and caches what's on the clipboard. @@ -76,6 +93,7 @@ Environment variables: - $CM_OWN_CLIPBOARD: take ownership of the clipboard (default: 1) - $CM_MAX_CLIPS: maximum number of clips to store, 0 for inf (default: 1000) - $CM_DIR: specify the base directory to store the cache dir in (default: $XDG_RUNTIME_DIR, $TMPDIR, or /tmp) +- $CM_SELECTIONS: space separated list of the selections to manage (default: "clipboard primary") EOF exit 0 fi @@ -99,7 +117,7 @@ sleep_cmd=(sleep "${CM_SLEEP:-0.5}") while true; do # We need to take ownership synchronously before we run `clipnotify` as # otherwise we could enter an infinite loop. - if (( CM_OWN_CLIPBOARD )); then + if (( CM_OWN_CLIPBOARD )) && element_in clipboard "${cm_selections[@]}"; then # Take ownership of the clipboard, in case the original application # is unable to serve the clipboard request (due to being suspended, # etc). @@ -129,7 +147,7 @@ while true; do exit 1 fi - for selection in clipboard primary; do + for selection in "${cm_selections[@]}"; do data=$(_xsel -o --"$selection"; printf x) debug "Data before stripping: $data" From a85f8998b40626ac88dce5c5f32a8e9f538a983b Mon Sep 17 00:00:00 2001 From: Chris Down Date: Tue, 20 Feb 2018 09:29:49 +0000 Subject: [PATCH 10/23] Revert "Make main work block async" There is currently a bug where this causes a copy storm at startup. More worryingly, more and more newlines are appended to the end, so something is going wrong in copy logic anyway, async or not. This reverts commit 11304d3a60dfc469759c4662743f792cd5d4ca80. --- clipmenud | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/clipmenud b/clipmenud index 84d6972..f3f4e52 100755 --- a/clipmenud +++ b/clipmenud @@ -11,7 +11,7 @@ major_version=4 cache_dir=$CM_DIR/clipmenu.$major_version.$USER/ cache_file=$cache_dir/line_cache lock_file=$cache_dir/lock -lock_timeout=5 +lock_timeout=2 has_clipnotify=0 # This comes from the environment, so we rely on word splitting. @@ -141,7 +141,6 @@ while true; do fi fi - { if ! flock -x -w "$lock_timeout" "$lock_fd"; then printf 'ERROR: %s\n' 'Timed out waiting for lock' >&2 exit 1 @@ -192,10 +191,8 @@ while true; do done flock -u "$lock_fd" - } & if (( CM_ONESHOT )); then - wait debug 'Oneshot mode enabled, exiting' break fi From 6825c302dc892cb757d644055184defe3f3121b1 Mon Sep 17 00:00:00 2001 From: Chris Down Date: Tue, 20 Feb 2018 10:07:58 +0000 Subject: [PATCH 11/23] Revert "Remove CM_ONESHOT-specific logic in flock failure checks" This reverts commit 728d242d3c196a3fb2ed99c388581eab8edf3b6e. --- clipmenud | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/clipmenud b/clipmenud index f3f4e52..d4c4fb9 100755 --- a/clipmenud +++ b/clipmenud @@ -142,8 +142,14 @@ while true; do fi if ! flock -x -w "$lock_timeout" "$lock_fd"; then - printf 'ERROR: %s\n' 'Timed out waiting for lock' >&2 - exit 1 + if (( CM_ONESHOT )); then + printf 'ERROR: %s\n' 'Timed out waiting for lock' >&2 + exit 1 + else + printf 'ERROR: %s\n' \ + 'Timed out waiting for lock, skipping this run' >&2 + continue + fi fi for selection in "${cm_selections[@]}"; do From 9a52f7ddba821da6d2b84cca398a483851953af5 Mon Sep 17 00:00:00 2001 From: Chris Down Date: Tue, 20 Feb 2018 10:08:13 +0000 Subject: [PATCH 12/23] Revert "Make sure we don't break #34 in new pre-ownership" This reverts commit fe6986110197aa73f22fdcbc4f4991f8350ee8b3. --- clipmenud | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/clipmenud b/clipmenud index d4c4fb9..b0efe22 100755 --- a/clipmenud +++ b/clipmenud @@ -125,10 +125,7 @@ while true; do # Primary is excluded from the change of ownership as applications # sometimes act up if clipboard focus is taken away from them -- # for example, urxvt will unhilight text, which is undesirable. - # - # We need to check if the clipboard is empty to mitigate #34. - data=$(_xsel -o --clipboard; printf x) - [[ $data != x ]] && _xsel -i --clipboard <<< "${data%x}" + _xsel -o --clipboard | _xsel -i --clipboard fi if ! (( CM_ONESHOT )); then From 9c436084f26e9d15188b153b1b36780fe4e17502 Mon Sep 17 00:00:00 2001 From: Chris Down Date: Tue, 20 Feb 2018 10:08:20 +0000 Subject: [PATCH 13/23] Revert "Use xsel -o/-i instead of -k" This reverts commit 47eb3d182c22e4ab2584562ef9dd215df43796dd. --- clipmenud | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clipmenud b/clipmenud index b0efe22..644d1c3 100755 --- a/clipmenud +++ b/clipmenud @@ -125,7 +125,7 @@ while true; do # Primary is excluded from the change of ownership as applications # sometimes act up if clipboard focus is taken away from them -- # for example, urxvt will unhilight text, which is undesirable. - _xsel -o --clipboard | _xsel -i --clipboard + _xsel -k --clipboard fi if ! (( CM_ONESHOT )); then From 52b144d970f1a2aae73059fde52e8e614b5bc3e1 Mon Sep 17 00:00:00 2001 From: Chris Down Date: Tue, 20 Feb 2018 10:09:24 +0000 Subject: [PATCH 14/23] Revert "Take clipboard ownership prior to clipnotify" This reverts commit eb7d2b94817e8b66f62dc372a204a78a12e3ef0f. --- clipmenud | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/clipmenud b/clipmenud index 644d1c3..b7508e4 100755 --- a/clipmenud +++ b/clipmenud @@ -115,19 +115,6 @@ exec {lock_fd}> "$lock_file" sleep_cmd=(sleep "${CM_SLEEP:-0.5}") while true; do - # We need to take ownership synchronously before we run `clipnotify` as - # otherwise we could enter an infinite loop. - if (( CM_OWN_CLIPBOARD )) && element_in clipboard "${cm_selections[@]}"; then - # Take ownership of the clipboard, in case the original application - # is unable to serve the clipboard request (due to being suspended, - # etc). - # - # Primary is excluded from the change of ownership as applications - # sometimes act up if clipboard focus is taken away from them -- - # for example, urxvt will unhilight text, which is undesirable. - _xsel -k --clipboard - fi - if ! (( CM_ONESHOT )); then if (( has_clipnotify )); then # Fall back to polling if clipnotify fails @@ -177,6 +164,22 @@ while true; do debug "Writing $first_line to $cache_file" printf '%s\n' "$first_line" >> "$cache_file" + if (( CM_OWN_CLIPBOARD )) && [[ $selection != primary ]] && + element_in clipboard "${cm_selections[@]}"; then + # Take ownership of the clipboard, in case the original application + # is unable to serve the clipboard request (due to being suspended, + # etc). + # + # Primary is excluded from the change of ownership as applications + # sometimes act up if clipboard focus is taken away from them -- + # for example, urxvt will unhilight text, which is undesirable. + # + # We can't colocate this with the above copying code because + # https://github.com/cdown/clipmenu/issues/34 requires knowing if + # we would skip first. + _xsel -k --"$selection" + fi + if (( CM_MAX_CLIPS )); then mapfile -t to_remove < <( head -n -"$CM_MAX_CLIPS" "$cache_file" | From 7b4267868e6a7881e5f80509211d3a9a0411fb68 Mon Sep 17 00:00:00 2001 From: Chris Down Date: Tue, 20 Feb 2018 10:09:50 +0000 Subject: [PATCH 15/23] Revert "Remove last_data checks" This reverts commit 55407ba3f622c99b1efafbe6b6371680f6abd8f6. --- clipmenud | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/clipmenud b/clipmenud index b7508e4..f02c272 100755 --- a/clipmenud +++ b/clipmenud @@ -103,6 +103,8 @@ fi # shellcheck disable=SC2174 mkdir -p -m0700 "$cache_dir" +declare -A last_data + command -v clipnotify >/dev/null 2>&1 && has_clipnotify=1 if ! (( has_clipnotify )); then @@ -153,16 +155,29 @@ while true; do continue fi + if [[ ${last_data[$selection]} == "$data" ]]; then + debug 'Skipping as last selection is the same as this one' + continue + fi + + last_data[$selection]=$data + first_line=$(get_first_line "$data") debug "New clipboard entry on $selection selection: \"$first_line\"" - filename="$cache_dir/$(cksum <<< "$first_line")" - debug "Writing $data to $filename" - printf '%s' "$data" > "$filename" + # Without checking ${last_data[any]}, we often double write since both + # selections get the same content + if [[ ${last_data[any]} != "$data" ]]; then + filename="$cache_dir/$(cksum <<< "$first_line")" + debug "Writing $data to $filename" + printf '%s' "$data" > "$filename" - debug "Writing $first_line to $cache_file" - printf '%s\n' "$first_line" >> "$cache_file" + debug "Writing $first_line to $cache_file" + printf '%s\n' "$first_line" >> "$cache_file" + fi + + last_data[any]=$data if (( CM_OWN_CLIPBOARD )) && [[ $selection != primary ]] && element_in clipboard "${cm_selections[@]}"; then From c7c894a0238d531aeb4aab8ec2361bd1b2a14768 Mon Sep 17 00:00:00 2001 From: Chris Down Date: Tue, 20 Feb 2018 10:29:23 +0000 Subject: [PATCH 16/23] Use separate line cache for each selection --- clipmenu | 9 ++++++--- clipmenud | 10 ++++++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/clipmenu b/clipmenu index b526f17..b5a4abf 100755 --- a/clipmenu +++ b/clipmenu @@ -3,12 +3,12 @@ : "${CM_LAUNCHER=dmenu}" : "${CM_DIR="${XDG_RUNTIME_DIR-"${TMPDIR-/tmp}"}"}" -major_version=4 +major_version=5 shopt -s nullglob cache_dir=$CM_DIR/clipmenu.$major_version.$USER -cache_file=$cache_dir/line_cache +cache_file_prefix=$cache_dir/line_cache if [[ $1 == --help ]] || [[ $1 == -h ]]; then cat << 'EOF' @@ -34,7 +34,10 @@ fi # whether `-l` is also in "$@", because the way that dmenu works allows a later # argument to override an earlier one. That is, if the user passes in `-l`, our # one will be ignored. -chosen_line=$(tac "$cache_file" | awk '!seen[$0]++' | "$CM_LAUNCHER" -l 8 "$@") +chosen_line=$( + cat "$cache_file_prefix"_* /dev/null | sort -rnk 1 | cut -d' ' -f2- | + awk '!seen[$0]++' | "$CM_LAUNCHER" -l 8 "$@" +) [[ $chosen_line ]] || exit 1 diff --git a/clipmenud b/clipmenud index f02c272..9fd0033 100755 --- a/clipmenud +++ b/clipmenud @@ -7,9 +7,9 @@ : "${CM_MAX_CLIPS=1000}" : "${CM_SELECTIONS=clipboard primary}" -major_version=4 +major_version=5 cache_dir=$CM_DIR/clipmenu.$major_version.$USER/ -cache_file=$cache_dir/line_cache +cache_file_prefix=$cache_dir/line_cache lock_file=$cache_dir/lock lock_timeout=2 has_clipnotify=0 @@ -166,6 +166,8 @@ while true; do debug "New clipboard entry on $selection selection: \"$first_line\"" + cache_file=${cache_file_prefix}_$selection + # Without checking ${last_data[any]}, we often double write since both # selections get the same content if [[ ${last_data[any]} != "$data" ]]; then @@ -174,7 +176,7 @@ while true; do printf '%s' "$data" > "$filename" debug "Writing $first_line to $cache_file" - printf '%s\n' "$first_line" >> "$cache_file" + printf '%d %s\n' "$(date +%s)" "$first_line" >> "$cache_file" fi last_data[any]=$data @@ -195,7 +197,7 @@ while true; do _xsel -k --"$selection" fi - if (( CM_MAX_CLIPS )); then + if (( CM_MAX_CLIPS )) && [[ -f $cache_file ]]; then mapfile -t to_remove < <( head -n -"$CM_MAX_CLIPS" "$cache_file" | while read -r line; do cksum <<< "$line"; done From 8896df90115093ca6044bb8d6839c8bcc1f13c14 Mon Sep 17 00:00:00 2001 From: Chris Down Date: Tue, 20 Feb 2018 10:30:54 +0000 Subject: [PATCH 17/23] Use -o/-i, not -k -k is not selection based --- clipmenud | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clipmenud b/clipmenud index 9fd0033..f273aa2 100755 --- a/clipmenud +++ b/clipmenud @@ -194,7 +194,7 @@ while true; do # We can't colocate this with the above copying code because # https://github.com/cdown/clipmenu/issues/34 requires knowing if # we would skip first. - _xsel -k --"$selection" + _xsel -o --clipboard | _xsel -i --clipboard fi if (( CM_MAX_CLIPS )) && [[ -f $cache_file ]]; then From 0b885650fe63d7679e4fa51c06ae9511dac7af42 Mon Sep 17 00:00:00 2001 From: Chris Down Date: Tue, 20 Feb 2018 10:38:48 +0000 Subject: [PATCH 18/23] Revert "Remove partial clip dedupe support" This reverts commit caa009b222e6b859c86de1b44409f9a013f0ae10. --- clipmenud | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/clipmenud b/clipmenud index f273aa2..684e776 100755 --- a/clipmenud +++ b/clipmenud @@ -160,6 +160,15 @@ while true; do continue fi + # If we were in the middle of doing a selection when the previous poll + # ran, then we may have got a partial clip. + possible_partial=${last_data[$selection]} + if [[ $possible_partial && $data == "$possible_partial"* ]]; then + debug "$possible_partial is a possible partial of $data" + debug "Removing ${last_filename[$selection]}" + rm -- "${last_filename[$selection]}" + fi + last_data[$selection]=$data first_line=$(get_first_line "$data") From 1896ceaccc921100da04744845eb17bfa23554d6 Mon Sep 17 00:00:00 2001 From: Chris Down Date: Tue, 20 Feb 2018 10:39:30 +0000 Subject: [PATCH 19/23] Act on possible partials in both directions --- clipmenud | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clipmenud b/clipmenud index 684e776..0f0c666 100755 --- a/clipmenud +++ b/clipmenud @@ -163,7 +163,8 @@ while true; do # If we were in the middle of doing a selection when the previous poll # ran, then we may have got a partial clip. possible_partial=${last_data[$selection]} - if [[ $possible_partial && $data == "$possible_partial"* ]]; then + if [[ $possible_partial && $data == "$possible_partial"* ]] || + [[ $possible_partial && $data == *"$possible_partial" ]]; then debug "$possible_partial is a possible partial of $data" debug "Removing ${last_filename[$selection]}" rm -- "${last_filename[$selection]}" From 4e969bd67dc008fe82b309a3d8a4f924bf274421 Mon Sep 17 00:00:00 2001 From: Chris Down Date: Tue, 20 Feb 2018 11:03:35 +0000 Subject: [PATCH 20/23] Truncate cache file on duplicate --- clipmenud | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/clipmenud b/clipmenud index 0f0c666..ac4954d 100755 --- a/clipmenud +++ b/clipmenud @@ -104,6 +104,8 @@ fi mkdir -p -m0700 "$cache_dir" declare -A last_data +declare -A last_filename +declare -A last_cache_file_output command -v clipnotify >/dev/null 2>&1 && has_clipnotify=1 @@ -139,6 +141,7 @@ while true; do fi for selection in "${cm_selections[@]}"; do + cache_file=${cache_file_prefix}_$selection data=$(_xsel -o --"$selection"; printf x) debug "Data before stripping: $data" @@ -160,6 +163,7 @@ while true; do continue fi + # If we were in the middle of doing a selection when the previous poll # ran, then we may have got a partial clip. possible_partial=${last_data[$selection]} @@ -167,29 +171,34 @@ while true; do [[ $possible_partial && $data == *"$possible_partial" ]]; then debug "$possible_partial is a possible partial of $data" debug "Removing ${last_filename[$selection]}" + + previous_size=$(wc -c <<< "${last_cache_file_output[$selection]}") + truncate -s -"$previous_size" "$cache_file" + rm -- "${last_filename[$selection]}" fi last_data[$selection]=$data + last_filename[$selection]=$filename first_line=$(get_first_line "$data") debug "New clipboard entry on $selection selection: \"$first_line\"" - cache_file=${cache_file_prefix}_$selection - # Without checking ${last_data[any]}, we often double write since both # selections get the same content + cache_file_output="$(date +%s) $first_line" if [[ ${last_data[any]} != "$data" ]]; then filename="$cache_dir/$(cksum <<< "$first_line")" debug "Writing $data to $filename" printf '%s' "$data" > "$filename" - debug "Writing $first_line to $cache_file" - printf '%d %s\n' "$(date +%s)" "$first_line" >> "$cache_file" + debug "Writing $cache_file_output to $cache_file" + printf '%s\n' "$cache_file_output" >> "$cache_file" fi last_data[any]=$data + last_cache_file_output[$selection]=$cache_file_output if (( CM_OWN_CLIPBOARD )) && [[ $selection != primary ]] && element_in clipboard "${cm_selections[@]}"; then From 1a145d298f86689542f58414c69159ebda3c87b2 Mon Sep 17 00:00:00 2001 From: Chris Down Date: Tue, 20 Feb 2018 11:30:07 +0000 Subject: [PATCH 21/23] Fix tests to use version 5 format --- tests/test-clipmenu | 9 ++++----- tests/test-perf | 6 +++--- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/tests/test-clipmenu b/tests/test-clipmenu index 3916c36..02dc711 100755 --- a/tests/test-clipmenu +++ b/tests/test-clipmenu @@ -6,9 +6,9 @@ set -o pipefail : "${CM_DIR="${XDG_RUNTIME_DIR-"${TMPDIR-/tmp}"}"}" -major_version=4 +major_version=5 dir=$CM_DIR/clipmenu.$major_version.$USER -cache_file=$dir/line_cache +cache_file=$dir/line_cache_primary if [[ $0 == /* ]]; then location=${0%/*} @@ -58,8 +58,8 @@ rm -rf "$dir" mkdir -p "$dir" cat > "$cache_file" << 'EOF' -Selected text. (2 lines) -Selected text 2. (2 lines) +1234 Selected text. (2 lines) +1235 Selected text 2. (2 lines) EOF cat > "$dir/$(cksum <<< 'Selected text. (2 lines)')" << 'EOF' @@ -70,7 +70,6 @@ EOF ### TESTS ### temp=$(mktemp) -trap 'rm -f -- "$temp"' EXIT /tmp/clipmenu --foo bar > "$temp" 2>&1 diff --git a/tests/test-perf b/tests/test-perf index 9a7a767..cde9e3a 100755 --- a/tests/test-perf +++ b/tests/test-perf @@ -1,6 +1,6 @@ #!/bin/bash -major_version=4 +major_version=5 msg() { printf '>>> %s\n' "$@" >&2 @@ -9,7 +9,7 @@ msg() { : "${CM_DIR="${XDG_RUNTIME_DIR-"${TMPDIR-/tmp}"}"}" dir=$CM_DIR/clipmenu.$major_version.$USER -cache_file=$dir/line_cache +cache_file=$dir/line_cache_primary log=$(mktemp) tim=$(mktemp) @@ -62,7 +62,7 @@ if ! (( NO_RECREATE )); then ) read -r first_line_raw <<< "$data" printf -v first_line '%s (%s lines)\n' "$first_line_raw" "$num_lines" - printf '%s' "$first_line" >> "$cache_file" + printf '%d %s' "$i" "$first_line" >> "$cache_file" fn=$dir/$(cksum <<< "$first_line") printf '%s' "$data" > "$fn" done From ee0a6f6d31a20d1c41279a1371c4fd32b70d4489 Mon Sep 17 00:00:00 2001 From: Chris Down Date: Tue, 20 Feb 2018 11:31:08 +0000 Subject: [PATCH 22/23] Add shellcheck disable for CM_SELECTIONS --- clipmenud | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clipmenud b/clipmenud index ac4954d..68c87fc 100755 --- a/clipmenud +++ b/clipmenud @@ -5,6 +5,9 @@ : "${CM_DEBUG=0}" : "${CM_DIR="${XDG_RUNTIME_DIR-"${TMPDIR-/tmp}"}"}" : "${CM_MAX_CLIPS=1000}" + +# Shellcheck is mistaken here, this is used later as lowercase. +# shellcheck disable=SC2153 : "${CM_SELECTIONS=clipboard primary}" major_version=5 From 44d7baa27a4adf93ce423dee8a40a1c862f7e26f Mon Sep 17 00:00:00 2001 From: Chris Down Date: Tue, 20 Feb 2018 11:36:00 +0000 Subject: [PATCH 23/23] Have CM_MAX_CLIPS understand new version 5 format --- clipmenud | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clipmenud b/clipmenud index 68c87fc..cc5adcc 100755 --- a/clipmenud +++ b/clipmenud @@ -222,7 +222,7 @@ while true; do if (( CM_MAX_CLIPS )) && [[ -f $cache_file ]]; then mapfile -t to_remove < <( head -n -"$CM_MAX_CLIPS" "$cache_file" | - while read -r line; do cksum <<< "$line"; done + while read -r line; do cksum <<< "${line#* }"; done ) num_to_remove="${#to_remove[@]}" if (( num_to_remove )); then