Push-to-talk hotkey never registers on Windows: a bare AltLeft is not a registrable shortcut #51
Labels
No labels
needs-info
needs-triage
ready-for-agent
ready-for-human
v2
wayfinder:grilling
wayfinder:map
wayfinder:prototype
wayfinder:research
wayfinder:task
wayfinder:ticket
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
aiko/denpa#51
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Push-to-talk on the desktop pal never registers its hotkey on Windows. Observed on launch, from
mainatf796efa:src-tauri/src/lib.rs:42-44registers a bare modifier key with no modifier combination:Windows implements global shortcuts through
RegisterHotKey, which takes a virtual-key code plus a modifier mask. A bareAltis a modifier, not a key it will accept as the hotkey itself — so the registration fails at startup.mic_toggle_shortcut(Ctrl+Shift+M-shaped) andinterrupt_shortcut(Ctrl+Shift+I) both register without complaint, because each carries a real modifier combination.Why it matters
The comment above the function records exactly why the bare key was chosen:
That reasoning is sound and the mechanism cannot deliver it on Windows. The handler at
lib.rs:58-68is careful to forward both edges — "a held key that only reported its press would leave the mic open" — and none of it ever runs, because nothing is registered.The failure is a
log::warn!and nothing else (lib.rs:123). The app starts normally, the pal appears, and holding Alt does nothing. Voice is v1-blocking on desktop and this is the pal's only way in, so it fails in the quietest possible way on a path that matters.What is needed
RegisterHotKey. A low-level keyboard hook can observe a bare held modifier where a hotkey registration cannot; whichever is chosen must still deliver both edges.Related
#43— the Rust/webview audio boundary; whatever owns the mic has to be driven by this trigger#35— moves capture into Rust, but the trigger is a separate problem from the capture#33— press-to-talk transmitting nothing on a short press; that is the dock's on-screen key, this is the desktop hotkey#18— the transparent pal this gesture belongs toStill real, and no longer blocking voice. Recorded so the priority is not misread from the title.
#35's desktop half landed, so press-to-talk works from the on-screen key — a real conversation was held on 2026-07-30. What remains broken is the OS-level hotkey, whichlib.rs:43registers as a bareAltLeftthatRegisterHotKeywill not accept.That matters for the surface it was built for: the transparent desktop pal (
#18), whose window is never focused, so a key it can hear at all has to be an OS-level hotkey. Voice in the full receiver does not depend on it.Moved to
v2, 2026-07-30. Out of the v1 run by decision, not by difficulty.v1 is "the conversation works" on the desktop, the OnePlus Pad and the Pixel (
aiko/denpa#41). Voice already works from the on-screen key, so nothing about the conversation is blocked on this. The hotkey serves exactly one surface — the transparent pal, whose window is never focused — which makes it a convenience on a path that already has a working alternative.The defect itself stands as written:
lib.rs:43registers a bareAltLeft, andRegisterHotKeyrejects a modifier with no key. Nothing here is stale; it is scheduled, not shelved.Decided 2026-07-31: the gesture becomes a chord. Design § 1b changes; the bare held key is given up.
RegisterHotKeywill not accept a modifier as the key itself, so the held-AltLeftgesture cannot be delivered by the mechanism the pal uses. The alternative that preserves it — a low-levelSetWindowsHookExkeyboard hook — can observe a bare held modifier, and is being turned down deliberately: it is a Windows-specific global input-capture path that would have to be owned and kept working forever, in exchange for a gesture that a chord serves nearly as well.So: a chord with a real modifier combination,
Ctrl+Alt-shaped, alongsidemic_toggle_shortcutandinterrupt_shortcutwhich register today without complaint.push_to_talk_shortcutatlib.rs:42-44changes shape; the handler atlib.rs:58-68does not — both edges still matter, and a held key that only reported its press would still leave the mic open.This is a change to design § 1b, not an implementation detail. Record it there: the gesture is a held chord, not a held bare key, because the OS will not register the latter globally.
Separately, and regardless of which gesture wins: a failed registration must not be a
log::warn!the app shrugs off (lib.rs:123). Today the app starts normally, the pal appears, and holding the key does nothing, with the only evidence in a log nobody is reading. Either the gesture works or the pal says it cannot listen.Staying on
v2— the priority set on 2026-07-30 is unchanged, since voice already works from the on-screen key and this serves only the transparent pal. What is settled now is the shape, so the ticket is buildable when it comes up.Acceptance
push_to_talk_shortcutregisters a modifier chord, and registers cleanly on Windows at startupBuilt and merged, with one correction to the decision.
The decision recorded on 2026-07-31 said the gesture becomes a chord,
Ctrl+Alt-shaped. Taken literally that is two modifiers and no key, whichRegisterHotKeyrejects for exactly the reason the bareAltLeftwas rejected — the siblings that do register,Ctrl+Shift+MandCtrl+Shift+I, each carry a real key. So the chord isCtrl+Alt+Space: the shape that was decided, plus the key the mechanism requires, on the conventional talk key.Verified on Windows. The app now launches with zero registration warnings. Every launch before it logged
Unable to register hotkey: Unknown VKCode for AltLeft— including three of tonight's, which is how this got picked up again.The handler is untouched: both edges still drive the mic, because a held key that only reported its press would leave it open.
Design § 1b records the chord and why the bare held key was given up — the low-level
SetWindowsHookExhook that could have observed it is a Windows-only global input-capture path to own forever, for a gesture a chord serves nearly as well.Acceptance
push_to_talk_shortcutregisters a modifier chord, cleanly, on Windows at startupThe fourth item is left rather than guessed: what the pal says when it cannot listen, and where it says it, is a design decision about a surface with very little room. Registering cleanly makes it a rarer case but not an impossible one — another app can still hold the combo, which is the collision
lib.rs's comment already records seeing in the wild.Staying on
v2; the priority set on 2026-07-30 is unchanged.Closed by
946ef93— push to talk became a chord Windows will actually register.RegisterHotKeytakes a modifier mask plus a key, so a bareAltLeftwas never registrable;src-tauri/src/lib.rs:43records the reason and names this issue.