Skip to content

feat: press media, volume and brightness keys as system-defined events - #80

Merged
iamnbutler merged 1 commit into
mainfrom
feat/media-keys
Oct 17, 2026
Merged

iamnbutler merged 1 commit into
mainfrom
feat/media-keys

Conversation

@iamnbutler

@iamnbutler iamnbutler commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

desktop_system_key now also presses the media, volume and brightness keys:

  • play_pause, next_track, previous_track
  • volume_up, volume_down, mute
  • brightness_up, brightness_down
  • keyboard_brightness_up, keyboard_brightness_down

These keys take no modifiers. This closes the media-key part of #75.

How

These keys have no key code: the keyboard sends them as NX_SYSDEFINED events (subtype 8, NX_SUBTYPE_AUX_CONTROL_BUTTONS), and Peekaboo can't post those. So a new patch, peekaboo-system-defined-key.patch, last in the stack, adds a Bridge operation, systemDefinedKey. It is modeled on moveMouse:

  • It needs only event posting (.postEvent) and has a global target.
  • It reports one foreground dispatch with dispatchedUnverified.
  • The host builds the press and the release before posting either, so a failed build never leaves a key held. Then it posts both to the HID tap.
  • The patch updates, in the same patch, the wire count (128 → 129, after feat: enter or exit native full screen for one window #78 took it to 128), the permission partition and the mutating-operation inventory.

The runtime allowlist adds systemDefinedKey. The client refuses a media key before dispatch when:

  • modifiers are given,
  • the runtime doesn't offer the operation, or
  • the session is locked.

Once a key is dispatched the result is always unknown, because nothing reads back the volume, brightness or playback state. The message warns not to blindly repeat play_pause or mute, since they toggle.

The keyboard backlight keys were cheap to include (NX_KEYTYPE_ILLUMINATION_UP/DOWN). Display brightness via these events may not reach every display, especially external ones. That is why the result stays unknown, and the docs say so.

Checks

  • npm run check and npm test pass (21, on top of feat: hold modifier keys during clicks, and press key sequences against one snapshot #77). The new test/systemkey.test.mjs covers the refusals and the tool schema.
  • The narrow Peekaboo suites pass: PeekabooBridgeRequestPlanTests, PeekabooBridgeOperationSemanticPlanTests and PeekabooBridgeOperationResultSemanticsTests, 72 tests.
  • A wider PeekabooTests *Bridge* run did not finish within the 580 s cap. Before the cap it reported timeouts in application launch, dialog input and targeted click tests (responseLost / Operation timed out) with the screen locked. None of these use the new operation.
  • Native and the signed dev app build. Smoke test against the dev app:
    • modifiers with a media key are refused in TypeScript;
    • mute reaches native and is refused before dispatch because the session is locked.
  • Live checks are pending (screen locked) and listed in Run the live checks deferred while the screen was locked #42.

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