Lighting as a macropad subcommand; knobless pads start in the flat view
Running the backend module directly made runpy warn, since the package had already imported it. The lighting commands now sit with doctor, state and write. A pad without knobs lies flat on the desk, so it is drawn flat unless an orientation has been chosen. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -448,7 +448,7 @@ What the hardware can and can't do:
|
|||||||
| Key sequences | Yes, up to 10 keys with **one** set of modifiers for the whole sequence, no delays |
|
| Key sequences | Yes, up to 10 keys with **one** set of modifiers for the whole sequence, no delays |
|
||||||
| Mouse actions | The pad can, the app doesn't offer them yet |
|
| Mouse actions | The pad can, the app doesn't offer them yet |
|
||||||
| Layers | None |
|
| Layers | None |
|
||||||
| Lighting | 15 modes and a custom colour, from the command line: `python3 -m macropad_gui.backends.itprotech rgb "press: blue"` |
|
| Lighting | 15 modes and a custom colour, from the command line: `macropad lighting rgb "press: blue"`, `macropad lighting colour ff8800`, `macropad lighting show` |
|
||||||
|
|
||||||
Other sizes under vendor `8808` are found and offered **Ask the pad what it is**, which reads only. If the pad answers like an MK-09 does, the app adopts it. If you have one, a report from that button is all it takes to add it to the confirmed list.
|
Other sizes under vendor `8808` are found and offered **Ask the pad what it is**, which reads only. If the pad answers like an MK-09 does, the app adopts it. If you have one, a report from that button is all it takes to add it to the confirmed list.
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ The rest are for poking at the core from a terminal:
|
|||||||
python3 -m macropad_gui state
|
python3 -m macropad_gui state
|
||||||
python3 -m macropad_gui write key1 "ctrl+c"
|
python3 -m macropad_gui write key1 "ctrl+c"
|
||||||
python3 -m macropad_gui write dial1-right --media volumeup
|
python3 -m macropad_gui write dial1-right --media volumeup
|
||||||
|
python3 -m macropad_gui lighting show ITPROTECH pads only
|
||||||
|
python3 -m macropad_gui lighting rgb "press: blue"
|
||||||
|
python3 -m macropad_gui lighting colour ff8800
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
@@ -100,6 +103,46 @@ def cmd_write(a):
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def cmd_lighting(a):
|
||||||
|
"""Lighting mode and custom colour on pads whose backend has them."""
|
||||||
|
d = core.diagnose(keyd=False)
|
||||||
|
backend = core.backend_for(d.device) if d.device else None
|
||||||
|
if not d.device or not hasattr(backend, "set_lighting"):
|
||||||
|
print(f"{R}No pad with lighting control found.{O} "
|
||||||
|
+ (d.headline if not d.device else f"{backend.name} has no lighting here."))
|
||||||
|
return 1
|
||||||
|
from .backends import itprotech_proto as wire
|
||||||
|
modes = ", ".join(f"{i} {m}" for i, m in enumerate(wire.RGB_MODES))
|
||||||
|
if a.what == "show":
|
||||||
|
mode, colour = backend.lighting(d.device)
|
||||||
|
name = wire.RGB_MODES[mode] if mode is not None and mode < len(wire.RGB_MODES) else "?"
|
||||||
|
hexcol = "#%02x%02x%02x" % colour if colour else "?"
|
||||||
|
print(f"lighting {mode} ({name}), custom colour {hexcol}")
|
||||||
|
return 0
|
||||||
|
if not d.ready:
|
||||||
|
return cmd_doctor(None)
|
||||||
|
if a.what == "rgb":
|
||||||
|
want = (a.value or "").lower()
|
||||||
|
if want.isdigit() and int(want) < len(wire.RGB_MODES):
|
||||||
|
mode = int(want)
|
||||||
|
elif want in wire.RGB_MODES:
|
||||||
|
mode = wire.RGB_MODES.index(want)
|
||||||
|
else:
|
||||||
|
print(f"Pick a mode: {modes}")
|
||||||
|
return 1
|
||||||
|
backend.set_lighting(d.device, mode)
|
||||||
|
print(f"{G}Lighting set to {mode} ({wire.RGB_MODES[mode]}).{O}")
|
||||||
|
return 0
|
||||||
|
hexcol = (a.value or "").lstrip("#")
|
||||||
|
if len(hexcol) != 6 or any(c not in "0123456789abcdefABCDEF" for c in hexcol):
|
||||||
|
print("Give the colour as RRGGBB, e.g. ff8800")
|
||||||
|
return 1
|
||||||
|
rgb = tuple(int(hexcol[i:i + 2], 16) for i in (0, 2, 4))
|
||||||
|
backend.set_lighting(d.device, wire.RGB_CUSTOM, rgb)
|
||||||
|
print(f"{G}Custom colour #{hexcol.lower()} set (mode {wire.RGB_CUSTOM}).{O}")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
ap = argparse.ArgumentParser(prog="python3 -m macropad_gui")
|
ap = argparse.ArgumentParser(prog="python3 -m macropad_gui")
|
||||||
sub = ap.add_subparsers(dest="cmd")
|
sub = ap.add_subparsers(dest="cmd")
|
||||||
@@ -114,6 +157,9 @@ def main():
|
|||||||
w.add_argument("keys", nargs="?", help="e.g. 'ctrl+c' or 'a,b,c'")
|
w.add_argument("keys", nargs="?", help="e.g. 'ctrl+c' or 'a,b,c'")
|
||||||
w.add_argument("--media", help="e.g. volumeup")
|
w.add_argument("--media", help="e.g. volumeup")
|
||||||
w.add_argument("--delay", type=int, default=0)
|
w.add_argument("--delay", type=int, default=0)
|
||||||
|
li = sub.add_parser("lighting", help="lighting mode and colour (ITPROTECH pads)")
|
||||||
|
li.add_argument("what", choices=("show", "rgb", "colour", "color"))
|
||||||
|
li.add_argument("value", nargs="?", help="a mode number or name, or RRGGBB")
|
||||||
a = ap.parse_args()
|
a = ap.parse_args()
|
||||||
if a.cmd is None:
|
if a.cmd is None:
|
||||||
try:
|
try:
|
||||||
@@ -124,7 +170,8 @@ def main():
|
|||||||
return 1
|
return 1
|
||||||
return run()
|
return run()
|
||||||
return {"doctor": cmd_doctor, "state": cmd_state, "write": cmd_write,
|
return {"doctor": cmd_doctor, "state": cmd_state, "write": cmd_write,
|
||||||
"detect": cmd_detect, "actions": cmd_actions}[a.cmd](a)
|
"detect": cmd_detect, "actions": cmd_actions,
|
||||||
|
"lighting": cmd_lighting}[a.cmd](a)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -1227,6 +1227,11 @@ class Window(QMainWindow):
|
|||||||
self.desired.clear()
|
self.desired.clear()
|
||||||
self.pending_actions.clear()
|
self.pending_actions.clear()
|
||||||
self.pad.set_layout(self.state.layout) # drops a stale selection
|
self.pad.set_layout(self.state.layout) # drops a stale selection
|
||||||
|
# The upright view suits a pad that stands on its edge, which the
|
||||||
|
# knob pads do. A knobless pad lies flat, so draw it that way until
|
||||||
|
# the owner says otherwise.
|
||||||
|
if not self.state.layout.knobs and self.settings.value("orientation") is None:
|
||||||
|
self.pad.set_orientation("flat")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# ------------------------------------------------------------- write
|
# ------------------------------------------------------------- write
|
||||||
|
|||||||
@@ -14,12 +14,11 @@ Three things about this firmware shape the code:
|
|||||||
read again and has to come back byte for byte as sent. A write that
|
read again and has to come back byte for byte as sent. A write that
|
||||||
doesn't is reported as such, and the control is marked unknown.
|
doesn't is reported as such, and the control is marked unknown.
|
||||||
|
|
||||||
Run this module for the two things the GUI has no place for yet, lighting
|
Lighting and colour have no place in the GUI yet; they are a subcommand:
|
||||||
and colour:
|
|
||||||
|
|
||||||
python3 -m macropad_gui.backends.itprotech show
|
macropad lighting show
|
||||||
python3 -m macropad_gui.backends.itprotech rgb "press: blue"
|
macropad lighting rgb "press: blue"
|
||||||
python3 -m macropad_gui.backends.itprotech colour ff8800
|
macropad lighting colour ff8800
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import errno
|
import errno
|
||||||
@@ -195,60 +194,3 @@ class Itprotech(Backend):
|
|||||||
model=f"ITPROTECH {model}")
|
model=f"ITPROTECH {model}")
|
||||||
|
|
||||||
|
|
||||||
# -------------------------------------------------------------------- cli
|
|
||||||
|
|
||||||
def _main(argv):
|
|
||||||
import sys
|
|
||||||
from .. import core
|
|
||||||
|
|
||||||
def usage():
|
|
||||||
print(__doc__.split("Run this module")[1].strip("\n"))
|
|
||||||
return 2
|
|
||||||
|
|
||||||
if not argv or argv[0] not in ("show", "rgb", "colour", "color"):
|
|
||||||
return usage()
|
|
||||||
d = core.diagnose(keyd=False)
|
|
||||||
if not d.device or d.device.vid != wire.VID:
|
|
||||||
print("No ITPROTECH pad found." if not d.device else d.headline)
|
|
||||||
return 1
|
|
||||||
backend = Itprotech()
|
|
||||||
dev = d.device
|
|
||||||
if argv[0] == "show":
|
|
||||||
dump = backend._dump(dev)
|
|
||||||
for index, report in sorted(dump["keys"].items()):
|
|
||||||
got = wire.decode(report)
|
|
||||||
label = got[1].label() if got and got[1] else "(not decodable)"
|
|
||||||
print(f" key{index:<3} {report[:8].hex(' ')} {label}")
|
|
||||||
mode = dump["rgb"]
|
|
||||||
name = wire.RGB_MODES[mode] if mode is not None and mode < len(wire.RGB_MODES) else "?"
|
|
||||||
print(f" lighting: {mode} ({name}), colour "
|
|
||||||
f"{'#%02x%02x%02x' % dump['colour'] if dump['colour'] else '?'}")
|
|
||||||
return 0
|
|
||||||
if not d.ready:
|
|
||||||
print(d.headline)
|
|
||||||
return 1
|
|
||||||
if argv[0] == "rgb":
|
|
||||||
if len(argv) < 2:
|
|
||||||
return usage()
|
|
||||||
want = argv[1].lower()
|
|
||||||
mode = int(want) if want.isdigit() else (
|
|
||||||
wire.RGB_MODES.index(want) if want in wire.RGB_MODES else None)
|
|
||||||
if mode is None:
|
|
||||||
print("Modes: " + ", ".join(f"{i} {m}" for i, m in enumerate(wire.RGB_MODES)))
|
|
||||||
return 1
|
|
||||||
backend.set_lighting(dev, mode)
|
|
||||||
print(f"Lighting set to {mode} ({wire.RGB_MODES[mode]}).")
|
|
||||||
return 0
|
|
||||||
hexcol = argv[1].lstrip("#") if len(argv) > 1 else ""
|
|
||||||
if len(hexcol) != 6:
|
|
||||||
print("Give the colour as RRGGBB, e.g. ff8800")
|
|
||||||
return 1
|
|
||||||
rgb = tuple(int(hexcol[i:i + 2], 16) for i in (0, 2, 4))
|
|
||||||
backend.set_lighting(dev, wire.RGB_CUSTOM, rgb)
|
|
||||||
print(f"Custom colour #{hexcol} set, lighting mode {wire.RGB_CUSTOM}.")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
import sys
|
|
||||||
sys.exit(_main(sys.argv[1:]))
|
|
||||||
|
|||||||
Reference in New Issue
Block a user