commit 44c0fb3ad26dea8cc818d8a57aa414aecf22128d
parent fd665a298a1ace0637b5a6f3c4cd017c03c51216
Author: Chris Down <chris@chrisdown.name>
Date: Tue, 20 Feb 2018 11:38:34 +0000
Merge branch 'release/5.0.0'
Diffstat:
4 files changed, 70 insertions(+), 22 deletions(-)
diff --git 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
@@ -6,13 +6,22 @@
: "${CM_DIR="${XDG_RUNTIME_DIR-"${TMPDIR-/tmp}"}"}"
: "${CM_MAX_CLIPS=1000}"
-major_version=4
+# Shellcheck is mistaken here, this is used later as lowercase.
+# shellcheck disable=SC2153
+: "${CM_SELECTIONS=clipboard primary}"
+
+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
+# 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 +73,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 +96,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
@@ -86,6 +107,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
@@ -96,13 +119,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
@@ -117,7 +143,8 @@ while true; do
fi
fi
- for selection in clipboard primary; do
+ for selection in "${cm_selections[@]}"; do
+ cache_file=${cache_file_prefix}_$selection
data=$(_xsel -o --"$selection"; printf x)
debug "Data before stripping: $data"
@@ -139,7 +166,23 @@ 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"* ]] ||
+ [[ $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")
@@ -147,18 +190,21 @@ while true; do
# 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 '%s\n' "$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 ]]; then
+ 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).
@@ -170,13 +216,13 @@ 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 -o --clipboard | _xsel -i --clipboard
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
+ while read -r line; do cksum <<< "${line#* }"; done
)
num_to_remove="${#to_remove[@]}"
if (( num_to_remove )); then
diff --git 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
@@ -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