Add G522 headset layout to per-key painter

Wire the per-key painter UI to the G522's per-zone lighting setting and
ship a layout file matching the eight-LED earcup arrangement.

- lib/solaar/ui/perkey/layouts/headset_g522.py (new): 2x5 layout, four
  LEDs per earcup in a 2x2 grid with a gap column between, viewed from
  outside. Zone IDs used as labels (Logitech firmware numbering).
- lib/solaar/ui/perkey/layouts/__init__.py: register layout against
  feature 0x0620 (HEADSET_RGB_HOSTMODE) via name-substring match on
  "G522".
- lib/logitech_receiver/settings_templates.py: switch
  HeadsetPerZoneLighting from ChoicesMapValidator (limited to the
  COLORSPLUS named-color palette) to MapRangeValidator (any 24-bit
  RGB). Set editor_class so the painter UI takes over from the old
  MapChoice dropdowns. Drops choices_universe; matches the
  PerKeyLighting pattern.

LED positions per tester's reference:

    Left earcup          Right earcup
    | 8 | 7 |            | 6 | 5 |
    | 4 | 3 |            | 2 | 1 |
This commit is contained in:
Ken Sanislo 2026-05-10 15:58:15 -07:00
parent e34bcbe0b8
commit f3af38862d
3 changed files with 64 additions and 9 deletions

View File

@ -2265,10 +2265,9 @@ class HeadsetLEDsPrimary(settings.Setting):
class HeadsetPerZoneLighting(settings.Settings):
"""Per-zone LED color overrides.
Mirrors `PerKeyLighting`'s `Settings` + `ChoicesMapValidator` style —
one dropdown per discovered zone with the `COLORSPLUS` palette. The
"No change" entry means "inherit the current `LEDs Primary` color",
so users can paint individual zones without losing the base setup.
Mirrors `PerKeyLighting` keys are firmware zone IDs, values are
24-bit RGB ints with the `-1` sentinel meaning "inherit the current
`LEDs Primary` color." Surfaces in the UI via the per-key painter.
"""
name = "headset_per_zone_lighting"
@ -2279,20 +2278,20 @@ class HeadsetPerZoneLighting(settings.Settings):
)
feature = _F.HEADSET_RGB_HOSTMODE
persist = True
choices_universe = special_keys.COLORSPLUS
editor_class = "solaar.ui.perkey.control:PerKeyControl"
class rw_class(settings.FeatureRWMap):
pass
class validator_class(settings_validator.ChoicesMapValidator):
class validator_class(settings_validator.MapRangeValidator):
_COLOR_RANGE = settings_validator.Range(min=0, max=0xFFFFFF, byte_count=3)
@classmethod
def build(cls, setting_class, device):
zones = headset_rgb.discover_zones(device)
if not zones:
return None
choices_map = {
common.NamedInt(int(z), _("Zone") + " " + str(int(z))): setting_class.choices_universe for z in zones
}
choices_map = {common.NamedInt(int(z), _("Zone") + " " + str(int(z))): cls._COLOR_RANGE for z in zones}
return cls(choices_map) if choices_map else None
def read(self, cached=True):

View File

@ -26,6 +26,7 @@ from __future__ import annotations
from collections.abc import Callable
from ..layout import Layout
from . import headset_g522
from . import keyboard_ansi
from . import keyboard_iso_azerty
from . import keyboard_iso_qwerty
@ -140,3 +141,5 @@ for _family, (_full, _tkl) in _FAMILY_LAYOUTS.items():
register_layout(0x8081, _keyboard_matcher(_family, full_size=False), _tkl)
register_layout(0x8081, _name_contains("G502 X"), mouse_g502x.LAYOUT)
# HEADSET_RGB_HOSTMODE = 0x0620
register_layout(0x0620, _name_contains("G522"), headset_g522.LAYOUT)

View File

@ -0,0 +1,53 @@
## Copyright (C) 2026 Solaar Contributors https://pwr-solaar.github.io/Solaar/
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License along
## with this program; if not, write to the Free Software Foundation, Inc.,
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""LED layout for the G522 LIGHTSPEED headset.
Eight LEDs in two 2×2 grids one per earcup, viewed from outside:
Left earcup Right earcup
8 7 6 5
4 3 2 1
"""
from __future__ import annotations
from ..layout import Cell
from ..layout import Layout
_CELLS: tuple[Cell, ...] = (
# Left earcup (cols 0-1, outer view): top-left=8, top-right=7, bottom-left=4, bottom-right=3
Cell(zone_id=8, row=0, col=0, group="main", label="8"),
Cell(zone_id=7, row=0, col=1, group="main", label="7"),
Cell(zone_id=4, row=1, col=0, group="main", label="4"),
Cell(zone_id=3, row=1, col=1, group="main", label="3"),
# Right earcup (cols 3-4, outer view): top-left=6, top-right=5, bottom-left=2, bottom-right=1
Cell(zone_id=6, row=0, col=3, group="main", label="6"),
Cell(zone_id=5, row=0, col=4, group="main", label="5"),
Cell(zone_id=2, row=1, col=3, group="main", label="2"),
Cell(zone_id=1, row=1, col=4, group="main", label="1"),
)
LAYOUT: Layout = Layout(
cells=_CELLS,
rows=2,
cols=5,
description="Logitech G522 LIGHTSPEED headset (8 LEDs, 4 per earcup)",
)