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)