diff --git a/src/main/python/any_keycode_dialog.py b/src/main/python/any_keycode_dialog.py index bb3b925..0a42785 100644 --- a/src/main/python/any_keycode_dialog.py +++ b/src/main/python/any_keycode_dialog.py @@ -6,7 +6,8 @@ import operator from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QVBoxLayout, QLineEdit, QLabel from keycodes import KEYCODES_SPECIAL, KEYCODES_BASIC, KEYCODES_SHIFTED, KEYCODES_ISO, KEYCODES_BACKLIGHT, \ - KEYCODES_MEDIA, KEYCODES_USER, QK_LCTL, QK_LSFT, QK_LALT, QK_LGUI, QK_RCTL, QK_RSFT, QK_RALT, QK_RGUI, QK_LAYER_TAP + KEYCODES_MEDIA, KEYCODES_USER, QK_LCTL, QK_LSFT, QK_LALT, QK_LGUI, QK_RCTL, QK_RSFT, QK_RALT, QK_RGUI, QK_LAYER_TAP, \ + MOD_MEH, MOD_HYPR from util import tr @@ -39,6 +40,7 @@ def RCTL(kc): return (QK_RCTL | (kc)) def RSFT(kc): return (QK_RSFT | (kc)) def RALT(kc): return (QK_RALT | (kc)) def RGUI(kc): return (QK_RGUI | (kc)) +def C_S(kc): return (QK_LCTL | QK_LSFT | (kc)) def HYPR(kc): return (QK_LCTL | QK_LSFT | QK_LALT | QK_LGUI | (kc)) def MEH(kc): return (QK_LCTL | QK_LSFT | QK_LALT | (kc)) def LCAG(kc): return (QK_LCTL | QK_LALT | QK_LGUI | (kc)) @@ -85,6 +87,7 @@ functions = { "LCTL": LCTL, "LSFT": LSFT, "LALT": LALT, "LGUI": LGUI, "LOPT": LALT, "LCMD": LGUI, "LWIN": LGUI, "RCTL": RCTL, "RSFT": RSFT, "RALT": RALT, "RGUI": RGUI, "ALGR": RALT, "ROPT": RALT, "RCMD": RGUI, "RWIN": RGUI, "HYPR": HYPR, "MEH": MEH, "LCAG": LCAG, "SGUI": SGUI, "SCMD": SGUI, "SWIN": SGUI, + "C_S": C_S, "LCA": LCA, "LSA": LSA, "RSA": RSA, "RCS": RCS, "SAGR": RSA, "C": LCTL, "S": LSFT, "A": LALT, "G": LGUI, "LT": LT, "TO": TO, "MO": MO, "DF": DF, "TG": TG, "OSL": OSL, "LM": LM, "OSM": OSM, "TT": TT, "MT": MT, @@ -99,6 +102,40 @@ functions = { } +class AnyKeycode: + + def __init__(self): + self.ops = simpleeval.DEFAULT_OPERATORS.copy() + self.ops.update({ + ast.BitOr: operator.or_, + ast.BitXor: operator.xor, + ast.BitAnd: operator.and_, + }) + self.names = dict() + self.prepare_names() + + def prepare_names(self): + for kc in KEYCODES_SPECIAL + KEYCODES_BASIC + KEYCODES_SHIFTED + KEYCODES_ISO + KEYCODES_BACKLIGHT + \ + KEYCODES_MEDIA + KEYCODES_USER: + for qmk_id in kc.alias: + self.names[qmk_id] = kc.code + self.names.update({ + "MOD_LCTL": MOD_LCTL, + "MOD_LSFT": MOD_LSFT, + "MOD_LALT": MOD_LALT, + "MOD_LGUI": MOD_LGUI, + "MOD_RCTL": MOD_RCTL, + "MOD_RSFT": MOD_RSFT, + "MOD_RALT": MOD_RALT, + "MOD_RGUI": MOD_RGUI, + "MOD_MEH": MOD_MEH, + "MOD_HYPR": MOD_HYPR, + }) + + def decode(self, s): + return simpleeval.simple_eval(s, names=self.names, functions=functions, operators=self.ops) + + class AnyKeycodeDialog(QDialog): def __init__(self, *args, **kwargs): @@ -120,39 +157,15 @@ class AnyKeycodeDialog(QDialog): self.layout.addWidget(self.buttons) self.setLayout(self.layout) - self.ops = simpleeval.DEFAULT_OPERATORS.copy() - self.ops.update({ - ast.BitOr: operator.or_, - ast.BitXor: operator.xor, - ast.BitAnd: operator.and_, - }) - self.names = dict() - self.prepare_names() - + self.any = AnyKeycode() self.value = -1 self.on_change() - def prepare_names(self): - for kc in KEYCODES_SPECIAL + KEYCODES_BASIC + KEYCODES_SHIFTED + KEYCODES_ISO + KEYCODES_BACKLIGHT + \ - KEYCODES_MEDIA + KEYCODES_USER: - for qmk_id in kc.alias: - self.names[qmk_id] = kc.code - self.names.update({ - "MOD_LCTL": MOD_LCTL, - "MOD_LSFT": MOD_LSFT, - "MOD_LALT": MOD_LALT, - "MOD_LGUI": MOD_LGUI, - "MOD_RCTL": MOD_RCTL, - "MOD_RSFT": MOD_RSFT, - "MOD_RALT": MOD_RALT, - "MOD_RGUI": MOD_RGUI, - }) - def on_change(self): text = self.txt_entry.text() value = err = None try: - value = simpleeval.simple_eval(text, names=self.names, functions=functions, operators=self.ops) + value = self.any.decode(text) except Exception as e: err = str(e) diff --git a/src/main/python/keycodes.py b/src/main/python/keycodes.py index 30380c8..9bcbe26 100644 --- a/src/main/python/keycodes.py +++ b/src/main/python/keycodes.py @@ -34,6 +34,13 @@ class Keycode: if masked: self.masked_keycodes.add(code) + @classmethod + def find(cls, code): + for keycode in KEYCODES: + if keycode.code == code: + return keycode + return None + @classmethod def find_outer_keycode(cls, code): """ @@ -41,10 +48,7 @@ class Keycode: """ if cls.is_mask(code): code = code & 0xFF00 - for keycode in KEYCODES: - if keycode.code == code: - return keycode - return None + return cls.find(code) @classmethod def find_by_recorder_alias(cls, alias): @@ -76,17 +80,31 @@ class Keycode: return tooltip @classmethod - def serialize(cls, qmk_id): - # TODO - pass + def serialize(cls, code): + if not cls.is_mask(code): + kc = cls.find(code) + if kc is not None: + return kc.qmk_id + elif cls.is_mask(code): + outer = cls.find_outer_keycode(code) + inner = cls.find(code & 0xFF) + if outer is not None and inner is not None: + return outer.qmk_id.replace("(kc)", "({})".format(inner.qmk_id)) + return code @classmethod def deserialize(cls, val): + from any_keycode_dialog import AnyKeycode + if isinstance(val, int): return val if "(" not in val and val in cls.qmk_id_to_keycode: return cls.qmk_id_to_keycode[val].code - # TODO: process macro-like keycodes with () etc + anykc = AnyKeycode() + try: + return anykc.decode(val) + except Exception: + pass return 0 diff --git a/src/main/python/test/test_keycode.py b/src/main/python/test/test_keycode.py index a6f5f67..1962213 100644 --- a/src/main/python/test/test_keycode.py +++ b/src/main/python/test/test_keycode.py @@ -6,5 +6,8 @@ from keycodes import Keycode class TestKeycode(unittest.TestCase): def test_serialize(self): - print(Keycode.serialize(0x100)) - print(Keycode.deserialize("KC_A")) + # at a minimum, we should be able to deserialize/serialize everything + for x in range(2 ** 16): + s = Keycode.serialize(x) + d = Keycode.deserialize(s) + self.assertEqual(d, x, "{} serialized into {} deserialized into {}".format(x, s, d))