Skip to content

feat(studio)!: hotkeys bind by physical key - #84

Merged
kixelated merged 5 commits into
mainfrom
quest/m1/hotkeys-keypad
Nov 7, 2026
Merged

kixelated merged 5 commits into
mainfrom
quest/m1/hotkeys-keypad

Conversation

@kixelated

@kixelated kixelated commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

Completes quest/m1/hotkeys-keypad.md, re-planned in moq-dev/studio#68: a hotkey is the physical key the operator pressed, on every platform.

Changes

  • A Binding is now winit's PhysicalKey, the modifiers, and what the layout printed on the key when it was bound (key_without_modifiers). The window's winit event handler hears each key before egui (hotkeys::Keyboard), so keypad keys stay apart from the main row and a key egui cannot name, or winit leaves Unidentified, still binds. This replaces the keypad twin tracking (hotkeys/keypad.rs, deleted).
    • While the Hotkeys panel waits, the first non-modifier key is taken there and no key reaches egui, so Ctrl+C or Ctrl+Z bind rather than copy or undo.
    • Where keys work only with focus, a bound key fires from that handler and is kept from egui, as before.
  • X11 grabs by keycode itself through x11rb (hotkeys/x11.rs): keycode = winit scancode + 8, for named and Unidentified keys alike, with every NumLock/CapsLock mix, and XKB detectable auto-repeat so a held key fires once. global-hotkey is no longer built on Linux. The layout lookup, the "release would hit another key" check, and the restart note are gone.
  • macOS and Windows register through global-hotkey (hotkeys/native.rs), with winit's KeyCode mapped to global-hotkey's Code, so Numpad* register as keypad keys. A key winit leaves Unidentified is refused, saying global-hotkey cannot name it.
  • Windows maps the letter, digit, and punctuation keys through the foreground window's layout (hotkeys/layout.rs). A task reads that layout four times a second, since Windows tells no other app when it changes, and wakes the window, whose logic registers every binding again through the new layout; this also runs while the window is hidden. Unregistering is by id, so nothing strands.
  • Wayland asks the portal for KP_* keysyms for keypad keys, and a non-ASCII character by its Unicode keysym (U00F6 for ö). Shortcut ids name the physical key (scene/ctrl+Digit1, scene/Numpad1).
  • The panel shows a keypad key as Keypad 1, and a key as the layout printed it (ö as Ö).
  • README.md's Hotkeys section drops the keypad refusal, the X11 letter refusal, the restart note, and the layout read at start or bind; it states the Windows keypad limits below.
  • Deletes the quest.

Choices made without asking

  • Windows refuses keypad Enter. RegisterHotKey matches virtual keys, and keypad Enter and Enter are both VK_RETURN, so the system cannot tell them apart; a binding on Enter fires for both, and keypad keys fire only with NumLock on (NumLock off turns keypad 1 into VK_END). Telling them apart needs a low-level keyboard hook (WH_KEYBOARD_LL) in place of RegisterHotKey, which Windows silently removes from a slow process and which anti-cheat software watches for, so I kept RegisterHotKey and documented the limit.
  • The Windows layout is polled, every 250 ms on the studio's tokio runtime through kio::clock::interval, as autosave.rs polls the scene file: no event reports another window's layout change.
  • Persisted bindings change shape, so bindings saved by an earlier build are dropped on load and must be bound again.

Public API and wire impact

None outside the binary. The settings key hotkeys changes format (breaking, see above); the portal shortcut ids for scenes change, so the desktop asks for scene shortcuts again once.

Validation

  • macOS (this Mac): just ci at the merged head 37ac09f (265 tests passed).
  • Linux (nixos, x86_64): cargo clippy -p moq-studio --all-targets -D warnings; cargo nextest run -p moq-studio hotkeys.
  • X11, end to end on Xvfb with lavapipe on nixos: the panel reported global reach, and bound keypad 1 (pressed with NumLock off, shown Keypad 1) to Cut, the 1 above the letters to Transition, ö on a German layout (shown Ö) to Replay, XF86MailForward (evdev 233, which winit leaves Unidentified) to Save clip, and Ctrl+C to recording, with no refusal. Pressing each fired its own action in the status bar (cut to Main, took Main, the Replay and Save clip errors, recording into ...); keypad 1 fired with NumLock on too, and after setxkbmap us the key that typed ö still fired Replay. XWayland was not exercised: the GNOME session on that host is locked, so no input reaches its X clients.
  • Windows: not run (no host, quest/m0/windows-host.md). cargo check of the whole crate for x86_64-pc-windows-msvc fails in C build scripts (tract-linalg) without an MSVC toolchain, so hotkeys/native.rs and hotkeys/layout.rs were compiled and clippy-checked for x86_64-pc-windows-msvc in a scratch crate with the same dependencies. The layout follow, the keypad registrations, and the keypad Enter refusal have not run on Windows.

Follow-ups

  • cargo build -p moq-studio (and cargo check of the bin alone) fails on main too: health.rs awaits PrefixConsumer::next, which moq-stats builds only with its async feature, enabled today only by the moq-relay dev-dependency. moq-studio's own moq-stats dependency should enable async. Workspace and --all-targets builds pass, so CI does not see it.
  • Run the Windows half on the Windows host once quest/m0/windows-host.md lands: keypad registration, the keypad Enter refusal, and a layout switch (Win+Space) while a game has focus.

Review

Codex (gpt-6.1-sol, medium) via quest review --base origin/main, round 1 on head 37ac09f: no findings.

(Written by Claude Opus 5.5)

🤖 Generated with Claude Code

Quest-Claim: 3ec65ecaa92cd3bf
@pullfrog

pullfrog Bot commented Nov 7, 2026

Copy link
Copy Markdown

Pullfrog  | Review this ➔

kixelated and others added 3 commits November 7, 2026 00:41
A binding is winit's physical key, taken from the window's own key event
before egui, so keypad keys bind apart from the main row and any key binds.
X11 grabs by keycode through x11rb instead of global-hotkey, and Windows
registers every binding again when the foreground window's layout changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated kixelated changed the title Hotkeys bind by physical key feat(studio)!: hotkeys bind by physical key Nov 7, 2026
@kixelated
kixelated marked this pull request as ready for review November 7, 2026 10:57
@coderabbitai

coderabbitai Bot commented Nov 7, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 32 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 950c8631-608d-4b04-8315-6a79e43ad7ac
Commits

Reviewing files that changed from the base of the PR and between e056c3d and b173523.

Files ignored due to path filters: 1

  • Cargo.lock is excluded by !**/*.lock
Files selected for processing: 12

  • README.md
  • quest/m1/README.md
  • quest/m1/hotkeys-keypad.md
  • rs/moq-studio/Cargo.toml
  • rs/moq-studio/src/hotkeys.rs
  • rs/moq-studio/src/hotkeys/keypad.rs
  • rs/moq-studio/src/hotkeys/layout.rs
  • rs/moq-studio/src/hotkeys/native.rs
  • rs/moq-studio/src/hotkeys/portal.rs
  • rs/moq-studio/src/hotkeys/x11.rs
  • rs/moq-studio/src/main.rs
  • rs/moq-studio/src/ui.rs
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@kixelated kixelated left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review by review (OpenAI)

Reviewed head: 37ac09f
Base: 107f736

No actionable correctness findings in this static review of the 13-file change.

Direction and coverage:

  • Physical-key identity now survives binding capture before egui, including keypad keys and unidentified native keys. Duplicate detection and action lookup use physical position and modifiers rather than the display label.
  • X11 grabs and releases the same keycodes, handles the configured lock combinations, and suppresses detectable repeat presses. The Wayland portal receives keypad/Unicode keysyms while retaining desktop-selected shortcut behavior.
  • Windows foreground-layout changes trigger re-registration on the window thread; native mapping and registration lifecycle were checked against the global-hotkey backend. The documented Windows keypad Enter and NumLock restrictions remain platform limitations, not guarantees of physical-key independence there.
  • Saved bindings from the old format must be rebound, as the PR documents. Keep that migration warning and complete the stated Windows runtime follow-up before treating its layout-switch behavior as validated.

Verification limits: GitHub-only source and diff inspection. No builds, unit tests, X11/XWayland/Wayland sessions, macOS or Windows keyboard tests executed. The reported macOS/Linux checks and Xvfb exercises were not independently reproduced; Windows runtime behavior remains unverified. COMMENT only, not approval.

(Written by review (OpenAI))

@kixelated
kixelated enabled auto-merge (squash) November 7, 2026 11:19
@kixelated

Copy link
Copy Markdown
Contributor Author

Pick (/quest-complete, maintainer away under /afk, recommended option): ✅ /quest-merge on head b173523, a conflict-free merge of main into the reviewed head 37ac09f. Codex found nothing, and the PR leaves no decisions open.

(Written by Claude Opus 5.5)

@kixelated
kixelated merged commit cb02559 into main Nov 7, 2026
3 checks passed
@kixelated
kixelated deleted the quest/m1/hotkeys-keypad branch November 7, 2026 11:35
kixelated added a commit that referenced this pull request Nov 7, 2026
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant