From edece0488d8c8f42b594311771623da510bffe63 Mon Sep 17 00:00:00 2001 From: Vitali Graf Date: Mon, 6 Apr 2026 20:50:44 +0200 Subject: [PATCH] fix: listen on all matching input devices, not just the first keyboard --- whisper_local/hotkey.py | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/whisper_local/hotkey.py b/whisper_local/hotkey.py index 8f4f6d3..24d8505 100644 --- a/whisper_local/hotkey.py +++ b/whisper_local/hotkey.py @@ -13,18 +13,22 @@ logger = logging.getLogger(__name__) AsyncCallback = Callable[[], Coroutine] -def find_keyboard_device() -> InputDevice: - """Findet das erste Keyboard-Device in /dev/input/.""" - devices = [InputDevice(path) for path in evdev.list_devices()] - for device in devices: +def find_keyboard_devices(key_name: str) -> list[InputDevice]: + """Findet alle Devices die den angegebenen Key unterstützen.""" + matches = [] + for path in evdev.list_devices(): + device = InputDevice(path) capabilities = device.capabilities(verbose=True) for (etype_name, _etype_code), events in capabilities.items(): if etype_name == "EV_KEY": key_names = [name for name, _code in events] - if "KEY_A" in key_names: - logger.info("Keyboard gefunden: %s (%s)", device.name, device.path) - return device - raise RuntimeError("Kein Keyboard-Device gefunden in /dev/input/") + if key_name in key_names: + logger.info("Device mit %s gefunden: %s (%s)", key_name, device.name, device.path) + matches.append(device) + break + if not matches: + raise RuntimeError(f"Kein Device mit {key_name} gefunden in /dev/input/") + return matches class HotkeyListener: @@ -43,15 +47,21 @@ class HotkeyListener: elif not key_down and self.on_release: await self.on_release() - async def listen(self) -> None: - """Lauscht auf evdev-Events der konfigurierten Taste.""" - device = find_keyboard_device() - logger.info("Lausche auf %s auf %s", self.key_name, device.name) + async def _read_device(self, device: InputDevice) -> None: + """Liest Events von einem einzelnen Device.""" async for event in device.async_read_loop(): if event.type == ecodes.EV_KEY and event.code == self.key_code: if event.value == 1: # Key down - logger.debug("%s gedrückt", self.key_name) + logger.debug("%s gedrückt (via %s)", self.key_name, device.path) await self._handle_key_event(key_down=True) elif event.value == 0: # Key up - logger.debug("%s losgelassen", self.key_name) + logger.debug("%s losgelassen (via %s)", self.key_name, device.path) await self._handle_key_event(key_down=False) + + async def listen(self) -> None: + """Lauscht auf evdev-Events der konfigurierten Taste auf allen passenden Devices.""" + devices = find_keyboard_devices(self.key_name) + for dev in devices: + logger.info("Lausche auf %s auf %s (%s)", self.key_name, dev.name, dev.path) + tasks = [asyncio.create_task(self._read_device(dev)) for dev in devices] + await asyncio.gather(*tasks)