This commit is contained in:
Ken Sanislo 2026-07-07 21:17:25 -07:00 committed by GitHub
commit 3834f1e7dc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 160 additions and 0 deletions

View File

@ -32,6 +32,7 @@ from . import keyboard_iso_azerty
from . import keyboard_iso_qwerty
from . import keyboard_iso_qwertz
from . import keyboard_jis
from . import keyboard_pro_x_rapid
from . import mouse_g502x
# (feature_id, matcher, layout). Matcher receives a `hint` dict the editor
@ -135,6 +136,29 @@ def _keyboard_matcher(family: str, full_size: bool) -> Callable[[dict], bool]:
return match
def _pro_x_rapid_matcher(family: str) -> Callable[[dict], bool]:
"""Match the PRO X RAPID (reported as "PRO X RAPID", TKL) for one region.
Same country-code family routing as the generic keyboards; only the media
top row differs."""
named = _name_contains("PRO X RAPID")
def match(hint: dict) -> bool:
if hint.get("kind") != "keyboard":
return False
if not named(hint):
return False
if _has_numpad(hint): # PRO X RAPID is TKL
return False
return _keyboard_family(hint) == family
return match
# PRO X RAPID — regional generated base + a customized media top row.
# Registered ahead of the generic family matchers so it wins for this model.
for _family, (_full, _tkl) in _FAMILY_LAYOUTS.items():
register_layout(0x8081, _pro_x_rapid_matcher(_family), keyboard_pro_x_rapid.with_media_top_row(_tkl))
# PER_KEY_LIGHTING_V2 = 0x8081
for _family, (_full, _tkl) in _FAMILY_LAYOUTS.items():
register_layout(0x8081, _keyboard_matcher(_family, full_size=True), _full)

View File

@ -0,0 +1,74 @@
## 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.
"""PRO X RAPID (PRO X TKL RAPID) per-key layout.
Same generated regional main block as any TKL keyboard, plus a customized
media top row. This board wires five dedicated media keys above the F-row with
board-specific zone ids that the canonical extras map (153-158) does not cover;
ids and positions are hardware-probed (ported from the OpenRGB key-map for this
model). The logo has no addressable LED here.
"""
from __future__ import annotations
from ..layout import Cell
from ..layout import Layout
# Dedicated media keys, hardware-probed. Columns align above the F-row
# (F4=col5, F9-F12=cols10-13). Zone ids are board-specific — NOT the canonical
# 153-158 extras — and the logo (canonical 210) is unlit on this model.
# group="media" keeps these out of strip_groups so they render in the matrix.
MEDIA_TOP_ROW: tuple[Cell, ...] = (
Cell(zone_id=150, row=0, col=5, group="media", label="Bright"),
Cell(zone_id=155, row=0, col=10, group="media", label="Prev"),
Cell(zone_id=152, row=0, col=11, group="media", label="Play"),
Cell(zone_id=154, row=0, col=12, group="media", label="Next"),
Cell(zone_id=153, row=0, col=13, group="media", label="Mute"),
)
def with_media_top_row(base: Layout) -> Layout:
"""Return `base` with every cell pushed down one row and the PRO X RAPID
media top row placed at row 0.
The media keys are explicit bound cells, so they paint directly with no
EXTRAS_ALLOWLIST entry, while `base`'s `extra_zones` still filters the
phantom zones this board shares with the G515 (47, 97, 99-103, 254).
"""
shifted = tuple(
Cell(
zone_id=c.zone_id,
row=c.row + 1,
col=c.col,
width=c.width,
height=c.height,
group=c.group,
label=c.label,
x=c.x,
y=c.y,
)
for c in base.cells
)
return Layout(
cells=MEDIA_TOP_ROW + shifted,
rows=base.rows + 1,
cols=base.cols,
strip_groups=base.strip_groups,
supported_tools=base.supported_tools,
extra_zones=base.extra_zones,
description="PRO X RAPID",
)

View File

@ -0,0 +1,62 @@
from solaar.ui.perkey.binding import bind
from solaar.ui.perkey.layouts import layout_for
# PRO X RAPID hardware-probed media zones (board-specific, not canonical 153-158).
_RAPID_MEDIA = {150, 152, 153, 154, 155}
# Phantom zones this board shares with the G515 (not physical keys).
_SHARED_PHANTOMS = {47, 97}
def _rapid_hint(name="PRO X RAPID", country=1):
return {
"kind": "keyboard",
"wpid": "C35B",
"codename": name,
"name": name,
"keyboard_layout": country, # 1 = ANSI
"zones": [38, 55, 66, *sorted(_RAPID_MEDIA), *sorted(_SHARED_PHANTOMS)],
"zone_count": 9,
}
def test_pro_x_rapid_gets_media_top_row():
layout = layout_for(0x8081, _rapid_hint())
assert layout.description == "PRO X RAPID"
media = {c.zone_id: (c.row, c.col, c.label) for c in layout.cells if c.group == "media"}
assert media == {
150: (0, 5, "Bright"),
155: (0, 10, "Prev"),
152: (0, 11, "Play"),
154: (0, 12, "Next"),
153: (0, 13, "Mute"),
}
# The generated base is pushed down one row to make room for the media row.
esc = next(c for c in layout.cells if c.zone_id == 38)
assert esc.row == 1
def test_pro_x_rapid_media_keys_are_paintable_and_phantoms_dropped():
layout = layout_for(0x8081, _rapid_hint())
bound = bind(layout, _rapid_hint()["zones"], lambda z: f"KEY {z}")
media_bound = {bc.cell.zone_id: bc.bound for bc in bound.matrix if bc.cell.group == "media"}
assert media_bound == {z: True for z in _RAPID_MEDIA}
# Shared phantoms must not surface as paintable strip swatches.
strip_zones = {bc.cell.zone_id for bc in bound.strip}
assert _SHARED_PHANTOMS.isdisjoint(strip_zones)
def test_pro_x_rapid_still_regional():
# ISO country code still routes to the ISO main block, with the media row on top.
iso = layout_for(0x8081, _rapid_hint(country=2)) # 2 = ISO QWERTY
assert iso.description == "PRO X RAPID"
assert any(c.group == "media" for c in iso.cells)
def test_non_rapid_tkl_unaffected():
# A G515 TKL keeps the generic layout: no media top row.
layout = layout_for(0x8081, _rapid_hint(name="G515 LS TKL"))
assert layout.description != "PRO X RAPID"
assert not any(c.group == "media" for c in layout.cells)