"""Консольный бэкенд клавиш — для разработки и теста в Linux. Глобальный хук клавиатуры (библиотека keyboard) на Linux требует root, поэтому здесь клавиши имитируются вводом в терминале: Enter — нажать/отпустить «Слушай» (первое нажатие = начать запись, второе = отпустить и обработать; аналог удержания кнопки); s + Enter — «Замолчи»; r + Enter — «Повтори»; q + Enter — выход. На Windows-ПК отец получит настоящие глобальные клавиши (keyboard_hook.py). """ from __future__ import annotations import threading from .base import KeyEvents class ConsoleBackend: name = "console" def __init__(self, events: KeyEvents) -> None: self._events = events self._talk_held = False self._running = False def start(self) -> None: import threading self._running = True thread = threading.Thread(target=self._loop, daemon=True) thread.start() def stop(self) -> None: self._running = False # --- внутреннее ------------------------------------------------------ def _loop(self) -> None: # Одиночные клавиши БЕЗ Enter (readchar): работает без root, когда # окно терминала в фокусе. Пробел = настоящий пробел, как на Windows. try: import readchar use_readchar = True except ImportError: use_readchar = False # фолбэк: строки + Enter (старое поведение) if use_readchar: self._loop_readchar(readchar) else: self._loop_lines() def _loop_readchar(self, readchar) -> None: while self._running: key = readchar.readkey() if key in ("\r", "\n"): continue # Enter игнорируем (не part of управления) if key == " ": self._events.on_dialog_toggle() # ПРОБЕЛ = тумблер диалога elif key == "s": self._events.on_stop() elif key == "r": self._events.on_repeat() elif key in ("q", "\x03"): # q или Ctrl+C self._events.on_quit() return # остальные клавиши игнорируются def _loop_lines(self) -> None: """Фолбэк: строки через Enter (если readchar не установлен).""" if self._events.on_dialog_toggle: print(" (readchar не установлен: пробел имитируй 'd' + Enter; " "pip install readchar — включит настоящий пробел)") while self._running: try: line = input() except EOFError: # stdin закрылся — выходим self._events.on_quit() return cmd = line.strip().lower() if cmd == "q": self._events.on_quit() return if cmd in ("d", "space", "пробел"): if self._events.on_dialog_toggle: self._events.on_dialog_toggle() elif cmd == "s": self._events.on_stop() elif cmd == "r": self._events.on_repeat() elif cmd == "": # Enter без текста = нажатие/отпускание «Слушай» (push-to-talk) if self._talk_held: self._talk_held = False self._events.on_talk_up() else: self._talk_held = True self._events.on_talk_down()