Files
2026-09-11 19:47:15 +03:00

100 lines
4.0 KiB
Python

"""Консольный бэкенд клавиш — для разработки и теста в 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()