fix: ptt auto-detect falls back to toggle mode

Textual does not expose key-release events, so KittyPtt
hold-mode never received key_up and stayed transmitting.
Auto-detect now tries evdev first, then falls back to
toggle (press-on/press-off). Default mode changed to toggle.
This commit is contained in:
Username
2026-02-24 12:50:40 +01:00
parent bc0da57625
commit 31ac90d2c9
5 changed files with 21 additions and 18 deletions

View File

@@ -18,8 +18,8 @@ tuimble --host mumble.example.com --user myname
## Push-to-Talk Modes ## Push-to-Talk Modes
- **hold** — hold key to transmit, release to stop (default) - **toggle** — press to start, press again to stop (default)
- **toggle** — press to start, press again to stop - **hold** — hold key to transmit, release to stop (requires evdev)
## Configuration ## Configuration

View File

@@ -410,18 +410,15 @@ class TuimbleApp(App):
def on_key(self, event: events.Key) -> None: def on_key(self, event: events.Key) -> None:
"""Handle PTT key events.""" """Handle PTT key events."""
ptt_key = self._config.ptt.key if event.key != self._config.ptt.key:
return
if isinstance(self._ptt, KittyPtt): if isinstance(self._ptt, TogglePtt):
if event.key == ptt_key: self._ptt.toggle()
is_release = getattr(event, "key_type", None) elif isinstance(self._ptt, KittyPtt):
if is_release == "release": # Kitty hold-mode requires key-release events that Textual
self._ptt.key_up() # does not expose; kept for explicit backend="kitty" only.
else: self._ptt.key_down()
self._ptt.key_down()
elif isinstance(self._ptt, TogglePtt):
if event.key == ptt_key:
self._ptt.toggle()
def _on_ptt_change(self, transmitting: bool) -> None: def _on_ptt_change(self, transmitting: bool) -> None:
self._audio.capturing = transmitting self._audio.capturing = transmitting

View File

@@ -29,7 +29,7 @@ class AudioConfig:
@dataclass @dataclass
class PttConfig: class PttConfig:
key: str = "f4" key: str = "f4"
mode: str = "hold" # hold | toggle mode: str = "toggle" # toggle | hold (hold requires evdev)
backend: str = "auto" # auto | kitty | evdev | toggle backend: str = "auto" # auto | kitty | evdev | toggle

View File

@@ -139,6 +139,12 @@ def detect_backend(callback: Callback, preference: str = "auto") -> PttBackend:
if preference == "toggle": if preference == "toggle":
return TogglePtt(callback) return TogglePtt(callback)
# auto: try kitty first (will be validated at runtime by the app), # auto: Textual does not expose key-release events, so kitty
# then evdev, then toggle # hold-mode is not viable. Try evdev, fall back to toggle.
return KittyPtt(callback) try:
import evdev as _evdev # noqa: F811
_ = _evdev.list_devices()
return EvdevPtt(callback)
except Exception:
return TogglePtt(callback)

View File

@@ -8,7 +8,7 @@ def test_default_config():
assert cfg.server.host == "localhost" assert cfg.server.host == "localhost"
assert cfg.server.port == 64738 assert cfg.server.port == 64738
assert cfg.audio.sample_rate == 48000 assert cfg.audio.sample_rate == 48000
assert cfg.ptt.mode == "hold" assert cfg.ptt.mode == "toggle"
def test_server_config(): def test_server_config():