Merge branch 'release/4.0.0'

This commit is contained in:
Chris Down 2017-10-25 01:58:03 +01:00
commit ef3102c5e9
6 changed files with 80 additions and 44 deletions

View file

@ -29,6 +29,11 @@ there, but it basically works like this:
1. `clipmenud` polls the clipboard every 0.5 seconds (or another interval as 1. `clipmenud` polls the clipboard every 0.5 seconds (or another interval as
configured with the `CM_SLEEP` environment variable). Unfortunately there's configured with the `CM_SLEEP` environment variable). Unfortunately there's
no interface to subscribe for changes in X11, so we must poll. no interface to subscribe for changes in X11, so we must poll.
Instead of polling, you can bind your copy key binding to also issue
`CM_ONESHOT=1 clipmenud`. However, there's no generic way to do this, since
any keys or mouse buttons could be bound to do this action in a number of
ways.
2. If `clipmenud` detects changes to the clipboard contents, it writes them out 2. If `clipmenud` detects changes to the clipboard contents, it writes them out
to the cache directory. to the cache directory.

View file

@ -1,21 +1,17 @@
#!/bin/bash #!/bin/bash
major_version=3 : "${CM_LAUNCHER=dmenu}"
: "${CM_DIR="${XDG_RUNTIME_DIR-"${TMPDIR-/tmp}"}"}"
major_version=4
shopt -s nullglob shopt -s nullglob
cache_dir=/tmp/clipmenu.$major_version.$USER cache_dir=$CM_DIR/clipmenu.$major_version.$USER
cache_file=$cache_dir/line_cache cache_file=$cache_dir/line_cache
: "${CM_LAUNCHER=dmenu}" if [[ $1 == --help ]] || [[ $1 == -h ]]; then
cat << 'EOF'
if [[ "$CM_LAUNCHER" == rofi ]]; then
# rofi supports dmenu-like arguments through the -dmenu flag
set -- -dmenu "$@"
fi
if [[ $1 == --help ]]; then
cat << EOF
clipmenu is a simple clipboard manager using dmenu and xsel. Launch this clipmenu is a simple clipboard manager using dmenu and xsel. Launch this
when you want to select a clip. when you want to select a clip.
@ -23,11 +19,17 @@ All arguments are passed through to dmenu itself.
Environment variables: Environment variables:
- \$CM_LAUNCHER: specify a dmenu-compatible launcher (default: dmenu) - $CM_LAUNCHER: specify a dmenu-compatible launcher (default: dmenu)
- $CM_DIR: specify the base directory to store the cache dir in (default: $XDG_RUNTIME_DIR, $TMPDIR, or /tmp)
EOF EOF
exit 0 exit 0
fi fi
if [[ "$CM_LAUNCHER" == rofi ]]; then
# rofi supports dmenu-like arguments through the -dmenu flag
set -- -dmenu "$@"
fi
# It's okay to hardcode `-l 8` here as a sensible default without checking # It's okay to hardcode `-l 8` here as a sensible default without checking
# whether `-l` is also in "$@", because the way that dmenu works allows a later # 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 # argument to override an earlier one. That is, if the user passes in `-l`, our

View file

@ -1,14 +1,16 @@
#!/bin/bash #!/bin/bash
major_version=3
cache_dir=/tmp/clipmenu.$major_version.$USER/
cache_file=$cache_dir/line_cache
lock_file=$cache_dir/lock
lock_timeout=2
: "${CM_ONESHOT=0}" : "${CM_ONESHOT=0}"
: "${CM_OWN_CLIPBOARD=1}" : "${CM_OWN_CLIPBOARD=1}"
: "${CM_DEBUG=0}" : "${CM_DEBUG=0}"
: "${CM_DIR="${XDG_RUNTIME_DIR-"${TMPDIR-/tmp}"}"}"
: "${CM_MAX_CLIPS=1000}"
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
_xsel() { _xsel() {
timeout 1 xsel --logfile /dev/stderr "$@" timeout 1 xsel --logfile /dev/stderr "$@"
@ -51,16 +53,32 @@ debug() {
fi fi
} }
if [[ $1 == --help ]] || [[ $1 == -h ]]; then
cat << 'EOF'
clipmenud is the daemon that collects and caches what's on the clipboard.
when you want to select a clip.
Environment variables:
- $CM_ONESHOT: run once immediately, do not loop (default: 0)
- $CM_DEBUG: turn on debugging output (default: 0)
- $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)
EOF
exit 0
fi
# It's ok that this only applies to the final directory. # It's ok that this only applies to the final directory.
# shellcheck disable=SC2174 # shellcheck disable=SC2174
mkdir -p -m0700 "$cache_dir" mkdir -p -m0700 "$cache_dir"
declare -A last_data declare -A last_data
declare -A last_filename
exec {lock_fd}> "$lock_file" exec {lock_fd}> "$lock_file"
while sleep "${CM_SLEEP:-0.5}"; do while (( CM_ONESHOT )) || sleep "${CM_SLEEP:-0.5}"; do
if ! flock -x -w "$lock_timeout" "$lock_fd"; then if ! flock -x -w "$lock_timeout" "$lock_fd"; then
if (( CM_ONESHOT )); then if (( CM_ONESHOT )); then
printf 'ERROR: %s\n' 'Timed out waiting for lock' >&2 printf 'ERROR: %s\n' 'Timed out waiting for lock' >&2
@ -94,29 +112,24 @@ while sleep "${CM_SLEEP:-0.5}"; do
continue continue
fi 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 last_data[$selection]=$data
last_filename[$selection]=$filename
first_line=$(get_first_line "$data") first_line=$(get_first_line "$data")
printf 'New clipboard entry on %s selection: "%s"\n' \ debug "New clipboard entry on $selection selection: \"$first_line\""
"$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")" filename="$cache_dir/$(cksum <<< "$first_line")"
debug "Writing $data to $filename" debug "Writing $data to $filename"
printf '%s' "$data" > "$filename" printf '%s' "$data" > "$filename"
debug "Writing $first_line to $cache_file" debug "Writing $first_line to $cache_file"
printf '%s\n' "$first_line" >> "$cache_file" printf '%s\n' "$first_line" >> "$cache_file"
fi
last_data[any]=$data
if (( CM_OWN_CLIPBOARD )) && [[ $selection != primary ]]; then if (( CM_OWN_CLIPBOARD )) && [[ $selection != primary ]]; then
# Take ownership of the clipboard, in case the original application # Take ownership of the clipboard, in case the original application
@ -132,6 +145,21 @@ while sleep "${CM_SLEEP:-0.5}"; do
# we would skip first. # we would skip first.
_xsel -o --"$selection" | _xsel -i --"$selection" _xsel -o --"$selection" | _xsel -i --"$selection"
fi fi
if (( CM_MAX_CLIPS )); then
mapfile -t to_remove < <(
head -n -"$CM_MAX_CLIPS" "$cache_file" |
while read -r line; do cksum <<< "$line"; done
)
num_to_remove="${#to_remove[@]}"
if (( num_to_remove )); then
debug "Removing $num_to_remove old clips"
rm -- "${to_remove[@]/#/"$cache_dir/"}"
trunc_tmp=$(mktemp)
tail -n "$CM_MAX_CLIPS" "$cache_file" | uniq > "$trunc_tmp"
mv -- "$trunc_tmp" "$cache_file"
fi
fi
done done
flock -u "$lock_fd" flock -u "$lock_fd"

View file

@ -14,8 +14,5 @@ ProtectKernelTunables=yes
RestrictAddressFamilies= RestrictAddressFamilies=
RestrictRealtime=yes RestrictRealtime=yes
ProtectSystem=strict
ReadWritePaths=/tmp
[Install] [Install]
WantedBy=default.target WantedBy=default.target

View file

@ -4,8 +4,10 @@ set -x
set -e set -e
set -o pipefail set -o pipefail
major_version=3 : "${CM_DIR="${XDG_RUNTIME_DIR-"${TMPDIR-/tmp}"}"}"
dir=/tmp/clipmenu.$major_version.$USER
major_version=4
dir=$CM_DIR/clipmenu.$major_version.$USER
cache_file=$dir/line_cache cache_file=$dir/line_cache
if [[ $0 == /* ]]; then if [[ $0 == /* ]]; then

View file

@ -1,12 +1,14 @@
#!/bin/bash #!/bin/bash
major_version=3 major_version=4
msg() { msg() {
printf '>>> %s\n' "$@" >&2 printf '>>> %s\n' "$@" >&2
} }
dir=/tmp/clipmenu.$major_version.$USER : "${CM_DIR="${XDG_RUNTIME_DIR-"${TMPDIR-/tmp}"}"}"
dir=$CM_DIR/clipmenu.$major_version.$USER
cache_file=$dir/line_cache cache_file=$dir/line_cache
log=$(mktemp) log=$(mktemp)