feat(clipboard): add read-only clipboard-check diagnostic script
Lists selection owner/metadata via wl-paste -l to tell Firefox, kitty OSC 52 and wl-copy apart when a copy appears to do nothing. 'clipboard-check.sh test' runs a 20s observation window. Never writes the clipboard or restarts anything. Lives in scripts/ with ~/clipboard-check.sh symlinked in, matching the existing scripts/ convention.
This commit is contained in:
1 parent
bf87375356
commit
4bb1c06883
1 file changed
+95
Executable
+95
@@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env bash
|
||||
# clipboard-check.sh -- diagnose "copy did nothing" on sway/Wayland.
|
||||
# READ-ONLY: lists selection metadata and reads text; never writes the clipboard,
|
||||
# never runs wl-copy, never restarts anything.
|
||||
# ./clipboard-check.sh -> status (what is configured right now)
|
||||
# ./clipboard-check.sh test -> 20s window: copy in Firefox, then tell me who failed
|
||||
set -uo pipefail
|
||||
|
||||
own_sig() { # identify the owner from its advertised type list
|
||||
local t
|
||||
t=$(wl-paste -l 2>&1); [[ -z "$t" ]] && { echo "(clipboard empty / no owner)"; return; }
|
||||
case "$t" in
|
||||
*x-moz-url-priv*|*text/html*) echo "Firefox (has moz/html types)" ;;
|
||||
*glfw+clipboard*) echo "kitty/GLFW terminal (OSC 52 or copy_on_select)" ;;
|
||||
*image/png*) echo "image (screenshot? wl-copy -b)" ;;
|
||||
*) echo "some other client: $(echo "$t" | tr '\n' ' ')" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
if [[ "${1:-status}" == test ]]; then
|
||||
echo "BEFORE owner: $(own_sig)"
|
||||
echo
|
||||
echo " >> Copy some text in Firefox NOW (Ctrl+C), then press Enter."
|
||||
read -r _
|
||||
echo "AFTER owner: $(own_sig)"
|
||||
echo
|
||||
if wl-paste -l >/dev/null 2>&1; then
|
||||
n=$(wl-paste 2>/dev/null | wc -c)
|
||||
echo " text available: $n byte(s)"
|
||||
[[ $n -gt 0 ]] && echo " preview: $(wl-paste 2>/dev/null | head -c 70 | tr '\n' ' ')"
|
||||
echo
|
||||
echo " VERDICT: the compositor HAS the copy -> the loss is on the paste side"
|
||||
echo " (stale PRIMARY for middle-click, or a clipboard-history keybind"
|
||||
echo " that no longer exists since clipman was removed)."
|
||||
else
|
||||
echo " VERDICT: the compositor has NOTHING -> Firefox never took ownership."
|
||||
echo " Usual causes, in order of likelihood:"
|
||||
echo " 1) Firefox lacked keyboard focus at Ctrl+C -- Wayland only"
|
||||
echo " accepts set_selection from the focused surface (a PiP window,"
|
||||
echo " a toast, or a workspace switch stealing focus is enough)."
|
||||
echo " 2) The page's own JS intercepted the copy event (sites that"
|
||||
echo " block copying). dom.event.clipboardevents.enabled=true by"
|
||||
echo " default, so page scripts may do this."
|
||||
echo " 3) The offer got cancelled mid-transfer ('selection cancelled'"
|
||||
echo " in sway's log) -- see: journalctl -b | grep 'selection cancelled'"
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "== components =="
|
||||
printf ' %-22s %s\n' "sway" "$(swaymsg --version 2>&1 | head -1)"
|
||||
printf ' %-22s %s\n' "wl-clipboard" "$(wl-copy --version 2>&1 | head -1)"
|
||||
printf ' %-22s %s\n' "firefox" "$(firefox --version 2>&1 | head -1) [$(pacman -Q firefox 2>/dev/null || echo '?')]"
|
||||
printf ' %-22s %s\n' "cliphist" "$(command -v cliphist || echo 'NOT INSTALLED (history keybinds are dead)')"
|
||||
echo
|
||||
echo "== clipboard managers / watchers (a watcher that dies mid-copy LOSES the copy) =="
|
||||
m=$(pgrep -af 'wl-paste|cliphist|clipman|copyq|klipper|parcellite|flameshot' 2>/dev/null || true)
|
||||
[[ -n "$m" ]] && echo "$m" | sed 's/^/ RUNNING /' || echo " none running <- no history is being saved, but also nothing can steal a copy"
|
||||
echo
|
||||
echo "== XWayland mixing (X11<->Wayland clipboard skew is a classic cause) =="
|
||||
if pgrep -x Xwayland >/dev/null 2>&1; then
|
||||
echo " Xwayland IS running -> any X11 app can own the selection out-of-band:"
|
||||
pgrep -a Xwayland | sed 's/^/ /'
|
||||
else
|
||||
echo " no Xwayland -> pure Wayland, that whole failure class is ruled out"
|
||||
fi
|
||||
echo
|
||||
echo "== window shells (a copy from an X11-surfaced Firefox behaves differently) =="
|
||||
swaymsg -t get_tree 2>/dev/null | python3 -c "
|
||||
import json,sys
|
||||
def walk(n):
|
||||
yield n
|
||||
for k in ('nodes','floating_nodes'):
|
||||
for c in n.get(k) or []: yield from walk(c)
|
||||
for n in walk(json.load(sys.stdin)):
|
||||
if n.get('pid') and n.get('type') in ('con','floating_con'):
|
||||
shell = 'x11' if n.get('window') is not None else 'wayland'
|
||||
wp = n.get('window_properties') or {}
|
||||
ident = str(n.get('app_id') or wp.get('class'))[:24]
|
||||
print(' %-8s %-26s %s' % (shell, ident, (n.get('name') or '')[:38]))
|
||||
" 2>&1 | sort -u | head -12
|
||||
echo
|
||||
echo "== current selections (read-only) =="
|
||||
printf ' CLIPBOARD owner : %s\n' "$(own_sig)"
|
||||
printf ' PRIMARY types : %s\n' "$(wl-paste -p -l 2>&1 | tr '\n' ' ' | cut -c1-100)"
|
||||
echo
|
||||
echo "== Firefox clipboard prefs (blank = default) =="
|
||||
P=$(ls ~/.mozilla/firefox/*/prefs.js 2>/dev/null | head -1)
|
||||
grep -hE 'dom\.event\.clipboardevents|middlepaste|widget\.wayland' "$P" 2>/dev/null | sed 's/^/ /' || true
|
||||
grep -hE 'user_pref' "$(dirname "$P")/user.js" 2>/dev/null | grep -iE 'clipboard|wayland' | sed 's/^/ user.js: /' || true
|
||||
echo
|
||||
echo "== past cancellations sway logged (each one = a copy that vanished) =="
|
||||
journalctl -b 0 --no-pager 2>/dev/null | grep -ic 'selection cancelled' | sed 's/^/ '\''selection cancelled'\'' events this boot: /'
|
||||
echo
|
||||
echo "Run '$0 test' to A/B an actual Firefox copy."
|
||||
Reference in new issue
Block a user