#!/usr/bin/env python3 """ openwhispr-ptt.py — Push-to-talk wrapper for OpenWhispr Turns Ctrl+Super (hold) into Ctrl+` toggle pairs: - Press Ctrl+Super → sends Ctrl+` to START dictation - Release Ctrl+Super → sends Ctrl+` to STOP dictation Requires: pynput, xdotool Usage: python3 openwhispr-ptt.py """ import subprocess import threading import time import signal import sys try: from pynput import keyboard except ImportError: print("Error: pynput not installed.") print("Install with: pip3 install pynput") sys.exit(1) # ── Configuration ──────────────────────────────────────────────────────────── # The key combo YOU hold to talk PTT_MODIFIER = keyboard.Key.ctrl_l PTT_KEY = keyboard.Key.cmd # Super/Windows key # Also accept right Ctrl PTT_MODIFIER_ALT = keyboard.Key.ctrl_r # The key combo OpenWhispr uses (Ctrl+`) # We simulate this via xdotool for reliability with Electron apps WHISPR_HOTKEY = "ctrl+grave" # ── State ──────────────────────────────────────────────────────────────────── is_recording = False press_time = 0 ctrl_held = False super_held = False lock = threading.Lock() def send_whispr_toggle(): """Send Ctrl+` to OpenWhispr via xdotool.""" try: subprocess.run( ["xdotool", "key", WHISPR_HOTKEY], timeout=2, capture_output=True, ) except FileNotFoundError: print("Error: xdotool not installed. Install with: sudo apt install xdotool") sys.exit(1) except subprocess.TimeoutExpired: pass def on_press(key): global is_recording, press_time, ctrl_held, super_held with lock: # Track modifier state — ignore key repeat (already True) if key in (PTT_MODIFIER, PTT_MODIFIER_ALT): ctrl_held = True elif key == PTT_KEY: super_held = True # Start recording when both held and not already recording if ctrl_held and super_held and not is_recording: press_time = time.time() is_recording = True send_whispr_toggle() # START print("\033[32m● Recording...\033[0m", flush=True) def on_release(key): global is_recording, press_time, ctrl_held, super_held with lock: released_ptt = False if key in (PTT_MODIFIER, PTT_MODIFIER_ALT): ctrl_held = False released_ptt = True elif key == PTT_KEY: super_held = False released_ptt = True # Stop recording when either key is truly released if is_recording and released_ptt: elapsed_ms = (time.time() - press_time) * 1000 send_whispr_toggle() # STOP is_recording = False print(f"\033[0m○ Stopped ({int(elapsed_ms)}ms)", flush=True) def main(): print("═══════════════════════════════════════════") print(" OpenWhispr Push-to-Talk") print("═══════════════════════════════════════════") print(" Hold: Ctrl + Super → start dictation") print(" Release: Ctrl + Super → stop dictation") print(" Quit: Ctrl + C") print("═══════════════════════════════════════════") print("Listening...\n") # Handle Ctrl+C gracefully signal.signal(signal.SIGINT, lambda *_: sys.exit(0)) with keyboard.Listener(on_press=on_press, on_release=on_release) as listener: listener.join() if __name__ == "__main__": main()