Fix PTT rapid-fire: track key state as booleans, ignore repeats
Super key generates rapid press/release events from key repeat. Changed from set-based tracking to boolean flags (ctrl_held, super_held) so repeated press events are ignored when the key is already held. Removed min-hold cancel logic. https://claude.ai/code/session_01XKYC1basxdwtHy71tky7xm
This commit is contained in:
+35
-29
@@ -29,18 +29,19 @@ except ImportError:
|
|||||||
PTT_MODIFIER = keyboard.Key.ctrl_l
|
PTT_MODIFIER = keyboard.Key.ctrl_l
|
||||||
PTT_KEY = keyboard.Key.cmd # Super/Windows key
|
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+`)
|
# The key combo OpenWhispr uses (Ctrl+`)
|
||||||
# We simulate this via xdotool for reliability with Electron apps
|
# We simulate this via xdotool for reliability with Electron apps
|
||||||
WHISPR_HOTKEY = "ctrl+grave"
|
WHISPR_HOTKEY = "ctrl+grave"
|
||||||
|
|
||||||
# Minimum hold time (ms) to avoid accidental triggers
|
|
||||||
MIN_HOLD_MS = 100
|
|
||||||
|
|
||||||
# ── State ────────────────────────────────────────────────────────────────────
|
# ── State ────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
pressed_keys = set()
|
|
||||||
is_recording = False
|
is_recording = False
|
||||||
press_time = 0
|
press_time = 0
|
||||||
|
ctrl_held = False
|
||||||
|
super_held = False
|
||||||
lock = threading.Lock()
|
lock = threading.Lock()
|
||||||
|
|
||||||
|
|
||||||
@@ -60,46 +61,51 @@ def send_whispr_toggle():
|
|||||||
|
|
||||||
|
|
||||||
def on_press(key):
|
def on_press(key):
|
||||||
global is_recording, press_time
|
global is_recording, press_time, ctrl_held, super_held
|
||||||
|
|
||||||
with lock:
|
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
|
# Start recording when both held and not already recording
|
||||||
if PTT_MODIFIER in pressed_keys and PTT_KEY in pressed_keys:
|
if ctrl_held and super_held and not is_recording:
|
||||||
if not is_recording:
|
press_time = time.time()
|
||||||
press_time = time.time()
|
is_recording = True
|
||||||
is_recording = True
|
send_whispr_toggle() # START
|
||||||
send_whispr_toggle() # START
|
print("\033[32m● Recording...\033[0m", flush=True)
|
||||||
print("\033[32m● Recording...\033[0m")
|
|
||||||
|
|
||||||
|
|
||||||
def on_release(key):
|
def on_release(key):
|
||||||
global is_recording, press_time
|
global is_recording, press_time, ctrl_held, super_held
|
||||||
|
|
||||||
with lock:
|
with lock:
|
||||||
# If either PTT key is released while recording
|
released_ptt = False
|
||||||
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
|
|
||||||
|
|
||||||
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():
|
def main():
|
||||||
print("═══════════════════════════════════════════")
|
print("═══════════════════════════════════════════")
|
||||||
print(" OpenWhispr Push-to-Talk")
|
print(" OpenWhispr Push-to-Talk")
|
||||||
print("═══════════════════════════════════════════")
|
print("═══════════════════════════════════════════")
|
||||||
print(f" Hold: Ctrl + Super → start dictation")
|
print(" Hold: Ctrl + Super → start dictation")
|
||||||
print(f" Release: Ctrl + Super → stop dictation")
|
print(" Release: Ctrl + Super → stop dictation")
|
||||||
print(f" Quit: Ctrl + C")
|
print(" Quit: Ctrl + C")
|
||||||
print("═══════════════════════════════════════════")
|
print("═══════════════════════════════════════════")
|
||||||
print("Listening...\n")
|
print("Listening...\n")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user