diff --git a/lib/logitech_receiver/settings.py b/lib/logitech_receiver/settings.py index b5ddd23c..39c8c872 100644 --- a/lib/logitech_receiver/settings.py +++ b/lib/logitech_receiver/settings.py @@ -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. diff --git a/lib/logitech_receiver/settings_templates.py b/lib/logitech_receiver/settings_templates.py index ef5a7688..6e6e7d22 100644 --- a/lib/logitech_receiver/settings_templates.py +++ b/lib/logitech_receiver/settings_templates.py @@ -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 diff --git a/lib/solaar/cli/show.py b/lib/solaar/cli/show.py index ab03a5b9..050b8509 100644 --- a/lib/solaar/cli/show.py +++ b/lib/solaar/cli/show.py @@ -375,14 +375,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:")