|
#!/bin/bash |
|
# handy external script paste — GNOME wayland |
|
# |
|
# Behavior: |
|
# - If an Emacs frame currently holds keyboard focus, insert the text |
|
# directly via emacsclient (no clipboard, no synthetic keystrokes). |
|
# Respects evil-mode state: |
|
# * normal/motion/visual -> kill-new + evil-paste-after (behaves like yank) |
|
# * insert/emacs/other -> plain (insert text) at point |
|
# - Otherwise, fall back to the original clipboard + ydotool Ctrl+Shift+V path. |
|
# |
|
# Notes: |
|
# - wtype doesn't work on GNOME (no zwp_virtual_keyboard_v1). |
|
# - ydotool uses /dev/uinput, works everywhere. |
|
# - CRITICAL: wl-copy forks a daemon that inherits our fds. handy's |
|
# paste_via_external_script uses .output() which blocks until all |
|
# stdout/stderr writers close. Without redirection, wl-copy's daemon |
|
# keeps fds open → handy blocks forever → zombie. |
|
|
|
set -u |
|
TEXT="${1-}" |
|
|
|
emacs_has_focus() { |
|
command -v emacsclient >/dev/null 2>&1 || return 1 |
|
local r |
|
r=$(timeout 0.4 emacsclient --eval \ |
|
'(and (seq-some (lambda (f) (eq (frame-focus-state f) t)) (frame-list)) t)' \ |
|
2>/dev/null) || return 1 |
|
[ "$r" = "t" ] |
|
} |
|
|
|
paste_into_emacs() { |
|
# base64 keeps newlines/quotes/emoji safe through shell + elisp string literals |
|
local b64 |
|
b64=$(printf '%s' "$TEXT" | base64 -w0) |
|
|
|
# Insert on the focused frame's buffer. If in an evil normal-ish state, |
|
# go through the kill-ring so it feels like a real yank (one undo entry, |
|
# respects evil semantics). Otherwise plain insert at point. |
|
emacsclient --eval " |
|
(let* ((text (decode-coding-string (base64-decode-string \"$b64\") 'utf-8)) |
|
(f (seq-find (lambda (f) (eq (frame-focus-state f) t)) (frame-list)))) |
|
(when f |
|
(with-selected-frame f |
|
(with-current-buffer (window-buffer (frame-selected-window f)) |
|
(condition-case err |
|
(let ((state (and (bound-and-true-p evil-mode) |
|
(boundp 'evil-state) evil-state))) |
|
(cond |
|
((memq state '(normal motion visual operator)) |
|
(kill-new text) |
|
(evil-paste-after 1)) |
|
(t |
|
(insert text)))) |
|
(error (message \"handy-paste: %S\" err)))))))" \ |
|
</dev/null >/dev/null 2>&1 |
|
} |
|
|
|
if emacs_has_focus; then |
|
paste_into_emacs |
|
exit 0 |
|
fi |
|
|
|
# --- fallback: clipboard + synthetic Ctrl+Shift+V --- |
|
|
|
# copy text to wayland clipboard (detach wl-copy's daemon from our fds) |
|
wl-copy -- "$TEXT" </dev/null >/dev/null 2>&1 |
|
|
|
# simulate Ctrl+Shift+V (terminal paste) via ydotool |
|
# keycodes: ctrl=29, shift=42, v=47 |
|
ydotool key 29:1 42:1 47:1 47:0 42:0 29:0 |