headset RGB: honor explicit black (0) onboard-effect colors
_HeadsetOnboardEffect.__init__ seeded per-effect defaults for any field that was falsy, so a Static color1 of 0x000000 (black) was treated as unset and overwritten with the white default — setting the onboard color to black turned the LEDs white instead of off. Switch the constructor to None-sentinel defaults: a field is seeded from _DEFAULTS only when genuinely absent (None), so an explicit 0 is honored. The UI's get_value() always passes explicit values, and a fresh effect-pick seeds its RANGE widgets UI-side via _apply_id_defaults, so animated effects still get sane defaults. Reported by @rouderz on PR #3181.
This commit is contained in:
parent
5f6def437e
commit
7c7666466a
|
|
@ -2723,13 +2723,12 @@ class _HeadsetOnboardEffect:
|
||||||
leading clusterIndex). intensity is a 0-100 percent; saturation is a raw
|
leading clusterIndex). intensity is a 0-100 percent; saturation is a raw
|
||||||
0-255 byte (same as the keyboard RGB effects)."""
|
0-255 byte (same as the keyboard RGB effects)."""
|
||||||
|
|
||||||
# Per-effect default parameter values, applied to any field the effect
|
# Per-effect default parameter values, applied only to fields left
|
||||||
# uses that would otherwise be 0. Picking an effect in the UI rebuilds
|
# unset (passed as None). An explicit value is always honored — passing
|
||||||
# this object from the (initially zeroed) field widgets, so without
|
# 0 means the caller chose 0, e.g. a black Static color1 turns the LEDs
|
||||||
# seeding the picker emits an all-zero frame: the firmware rejects
|
# off. Only a genuinely absent field falls back to the default (a fresh
|
||||||
# ColorCycle/ColorWave outright and renders Breathing/DualColor/Static
|
# effect-pick seeds its RANGE widgets UI-side via _apply_id_defaults).
|
||||||
# as black or zero-intensity. intensity 0 in particular reads as "LEDs
|
# Defaults confirmed against the LGHUB binary decode of 0x0621.
|
||||||
# off". Defaults confirmed against the LGHUB binary decode of 0x0621.
|
|
||||||
_DEFAULTS = {
|
_DEFAULTS = {
|
||||||
0: {"color1": 0xFFFFFF}, # Static / Fixed
|
0: {"color1": 0xFFFFFF}, # Static / Fixed
|
||||||
1: {"intensity": 100, "saturation": 255, "period": 5000}, # Color Cycle
|
1: {"intensity": 100, "saturation": 255, "period": 5000}, # Color Cycle
|
||||||
|
|
@ -2740,24 +2739,21 @@ class _HeadsetOnboardEffect:
|
||||||
|
|
||||||
# speed is accepted only to load configs persisted before the 0x0621
|
# speed is accepted only to load configs persisted before the 0x0621
|
||||||
# decode (DualColor byte 6 was mislabelled "speed"; it is intensity).
|
# decode (DualColor byte 6 was mislabelled "speed"; it is intensity).
|
||||||
def __init__(self, ID=0, color1=0, color2=0, intensity=0, saturation=0, period=0, speed=0, direction=0):
|
def __init__(self, ID=0, color1=None, color2=None, intensity=None, saturation=None, period=None, speed=0, direction=None):
|
||||||
self.ID = int(ID)
|
self.ID = int(ID)
|
||||||
|
defaults = self._DEFAULTS.get(self.ID, {})
|
||||||
|
color1 = defaults.get("color1", 0) if color1 is None else color1
|
||||||
|
color2 = defaults.get("color2", 0) if color2 is None else color2
|
||||||
|
intensity = defaults.get("intensity", 0) if intensity is None else intensity
|
||||||
|
saturation = defaults.get("saturation", 0) if saturation is None else saturation
|
||||||
|
period = defaults.get("period", 0) if period is None else period
|
||||||
|
direction = defaults.get("direction", 0) if direction is None else direction
|
||||||
self.intensity = max(0, min(100, int(intensity)))
|
self.intensity = max(0, min(100, int(intensity)))
|
||||||
self.saturation = max(0, min(255, int(saturation)))
|
self.saturation = max(0, min(255, int(saturation)))
|
||||||
self.period = max(0, min(0xFFFF, int(period)))
|
self.period = max(0, min(0xFFFF, int(period)))
|
||||||
self.direction = max(0, min(3, int(direction)))
|
self.direction = max(0, min(3, int(direction)))
|
||||||
for k, v in (("color1", color1), ("color2", color2)):
|
for k, v in (("color1", color1), ("color2", color2)):
|
||||||
setattr(self, k, common.ColorInt(int(v) & 0xFFFFFF))
|
setattr(self, k, common.ColorInt(int(v) & 0xFFFFFF))
|
||||||
# Seed any field the selected effect uses that arrived as 0 so the
|
|
||||||
# picker never sends an all-zero (rejected / black) frame. An effect
|
|
||||||
# actually active on the device reports non-zero values, so reads
|
|
||||||
# via from_bytes are unaffected.
|
|
||||||
for field, default in self._DEFAULTS.get(self.ID, {}).items():
|
|
||||||
if not getattr(self, field):
|
|
||||||
if field.startswith("color"):
|
|
||||||
setattr(self, field, common.ColorInt(default & 0xFFFFFF))
|
|
||||||
else:
|
|
||||||
setattr(self, field, default)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_bytes(cls, data, options=None):
|
def from_bytes(cls, data, options=None):
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue