Skip to content

feat: add G502 LIGHTSPEED support (by @RisenID) - #1729

Open
Fede2782 wants to merge 22 commits into
AprilNEA:masterfrom
Fede2782:master
Open

Fede2782 wants to merge 22 commits into
AprilNEA:masterfrom
Fede2782:master

Conversation

@Fede2782

@Fede2782 Fede2782 commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Full support for the G502 LIGHTSPEED (wpid 0x407f, receiver c539): every button can be remapped, including G6–G9 and the wheel tilts. It also adds G-Shift, report rate, and onboard-profile selection.

G-series mice have no 0x1b04 ReprogControls. When a binding, the report rate or G-Shift needs it, the agent switches the mouse into 0x8100 host mode. It then mutes the captured buttons in the 0x8110 mapping and dispatches them from 0x8110 spy reports. Nothing is written to flash. Stopping the agent, or power-cycling the mouse, returns it to its onboard profile.

All protocol facts were verified on hardware: the spy bit layout, the mode and profile functions, the report rate being writable only in host mode, and native buttons in host mode.

Changes

  • hidpp: OnboardProfiles (0x8100): info, mode, current profile, memory read, profile directory and names. MouseButtonSpy (0x8110): button count, start/stop spy, mapping, event decode.
  • core:
    • ButtonId::G6–G9.
    • Actions NextDpiPreset, PreviousDpiPreset, DpiShift (hold) and GShift (hold, offered only on G-series mice).
    • GamingLayout for the G502 LIGHTSPEED: spy bit → button, host-silent slots, stock DPI presets.
    • Capabilities::{report_rate, onboard_profiles, button_spy}.
    • Per-device config onboard_memory, report_rate and gshift_bindings, plus Config::effective_onboard_memory. An explicit choice wins; otherwise host mode is implied by a rebound host-only button, the report rate or G-Shift.
  • device:
    • The capture session arms host mode or a profile. Spy edges become ButtonDown/ButtonUp. The session re-arms after a reconnect, and restore leaves host mode.
    • Arming fails, and the session retries, when the mouse rejects the profile or report rate. It no longer reports host mode as active.
    • New get_onboard read.
    • Pairing reports ReceiverFull when the receiver has no free slot.
  • agent-core:
    • The plan captures host-silent slots, rebound standard buttons, and every remappable button while G-Shift is in use. Gesture-bound buttons stay with the OS hook.
    • Dispatch uses the G-Shift layer while any shift button is held, and the OS hook does the same for gesture buttons.
    • DpiShift restores the DPI that was active before the press once the last held press ends, even when that DPI isn't a preset.
    • DPI writes from actions are queued per device, and the newest one wins. Waiting in the queue doesn't count against the write timeout. The Fn-lock write queue became the shared WriteOrder.
  • agent:
    • read_onboard RPC.
    • On Linux, the input hook is re-installed when an inventory change brings a device and the hook found no mouse at start or every hooked mouse disconnected. This fixes button remapping after booting with the receiver enumerated late.
  • ipc: PROTOCOL_VERSION 36. New read_onboard, HidppOperation::ReadOnboard (appended after the pointer-scaling operations from feat(hidpp): implement pointer motion scaling #1347) and PairingFailure::ReceiverFull. Goldens regenerated.
  • desktop:
    • The Buttons tab draws the depot's side and front renders together, with slotId pixel markers; side markers only appear when the side image loads. It also gets a Normal | G-Shift bindings switch.
    • The Pointer tab gets an Onboard Memory card (OpenLogi or an enabled profile) and a Report Rate control. Both re-read the mouse after a change, and the rate shown is the mouse's own reading.
  • cli: openlogi diag onboard.
  • assets: device_side render and G-number slot parsing.
  • ui: strings for all 24 locales.

Commits

The feature is one commit, and each later fix (mostly from review) is its own commit:

  1. feat: add G502 LIGHTSPEED support
  2. fix(ui): add the G502 strings to Czech
  3. fix(agent): order DPI writes per device, outside the write timeout
  4. fix(agent): restore the pre-shift DPI after the last DPI shift ends
  5. fix(agent): keep G-Shift on until every shift button is released
  6. fix(agent): apply the G-Shift layer to gesture buttons
  7. fix(device): fail arming when the mouse rejects a profile or report rate
  8. fix(desktop): show the report rate the mouse reports after a change
  9. fix(desktop): fit the onboard card and show the profile's report rate
  10. fix(desktop): draw side-view markers only when the side image loads
  11. fix(agent): reinstall the Linux hook only once a device appears

Testing

cargo fmt --all --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace
cargo +1.98 check --workspace --all-targets
cargo test -p openlogi-ipc --test wire_format
cargo test -p openlogi-ui --test catalog_parity
cargo xtask release check-publish

All pass on Linux (Fedora 44), along with CI's cargo doc step, typos and ast-grep. Every commit builds and passes clippy and the wire-format test on its own, and every fix commit passes the locale parity test. The feature commit alone misses the Czech strings, which the next commit adds. Clippy also passes for Windows (MSVC) and macOS on openlogi-core, openlogi-hidpp, openlogi-device and openlogi-agent-core; nothing was run on either.

Hardware-verified on a G502 LIGHTSPEED over its Lightspeed receiver, on Fedora 44 with KDE Wayland. The dev agent ran with OPENLOGI_PROFILE=dev and the agent log at debug; mode, profile, DPI and rate were read back over HID++.

  • Host mode arms with all 9 remappable buttons captured. The mapping mutes them, and the configured report rate (250 Hz) and DPI (1200) are applied.
  • An unsupported report rate (3 ms) is rejected with InvalidArgument. The session retries and the mouse stays on its onboard profile; the GUI shows the mouse's real 1000 Hz.
  • G9 → normal binding. G5 (G-Shift) held + G9 → shifted binding. With G5 and G4 both set to G-Shift, releasing one keeps the layer.
  • G4 with gestures and G5 as G-Shift: G4 stays with the OS hook (8 buttons captured). G4 → its gesture click action, G5 held + G4 → its shifted action, G4 → its gesture click action again.
  • G6 and G7 both set to DpiShift: one write to 400 DPI, restored to 1200 (not a preset) only after both are released. Tapping G6 six times quickly always ends at 1200.
  • Profile 2 (disabled) is rejected with InvalidArgument and the session retries. Profiles 1 and 5 switch, and the GUI's report rate updates from 500 Hz to 1000 Hz.
  • Stopping the agent returns the mouse to onboard mode with the profile's own DPI and rate.
  • After the mouse sleeps and wakes, host mode re-arms.
  • After unplugging and re-plugging the receiver, the installed agent re-installed the input hook without a restart.
  • openlogi diag onboard lists the mode, profiles with names, and supported rates.

Not tested: the G502 over USB, and other G-series models (only this model has a verified layout).

How to test

  1. With the agent running, open the G502 → Buttons and set G5 to G-Shift (Hold). Switch the bindings to G-Shift and give G9 an action.
  2. Hold G5 and press G9: the shifted action fires. G9 alone fires the normal one.
  3. Pointer → Onboard Memory: pick a profile, and the report rate shows that profile's rate. Pick OpenLogi to change the report rate.
  4. openlogi diag onboard prints the mouse's state.

Screenshots

Relates to #886, #730. Scroll Up/Down actions not scrolling on Linux is a separate bug, fixed in #1719.
signal-2026-10-08-18-11-54-918
signal-2026-10-08-18-11-54-918-1

@Fede2782 Fede2782 changed the title feat: add G502 LIGHTSPEED support feat: add G502 LIGHTSPEED support (by @RisenID) Oct 10, 2026
@greptile-apps

greptile-apps Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

[High impact] The changes since the previous review appear safe to merge.

Summary

Adds G502 LIGHTSPEED support with host-mode button capture, G-Shift, held DPI changes, onboard-profile selection, and report-rate controls.

  • The G502 LIGHTSPEED can send remapped buttons through OpenLogi.
  • G-Shift adds a second layer of button actions.
  • The Pointer tab shows the G502’s active profile and report rate.
  • The Buttons view shows the G502’s front and side together.
  • The Linux input hook returns when a mouse appears later.
  • Pairing reports when the receiver has no free slot.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  Config[Saved mouse settings] --> Choice{Onboard choice}
  Choice -->|Enabled profile| Profile[Use onboard profile]
  Choice -->|Host mode needed| Host[Enter host mode]
  Host --> Mapping[Mute captured buttons]
  Mapping --> Spy[Receive spy button reports]
  Spy --> Layer{G-Shift held?}
  Layer -->|Yes| Shifted[Use shifted binding]
  Layer -->|No| Normal[Use normal binding]
  Shifted --> Action[Run action]
  Normal --> Action
  Hook[OS hook gesture buttons] --> Action
  Host --> Stop[Stop or retire capture]
  Stop --> Restore[Return to onboard mode]
Loading

Reviews (6) · Last reviewed commit: "fix(desktop): keep the mouse view's rend..." · Reviewed by Greptile

Comment thread crates/openlogi-agent-core/src/dpi.rs Outdated
Comment thread crates/openlogi-agent-core/src/runtime.rs Outdated
Comment thread crates/openlogi-device/src/session/gesture/spy.rs Outdated
Comment thread crates/openlogi-desktop/src/services/device_reads.rs
Comment thread crates/openlogi-agent-core/src/runtime.rs Outdated
Comment thread crates/openlogi-agent-core/src/capture_plan.rs
Comment thread crates/openlogi-device/src/session/gesture.rs Outdated
Comment thread crates/openlogi-core/src/binding/action.rs Outdated
Comment thread crates/openlogi-desktop/src/app/detail.rs Outdated
Comment thread crates/openlogi-device/src/session/gesture/spy.rs Outdated
@davidbudnick davidbudnick added area: hidpp HID++ protocol and device feature support platform: all Cross-platform issue type: feature New feature request labels Oct 10, 2026
@yuzi-co

yuzi-co commented Oct 11, 2026

Copy link
Copy Markdown
Contributor

Nice work. Heads-up on overlap: #949 covers part of this (0x8110 turning buttons on, device_side render, slotId pixel markers), and #950 is the middle-click marker sitting on the side render. Your side + front drawing should fix that one for the G502 LIGHTSPEED.

One gap I couldn't rule out from the diff: GamingLayout only exists for the G502 LIGHTSPEED, but any mouse with 0x8110 now gets buttons: true. A G502 X or G705 has no layout, so as far as I can tell its Buttons panel would show G-button hotspots that nothing can capture. #949 handles that by keeping only middle/back/forward when there's no ReprogControls (no_button_diversion). Is that case handled somewhere I missed?

If this lands first I'll close #949, or cut it down to just that filter on top of yours if it's still needed.

yuzi-co and others added 2 commits October 11, 2026 15:06
@Fede2782

Copy link
Copy Markdown
Contributor Author

Nice work. Heads-up on overlap: #949 covers part of this (0x8110 turning buttons on, device_side render, slotId pixel markers), and #950 is the middle-click marker sitting on the side render. Your side + front drawing should fix that one for the G502 LIGHTSPEED.

One gap I couldn't rule out from the diff: GamingLayout only exists for the G502 LIGHTSPEED, but any mouse with 0x8110 now gets buttons: true. A G502 X or G705 has no layout, so as far as I can tell its Buttons panel would show G-button hotspots that nothing can capture. #949 handles that by keeping only middle/back/forward when there's no ReprogControls (no_button_diversion). Is that case handled somewhere I missed?

If this lands first I'll close #949, or cut it down to just that filter on top of yours if it's still needed.

Thanks for looking this over, and for the pointers to #949 and #950. Yes, drawing the side and front renders together puts the middle-click marker on the front render for the G502 LIGHTSPEED.

You're right about the gap. 0x8110 turned buttons on for every spy mouse, while only the G502 LIGHTSPEED has a GamingLayout. The G-button hotspots come from the layout, so a G502 X or G705 would not have shown G-buttons. It would still have got a Buttons tab though. With a depot whose markers are all slotId ones, like the G502's, the tab would be empty. Without an image it would show the generic fallback, whose DPI and gesture buttons can't be captured on a spy mouse.

Rather than work around it, I took your approach from #949 and adapted it to this branch. It's in fix: give gaming mice without ReprogControls a Buttons panel, with you as the author:

  • A gaming mouse with the 0x8100/0x8110 tables and no ReprogControls gets the Buttons and Actions Ring tabs.
  • Without a model layout it shows only middle, back and forward: G1–G5 are the standard HID buttons, and G6 and up are not guessed. That's your _g<N>_ rule, expressed here as a STANDARD_BUTTONS layout so it goes through the same two-view drawing. Middle click is therefore reachable on those mice too.
  • The fallback silhouette drops the DPI, gesture and thumb-wheel targets for such a mouse.
  • A gaming keyboard gets no Keys tab. Your G713 dump is the test for that, and the G502 and G705 dumps test the capability.

One difference from #949: no_button_diversion is a derived method here, not a stored field. buttons keeps meaning ReprogControls, so configs and caches written by older builds read back unchanged, the Keys gate needs no change, and the wire format stays as it is. On macOS the agent also keeps Back/Forward gestures in the OS hook for any such mouse, since none of them has 0x1b04 raw XY.

Not carried over: the diagnostics output and the fixture updates. The agent captures nothing through STANDARD_BUTTONS; host mode and G-Shift still need a real layout.

With that in, I think #949 can be closed as you offered. If I dropped something you want kept, say so and I'll add it.

The second commit, fix(desktop): keep the mouse view's render under the lint limit on master, is unrelated to your comment. Master grew MouseModelView::render by a line, which put the merged result at 101 lines against clippy's limit of 100.

@greptile-apps

greptile-apps Bot commented Oct 11, 2026

Copy link
Copy Markdown

Want your agent to iterate on Greptile's feedback? Start a greploop in Codex and it will work through the open comments and keep going until this PR reviews clean.

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

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.

3 participants