Files
Cachy-Predator-Keyboard/predator_control/layout.py
T
MrBlakeandClaude Opus 5.5 ea7ce49452 Initial release of Predator Control 1.0.0
PredatorSense replacement for the Acer Predator Helios 16 (PH16-71) on Linux:

- predatord: root service for profiles, fans (incl. temperature curves),
  keyboard RGB, lightbar and battery health; restores settings at boot
  and after resume, switches profiles on AC/battery changes
- Qt desktop app with dashboard, fan curve editor and per-key RGB studio
- Plasma 6 panel widget with quick controls
- predatorctl command line tool
- predator_wmi kernel module (DKMS) as a minimal WMI method bridge

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 18:38:14 +02:00

116 lines
4.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Physical keyboard layouts of the PH16-71 mapped to the 128 LED cells of the keyboard MCU.
cell = column * 6 + row (row 0 = bottom row). The mapping is based on the keymap
recorded by the Venator project (GPL-2.0). Corrections can be placed in
~/.config/predator-control/layout.json as {"<label>": <cell>}.
Each key: (label, cell, x, y, width, height) in key units.
"""
import json
import locale
import os
def _row(y, keys, x0=0.0, h=1.0):
out, x = [], x0
for label, cell, w in keys:
if label is not None:
out.append((label, cell, x, y, w, h))
x += w
return out
def _build(variant):
iso = variant == "iso-de"
keys = []
fw = 15.0 / 16
keys += _row(0, [("Esc", 5, fw)] + [("F%d" % i, 5 + 6 * i, fw) for i in range(1, 13)] +
[("PrtSc", 83, fw), ("Ins", 89, fw), ("Del", 95, fw)], h=0.8)
y = 0.9
nums = [str((i + 1) % 10) for i in range(10)]
keys += _row(y, [("^" if iso else "`", 4, 1)] + [(n, 10 + 6 * i, 1) for i, n in enumerate(nums)] +
[("ß" if iso else "-", 70, 1), ("´" if iso else "=", 76, 1), ("⌫", 82, 2)])
y += 1
top = "QWERTZUIOP" if iso else "QWERTYUIOP"
if iso:
keys += _row(y, [("Tab", 3, 1.5)] + [(c, 15 + 6 * i, 1) for i, c in enumerate(top)] +
[("Ü", 75, 1), ("+", 81, 1), ("↵", 86, 1.5)])
else:
keys += _row(y, [("Tab", 3, 1.5)] + [(c, 15 + 6 * i, 1) for i, c in enumerate(top)] +
[("[", 75, 1), ("]", 81, 1), ("\\", 87, 1.5)])
y += 1
if iso:
keys += _row(y, [("Caps", 2, 1.75)] + [(c, 20 + 6 * i, 1) for i, c in enumerate("ASDFGHJKL")] +
[("Ö", 74, 1), ("Ä", 80, 1), ("#", 87, 1), ("↵", 86, 1.25)])
else:
keys += _row(y, [("Caps", 2, 1.75)] + [(c, 20 + 6 * i, 1) for i, c in enumerate("ASDFGHJKL")] +
[(";", 74, 1), ("'", 80, 1), ("Enter", 86, 2.25)])
y += 1
if iso:
keys += _row(y, [("⇧", 1, 1.25), ("<", 13, 1)] + [(c, 19 + 6 * i, 1) for i, c in enumerate("YXCVBNM")] +
[(",", 61, 1), (".", 67, 1), ("-", 73, 1), ("⇧", 85, 2.75)])
else:
keys += _row(y, [("⇧", 1, 2.25)] + [(c, 19 + 6 * i, 1) for i, c in enumerate("ZXCVBNM")] +
[(",", 61, 1), (".", 67, 1), ("/", 73, 1), ("⇧", 85, 2.75)])
y += 1
keys += _row(y, [("Ctrl", 0, 1.25), ("Fn", 6, 1), ("⊞", 12, 1), ("Alt", 18, 1),
("", 24, 5), ("AltGr" if iso else "Alt", 60, 1), ("☰", 72, 1), ("Ctrl", 78, 1),
("←", 84, 1)])
keys.append(("↑", 90, 13.25, y, 1, 0.5))
keys.append(("↓", 96, 13.25, y + 0.5, 1, 0.5))
keys.append(("→", 102, 14.25, y, 1, 1))
# column of special keys between the main block and the numpad
sx = 15.6
for i, (lbl, cell) in enumerate([("⏻", 107), ("P", 106), ("⏯", 105), ("⏮", 104), ("⏭", 103)]):
keys.append((lbl, cell, sx, 0 if i == 0 else 0.9 + (i - 1), 1, 0.8 if i == 0 else 1))
# numpad
nx = 16.85
y = 0.9
keys += _row(y, [("Num", 112, 1), ("/", 118, 1), ("*", 124, 1), ("−", 123, 1)], x0=nx)
keys += _row(y + 1, [("7", 99, 1), ("8", 111, 1), ("9", 117, 1)], x0=nx)
keys.append(("+", 122, nx + 3, y + 1, 1, 2))
keys += _row(y + 2, [("4", 101, 1), ("5", 110, 1), ("6", 116, 1)], x0=nx)
keys += _row(y + 3, [("1", 100, 1), ("2", 109, 1), ("3", 115, 1)], x0=nx)
keys.append(("↵", 120, nx + 3, y + 3, 1, 2))
keys += _row(y + 4, [("0", 108, 2), ("," if iso else ".", 114, 1)], x0=nx)
return keys
VARIANTS = {"iso-de": "German (ISO)", "ansi-us": "US (ANSI)"}
def default_variant():
lang = (os.environ.get("LANG") or locale.getlocale()[0] or "").lower()
return "iso-de" if lang.startswith(("de", "at", "ch")) else "ansi-us"
def keys(variant=None):
ks = _build(variant or default_variant())
path = os.path.expanduser("~/.config/predator-control/layout.json")
try:
with open(path) as f:
over = json.load(f)
ks = [(l, over.get(l, c), x, y, w, h) for (l, c, x, y, w, h) in ks]
except (OSError, ValueError):
pass
return ks
def size(ks):
return max(k[2] + k[4] for k in ks), max(k[3] + k[5] for k in ks)
# cell groups for quick selection in the RGB studio
GROUPS = {
"WASD": [21, 20, 26, 32],
"Arrows": [84, 90, 96, 102],
"F-row": [5 + 6 * i for i in range(13)] + [83, 89, 95],
"Number row": [4] + [10 + 6 * i for i in range(10)] + [70, 76, 82],
"Numpad": [112, 118, 124, 123, 99, 111, 117, 122, 101, 110, 116, 100, 109, 115, 120, 108, 114],
"Special keys": [107, 106, 105, 104, 103],
"Modifiers": [0, 6, 12, 18, 60, 72, 78, 1, 85, 2, 3],
}