diff --git a/openwhispr-ptt.py b/openwhispr-ptt.py index 635c683..e290ad0 100644 --- a/openwhispr-ptt.py +++ b/openwhispr-ptt.py @@ -29,18 +29,19 @@ except ImportError: 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" -# Minimum hold time (ms) to avoid accidental triggers -MIN_HOLD_MS = 100 - # ── State ──────────────────────────────────────────────────────────────────── -pressed_keys = set() is_recording = False press_time = 0 +ctrl_held = False +super_held = False lock = threading.Lock() @@ -60,46 +61,51 @@ def send_whispr_toggle(): def on_press(key): - global is_recording, press_time + global is_recording, press_time, ctrl_held, super_held with lock: - pressed_keys.add(key) + # 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 - # Check if both PTT keys are held - if PTT_MODIFIER in pressed_keys and PTT_KEY in pressed_keys: - if not is_recording: - press_time = time.time() - is_recording = True - send_whispr_toggle() # START - print("\033[32m● Recording...\033[0m") + # 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 + global is_recording, press_time, ctrl_held, super_held with lock: - # If either PTT key is released while recording - if is_recording and key in (PTT_MODIFIER, PTT_KEY): - elapsed_ms = (time.time() - press_time) * 1000 - if elapsed_ms >= MIN_HOLD_MS: - send_whispr_toggle() # STOP - print(f"\033[0m○ Stopped ({int(elapsed_ms)}ms)") - else: - # Too short — send another toggle to cancel - send_whispr_toggle() - print(f"\033[33m○ Cancelled (too short: {int(elapsed_ms)}ms)\033[0m") - is_recording = False + released_ptt = False - pressed_keys.discard(key) + 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(f" Hold: Ctrl + Super → start dictation") - print(f" Release: Ctrl + Super → stop dictation") - print(f" Quit: Ctrl + C") + print(" Hold: Ctrl + Super → start dictation") + print(" Release: Ctrl + Super → stop dictation") + print(" Quit: Ctrl + C") print("═══════════════════════════════════════════") print("Listening...\n")