Skip to content

feat(core): add the Calculator hotkey as a divertable keyboard key - #1247

Open
thorstent wants to merge 1 commit into
AprilNEA:masterfrom
thorstent:feat/calculator-key
Open

feat(core): add the Calculator hotkey as a divertable keyboard key#1247
thorstent wants to merge 1 commit into
AprilNEA:masterfrom
thorstent:feat/calculator-key

Conversation

@thorstent

Copy link
Copy Markdown

Adds ButtonId::KeyCalculator and (0x000a, ButtonId::KeyCalculator) to KEYBOARD_KEY_CIDS, making the Calculator hotkey bindable like the existing nine keyboard keys.

Closes #1147.

Why diversion is the only option here

Unlike the F-row keys the table was built for, this control sits in the hotkey cluster beside the numpad (ERGO K860, MX Keys S) and is absent from the Signature series.

The part that makes this more than a missing table entry: with the keyboard in its macOS mode the firmware emits no HID usage at all for this key, so nothing above the HID layer has an event to intercept. I verified the absence at three levels on an ERGO K860 for Business:

  • macOS: no event in Karabiner-EventViewer. As controls in the same session, Volume Up reported consumer_key_code volume_increment and the Search key consumer_key_code ac_search (0x0221), so the observer was working and that receiver's consumer usages were visible.
  • Linux/Wayland: silent at libinput debug-events --show-keycodes, at raw evdev (evtest, including the receiver's Consumer Control node), and at the raw HID report level — no report arrives, so it is not a missing keycode mapping.
  • Switching the same keyboard to Windows mode, the key natively sends consumer usage 0x0192 (AL Calculator).

Logitech documents this constraint from the other side: their SIGNATURE K650 page lists the "Close browser tab key, Settings key and Calculator key" as requiring Options+ "to work on Mac" while working "out of the box on Windows and Chrome OS", and the K250 page names Calculator as an exception to "Most keys work on Windows, macOS and ChromeOS". Diversion is presumably how Options+ delivers it, and it is the only mechanism available to OpenLogi.

CID and firmware task (CALCULATOR) cross-checked against Solaar's special_keys.py — the same catalog the existing nine entries cite — and against the Logi asset metadata slot SLOT_NAME_CALCULATOR (slotId ergo-k860-2b364_c10). openlogi diag controls on the K860 reports the control as divertable:

  0x000a  0x000a  0x0434  divertable, analytics-events

As for every entry in the table, an unbound key is never diverted, so stock firmware behavior is unchanged unless the user binds it. In Windows mode, binding it suppresses the native AL Calculator usage, which matches how the other keys here behave.

KeyCalculator is appended last in both ButtonId and KEYBOARD_KEYS, matching KEYBOARD_KEY_CIDS order, per the append-only note in button.rs. No new Action is needed — RunShellCommand / OpenApplication already cover the obvious bindings.

Testing

Verified on hardware — ERGO K860 for Business (ERGO 860B, model id 2b364) on a Bolt receiver, macOS on Apple Silicon, keyboard in macOS mode, with:

[devices."serial:2345sc8037g8".bindings.KeyCalculator]
RunShellCommand = "open -a Calculator"

A release build of the patched agent arms the diversion and dispatches the press:

INFO openlogi_device::session::keyboard: keyboard key capture active index=1 keys=1 wake_rearm=true
INFO openlogi_agent_core::watchers::keyboard: keyboard key → handling binding button=Calculator Key action=Run Command

Calculator launches. Before the patch the same keypress produced nothing anywhere in the stack.

Local checks, with RUSTFLAGS="-D warnings":

  • cargo fmt --all -- --check — clean
  • cargo clippy --all-targets -- -D warnings for openlogi-core, openlogi-device, openlogi-ipc, openlogi-agent-core, openlogi-hid, openlogi-hidpp, openlogi-inject, openlogi-hook, openlogi-cli, openlogi-ui — clean
  • cargo test for the same ten packages — 1004 passed, 0 failed, including wire_format's protocol_version_is_pinned
  • cargo test -p openlogi-ui locale — catalog parity passes; calculator_key added at the same position in all 23 catalogs
  • cargo build --release -p openlogi-agent — success

Not run locally, per the "do not claim a skipped job passed" rule: cargo test -p openlogi-desktop i18n, and clippy/tests for openlogi-desktop, openlogi-overlay and openlogi-camera. gpui_macos's build script needs the Metal shader compiler, which ships only with full Xcode — unavailable on the machine I built on (xcrun: error: unable to find utility "metal"). Those are left to CI. The desktop-side change is limited to the locale catalogs plus the new ButtonId arm, both covered by the portable parity test above.

Note for review

PROTOCOL_VERSION is bumped 29 → 30 for the appended ButtonId variant. #1116 also claims 30; whichever lands first, I'll happily rebase this onto 31. Order in KEYBOARD_KEY_CIDS is kept aligned with ButtonId::KEYBOARD_KEYS, so the cross-crate ordering test #1116 introduces will hold if that merges first.

Happy to add a GUI affordance if wanted — these keyboard slots appear to be config-only today, so this follows the existing pattern.

Adds `ButtonId::KeyCalculator` and `(0x000a, ButtonId::KeyCalculator)` to
`KEYBOARD_KEY_CIDS`, so the Calculator hotkey can be bound like the existing
keyboard keys. Closes AprilNEA#1147.

Unlike the F-row keys the table was built for, this control sits in the hotkey
cluster beside the numpad (ERGO K860, MX Keys S) and is absent from the
Signature series. Diversion is the only way to reach it on macOS: with the
keyboard in its macOS mode the firmware emits no HID usage at all for this key,
so nothing above the HID layer has an event to intercept. In Windows mode the
same key natively sends consumer usage 0x0192 (`AL Calculator`).

CID and firmware task (`CALCULATOR`) cross-checked against Solaar's
`special_keys.py`, the same catalog the existing nine entries cite, and against
the Logi asset metadata slot `SLOT_NAME_CALCULATOR`.

As for every entry in the table, an unbound key is never diverted, so stock
firmware behavior is unchanged unless the user binds it.
@thorstent
thorstent requested a review from AprilNEA as a code owner September 2, 2026 13:56
@greptile-apps

greptile-apps Bot commented Sep 2, 2026

Copy link
Copy Markdown

Greptile Summary

Adds Calculator as a configurable, divertable keyboard control and advances the IPC protocol version.

  • Appends KeyCalculator to the shared button and keyboard-control catalogs.
  • Maps Logitech CID 0x000a into keyboard diversion.
  • Adds default-action and localization coverage across all catalogs.
  • Updates the protocol pin and changelog.

Confidence Score: 4/5

The configuration schema compatibility issue should be fixed before merging because a saved Calculator binding makes the configuration unreadable after downgrading.

KeyCalculator becomes a serialized ButtonId map key while the configuration schema remains unchanged, allowing older readers to accept the schema and then fail parsing the entire file.

Files Needing Attention: crates/openlogi-core/src/binding/button.rs and the configuration schema declaration in crates/openlogi-core/src/config.rs

Important Files Changed

Filename Overview
crates/openlogi-core/src/binding/button.rs Adds the Calculator identifier and metadata, but expands the persisted ButtonId key space without advancing the configuration schema.
crates/openlogi-device/src/session/keyboard.rs Adds CID 0x000a to capability-filtered keyboard diversion while preserving per-key graceful degradation.
crates/openlogi-ipc/src/ipc.rs Advances the strict IPC compatibility version to 30.
crates/openlogi-ipc/tests/wire_format.rs Updates the protocol-version pin consistently.
crates/openlogi-core/src/binding/defaults.rs Gives the new keyboard key the expected unbound default action.
crates/openlogi-ui/locales/en.toml Adds the canonical Calculator key label mirrored across all locale catalogs.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  K[Calculator control CID 0x000a] --> E[Enumerate divertable controls]
  E --> B{KeyCalculator binding exists?}
  B -->|No| N[Leave firmware behavior native]
  B -->|Yes| D[Divert control]
  D --> A[Dispatch configured action]
Loading

Fix all with Greploop Fix All in Codex Fix All in Claude Code

Reviews (1): Last reviewed commit: "feat(core): add the Calculator hotkey as..." | Re-trigger Greptile

/// key: it sits in the hotkey cluster above/right of the numpad on boards
/// like the ERGO K860 and MX Keys S, and is absent from the Signature
/// series.
///

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Configuration schema stays stale

When a KeyCalculator binding is saved and the user subsequently runs an older release, the schema-version-7 file passes the version gate but its new ButtonId key fails deserialization, causing the agent to run with defaults and the desktop to treat the saved configuration as read-only.

Fix in Codex Fix in Claude Code

@davidbudnick davidbudnick added type: feature New feature request area: hidpp HID++ protocol and device feature support platform: all Cross-platform issue labels Sep 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: hidpp HID++ protocol and device feature support platform: all Cross-platform issue type: feature New feature request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature / Bug]: MX Keys S: Calculator key (CID 0x000A) not captured/remappable on macOS

2 participants