Files
Blind/modules/hotkeys/keyboard_hook.py
T
2026-09-11 19:47:15 +03:00

57 lines
2.8 KiB
Python

"""Бэкенд глобальных клавиш на библиотеке 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()