diff --git a/docs/USAGE.md b/docs/USAGE.md index 10f4c24..bce6391 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -18,8 +18,8 @@ tuimble --host mumble.example.com --user myname ## Push-to-Talk Modes -- **hold** — hold key to transmit, release to stop (default) -- **toggle** — press to start, press again to stop +- **toggle** — press to start, press again to stop (default) +- **hold** — hold key to transmit, release to stop (requires evdev) ## Configuration diff --git a/src/tuimble/app.py b/src/tuimble/app.py index f316dd6..375f2ec 100644 --- a/src/tuimble/app.py +++ b/src/tuimble/app.py @@ -410,18 +410,15 @@ class TuimbleApp(App): def on_key(self, event: events.Key) -> None: """Handle PTT key events.""" - ptt_key = self._config.ptt.key + if event.key != self._config.ptt.key: + return - if isinstance(self._ptt, KittyPtt): - if event.key == ptt_key: - is_release = getattr(event, "key_type", None) - if is_release == "release": - self._ptt.key_up() - else: - self._ptt.key_down() - elif isinstance(self._ptt, TogglePtt): - if event.key == ptt_key: - self._ptt.toggle() + if isinstance(self._ptt, TogglePtt): + self._ptt.toggle() + elif isinstance(self._ptt, KittyPtt): + # Kitty hold-mode requires key-release events that Textual + # does not expose; kept for explicit backend="kitty" only. + self._ptt.key_down() def _on_ptt_change(self, transmitting: bool) -> None: self._audio.capturing = transmitting diff --git a/src/tuimble/config.py b/src/tuimble/config.py index 6f5bef3..ee2c8d9 100644 --- a/src/tuimble/config.py +++ b/src/tuimble/config.py @@ -29,7 +29,7 @@ class AudioConfig: @dataclass class PttConfig: key: str = "f4" - mode: str = "hold" # hold | toggle + mode: str = "toggle" # toggle | hold (hold requires evdev) backend: str = "auto" # auto | kitty | evdev | toggle diff --git a/src/tuimble/ptt.py b/src/tuimble/ptt.py index e64a706..c939947 100644 --- a/src/tuimble/ptt.py +++ b/src/tuimble/ptt.py @@ -139,6 +139,12 @@ def detect_backend(callback: Callback, preference: str = "auto") -> PttBackend: if preference == "toggle": return TogglePtt(callback) - # auto: try kitty first (will be validated at runtime by the app), - # then evdev, then toggle - return KittyPtt(callback) + # auto: Textual does not expose key-release events, so kitty + # hold-mode is not viable. Try evdev, fall back to toggle. + try: + import evdev as _evdev # noqa: F811 + + _ = _evdev.list_devices() + return EvdevPtt(callback) + except Exception: + return TogglePtt(callback) diff --git a/tests/test_config.py b/tests/test_config.py index e5f9f82..30b7ca5 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -8,7 +8,7 @@ def test_default_config(): assert cfg.server.host == "localhost" assert cfg.server.port == 64738 assert cfg.audio.sample_rate == 48000 - assert cfg.ptt.mode == "hold" + assert cfg.ptt.mode == "toggle" def test_server_config():