Skip to content

Instantly share code, notes, and snippets.

@okwalerie
Last active July 27, 2026 19:22
Show Gist options
  • Select an option

  • Save okwalerie/9d86c94f0853e236dc9749f53528bcab to your computer and use it in GitHub Desktop.

Select an option

Save okwalerie/9d86c94f0853e236dc9749f53528bcab to your computer and use it in GitHub Desktop.
Handy custom script for GNOME 43 / Silverblue / Wayland
#!/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

Output: External Script Clipboard Handing: Copy to Clipboard Auto Submit: Off

packages:

  • ydotool (included in bluefin-dx)
  • wl-clipboard / wl-copy (included in base image)
  • wtype (not used)
rpm-ostree status
● ostree-image-signed:docker://ghcr.io/ublue-os/bluefin-dx:gts
                   Digest: sha256:aee4da4540d813d494cf0492c25474f6063743cb009788651051b32201d358a7
                  Version: 43.20260317 (2026-03-17T01:52:16Z)
          LayeredPackages: gtk-layer-shell waydroid wtype
            LocalPackages: handy-0.7.7-1.x86_64
             InitramfsEtc: /etc/vconsole.conf
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment