System-wide voice hotkeys: PTT / mute / deafen work while a game has focus #2

Open
opened 2026-09-17 13:27:06 -04:00 by jared · 1 comment
Owner

Today PTT, mute and deafen are DOM keydown/keyup handlers in cinny (src/app/hooks/useCallHotkeys.ts) and only fire while the Lotus window (or the EC iframe) has focus. The moment a player alt-tabs into a fullscreen game, the core voice controls stop working. cinny-desktop has no tauri-plugin-global-shortcut.

Scope

  • Add tauri-plugin-global-shortcut (Rust + capability) and register the user's PTT key (press and release → needs the plugin's ShortcutState::Pressed/Released events), mute toggle and deafen toggle as OS-level shortcuts while a call is joined; unregister on leave/quit.
  • cinny side: extend useCallHotkeys so that in Tauri the global events drive the same pttActiveAtom / mute / deafen paths; keep the DOM path as the in-focus fallback and de-duplicate (a key that fires both globally and in-DOM must not toggle twice).
  • Optional short audio cue on toggle (off by default) so the state is audible when the window is not visible.
  • Settings → Calls: "Hotkeys work outside the window" toggle (default on in the desktop app), with a note that some games with anti-cheat block global hooks.
  • Conflicts: a global shortcut steals the key from every app; document that and reject bare letter keys without a modifier for mute/deafen (PTT may stay bare — it's hold-to-talk).

Acceptance

  • Windows: join a call, focus another fullscreen app, hold PTT → transmitting; tap mute/deafen → state changes; release/unregister on hangup.
  • Linux: X11 works; Wayland limitation documented (global shortcuts need the portal; plugin support is partial).
  • No double toggles when Lotus itself is focused.
Today PTT, mute and deafen are DOM `keydown/keyup` handlers in cinny (`src/app/hooks/useCallHotkeys.ts`) and only fire while the Lotus window (or the EC iframe) has focus. The moment a player alt-tabs into a fullscreen game, the core voice controls stop working. cinny-desktop has no `tauri-plugin-global-shortcut`. ### Scope - Add `tauri-plugin-global-shortcut` (Rust + capability) and register the user's PTT key (press **and** release → needs the plugin's `ShortcutState::Pressed/Released` events), mute toggle and deafen toggle as OS-level shortcuts while a call is joined; unregister on leave/quit. - cinny side: extend `useCallHotkeys` so that in Tauri the global events drive the same `pttActiveAtom` / mute / deafen paths; keep the DOM path as the in-focus fallback and de-duplicate (a key that fires both globally and in-DOM must not toggle twice). - Optional short audio cue on toggle (off by default) so the state is audible when the window is not visible. - Settings → Calls: "Hotkeys work outside the window" toggle (default on in the desktop app), with a note that some games with anti-cheat block global hooks. - Conflicts: a global shortcut steals the key from every app; document that and reject bare letter keys without a modifier for mute/deafen (PTT may stay bare — it's hold-to-talk). ### Acceptance - Windows: join a call, focus another fullscreen app, hold PTT → transmitting; tap mute/deafen → state changes; release/unregister on hangup. - Linux: X11 works; Wayland limitation documented (global shortcuts need the portal; plugin support is partial). - No double toggles when Lotus itself is focused.
jared added this to the Desktop 2026-Q4 milestone 2026-09-17 13:27:06 -04:00
jared added the enhancementpriority: higharea: nativearea: hotkeys labels 2026-09-17 13:27:06 -04:00
jared self-assigned this 2026-09-17 13:27:06 -04:00
Author
Owner

Implemented — needs a Windows test before closing.

  • cinny-desktop 4d0cfaa: src-tauri/src/native/hotkeys.rs — non-consuming GetAsyncKeyState poll (8 ms) while a call is joined; emits lotus-global-hotkey on press/release transitions. Not RegisterHotKey/global-shortcut on purpose: those would swallow the key from the game. Windows only; no-op elsewhere.
  • cinny 00584d78: useCallHotkeys registers {ptt, deafen} with the desktop on join, acts on the events only while the Lotus window does not have focus (DOM path owns the in-focus case), same modifier rules. Toggle: Settings → Calls → Hotkeys Work Outside the Window (desktop only, default on, device-local).

Test checklist (Windows build from the next desktop CI run)

  1. Join a voice room, enable PTT, focus another window (Notepad is enough; then a fullscreen game). Hold PTT → mic opens (check the speaking ring on another device); release → closes.
  2. Tap the deafen key while unfocused → deafen toggles once (not twice).
  3. Focus Lotus, type the PTT key in the composer → text is typed, mic does not open (DOM rules still apply).
  4. Hold PTT while unfocused, click into Lotus while holding → mic releases on focus (existing blur/focus guard).
  5. Hang up → no more events (poll stopped); the keys never stop reaching the other app at any point.
  6. Optional: a game with anti-cheat (Vanguard/EAC) — note whether the poll still sees the key.
Implemented — **needs a Windows test before closing.** - cinny-desktop `4d0cfaa`: `src-tauri/src/native/hotkeys.rs` — non-consuming `GetAsyncKeyState` poll (8 ms) while a call is joined; emits `lotus-global-hotkey` on press/release transitions. Not `RegisterHotKey`/global-shortcut on purpose: those would swallow the key from the game. Windows only; no-op elsewhere. - cinny `00584d78`: `useCallHotkeys` registers {ptt, deafen} with the desktop on join, acts on the events only while the Lotus window does **not** have focus (DOM path owns the in-focus case), same modifier rules. Toggle: Settings → Calls → *Hotkeys Work Outside the Window* (desktop only, default on, device-local). ### Test checklist (Windows build from the next desktop CI run) 1. Join a voice room, enable PTT, focus another window (Notepad is enough; then a fullscreen game). Hold PTT → mic opens (check the speaking ring on another device); release → closes. 2. Tap the deafen key while unfocused → deafen toggles once (not twice). 3. Focus Lotus, type the PTT key in the composer → text is typed, mic does not open (DOM rules still apply). 4. Hold PTT while unfocused, click into Lotus while holding → mic releases on focus (existing blur/focus guard). 5. Hang up → no more events (poll stopped); the keys never stop reaching the other app at any point. 6. Optional: a game with anti-cheat (Vanguard/EAC) — note whether the poll still sees the key.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: LotusGuild/cinny-desktop#2