Add regional keyboard layouts (ISO_QWERTY, QWERTZ, AZERTY, JIS) and fix copyright
Read HID++ feature 0x4540 KeyboardLayout to detect the device's country code, then route the per-key painter to a matching regional layout. Changes: - lib/logitech_receiver/hidpp20.py: new get_keyboard_layout() returning the HID Usage Table country code from feature 0x4540's first response byte. - lib/logitech_receiver/device.py: lazy device.keyboard_layout property, guarded by feature presence so devices without 0x4540 don't pay a query cost on access. - lib/solaar/ui/perkey/control.py: thread the country code into the editor hint dict. - lib/solaar/ui/perkey/layouts/_keyboard_base.py (new): factor out the function row, nav cluster, and numpad block as shared building blocks. Two main-block variants (ANSI with row 2 col 13 backslash, ISO without) cover all five regions. build_layout() applies per-zone label overrides on top of either main block. - lib/solaar/ui/perkey/layouts/keyboard_ansi.py: refactored to use the builder; same LAYOUT_FULL/LAYOUT_TKL exports. - lib/solaar/ui/perkey/layouts/keyboard_iso_qwerty.py (new): UK English ISO. Same shape as DE/FR/JIS but no label overrides. - lib/solaar/ui/perkey/layouts/keyboard_iso_qwertz.py (new): DE/Swiss -- Y/Z swap, Ü/Ö/Ä/ß placement. - lib/solaar/ui/perkey/layouts/keyboard_iso_azerty.py (new): FR -- A↔Q, W↔Z, French digit-row symbols, M repositioning. - lib/solaar/ui/perkey/layouts/keyboard_jis.py (new): JP -- @ / [ / : bracket-row relabels. - lib/solaar/ui/perkey/layouts/__init__.py: country-code-aware matchers, five families × two sizes (full/TKL). Defaults to ANSI when 0x4540 is unsupported or returns an unknown code. POUND, ISO_BACKSLASH, and the L-shape Enter top half (zone 46) are intentionally omitted from the ISO layouts -- same coverage as OpenRGB. ABNT2 (Brazilian) deferred until a confirmed Logitech BR RGB device shows up; adding it later is one new layout file plus a country-code entry. Also fix copyright headers on all new lib/solaar/ui/perkey/ files: the files were created in 2026, not 2024 as the headers said.
This commit is contained in:
parent
d8422d78d1
commit
1952e9ce98
|
|
@ -156,6 +156,7 @@ class Device:
|
||||||
self._tid_map = None # map from transports to product identifiers
|
self._tid_map = None # map from transports to product identifiers
|
||||||
self._persister = None # persister holds settings
|
self._persister = None # persister holds settings
|
||||||
self._led_effects = self._firmware = self._keys = self._remap_keys = self._gestures = self._force_buttons = None
|
self._led_effects = self._firmware = self._keys = self._remap_keys = self._gestures = self._force_buttons = None
|
||||||
|
self._keyboard_layout = None # lazy: country code from HID++ 0x4540, None if unsupported
|
||||||
self._profiles = self._backlight = self._settings = None
|
self._profiles = self._backlight = self._settings = None
|
||||||
self.registers = []
|
self.registers = []
|
||||||
self.notification_flags = None
|
self.notification_flags = None
|
||||||
|
|
@ -373,6 +374,13 @@ class Device:
|
||||||
self._polling_rate = rate if rate else self._polling_rate
|
self._polling_rate = rate if rate else self._polling_rate
|
||||||
return self._polling_rate
|
return self._polling_rate
|
||||||
|
|
||||||
|
@property
|
||||||
|
def keyboard_layout(self):
|
||||||
|
if self._keyboard_layout is None and self.online and self.protocol >= 2.0:
|
||||||
|
if SupportedFeature.KEYBOARD_LAYOUT_2 in self.features:
|
||||||
|
self._keyboard_layout = _hidpp20.get_keyboard_layout(self)
|
||||||
|
return self._keyboard_layout
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def led_effects(self):
|
def led_effects(self):
|
||||||
if not self._led_effects and self.online and self.protocol >= 2.0:
|
if not self._led_effects and self.online and self.protocol >= 2.0:
|
||||||
|
|
|
||||||
|
|
@ -2031,6 +2031,18 @@ class Hidpp20:
|
||||||
SupportedFeature._fallback = lambda x: f"unknown:{x:04X}"
|
SupportedFeature._fallback = lambda x: f"unknown:{x:04X}"
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def get_keyboard_layout(self, device: Device):
|
||||||
|
"""Return the device's keyboard layout country code, or None.
|
||||||
|
|
||||||
|
Country code semantics match the HID HUT keyboard country codes that
|
||||||
|
Logitech's KEYBOARD_LAYOUT_2 (0x4540) feature reports in the first byte.
|
||||||
|
Used by the per-key painter to pick the matching regional layout.
|
||||||
|
"""
|
||||||
|
result = device.feature_request(SupportedFeature.KEYBOARD_LAYOUT_2, 0x00)
|
||||||
|
if result:
|
||||||
|
return struct.unpack("!B", result[:1])[0]
|
||||||
|
return None
|
||||||
|
|
||||||
def config_change(self, device: Device, configuration, no_reply=False):
|
def config_change(self, device: Device, configuration, no_reply=False):
|
||||||
return device.feature_request(SupportedFeature.CONFIG_CHANGE, 0x10, configuration, no_reply=no_reply)
|
return device.feature_request(SupportedFeature.CONFIG_CHANGE, 0x10, configuration, no_reply=no_reply)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
## Copyright (C) 2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/
|
## Copyright (C) 2026 Solaar Contributors https://pwr-solaar.github.io/Solaar/
|
||||||
##
|
##
|
||||||
## This program is free software; you can redistribute it and/or modify
|
## 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
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
## Copyright (C) 2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/
|
## Copyright (C) 2026 Solaar Contributors https://pwr-solaar.github.io/Solaar/
|
||||||
##
|
##
|
||||||
## This program is free software; you can redistribute it and/or modify
|
## 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
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
## Copyright (C) 2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/
|
## Copyright (C) 2026 Solaar Contributors https://pwr-solaar.github.io/Solaar/
|
||||||
##
|
##
|
||||||
## This program is free software; you can redistribute it and/or modify
|
## 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
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
## Copyright (C) 2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/
|
## Copyright (C) 2026 Solaar Contributors https://pwr-solaar.github.io/Solaar/
|
||||||
##
|
##
|
||||||
## This program is free software; you can redistribute it and/or modify
|
## 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
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
|
@ -243,6 +243,7 @@ class PerKeyControl(Gtk.Box):
|
||||||
"wpid": getattr(device, "wpid", None),
|
"wpid": getattr(device, "wpid", None),
|
||||||
"codename": getattr(device, "codename", None),
|
"codename": getattr(device, "codename", None),
|
||||||
"name": getattr(device, "name", None),
|
"name": getattr(device, "name", None),
|
||||||
|
"keyboard_layout": getattr(device, "keyboard_layout", None),
|
||||||
"zones": list(self._sink.zones),
|
"zones": list(self._sink.zones),
|
||||||
"zone_count": len(self._sink.zones),
|
"zone_count": len(self._sink.zones),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
## Copyright (C) 2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/
|
## Copyright (C) 2026 Solaar Contributors https://pwr-solaar.github.io/Solaar/
|
||||||
##
|
##
|
||||||
## This program is free software; you can redistribute it and/or modify
|
## 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
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
## Copyright (C) 2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/
|
## Copyright (C) 2026 Solaar Contributors https://pwr-solaar.github.io/Solaar/
|
||||||
##
|
##
|
||||||
## This program is free software; you can redistribute it and/or modify
|
## 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
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
## Copyright (C) 2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/
|
## Copyright (C) 2026 Solaar Contributors https://pwr-solaar.github.io/Solaar/
|
||||||
##
|
##
|
||||||
## This program is free software; you can redistribute it and/or modify
|
## 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
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
## Copyright (C) 2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/
|
## Copyright (C) 2026 Solaar Contributors https://pwr-solaar.github.io/Solaar/
|
||||||
##
|
##
|
||||||
## This program is free software; you can redistribute it and/or modify
|
## 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
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
|
@ -27,6 +27,10 @@ from collections.abc import Callable
|
||||||
|
|
||||||
from ..layout import Layout
|
from ..layout import Layout
|
||||||
from . import keyboard_ansi
|
from . import keyboard_ansi
|
||||||
|
from . import keyboard_iso_azerty
|
||||||
|
from . import keyboard_iso_qwerty
|
||||||
|
from . import keyboard_iso_qwertz
|
||||||
|
from . import keyboard_jis
|
||||||
from . import mouse_g502x
|
from . import mouse_g502x
|
||||||
|
|
||||||
# (feature_id, matcher, layout). Matcher receives a `hint` dict the editor
|
# (feature_id, matcher, layout). Matcher receives a `hint` dict the editor
|
||||||
|
|
@ -65,23 +69,74 @@ def _name_contains(*needles: str) -> Callable[[dict], bool]:
|
||||||
return match
|
return match
|
||||||
|
|
||||||
|
|
||||||
# --- Keyboards: distinguish full-size from TKL by presence of a numpad zone.
|
# --- Keyboard region routing ---
|
||||||
# Counting zones is unreliable (G515 reports phantom zones 47, 97, 99-103, 254
|
# Country code → layout family. Codes from HID++ feature 0x4540 KeyboardLayout.
|
||||||
# that diverge from the keycap count).
|
_KEYBOARD_FAMILY_BY_COUNTRY: dict[int, str] = {
|
||||||
|
1: "ansi",
|
||||||
|
# ISO QWERTY (UK + ES/IT/PT/BE/Nordic — same shape, different keycap legends)
|
||||||
|
2: "iso_qwerty",
|
||||||
|
5: "iso_qwerty",
|
||||||
|
8: "iso_qwerty",
|
||||||
|
0x0B: "iso_qwerty",
|
||||||
|
0x0D: "iso_qwerty",
|
||||||
|
0x0E: "iso_qwerty",
|
||||||
|
0x0F: "iso_qwerty",
|
||||||
|
0x16: "iso_qwerty",
|
||||||
|
0x1D: "iso_qwerty",
|
||||||
|
0x21: "iso_qwerty",
|
||||||
|
0x24: "iso_qwerty",
|
||||||
|
# ISO QWERTZ (DE/Swiss)
|
||||||
|
3: "iso_qwertz",
|
||||||
|
7: "iso_qwertz",
|
||||||
|
# ISO AZERTY (FR)
|
||||||
|
4: "iso_azerty",
|
||||||
|
# JIS
|
||||||
|
9: "jis",
|
||||||
|
0x3E: "jis",
|
||||||
|
}
|
||||||
|
|
||||||
|
_FAMILY_LAYOUTS = {
|
||||||
|
"ansi": (keyboard_ansi.LAYOUT_FULL, keyboard_ansi.LAYOUT_TKL),
|
||||||
|
"iso_qwerty": (keyboard_iso_qwerty.LAYOUT_FULL, keyboard_iso_qwerty.LAYOUT_TKL),
|
||||||
|
"iso_qwertz": (keyboard_iso_qwertz.LAYOUT_FULL, keyboard_iso_qwertz.LAYOUT_TKL),
|
||||||
|
"iso_azerty": (keyboard_iso_azerty.LAYOUT_FULL, keyboard_iso_azerty.LAYOUT_TKL),
|
||||||
|
"jis": (keyboard_jis.LAYOUT_FULL, keyboard_jis.LAYOUT_TKL),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def _has_numpad(hint: dict) -> bool:
|
def _has_numpad(hint: dict) -> bool:
|
||||||
|
"""Numpad presence is read from the device's reported zone bitmap rather
|
||||||
|
than counting zones — G515 reports phantom zones (47, 97, 99-103, 254)
|
||||||
|
that diverge from the keycap count.
|
||||||
|
"""
|
||||||
zones = set(hint.get("zones", ()))
|
zones = set(hint.get("zones", ()))
|
||||||
return 80 in zones or 95 in zones
|
return 80 in zones or 95 in zones
|
||||||
|
|
||||||
|
|
||||||
def _is_full_keyboard(hint: dict) -> bool:
|
def _keyboard_family(hint: dict) -> str:
|
||||||
return hint.get("kind") == "keyboard" and _has_numpad(hint)
|
"""Pick a layout family from the device's HID++ keyboard layout country
|
||||||
|
code. Defaults to "ansi" when the code is missing or unknown.
|
||||||
|
"""
|
||||||
|
code = hint.get("keyboard_layout")
|
||||||
|
if code is None:
|
||||||
|
return "ansi"
|
||||||
|
return _KEYBOARD_FAMILY_BY_COUNTRY.get(int(code), "ansi")
|
||||||
|
|
||||||
|
|
||||||
def _is_tkl_keyboard(hint: dict) -> bool:
|
def _keyboard_matcher(family: str, full_size: bool) -> Callable[[dict], bool]:
|
||||||
return hint.get("kind") == "keyboard" and not _has_numpad(hint)
|
def match(hint: dict) -> bool:
|
||||||
|
if hint.get("kind") != "keyboard":
|
||||||
|
return False
|
||||||
|
if _has_numpad(hint) != full_size:
|
||||||
|
return False
|
||||||
|
return _keyboard_family(hint) == family
|
||||||
|
|
||||||
|
return match
|
||||||
|
|
||||||
|
|
||||||
# PER_KEY_LIGHTING_V2 = 0x8081
|
# PER_KEY_LIGHTING_V2 = 0x8081
|
||||||
register_layout(0x8081, _is_full_keyboard, keyboard_ansi.LAYOUT_FULL)
|
for _family, (_full, _tkl) in _FAMILY_LAYOUTS.items():
|
||||||
register_layout(0x8081, _is_tkl_keyboard, keyboard_ansi.LAYOUT_TKL)
|
register_layout(0x8081, _keyboard_matcher(_family, full_size=True), _full)
|
||||||
|
register_layout(0x8081, _keyboard_matcher(_family, full_size=False), _tkl)
|
||||||
|
|
||||||
register_layout(0x8081, _name_contains("G502 X"), mouse_g502x.LAYOUT)
|
register_layout(0x8081, _name_contains("G502 X"), mouse_g502x.LAYOUT)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,230 @@
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
"""Shared building blocks for regional keyboard layouts.
|
||||||
|
|
||||||
|
Each region (ANSI, ISO_QWERTY, ISO_QWERTZ, ISO_AZERTY, JIS) shares the function
|
||||||
|
row, nav-cluster, and numpad blocks; only the main alpha block differs (ANSI
|
||||||
|
includes the row 2 col 13 backslash, ISO doesn't). Regional label overrides on
|
||||||
|
top of either main block produce the final layout.
|
||||||
|
|
||||||
|
Cell positions and groupings adapted from OpenRGB's KeyboardLayoutManager.
|
||||||
|
Zone IDs are firmware values reported by Logitech HID++ feature 0x8081
|
||||||
|
(PER_KEY_LIGHTING_V2).
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from ..layout import Cell
|
||||||
|
from ..layout import Layout
|
||||||
|
|
||||||
|
# --- Function row: ESC + F1..F12 (shared across all regions).
|
||||||
|
FN_ROW: tuple[Cell, ...] = (
|
||||||
|
Cell(zone_id=38, row=0, col=0, group="fn_row", label="Esc"),
|
||||||
|
Cell(zone_id=55, row=0, col=2, group="fn_row", label="F1"),
|
||||||
|
Cell(zone_id=56, row=0, col=3, group="fn_row", label="F2"),
|
||||||
|
Cell(zone_id=57, row=0, col=4, group="fn_row", label="F3"),
|
||||||
|
Cell(zone_id=58, row=0, col=5, group="fn_row", label="F4"),
|
||||||
|
Cell(zone_id=59, row=0, col=6, group="fn_row", label="F5"),
|
||||||
|
Cell(zone_id=60, row=0, col=7, group="fn_row", label="F6"),
|
||||||
|
Cell(zone_id=61, row=0, col=8, group="fn_row", label="F7"),
|
||||||
|
Cell(zone_id=62, row=0, col=9, group="fn_row", label="F8"),
|
||||||
|
Cell(zone_id=63, row=0, col=10, group="fn_row", label="F9"),
|
||||||
|
Cell(zone_id=64, row=0, col=11, group="fn_row", label="F10"),
|
||||||
|
Cell(zone_id=65, row=0, col=12, group="fn_row", label="F11"),
|
||||||
|
Cell(zone_id=66, row=0, col=13, group="fn_row", label="F12"),
|
||||||
|
)
|
||||||
|
|
||||||
|
# --- Nav cluster + arrows (shared).
|
||||||
|
EXTRAS: tuple[Cell, ...] = (
|
||||||
|
Cell(zone_id=67, row=0, col=14, group="extras", label="PrtSc"),
|
||||||
|
Cell(zone_id=68, row=0, col=15, group="extras", label="ScrLk"),
|
||||||
|
Cell(zone_id=69, row=0, col=16, group="extras", label="Pause"),
|
||||||
|
Cell(zone_id=70, row=1, col=14, group="extras", label="Ins"),
|
||||||
|
Cell(zone_id=71, row=1, col=15, group="extras", label="Home"),
|
||||||
|
Cell(zone_id=72, row=1, col=16, group="extras", label="PgUp"),
|
||||||
|
Cell(zone_id=73, row=2, col=14, group="extras", label="Del"),
|
||||||
|
Cell(zone_id=74, row=2, col=15, group="extras", label="End"),
|
||||||
|
Cell(zone_id=75, row=2, col=16, group="extras", label="PgDn"),
|
||||||
|
Cell(zone_id=79, row=4, col=15, group="extras", label="↑"),
|
||||||
|
Cell(zone_id=77, row=5, col=14, group="extras", label="←"),
|
||||||
|
Cell(zone_id=78, row=5, col=15, group="extras", label="↓"),
|
||||||
|
Cell(zone_id=76, row=5, col=16, group="extras", label="→"),
|
||||||
|
)
|
||||||
|
|
||||||
|
# --- Numpad block (only on full-size keyboards).
|
||||||
|
NUMPAD: tuple[Cell, ...] = (
|
||||||
|
Cell(zone_id=80, row=1, col=17, group="numpad", label="Num"),
|
||||||
|
Cell(zone_id=81, row=1, col=18, group="numpad", label="/"),
|
||||||
|
Cell(zone_id=82, row=1, col=19, group="numpad", label="*"),
|
||||||
|
Cell(zone_id=83, row=1, col=20, group="numpad", label="-"),
|
||||||
|
Cell(zone_id=92, row=2, col=17, group="numpad", label="7"),
|
||||||
|
Cell(zone_id=93, row=2, col=18, group="numpad", label="8"),
|
||||||
|
Cell(zone_id=94, row=2, col=19, group="numpad", label="9"),
|
||||||
|
Cell(zone_id=84, row=2, col=20, height=2.0, group="numpad", label="+"),
|
||||||
|
Cell(zone_id=89, row=3, col=17, group="numpad", label="4"),
|
||||||
|
Cell(zone_id=90, row=3, col=18, group="numpad", label="5"),
|
||||||
|
Cell(zone_id=91, row=3, col=19, group="numpad", label="6"),
|
||||||
|
Cell(zone_id=86, row=4, col=17, group="numpad", label="1"),
|
||||||
|
Cell(zone_id=87, row=4, col=18, group="numpad", label="2"),
|
||||||
|
Cell(zone_id=88, row=4, col=19, group="numpad", label="3"),
|
||||||
|
Cell(zone_id=85, row=4, col=20, height=2.0, group="numpad", label="Enter"),
|
||||||
|
Cell(zone_id=95, row=5, col=17, width=2.0, group="numpad", label="0"),
|
||||||
|
Cell(zone_id=96, row=5, col=19, group="numpad", label="."),
|
||||||
|
)
|
||||||
|
|
||||||
|
# --- Main alpha block, ANSI (104-key). Includes row 2 col 13 backslash and
|
||||||
|
# omits POUND (row 3 col 12) + ISO_BACKSLASH (row 4 col 1).
|
||||||
|
MAIN_ANSI: tuple[Cell, ...] = (
|
||||||
|
# Row 1: backtick + numbers + minus/equals + backspace
|
||||||
|
Cell(zone_id=50, row=1, col=0, group="main", label="`"),
|
||||||
|
Cell(zone_id=27, row=1, col=1, group="main", label="1"),
|
||||||
|
Cell(zone_id=28, row=1, col=2, group="main", label="2"),
|
||||||
|
Cell(zone_id=29, row=1, col=3, group="main", label="3"),
|
||||||
|
Cell(zone_id=30, row=1, col=4, group="main", label="4"),
|
||||||
|
Cell(zone_id=31, row=1, col=5, group="main", label="5"),
|
||||||
|
Cell(zone_id=32, row=1, col=6, group="main", label="6"),
|
||||||
|
Cell(zone_id=33, row=1, col=7, group="main", label="7"),
|
||||||
|
Cell(zone_id=34, row=1, col=8, group="main", label="8"),
|
||||||
|
Cell(zone_id=35, row=1, col=9, group="main", label="9"),
|
||||||
|
Cell(zone_id=36, row=1, col=10, group="main", label="0"),
|
||||||
|
Cell(zone_id=42, row=1, col=11, group="main", label="-"),
|
||||||
|
Cell(zone_id=43, row=1, col=12, group="main", label="="),
|
||||||
|
Cell(zone_id=39, row=1, col=13, group="main", label="Bksp"),
|
||||||
|
# Row 2: tab + qwerty + brackets + backslash
|
||||||
|
Cell(zone_id=40, row=2, col=0, group="main", label="Tab"),
|
||||||
|
Cell(zone_id=17, row=2, col=1, group="main", label="Q"),
|
||||||
|
Cell(zone_id=23, row=2, col=2, group="main", label="W"),
|
||||||
|
Cell(zone_id=5, row=2, col=3, group="main", label="E"),
|
||||||
|
Cell(zone_id=18, row=2, col=4, group="main", label="R"),
|
||||||
|
Cell(zone_id=20, row=2, col=5, group="main", label="T"),
|
||||||
|
Cell(zone_id=25, row=2, col=6, group="main", label="Y"),
|
||||||
|
Cell(zone_id=21, row=2, col=7, group="main", label="U"),
|
||||||
|
Cell(zone_id=9, row=2, col=8, group="main", label="I"),
|
||||||
|
Cell(zone_id=15, row=2, col=9, group="main", label="O"),
|
||||||
|
Cell(zone_id=16, row=2, col=10, group="main", label="P"),
|
||||||
|
Cell(zone_id=44, row=2, col=11, group="main", label="["),
|
||||||
|
Cell(zone_id=45, row=2, col=12, group="main", label="]"),
|
||||||
|
Cell(zone_id=46, row=2, col=13, group="main", label="\\"),
|
||||||
|
# Row 3: caps + asdf-row + semi/quote + enter
|
||||||
|
Cell(zone_id=54, row=3, col=0, group="main", label="Caps"),
|
||||||
|
Cell(zone_id=1, row=3, col=1, group="main", label="A"),
|
||||||
|
Cell(zone_id=19, row=3, col=2, group="main", label="S"),
|
||||||
|
Cell(zone_id=4, row=3, col=3, group="main", label="D"),
|
||||||
|
Cell(zone_id=6, row=3, col=4, group="main", label="F"),
|
||||||
|
Cell(zone_id=7, row=3, col=5, group="main", label="G"),
|
||||||
|
Cell(zone_id=8, row=3, col=6, group="main", label="H"),
|
||||||
|
Cell(zone_id=10, row=3, col=7, group="main", label="J"),
|
||||||
|
Cell(zone_id=11, row=3, col=8, group="main", label="K"),
|
||||||
|
Cell(zone_id=12, row=3, col=9, group="main", label="L"),
|
||||||
|
Cell(zone_id=48, row=3, col=10, group="main", label=";"),
|
||||||
|
Cell(zone_id=49, row=3, col=11, group="main", label="'"),
|
||||||
|
Cell(zone_id=37, row=3, col=13, group="main", label="Enter"),
|
||||||
|
# Row 4: shift + zxcv-row + comma/period/slash + rshift
|
||||||
|
Cell(zone_id=105, row=4, col=0, group="main", label="Shift"),
|
||||||
|
Cell(zone_id=26, row=4, col=2, group="main", label="Z"),
|
||||||
|
Cell(zone_id=24, row=4, col=3, group="main", label="X"),
|
||||||
|
Cell(zone_id=3, row=4, col=4, group="main", label="C"),
|
||||||
|
Cell(zone_id=22, row=4, col=5, group="main", label="V"),
|
||||||
|
Cell(zone_id=2, row=4, col=6, group="main", label="B"),
|
||||||
|
Cell(zone_id=14, row=4, col=7, group="main", label="N"),
|
||||||
|
Cell(zone_id=13, row=4, col=8, group="main", label="M"),
|
||||||
|
Cell(zone_id=51, row=4, col=9, group="main", label=","),
|
||||||
|
Cell(zone_id=52, row=4, col=10, group="main", label="."),
|
||||||
|
Cell(zone_id=53, row=4, col=11, group="main", label="/"),
|
||||||
|
Cell(zone_id=109, row=4, col=13, group="main", label="Shift"),
|
||||||
|
# Row 5: bottom row. Space spans cols 3..9 visually.
|
||||||
|
Cell(zone_id=104, row=5, col=0, group="main", label="Ctrl"),
|
||||||
|
Cell(zone_id=107, row=5, col=1, group="main", label="Win"),
|
||||||
|
Cell(zone_id=106, row=5, col=2, group="main", label="Alt"),
|
||||||
|
Cell(zone_id=41, row=5, col=3, width=7.0, group="main", label="Space"),
|
||||||
|
Cell(zone_id=110, row=5, col=10, group="main", label="AltGr"),
|
||||||
|
Cell(zone_id=111, row=5, col=11, group="main", label="Win"),
|
||||||
|
Cell(zone_id=98, row=5, col=12, group="main", label="Menu"),
|
||||||
|
Cell(zone_id=108, row=5, col=13, group="main", label="Ctrl"),
|
||||||
|
)
|
||||||
|
|
||||||
|
# --- Main alpha block, ISO. Same as ANSI minus the row 2 col 13 backslash;
|
||||||
|
# on ISO that position is the top half of the L-shape Enter, addressed
|
||||||
|
# by zone 37 (the main Enter cell at row 3 col 13). Zone 46 is silently
|
||||||
|
# unaddressable on ISO layouts — same limitation as OpenRGB's UI.
|
||||||
|
MAIN_ISO: tuple[Cell, ...] = tuple(c for c in MAIN_ANSI if not (c.row == 2 and c.col == 13))
|
||||||
|
|
||||||
|
# --- Curated allowlist for unmapped device zones surfaced in the bottom strip.
|
||||||
|
# G-keys, logo, media, brightness — the canonical "extras" Logitech firmware
|
||||||
|
# actually addresses. Phantom zones (e.g. G515's 47, 97, 99-103, 254) drop.
|
||||||
|
EXTRAS_ALLOWLIST: frozenset[int] = frozenset(
|
||||||
|
{
|
||||||
|
153, # Brightness
|
||||||
|
155, # Play/Pause
|
||||||
|
156, # Mute
|
||||||
|
157, # Next
|
||||||
|
158, # Previous
|
||||||
|
180, # G1
|
||||||
|
181, # G2
|
||||||
|
182, # G3
|
||||||
|
183, # G4
|
||||||
|
184, # G5
|
||||||
|
210, # Logo
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _relabel(cells: tuple[Cell, ...], overrides: dict[int, str]) -> tuple[Cell, ...]:
|
||||||
|
"""Return a new tuple where any cell whose zone_id is in `overrides` has
|
||||||
|
its label replaced. Unaffected cells pass through unchanged.
|
||||||
|
"""
|
||||||
|
if not overrides:
|
||||||
|
return cells
|
||||||
|
return tuple(
|
||||||
|
Cell(
|
||||||
|
zone_id=c.zone_id,
|
||||||
|
row=c.row,
|
||||||
|
col=c.col,
|
||||||
|
width=c.width,
|
||||||
|
height=c.height,
|
||||||
|
group=c.group,
|
||||||
|
label=overrides[c.zone_id] if c.zone_id in overrides else c.label,
|
||||||
|
x=c.x,
|
||||||
|
y=c.y,
|
||||||
|
)
|
||||||
|
for c in cells
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def build_layout(
|
||||||
|
main_cells: tuple[Cell, ...],
|
||||||
|
*,
|
||||||
|
include_numpad: bool,
|
||||||
|
label_overrides: dict[int, str] | None = None,
|
||||||
|
description: str = "",
|
||||||
|
) -> Layout:
|
||||||
|
"""Assemble a regional keyboard layout from a chosen main block + the
|
||||||
|
shared fn-row / extras / (optionally) numpad blocks. Apply per-zone
|
||||||
|
label overrides to every cell whose zone matches.
|
||||||
|
"""
|
||||||
|
cells = FN_ROW + main_cells + EXTRAS
|
||||||
|
if include_numpad:
|
||||||
|
cells = cells + NUMPAD
|
||||||
|
cells = _relabel(cells, label_overrides or {})
|
||||||
|
cols = 21 if include_numpad else 17
|
||||||
|
return Layout(
|
||||||
|
cells=cells,
|
||||||
|
rows=6,
|
||||||
|
cols=cols,
|
||||||
|
extra_zones=EXTRAS_ALLOWLIST,
|
||||||
|
description=description,
|
||||||
|
)
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
## Copyright (C) 2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/
|
## Copyright (C) 2026 Solaar Contributors https://pwr-solaar.github.io/Solaar/
|
||||||
##
|
##
|
||||||
## This program is free software; you can redistribute it and/or modify
|
## 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
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
|
@ -20,177 +20,23 @@ Cell positions and groupings derived from OpenRGB's KeyboardLayoutManager
|
||||||
(KeyboardLayoutManager.cpp), Copyright (C) Chris M (Dr_No), licensed under
|
(KeyboardLayoutManager.cpp), Copyright (C) Chris M (Dr_No), licensed under
|
||||||
GPL-2.0-or-later. This file ports the static ANSI data only; the runtime
|
GPL-2.0-or-later. This file ports the static ANSI data only; the runtime
|
||||||
opcode interpreter for regional overlays is intentionally not included.
|
opcode interpreter for regional overlays is intentionally not included.
|
||||||
|
|
||||||
Zone IDs are the firmware values reported by Logitech HID++ feature 0x8081
|
|
||||||
(PER_KEY_LIGHTING_V2).
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from ..layout import Cell
|
|
||||||
from ..layout import Layout
|
from ..layout import Layout
|
||||||
|
from ._keyboard_base import MAIN_ANSI
|
||||||
|
from ._keyboard_base import build_layout
|
||||||
|
|
||||||
# Main alpha block (KLM keyboard_zone_main, ANSI variant).
|
LAYOUT_FULL: Layout = build_layout(
|
||||||
# ANSI removes the ISO backslash (row 4 col 1) and POUND (row 3 col 12).
|
MAIN_ANSI,
|
||||||
_MAIN: tuple[Cell, ...] = (
|
include_numpad=True,
|
||||||
# Row 1: backtick + numbers + minus/equals + backspace
|
|
||||||
Cell(zone_id=50, row=1, col=0, group="main", label="`"),
|
|
||||||
Cell(zone_id=27, row=1, col=1, group="main", label="1"),
|
|
||||||
Cell(zone_id=28, row=1, col=2, group="main", label="2"),
|
|
||||||
Cell(zone_id=29, row=1, col=3, group="main", label="3"),
|
|
||||||
Cell(zone_id=30, row=1, col=4, group="main", label="4"),
|
|
||||||
Cell(zone_id=31, row=1, col=5, group="main", label="5"),
|
|
||||||
Cell(zone_id=32, row=1, col=6, group="main", label="6"),
|
|
||||||
Cell(zone_id=33, row=1, col=7, group="main", label="7"),
|
|
||||||
Cell(zone_id=34, row=1, col=8, group="main", label="8"),
|
|
||||||
Cell(zone_id=35, row=1, col=9, group="main", label="9"),
|
|
||||||
Cell(zone_id=36, row=1, col=10, group="main", label="0"),
|
|
||||||
Cell(zone_id=42, row=1, col=11, group="main", label="-"),
|
|
||||||
Cell(zone_id=43, row=1, col=12, group="main", label="="),
|
|
||||||
Cell(zone_id=39, row=1, col=13, group="main", label="Bksp"),
|
|
||||||
# Row 2: tab + qwerty + brackets + backslash
|
|
||||||
Cell(zone_id=40, row=2, col=0, group="main", label="Tab"),
|
|
||||||
Cell(zone_id=17, row=2, col=1, group="main", label="Q"),
|
|
||||||
Cell(zone_id=23, row=2, col=2, group="main", label="W"),
|
|
||||||
Cell(zone_id=5, row=2, col=3, group="main", label="E"),
|
|
||||||
Cell(zone_id=18, row=2, col=4, group="main", label="R"),
|
|
||||||
Cell(zone_id=20, row=2, col=5, group="main", label="T"),
|
|
||||||
Cell(zone_id=25, row=2, col=6, group="main", label="Y"),
|
|
||||||
Cell(zone_id=21, row=2, col=7, group="main", label="U"),
|
|
||||||
Cell(zone_id=9, row=2, col=8, group="main", label="I"),
|
|
||||||
Cell(zone_id=15, row=2, col=9, group="main", label="O"),
|
|
||||||
Cell(zone_id=16, row=2, col=10, group="main", label="P"),
|
|
||||||
Cell(zone_id=44, row=2, col=11, group="main", label="["),
|
|
||||||
Cell(zone_id=45, row=2, col=12, group="main", label="]"),
|
|
||||||
Cell(zone_id=46, row=2, col=13, group="main", label="\\"),
|
|
||||||
# Row 3: caps + asdf-row + semi/quote + enter
|
|
||||||
Cell(zone_id=54, row=3, col=0, group="main", label="Caps"),
|
|
||||||
Cell(zone_id=1, row=3, col=1, group="main", label="A"),
|
|
||||||
Cell(zone_id=19, row=3, col=2, group="main", label="S"),
|
|
||||||
Cell(zone_id=4, row=3, col=3, group="main", label="D"),
|
|
||||||
Cell(zone_id=6, row=3, col=4, group="main", label="F"),
|
|
||||||
Cell(zone_id=7, row=3, col=5, group="main", label="G"),
|
|
||||||
Cell(zone_id=8, row=3, col=6, group="main", label="H"),
|
|
||||||
Cell(zone_id=10, row=3, col=7, group="main", label="J"),
|
|
||||||
Cell(zone_id=11, row=3, col=8, group="main", label="K"),
|
|
||||||
Cell(zone_id=12, row=3, col=9, group="main", label="L"),
|
|
||||||
Cell(zone_id=48, row=3, col=10, group="main", label=";"),
|
|
||||||
Cell(zone_id=49, row=3, col=11, group="main", label="'"),
|
|
||||||
Cell(zone_id=37, row=3, col=13, group="main", label="Enter"),
|
|
||||||
# Row 4: shift + zxcv-row + comma/period/slash + rshift
|
|
||||||
Cell(zone_id=105, row=4, col=0, group="main", label="Shift"),
|
|
||||||
Cell(zone_id=26, row=4, col=2, group="main", label="Z"),
|
|
||||||
Cell(zone_id=24, row=4, col=3, group="main", label="X"),
|
|
||||||
Cell(zone_id=3, row=4, col=4, group="main", label="C"),
|
|
||||||
Cell(zone_id=22, row=4, col=5, group="main", label="V"),
|
|
||||||
Cell(zone_id=2, row=4, col=6, group="main", label="B"),
|
|
||||||
Cell(zone_id=14, row=4, col=7, group="main", label="N"),
|
|
||||||
Cell(zone_id=13, row=4, col=8, group="main", label="M"),
|
|
||||||
Cell(zone_id=51, row=4, col=9, group="main", label=","),
|
|
||||||
Cell(zone_id=52, row=4, col=10, group="main", label="."),
|
|
||||||
Cell(zone_id=53, row=4, col=11, group="main", label="/"),
|
|
||||||
Cell(zone_id=109, row=4, col=13, group="main", label="Shift"),
|
|
||||||
# Row 5: bottom row. Space spans cols 3..9 visually.
|
|
||||||
Cell(zone_id=104, row=5, col=0, group="main", label="Ctrl"),
|
|
||||||
Cell(zone_id=107, row=5, col=1, group="main", label="Win"),
|
|
||||||
Cell(zone_id=106, row=5, col=2, group="main", label="Alt"),
|
|
||||||
Cell(zone_id=41, row=5, col=3, width=7.0, group="main", label="Space"),
|
|
||||||
Cell(zone_id=110, row=5, col=10, group="main", label="AltGr"),
|
|
||||||
Cell(zone_id=111, row=5, col=11, group="main", label="Win"),
|
|
||||||
Cell(zone_id=98, row=5, col=12, group="main", label="Menu"),
|
|
||||||
Cell(zone_id=108, row=5, col=13, group="main", label="Ctrl"),
|
|
||||||
)
|
|
||||||
|
|
||||||
# Function row (KLM keyboard_zone_fn_row): ESC + F1..F12.
|
|
||||||
_FN_ROW: tuple[Cell, ...] = (
|
|
||||||
Cell(zone_id=38, row=0, col=0, group="fn_row", label="Esc"),
|
|
||||||
Cell(zone_id=55, row=0, col=2, group="fn_row", label="F1"),
|
|
||||||
Cell(zone_id=56, row=0, col=3, group="fn_row", label="F2"),
|
|
||||||
Cell(zone_id=57, row=0, col=4, group="fn_row", label="F3"),
|
|
||||||
Cell(zone_id=58, row=0, col=5, group="fn_row", label="F4"),
|
|
||||||
Cell(zone_id=59, row=0, col=6, group="fn_row", label="F5"),
|
|
||||||
Cell(zone_id=60, row=0, col=7, group="fn_row", label="F6"),
|
|
||||||
Cell(zone_id=61, row=0, col=8, group="fn_row", label="F7"),
|
|
||||||
Cell(zone_id=62, row=0, col=9, group="fn_row", label="F8"),
|
|
||||||
Cell(zone_id=63, row=0, col=10, group="fn_row", label="F9"),
|
|
||||||
Cell(zone_id=64, row=0, col=11, group="fn_row", label="F10"),
|
|
||||||
Cell(zone_id=65, row=0, col=12, group="fn_row", label="F11"),
|
|
||||||
Cell(zone_id=66, row=0, col=13, group="fn_row", label="F12"),
|
|
||||||
)
|
|
||||||
|
|
||||||
# Extras cluster (KLM keyboard_zone_extras): nav block + arrows.
|
|
||||||
_EXTRAS: tuple[Cell, ...] = (
|
|
||||||
Cell(zone_id=67, row=0, col=14, group="extras", label="PrtSc"),
|
|
||||||
Cell(zone_id=68, row=0, col=15, group="extras", label="ScrLk"),
|
|
||||||
Cell(zone_id=69, row=0, col=16, group="extras", label="Pause"),
|
|
||||||
Cell(zone_id=70, row=1, col=14, group="extras", label="Ins"),
|
|
||||||
Cell(zone_id=71, row=1, col=15, group="extras", label="Home"),
|
|
||||||
Cell(zone_id=72, row=1, col=16, group="extras", label="PgUp"),
|
|
||||||
Cell(zone_id=73, row=2, col=14, group="extras", label="Del"),
|
|
||||||
Cell(zone_id=74, row=2, col=15, group="extras", label="End"),
|
|
||||||
Cell(zone_id=75, row=2, col=16, group="extras", label="PgDn"),
|
|
||||||
Cell(zone_id=79, row=4, col=15, group="extras", label="↑"),
|
|
||||||
Cell(zone_id=77, row=5, col=14, group="extras", label="←"),
|
|
||||||
Cell(zone_id=78, row=5, col=15, group="extras", label="↓"),
|
|
||||||
Cell(zone_id=76, row=5, col=16, group="extras", label="→"),
|
|
||||||
)
|
|
||||||
|
|
||||||
# Numpad (KLM keyboard_zone_numpad). NumPad + and Enter span 2 rows tall.
|
|
||||||
_NUMPAD: tuple[Cell, ...] = (
|
|
||||||
Cell(zone_id=80, row=1, col=17, group="numpad", label="Num"),
|
|
||||||
Cell(zone_id=81, row=1, col=18, group="numpad", label="/"),
|
|
||||||
Cell(zone_id=82, row=1, col=19, group="numpad", label="*"),
|
|
||||||
Cell(zone_id=83, row=1, col=20, group="numpad", label="-"),
|
|
||||||
Cell(zone_id=92, row=2, col=17, group="numpad", label="7"),
|
|
||||||
Cell(zone_id=93, row=2, col=18, group="numpad", label="8"),
|
|
||||||
Cell(zone_id=94, row=2, col=19, group="numpad", label="9"),
|
|
||||||
Cell(zone_id=84, row=2, col=20, height=2.0, group="numpad", label="+"),
|
|
||||||
Cell(zone_id=89, row=3, col=17, group="numpad", label="4"),
|
|
||||||
Cell(zone_id=90, row=3, col=18, group="numpad", label="5"),
|
|
||||||
Cell(zone_id=91, row=3, col=19, group="numpad", label="6"),
|
|
||||||
Cell(zone_id=86, row=4, col=17, group="numpad", label="1"),
|
|
||||||
Cell(zone_id=87, row=4, col=18, group="numpad", label="2"),
|
|
||||||
Cell(zone_id=88, row=4, col=19, group="numpad", label="3"),
|
|
||||||
Cell(zone_id=85, row=4, col=20, height=2.0, group="numpad", label="Enter"),
|
|
||||||
Cell(zone_id=95, row=5, col=17, width=2.0, group="numpad", label="0"),
|
|
||||||
Cell(zone_id=96, row=5, col=19, group="numpad", label="."),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# Curated allowlist for unmapped device zones surfaced in the bottom strip.
|
|
||||||
# Mirrors OpenRGB's `hidpp20_key_name_to_zone` extras: brightness, media,
|
|
||||||
# G1-G5, logo. Anything else (e.g. G515 phantoms 47, 97, 99-103, 254) is
|
|
||||||
# dropped by the binder.
|
|
||||||
_EXTRAS_ALLOWLIST: frozenset[int] = frozenset(
|
|
||||||
{
|
|
||||||
153, # Brightness
|
|
||||||
155, # Play/Pause
|
|
||||||
156, # Mute
|
|
||||||
157, # Next
|
|
||||||
158, # Previous
|
|
||||||
180, # G1
|
|
||||||
181, # G2
|
|
||||||
182, # G3
|
|
||||||
183, # G4
|
|
||||||
184, # G5
|
|
||||||
210, # Logo
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
LAYOUT_FULL: Layout = Layout(
|
|
||||||
cells=_FN_ROW + _MAIN + _EXTRAS + _NUMPAD,
|
|
||||||
rows=6,
|
|
||||||
cols=21,
|
|
||||||
extra_zones=_EXTRAS_ALLOWLIST,
|
|
||||||
description="ANSI QWERTY 104-key full-size",
|
description="ANSI QWERTY 104-key full-size",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
LAYOUT_TKL: Layout = Layout(
|
LAYOUT_TKL: Layout = build_layout(
|
||||||
cells=_FN_ROW + _MAIN + _EXTRAS,
|
MAIN_ANSI,
|
||||||
rows=6,
|
include_numpad=False,
|
||||||
cols=17,
|
|
||||||
extra_zones=_EXTRAS_ALLOWLIST,
|
|
||||||
description="ANSI QWERTY tenkeyless",
|
description="ANSI QWERTY tenkeyless",
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
"""ISO AZERTY layout (FR).
|
||||||
|
|
||||||
|
ISO shape plus French label overrides — A↔Q, W↔Z, M repositioned, French
|
||||||
|
digit-row symbols (& é " ' ( - è _ ç à ). Adapted from OpenRGB.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from ..layout import Layout
|
||||||
|
from ._keyboard_base import MAIN_ISO
|
||||||
|
from ._keyboard_base import build_layout
|
||||||
|
|
||||||
|
# zone_id → French label
|
||||||
|
_OVERRIDES: dict[int, str] = {
|
||||||
|
# Row 1 (digit row → French symbols)
|
||||||
|
50: "²", # backtick → super-2
|
||||||
|
27: "&", # 1
|
||||||
|
28: "é", # 2
|
||||||
|
29: '"', # 3
|
||||||
|
30: "'", # 4
|
||||||
|
31: "(", # 5
|
||||||
|
32: "-", # 6
|
||||||
|
33: "è", # 7
|
||||||
|
34: "_", # 8
|
||||||
|
35: "ç", # 9
|
||||||
|
36: "à", # 0
|
||||||
|
42: ")", # minus → close-paren
|
||||||
|
# Row 2 — Q/A and W/Z swaps, brackets relabeled
|
||||||
|
17: "A", # Q-position → A
|
||||||
|
23: "Z", # W-position → Z
|
||||||
|
44: "^", # [-position → caret
|
||||||
|
45: "$", # ]-position → dollar
|
||||||
|
# Row 3 — A → Q; M moves up to ; position
|
||||||
|
1: "Q", # A-position → Q
|
||||||
|
48: "M", # ;-position → M
|
||||||
|
49: "ù", # '-position → ù
|
||||||
|
# Row 4 — Z-position becomes W; comma row shifts
|
||||||
|
26: "W", # Z-position → W
|
||||||
|
13: ",", # M-position → comma
|
||||||
|
51: ";", # ,-position → semicolon
|
||||||
|
52: ":", # .-position → colon
|
||||||
|
53: "!", # /-position → exclamation
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LAYOUT_FULL: Layout = build_layout(
|
||||||
|
MAIN_ISO,
|
||||||
|
include_numpad=True,
|
||||||
|
label_overrides=_OVERRIDES,
|
||||||
|
description="ISO AZERTY (FR) full-size",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
LAYOUT_TKL: Layout = build_layout(
|
||||||
|
MAIN_ISO,
|
||||||
|
include_numpad=False,
|
||||||
|
label_overrides=_OVERRIDES,
|
||||||
|
description="ISO AZERTY (FR) tenkeyless",
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
"""ISO_QWERTY keyboard layouts (UK English ISO + other QWERTY ISO regions).
|
||||||
|
|
||||||
|
Same English keycap legends as ANSI; differs only in shape — the row 2 col 13
|
||||||
|
backslash on ANSI doesn't exist on ISO (that position is the upper half of the
|
||||||
|
L-shape Enter, addressed by zone 37). Used for UK and any other region whose
|
||||||
|
country code maps to "iso_qwerty" without a more specific layout (Spanish,
|
||||||
|
Italian, Portuguese, Belgian, Nordic — those keyboards have the same shape
|
||||||
|
as UK ISO; only their physical keycap legends differ, which our painter
|
||||||
|
doesn't reproduce verbatim).
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from ..layout import Layout
|
||||||
|
from ._keyboard_base import MAIN_ISO
|
||||||
|
from ._keyboard_base import build_layout
|
||||||
|
|
||||||
|
LAYOUT_FULL: Layout = build_layout(
|
||||||
|
MAIN_ISO,
|
||||||
|
include_numpad=True,
|
||||||
|
description="ISO QWERTY 103-key full-size",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
LAYOUT_TKL: Layout = build_layout(
|
||||||
|
MAIN_ISO,
|
||||||
|
include_numpad=False,
|
||||||
|
description="ISO QWERTY tenkeyless",
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
"""ISO QWERTZ layout (DE / CH).
|
||||||
|
|
||||||
|
ISO shape plus German label overrides (Y/Z swap, Ü/Ö/Ä/ß placement).
|
||||||
|
Adapted from OpenRGB.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from ..layout import Layout
|
||||||
|
from ._keyboard_base import MAIN_ISO
|
||||||
|
from ._keyboard_base import build_layout
|
||||||
|
|
||||||
|
# zone_id → German label
|
||||||
|
_OVERRIDES: dict[int, str] = {
|
||||||
|
50: "^", # row 1 col 0 — caret/degree (DE keycap)
|
||||||
|
42: "ß", # row 1 col 11 — eszett
|
||||||
|
43: "´", # row 1 col 12 — acute accent
|
||||||
|
25: "Z", # row 2 col 6 — Y/Z swap
|
||||||
|
44: "Ü", # row 2 col 11
|
||||||
|
45: "+", # row 2 col 12
|
||||||
|
48: "Ö", # row 3 col 10
|
||||||
|
49: "Ä", # row 3 col 11
|
||||||
|
26: "Y", # row 4 col 2 — Y/Z swap
|
||||||
|
53: "-", # row 4 col 11
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LAYOUT_FULL: Layout = build_layout(
|
||||||
|
MAIN_ISO,
|
||||||
|
include_numpad=True,
|
||||||
|
label_overrides=_OVERRIDES,
|
||||||
|
description="ISO QWERTZ (DE/CH) full-size",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
LAYOUT_TKL: Layout = build_layout(
|
||||||
|
MAIN_ISO,
|
||||||
|
include_numpad=False,
|
||||||
|
label_overrides=_OVERRIDES,
|
||||||
|
description="ISO QWERTZ (DE/CH) tenkeyless",
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
"""JIS layout (JP).
|
||||||
|
|
||||||
|
ISO shape with Japanese keycap relabels for the bracket / colon positions.
|
||||||
|
Adapted from OpenRGB. JIS keyboards also have additional kana-control keys
|
||||||
|
near the spacebar (henkan/muhenkan/kana) that aren't represented here —
|
||||||
|
matches OpenRGB's coverage.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from ..layout import Layout
|
||||||
|
from ._keyboard_base import MAIN_ISO
|
||||||
|
from ._keyboard_base import build_layout
|
||||||
|
|
||||||
|
# zone_id → JIS label
|
||||||
|
_OVERRIDES: dict[int, str] = {
|
||||||
|
44: "@", # row 2 col 11 — bracket-position becomes at-sign
|
||||||
|
45: "[", # row 2 col 12 — bracket shifts left
|
||||||
|
49: ":", # row 3 col 11 — quote-position becomes colon
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LAYOUT_FULL: Layout = build_layout(
|
||||||
|
MAIN_ISO,
|
||||||
|
include_numpad=True,
|
||||||
|
label_overrides=_OVERRIDES,
|
||||||
|
description="JIS (JP) full-size",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
LAYOUT_TKL: Layout = build_layout(
|
||||||
|
MAIN_ISO,
|
||||||
|
include_numpad=False,
|
||||||
|
label_overrides=_OVERRIDES,
|
||||||
|
description="JIS (JP) tenkeyless",
|
||||||
|
)
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
## Copyright (C) 2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/
|
## Copyright (C) 2026 Solaar Contributors https://pwr-solaar.github.io/Solaar/
|
||||||
##
|
##
|
||||||
## This program is free software; you can redistribute it and/or modify
|
## 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
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
## Copyright (C) 2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/
|
## Copyright (C) 2026 Solaar Contributors https://pwr-solaar.github.io/Solaar/
|
||||||
##
|
##
|
||||||
## This program is free software; you can redistribute it and/or modify
|
## 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
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
## Copyright (C) 2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/
|
## Copyright (C) 2026 Solaar Contributors https://pwr-solaar.github.io/Solaar/
|
||||||
##
|
##
|
||||||
## This program is free software; you can redistribute it and/or modify
|
## 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
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
## Copyright (C) 2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/
|
## Copyright (C) 2026 Solaar Contributors https://pwr-solaar.github.io/Solaar/
|
||||||
##
|
##
|
||||||
## This program is free software; you can redistribute it and/or modify
|
## 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
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue