Skip to content

fix(macOS): make Mission Control and Spaces hotkeys work - #154

Open
sethdmoore wants to merge 1 commit into
LizardByte:masterfrom
sethdmoore:fix/macos-implicit-key-flags-upstream
Open

sethdmoore wants to merge 1 commit into
LizardByte:masterfrom
sethdmoore:fix/macos-implicit-key-flags-upstream

Conversation

@sethdmoore

Copy link
Copy Markdown

Description

On macOS, keyboard events injected through the CoreGraphics backend carry only the tracked modifier flags. Physical keyboards report arrow, navigation (Home, End, Page Up, Page Down, Forward Delete, Help/Insert) and function keys with kCGEventFlagMaskSecondaryFn, and arrow and keypad keys with k¢∞∞CGEventFlagMaskNumericPad. Since macOS Mojave the system hotkey layer matches on those flags, so an injected Control+Up never triggers Mission Control and Control+Left/Right never switch Spaces, even though Control+letter shortcuts work and arrow keys type fine in applications. The same limitation is documented for other synthetic-input tools, for example Hammerspoon/hammerspoon#1946, where adding the Fn flag is the confirmed fix.

This change adds an implicit_key_flags() table to the macOS backend and ORs its result into the flags of each posted key event:

  • Arrow keys: SecondaryFn | NumericPad
  • Home, End, Page Up, Page Down, Forward Delete, Help, F1 to F20: SecondaryFn
  • Keypad keys: NumericPad
  • Everything else, including modifiers: unchanged

The flags are applied per event only and are not stored in the shared modifier state, so they do not leak onto later key or mouse events.

Event construction moves from MacosKeyboard::submit() into a create_keyboard_event() helper so tests can inspect the built CGEvent without posting it. New unit tests:

  • AddsImplicitFlagsForFunctionAndKeypadKeys checks the per-key flag table.
  • BuildsControlArrowEventsWithFnFlags builds the events for Control down, Up down/up, A down/up, Control up and checks that the arrow events carry Control | SecondaryFn | NumericPad, the A events carry only Control, and the shared modifier state never picks up the per-key flags.
  • BuildsKeypadEventsWithNumericPadFlag checks a keypad key against Return.

Both event tests fail against the previous code (the Control+Up event carried 0x40000, Control only, instead of 0xA40000). test_libvirtualhid passes on macOS 26.6 arm64 (109 passed, 2 Linux-only smoke tests skipped). The behavior is documented in docs/platform-support.md.

After the changes, all control+arrow hotkeys now work.

Screenshot

Issues Fixed or Closed

  • Downstream report: LizardByte/Sunshine# (macOS host, Control+Arrow shortcuts from Moonlight are ignored). That issue tracks bumping the Sunshine submodule once this is released.

Roadmap Issues

Type of Change

  • feat: New feature (non-breaking change which adds functionality)
  • fix: Bug fix (non-breaking change which fixes an issue)
  • docs: Documentation only changes
  • style: Changes that do not affect the meaning of the code (white-space, formatting, missing semicolons, etc.)
  • refactor: Code change that neither fixes a bug nor adds a feature
  • perf: Code change that improves performance
  • test: Adding missing tests or correcting existing tests
  • build: Changes that affect the build system or external dependencies
  • ci: Changes to CI configuration files and scripts
  • chore: Other changes that don't modify src or test files
  • revert: Reverts a previous commit
  • BREAKING CHANGE: Introduces a breaking change (can be combined with any type above)

Checklist

  • Code follows the style guidelines of this project
  • Code has been self-reviewed
  • Code has been commented, particularly in hard-to-understand areas
  • Code docstring/documentation-blocks for new or existing methods/components have been added or updated
  • Unit tests have been added or updated for any new or modified functionality

AI Usage

See our AI usage policy.

  • None: No AI tools were used in creating this PR
  • Light: AI provided minor assistance (formatting, simple suggestions)
  • Moderate: AI helped with code generation or debugging specific parts
  • Heavy: AI generated most or all of the code changes

@CLAassistant

CLAassistant commented Sep 21, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

Control+Up, Control+Down, and Control+Left/Right did nothing on a macOS
host, so a streaming client could not open Mission Control, show the
app's windows, or switch desktops (Spaces), while every other shortcut
worked

- macOS reports physical arrow, navigation, and function keys with the
  secondary-Fn flag, and arrow and keypad keys with the numeric-pad
  flag. The built-in hotkeys are registered as Control+Fn+Arrow and are
  matched against those flags
- The backend posted key events with only the tracked modifier flags,
  so its Control+Up never matched the Mission Control hotkey
- Applications accept an arrow key either way, so cursor movement and
  Control+letter shortcuts worked and hid the bug

The backend now adds the flags a physical key would carry, per event

- Arrows get secondary-Fn and numeric-pad, Home/End/Page Up/Page Down/
  Forward Delete/Help and F1-F20 get secondary-Fn, keypad keys get
  numeric-pad
- The flags are not stored in the shared modifier state, so they do not
  leak onto later key or mouse events
- Event construction moves out of submit() into create_keyboard_event()
  so tests can inspect the CGEvent without posting it
- Unit tests build the events for a Control+Up, Control+A, and keypad
  sequence and check the flags and the tracked modifier state; both
  fail against the previous code

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@sethdmoore
sethdmoore force-pushed the fix/macos-implicit-key-flags-upstream branch from 1ecb938 to e846e17 Compare September 22, 2026 21:48
@sonarqubecloud

Copy link
Copy Markdown

This branch has not been deployed

No deployments
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.

2 participants