Files
dikte/paste.py
T
yusufipk 011493a9bc Ship full-resolution WebP screenshots, drop em dashes everywhere
The screenshots were downscaled to 430 px wide, which made the UI text
blurry. Restore them at native 1292 px as lossless WebP, which is also
half the size of the original PNGs (72 KB against 155 KB for the largest).

Rewrite every em dash in prose, comments, docstrings and interface strings
as ordinary punctuation.
2026-07-25 19:31:10 +07:00

91 lines
2.8 KiB
Python

"""Clipboard (wl-clipboard) and key injection (ydotool)."""
import shutil
import subprocess
import time
from i18n import t
# Linux input event codes (linux/input-event-codes.h)
KEYCODES = {
"ctrl": 29, "control": 29, "shift": 42, "alt": 56, "super": 125, "meta": 125,
"v": 47, "insert": 110, "enter": 28, "return": 28,
}
class PasteError(Exception):
pass
def read_clipboard():
if not shutil.which("wl-paste"):
return None
try:
res = subprocess.run(["wl-paste", "--no-newline"], capture_output=True, timeout=5)
except (subprocess.SubprocessError, OSError):
return None
return res.stdout if res.returncode == 0 else None
def _run_wl_copy(payload):
"""wl-copy forks to keep owning the selection; leaving its pipes open makes
subprocess.run wait for EOF forever, hence DEVNULL."""
return subprocess.run(
["wl-copy"],
input=payload,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
timeout=10,
)
def copy(text):
if not shutil.which("wl-copy"):
raise PasteError(t("wl-copy not found. Install wl-clipboard."))
try:
res = _run_wl_copy(text.encode("utf-8"))
except (subprocess.SubprocessError, OSError) as exc:
raise PasteError(t("Could not copy to clipboard: {error}", error=exc)) from exc
if res.returncode != 0:
raise PasteError(t("wl-copy exited with code {code}.", code=res.returncode))
def copy_bytes(data):
if data is None or not shutil.which("wl-copy"):
return
try:
_run_wl_copy(data)
except (subprocess.SubprocessError, OSError):
pass
def ydotool_ready():
return shutil.which("ydotool") is not None
def press(shortcut="ctrl+v", delay=0.12):
"""Press a key combination through ydotool, e.g. 'ctrl+v'."""
if not ydotool_ready():
raise PasteError(t("ydotool not found, cannot paste automatically."))
codes = []
for key in (k.strip().lower() for k in shortcut.split("+") if k.strip()):
code = KEYCODES.get(key)
if code is None:
raise PasteError(t("Unknown key: {key}", key=key))
codes.append(code)
seq = [f"{c}:1" for c in codes] + [f"{c}:0" for c in reversed(codes)]
time.sleep(delay) # let the selection settle and focus come back
try:
res = subprocess.run(["ydotool", "key", *seq], capture_output=True,
text=True, timeout=10)
except (subprocess.SubprocessError, OSError) as exc:
raise PasteError(t("Could not run ydotool: {error}", error=exc)) from exc
if res.returncode != 0:
raise PasteError(t(
"ydotool failed: {error}\nIs ydotoold running? "
"(systemctl --user status ydotool)",
error=res.stderr.strip() or "unknown error",
))