85 lines
2.5 KiB
Python
85 lines
2.5 KiB
Python
import os
|
|
import shutil
|
|
import subprocess
|
|
import time
|
|
|
|
from whisper_app import app, config
|
|
|
|
|
|
def _pynput_type(text):
|
|
from pynput.keyboard import Controller as KeyboardController
|
|
KeyboardController().type(text)
|
|
|
|
|
|
def _wl_paste():
|
|
"""Read current clipboard contents, returns None on failure."""
|
|
try:
|
|
result = subprocess.run(
|
|
["wl-paste", "--no-newline"],
|
|
capture_output=True, timeout=2,
|
|
)
|
|
if result.returncode == 0:
|
|
return result.stdout
|
|
except (subprocess.TimeoutExpired, FileNotFoundError):
|
|
pass
|
|
return None
|
|
|
|
|
|
def _wl_copy_bytes(data):
|
|
"""Restore clipboard from raw bytes."""
|
|
try:
|
|
subprocess.run(
|
|
["wl-copy"],
|
|
input=data, check=False, timeout=2,
|
|
)
|
|
except (subprocess.TimeoutExpired, FileNotFoundError):
|
|
pass
|
|
|
|
|
|
def _paste_via_ydotool():
|
|
"""Simulate Ctrl+V using ydotool (kernel-level, works on all compositors)."""
|
|
subprocess.run(["ydotool", "key", "29:1", "47:1", "47:0", "29:0"], check=False)
|
|
|
|
|
|
def _paste_via_wtype():
|
|
"""Simulate Ctrl+V using wtype (native Wayland, wlroots only)."""
|
|
subprocess.run(["wtype", "-M", "ctrl", "-P", "v", "-p", "v", "-m", "ctrl"],
|
|
check=False)
|
|
|
|
|
|
def _paste_via_xdotool():
|
|
"""Simulate Ctrl+V using xdotool (XWayland fallback)."""
|
|
subprocess.run(["xdotool", "key", "ctrl+v"], check=False)
|
|
|
|
|
|
def _pick_paste_cmd():
|
|
"""Select best available paste tool for the current Wayland session."""
|
|
if shutil.which("ydotool"):
|
|
return _paste_via_ydotool
|
|
if shutil.which("wtype"):
|
|
return _paste_via_wtype
|
|
return _paste_via_xdotool
|
|
|
|
|
|
def type_text(text):
|
|
"""Type text into the active window, cross-platform."""
|
|
if os.name == "nt":
|
|
_pynput_type(text)
|
|
return
|
|
session = os.environ.get("XDG_SESSION_TYPE", "")
|
|
if session == "wayland" and shutil.which("wl-copy"):
|
|
delay = config.config.get("paste_delay_ms", 300) / 1000.0
|
|
old_clipboard = _wl_paste()
|
|
subprocess.run(["wl-copy", "--", text], check=False)
|
|
time.sleep(0.05)
|
|
paste_fn = _pick_paste_cmd()
|
|
app.log(f"typer: using {paste_fn.__name__}, session={session}")
|
|
paste_fn()
|
|
time.sleep(delay)
|
|
if old_clipboard is not None:
|
|
_wl_copy_bytes(old_clipboard)
|
|
elif shutil.which("xdotool"):
|
|
subprocess.run(["xdotool", "type", "--clearmodifiers", "--", text], check=False)
|
|
else:
|
|
_pynput_type(text)
|