PerKeyLighting: drop misleading live-read output in solaar show
The 0x8081 PerKeyLighting v2 protocol has no GetIndividualRgbZones
function — there's no way to ask the device what colors are currently
on the per-key buffer over HID++. PerKeyLighting.read() papered over
this gap by fabricating an all-"No change" sentinel map whenever the
caller asked for a live (uncached) read, which produced misleading
solaar show output like:
Per-key Lighting (saved): {1:0xc01c28, 2:0xc52d26, ...}
Per-key Lighting : {1:No change, 2:No change, ...}
— even with the keyboard clearly running the saved colors.
Two-part fix:
1. PerKeyLighting.read() now returns self._value if populated (from
prior write or persister) regardless of `cached`. Only fabricates
the all-"No change" map when there's truly no state — fresh device,
no prior write, nothing persisted. This is the right starting state
in that case because per-key writes are additive over a buffer of
unknown content.
Other callers benefit too: solaar config calls read(cached=False) on
per-key settings to display current values, and prior to this
change it would get fabricated sentinels back instead of the
in-memory map.
2. Add a `live_readable = True` class attribute to Setting (default
preserves existing behavior). Override to False on PerKeyLighting.
solaar show gates its live-read print on this flag, so non-readable
settings show only the (saved) line — which is the authoritative
record of what was last written to a setting whose live state can't
be read back.
After both fixes solaar show prints just:
Per-key Lighting (saved): {1:0xc01c28, 2:0xc52d26, ...}
— honest about what we know.
690 tests pass; pre-commit clean.
This commit is contained in:
parent
fc19860f76
commit
ad96dd395b
|
|
@ -61,6 +61,11 @@ class Setting:
|
|||
validator_class = None
|
||||
validator_options = {}
|
||||
display = True # display setting in UI
|
||||
# Set False for settings whose value cannot be read back from the device
|
||||
# (e.g. PerKeyLighting — the 0x8081 protocol has no GetIndividualRgbZones).
|
||||
# `solaar show` uses this to suppress the "(live)" output line that would
|
||||
# otherwise print a fabricated value misleadingly.
|
||||
live_readable = True
|
||||
# Optional UI editor override as "module.path:ClassName". Resolved by the
|
||||
# config panel before the Kind dispatch. Kept as a string so this module
|
||||
# stays free of GTK imports — the FE/BE seam is preserved.
|
||||
|
|
|
|||
|
|
@ -1894,6 +1894,11 @@ class PerKeyLighting(settings.Settings):
|
|||
feature = _F.PER_KEY_LIGHTING_V2
|
||||
keys_universe = special_keys.KEYCODES
|
||||
editor_class = "solaar.ui.perkey.control:PerKeyControl"
|
||||
# 0x8081 has no GetIndividualRgbZones — there's no way to ask the device
|
||||
# what colors are currently on the per-key buffer. read() returns the
|
||||
# canonical in-memory map for callers that need a value, but `solaar show`
|
||||
# honors this flag and skips its live-read line to avoid misleading output.
|
||||
live_readable = False
|
||||
|
||||
@staticmethod
|
||||
def _wrap_color(value):
|
||||
|
|
@ -1916,12 +1921,20 @@ class PerKeyLighting(settings.Settings):
|
|||
super().update_key_value(key, self._wrap_color(value), save)
|
||||
|
||||
def read(self, cached=True):
|
||||
# The 0x8081 protocol has no GetIndividualRgbZones — the device cannot
|
||||
# report its current per-key buffer back. So a "live" read is fictional:
|
||||
# we either return what we last wrote (the persisted/in-memory map,
|
||||
# which is the canonical truth for this setting) or, on a fresh device
|
||||
# with no persisted state, fabricate an all-"No change" sentinel map
|
||||
# as the starting point. Returning the cached value unconditionally
|
||||
# also fixes `solaar show` showing every key as "No change" on the
|
||||
# live line — it now matches what's actually on the keyboard.
|
||||
self._pre_read(cached)
|
||||
if cached and self._value is not None:
|
||||
if self._value is not None:
|
||||
return self._value
|
||||
reply_map = {}
|
||||
for key in self._validator.choices:
|
||||
reply_map[int(key)] = special_keys.COLORSPLUS["No change"] # this signals no change
|
||||
reply_map[int(key)] = special_keys.COLORSPLUS["No change"] # starting state, no per-key write yet
|
||||
self._value = reply_map
|
||||
return reply_map
|
||||
|
||||
|
|
|
|||
|
|
@ -404,14 +404,19 @@ def _print_device(dev, num=None):
|
|||
):
|
||||
v = setting.val_to_string(setting._device.persister.get(setting.name))
|
||||
print(f" {setting.label} (saved): {v}")
|
||||
try:
|
||||
v = setting.read(False)
|
||||
v = setting.val_to_string(v)
|
||||
except exceptions.FeatureCallError as e:
|
||||
v = "HID++ error " + str(e)
|
||||
except AssertionError as e:
|
||||
v = "AssertionError " + str(e)
|
||||
print(f" {setting.label} : {v}")
|
||||
# Settings whose value cannot be read back from the device
|
||||
# (e.g. PerKeyLighting — 0x8081 has no GetIndividualRgbZones)
|
||||
# suppress the live-read line; the saved line above is the
|
||||
# authoritative record. See Setting.live_readable.
|
||||
if getattr(setting, "live_readable", True):
|
||||
try:
|
||||
v = setting.read(False)
|
||||
v = setting.val_to_string(v)
|
||||
except exceptions.FeatureCallError as e:
|
||||
v = "HID++ error " + str(e)
|
||||
except AssertionError as e:
|
||||
v = "AssertionError " + str(e)
|
||||
print(f" {setting.label} : {v}")
|
||||
|
||||
if dev.online and dev.keys:
|
||||
print(f" Has {len(dev.keys)} reprogrammable keys:")
|
||||
|
|
|
|||
Loading…
Reference in New Issue