"""Бэкенд глобальных клавиш на библиотеке keyboard — целевой для Windows. Перехватывает клавиши ГЛОБАЛЬНО (окно может быть свёрнуто) — именно то, что нужно отцу. Требования/ограничения библиотеки keyboard: - Windows: работает без админ-прав; - Linux: требует root (поэтому в разработке используем console-бэкенд); - проект библиотеки не развивается с 2020 — если на Windows появятся проблемы, заменяем на pynput (интерфейс KeyEvents менять не придётся). Клавиши задаются в .env (HOTKEY_TALK / HOTKEY_STOP / HOTKEY_REPEAT); по умолчанию: space / esc / enter. Имена — как в библиотеке keyboard (numpad-клавиши подберём при настройке на Windows, напр. «num 0», «num enter»). """ from __future__ import annotations from .base import KeyEvents class KeyboardHookBackend: name = "keys" def __init__(self, events: KeyEvents, talk: str = "num 0", stop_key: str = "esc", repeat: str = "enter", dialog: str = "space") -> None: self._events = events self._talk = talk self._stop_key = stop_key self._repeat = repeat self._dialog = dialog # ПРОБЕЛ: тумблер диалога self._started = False def start(self) -> None: import keyboard # ленивый импорт: на Linux без root упадёт — см. console if self._dialog and self._events.on_dialog_toggle: keyboard.add_hotkey(self._dialog, self._events.on_dialog_toggle, suppress=True) if self._talk: keyboard.on_press_key(self._talk, self._talk_press) keyboard.on_release_key(self._talk, self._talk_release) keyboard.add_hotkey(self._stop_key, self._events.on_stop, suppress=True) keyboard.add_hotkey(self._repeat, self._events.on_repeat, suppress=True) self._started = True def stop(self) -> None: if self._started: import keyboard keyboard.unhook_all() self._started = False # --- внутреннее ------------------------------------------------------ def _talk_press(self, event) -> None: # noqa: ANN001 # Библиотека шлёт повторные down-события при удержании; лишние вызовы # безопасны: машина состояний игнорирует on_talk_down в состоянии listening. self._events.on_talk_down() def _talk_release(self, event) -> None: # noqa: ANN001 self._events.on_talk_up()