Skip to content

feat(hid,core): drive solid colour over RgbEffects (0x8071) - #523

Open
Abnersouza7 wants to merge 2 commits into
AprilNEA:masterfrom
Abnersouza7:feat/rgb-effects-8071
Open

feat(hid,core): drive solid colour over RgbEffects (0x8071)#523
Abnersouza7 wants to merge 2 commits into
AprilNEA:masterfrom
Abnersouza7:feat/rgb-effects-8071

Conversation

@Abnersouza7

Copy link
Copy Markdown
Contributor

Summary

Adds support for HID++ RgbEffects (feature 0x8071), the per-cluster RGB
effect engine that succeeds ColorLedEffects (0x8070). G-series wireless mice
expose 0x8071 instead of 0x8070: a G903 LIGHTSPEED reports 0x8071 and
neither 0x8070 nor 0x8080, so its LEDs were unreachable, Capabilities::lighting
stayed false, and it was denied a Lighting tab.

The 0x8071 protocol wrapper already existed in openlogi-hidpp — fully typed,
tested, and with no consumer anywhere outside that crate. This PR wires it into
the write path and the capability gate. No IPC or config change is needed: the
lighting panel already sends a plain RGB triple, so flipping the capability is
enough to give those devices the existing panel.

The trade-off this ships with

setRgbClusterEffect is refused until software claims the clusters, and the claim
is what holds the colour — releasing it hands every cluster back to the device's
effect engine, which resumes its onboard effect and discards the colour. So the
claim cannot be scoped to the write; it lasts until the next colour, a power cycle,
or an explicit release.

The claim is all-or-nothing (SwControlFlags::ALL_CLUSTERS). On a device with a
cluster this path cannot light, claiming control extinguishes that cluster. That
is the case on a G903: its Primary cluster (the DPI indicator) accepts every
write without error and never lights — not with the fixed effect, and not even
with the cycling effect the firmware itself runs on it. Its logo cluster is
unaffected and takes the colour normally.

Nothing observable distinguishes such a cluster beforehand: the device reports it,
lists a fixed effect for it, and acknowledges the write. So this cannot be detected
and skipped. diag lighting --release-control is the escape hatch, and the
behaviour is documented on set_color_rgb_effects.

Worth a maintainer decision: this ships the capability for every 0x8071 device.
Gating it more narrowly would penalise devices whose clusters all respond, but it
does mean a G903 user picking a colour loses the DPI indicator until they release
control or power-cycle. Happy to restrict it if you would rather not take that.

Changes

  • hid: set_keyboard_color grows an 0x8071 rung above 0x8070/0x8080.
    It claims software control, reads the cluster count, and applies the fixed
    effect to every cluster — a G903's logo and DPI indicator are separate clusters,
    so writing only the first leaves it half-painted. The fixed effect is located by
    effectID rather than by a guessed index, since effect indices are per-cluster
    and firmware-ordered. Volatile, like the 0x8070 path. Adds dump_rgb_clusters
    (cluster/effect table plus the current software-control state) and
    release_rgb_control (hands the clusters back without a power cycle).
  • core: Capabilities::lighting counts 0x8071, so those devices offer the
    existing Lighting panel.
  • cli: diag lighting gains --method rgb, --info (dump the cluster/effect
    table and exit) and --release-control (undo the claim and exit); the colour is
    required unless one of those is given. It now picks its device through
    select_device, so it reaches a wireless mouse behind a receiver instead of only
    wired keyboards.

RGB_EFFECT_ID_FIXED is marked in the source as a verified observation rather than
a documented constant: the 0x8071 spec is not public, so the id follows the
0x8070 effect vocabulary and was confirmed on hardware.

Testing

cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace

All green (21 suites). Two CLI parser tests were added for the new
colour-optional cases, and one capability test for an 0x8071-only device.

Hardware-verified on Linux against a G903 LIGHTSPEED behind a 046d:c539
receiver. That receiver is not recognised on master, so verification used a
local build with the Lightspeed receiver support from #510/#459 applied; the
changes in this PR are independent of both and touch no file either one touches.

  • diag lighting --info reports 2 clusters (location=0x0001 Primary,
    location=0x0002 Logo), each listing 4 effects with the fixed effect at index 1.
  • --method rgb and Auto both drive 0x8071, cluster_count=2; red, green,
    blue, cyan and magenta all applied to the logo cluster.
  • Auto on a wired PRO Gaming Keyboard logs no 0x8071 cluster engine — trying the 0x8070 zone engine and applies via 0x8070 (zone_count=2) — the existing
    path is unchanged.
  • --release-control drops the claim (verified by reading it back:
    all_clusters=false) and the device resumes its own effect.

Not verified: macOS and Windows; any 0x8071 device other than a G903; and
whether a device whose clusters all respond exists in the wild — the Primary-cluster
behaviour above is a single-device observation, not a claim about the format.

G-series wireless mice expose the per-cluster RgbEffects engine (0x8071)
instead of ColorLedEffects (0x8070) — a G903 LIGHTSPEED reports 0x8071
and neither 0x8070 nor 0x8080. The 0x8071 wrapper already existed in
openlogi-hidpp but had no consumer, so those devices had unreachable
LEDs and Capabilities::lighting stayed false, denying them a Lighting
tab in the GUI.

- hid: set_keyboard_color grows an 0x8071 rung above 0x8070/0x8080. It
  takes software control of the clusters (required before
  setRgbClusterEffect), reads the cluster count, and applies the fixed
  effect to every cluster — a G903's logo and DPI indicator are separate
  clusters, so writing only the first leaves it half-painted. The fixed
  effect is located by effectID rather than by a guessed index, since
  effect indices are per-cluster and firmware-ordered. Volatile like the
  0x8070 path, so a colour pick overrides the running onboard effect
  without spending flash cycles.
- core: Capabilities::lighting counts 0x8071, so those devices offer the
  existing Lighting panel. No IPC or config change — the panel already
  sends a plain RGB triple.
- cli: diag lighting gains --method rgb and picks its device via
  select_device, so it can reach a mouse behind a receiver instead of
  only wired keyboards.

Verified on a G903 LIGHTSPEED (046d:c539 receiver): cluster_count=2,
both clusters driven, red/green/blue all applied via Auto and --method
rgb.
A colour write claims RgbEffects software control and must keep it: the
claim is what holds the colour, and releasing it hands every cluster back
to the device's own effect engine, which resumes its onboard effect and
discards the colour (observed on a G903 LIGHTSPEED).

The claim is all-or-nothing, so on a device with a cluster this path
cannot light it silently extinguishes that cluster. A G903's Primary
cluster (the DPI indicator) accepts every write without error and never
lights — not with the fixed effect, not with the cycling effect the
firmware itself runs there — so claiming control turns it off with no way
to take over. Nothing observable distinguishes that cluster beforehand.

- hid: dump_rgb_clusters reports the cluster/effect table plus the current
  software-control state; release_rgb_control hands the clusters back
  without a power cycle.
- cli: diag lighting gains --info (dump and exit) and --release-control
  (undo and exit); the colour is required unless one of them is given.
@greptile-apps

greptile-apps Bot commented Aug 6, 2026

Copy link
Copy Markdown

Greptile Summary

Adds HID++ 0x8071 per-cluster solid-color support and exposes it through capability detection and lighting diagnostics.

  • Claims 0x8071 software control and writes the fixed effect across reported clusters.
  • Adds cluster inspection and explicit control-release diagnostics.
  • Extends lighting capability gating and device selection to wireless RGB devices.

Confidence Score: 3/5

The PR should not merge until failed 0x8071 writes release software control and RGB-specific diagnostics select an actually compatible device.

A metadata or effect-write failure can leave onboard lighting disabled after returning an error, and mode-specific CLI operations can target a legacy-lighting device despite a compatible 0x8071 device being online.

Files Needing Attention: crates/openlogi-hid/src/write/lighting.rs; crates/openlogi-cli/src/cmd/diag/lighting.rs

Important Files Changed

Filename Overview
crates/openlogi-hid/src/write/lighting.rs Adds the 0x8071 write and diagnostic implementation, but failure paths can retain software control and effect lookup uses the table position rather than the returned effect index.
crates/openlogi-cli/src/cmd/diag/lighting.rs Adds RGB diagnostics and shared device selection, but 0x8071-only operations can select a legacy-lighting device.
crates/openlogi-core/src/device.rs Correctly extends measured lighting capability detection to devices exposing only feature 0x8071.
crates/openlogi-hid/src/lib.rs Re-exports the new RGB diagnostic and control types and functions.
crates/openlogi-hid/src/write.rs Exposes the new lighting module API without changing write plumbing.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  UI[GUI or diag color request] --> Auto{Lighting method}
  Auto -->|0x8071 available| Claim[Claim ALL_CLUSTERS]
  Claim --> Metadata[Read clusters and effects]
  Metadata --> Write[Apply fixed effect to each cluster]
  Auto -->|No 0x8071| E8070[Try 0x8070 zone effects]
  E8070 -->|Unavailable| E8080[Try 0x8080 per-key lighting]
  Diag[diag --info or --release-control] --> Select[Select lighting device]
  Select --> RGBAPI[Invoke 0x8071-specific API]
Loading

Fix All in Codex Fix All in Claude Code

Reviews (1): Last reviewed commit: "feat(hid,cli): report and undo 0x8071 so..." | Re-trigger Greptile

Comment on lines +415 to +421
feature
.set_sw_control(
SwControlFlags::ALL_CLUSTERS,
EventsNotificationFlags::empty(),
)
.await
.map_err(classify_rgb_error)?;

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 Failed writes retain cluster control

When cluster metadata validation or a later effect write fails after ALL_CLUSTERS is claimed, the error path returns without releasing software control, causing onboard lighting to remain disabled and leaving the device dark or partially painted until explicit release or a power cycle.

Knowledge Base Used: openlogi-hid: device inventory, pairing, and HID++ feature control

Fix in Codex Fix in Claude Code

})?;
// The three features `set_keyboard_color` can drive — auto-skip devices with
// no LEDs to paint.
let (route, name) = select_device(args.device.as_deref(), &[0x8071, 0x8070, 0x8080]).await?;

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 RGB diagnostics select legacy devices

When a 0x8070- or 0x8080-only device precedes the intended 0x8071 device, the union feature filter selects that legacy device for --info, --release-control, or --method rgb, causing the 0x8071-only operation to fail with FeatureUnsupported despite a compatible device being online.

Knowledge Base Used: OpenLogi CLI

Fix in Codex Fix in Claude Code

Comment on lines +490 to +492
if info.effect_id == RGB_EFFECT_ID_FIXED {
return Ok(effect);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Use firmware-reported effect index

If firmware reports a cluster_effect_index that differs from the effect table position, returning the loop counter passes the wrong identifier to set_rgb_cluster_effect, selecting another effect or causing the write to be rejected.

Suggested change
if info.effect_id == RGB_EFFECT_ID_FIXED {
return Ok(effect);
}
if info.effect_id == RGB_EFFECT_ID_FIXED {
return Ok(info.cluster_effect_index);
}

Knowledge Base Used: openlogi-hid: device inventory, pairing, and HID++ feature control

Fix in Codex Fix in Claude Code

@davidbudnick davidbudnick added type: feature New feature request platform: all Cross-platform issue labels Aug 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

platform: all Cross-platform issue type: feature New feature request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants