mirror of
https://github.com/yusufipk/dikte.git
synced 2026-09-11 19:06:11 +00:00
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.
91 lines
2.8 KiB
Python
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",
|
|
))
|