def save_layout(self): """ Serializes current layout to a binary """ data = {"version": 1, "uid": self.keyboard_id} layout = [] for l in range(self.layers): layer = [] layout.append(layer) for r in range(self.rows): row = [] layer.append(row) for c in range(self.cols): val = self.layout.get((l, r, c), -1) row.append(Keycode.serialize(val)) encoder_layout = [] for l in range(self.layers): layer = [] for e in range(self.encoder_count): cw = (l, e, 0) ccw = (l, e, 1) layer.append([Keycode.serialize(self.encoder_layout.get(cw, -1)), Keycode.serialize(self.encoder_layout.get(ccw, -1))]) encoder_layout.append(layer) data["layout"] = layout data["encoder_layout"] = encoder_layout data["layout_options"] = self.layout_options data["macro"] = self.save_macro() data["vial_protocol"] = self.vial_protocol data["via_protocol"] = self.via_protocol return json.dumps(data).encode("utf-8")
def restore_layout(self, data): """ Restores saved layout """ data = json.loads(data.decode("utf-8")) # restore keymap for l, layer in enumerate(data["layout"]): for r, row in enumerate(layer): for c, code in enumerate(row): if (l, r, c) in self.layout: self.set_key(l, r, c, Keycode.deserialize(code)) # restore encoders for l, layer in enumerate(data["encoder_layout"]): for e, encoder in enumerate(layer): self.set_encoder(l, e, 0, Keycode.deserialize(encoder[0])) self.set_encoder(l, e, 1, Keycode.deserialize(encoder[1])) self.set_layout_options(data["layout_options"]) # we need to unlock the keyboard before we can restore the macros, lock it afterwards # only do that if it's different from current macros macro = base64.b64decode(data["macro"]) if macro != self.macro: Unlocker.unlock(self) self.set_macro(macro) self.lock()
def refresh_layer_display(self): """ Refresh text on key widgets to display data corresponding to current layer """ self.container.update_layout() for idx, btn in enumerate(self.layer_buttons): btn.setEnabled(idx != self.current_layer) btn.setChecked(idx == self.current_layer) for widget in self.container.widgets: code = self.code_for_widget(widget) text = self.get_label(code) tooltip = Keycode.tooltip(code) mask = Keycode.is_mask(code) mask_text = self.get_label(code & 0xFF) if mask: text = text.split("\n")[0] widget.masked = mask widget.setText(text) widget.setMaskText(mask_text) widget.setToolTip(tooltip) if self.code_is_overriden(code): widget.setColor(QApplication.palette().color(QPalette.Link)) else: widget.setColor(None) self.container.update() self.container.updateGeometry()
def test_serialize(self): # 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))
def display_keycode(cls, widget, code): text = cls.get_label(code) tooltip = Keycode.tooltip(code) mask = Keycode.is_mask(code) mask_text = cls.get_label(code & 0xFF) if mask: text = text.split("\n")[0] widget.masked = mask widget.setText(text) widget.setMaskText(mask_text) widget.setToolTip(tooltip) if cls.code_is_overriden(code): widget.setColor(QApplication.palette().color(QPalette.Link)) else: widget.setColor(None)
def __init__(self, initial): super().__init__() self.setWindowTitle(tr("AnyKeycodeDialog", "Enter an arbitrary keycode")) self.buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.buttons.accepted.connect(self.accept) self.buttons.rejected.connect(self.reject) self.lbl_computed = QLabel() self.txt_entry = QLineEdit() self.txt_entry.textChanged.connect(self.on_change) self.layout = QVBoxLayout() self.layout.addWidget(self.txt_entry) self.layout.addWidget(self.lbl_computed) self.layout.addWidget(self.buttons) self.setLayout(self.layout) self.value = initial ser = Keycode.serialize(initial) if isinstance(ser, int): ser = hex(ser) self.txt_entry.setText(ser) self.on_change()
def on_change(self): text = self.txt_entry.text() value = err = None try: value = Keycode.deserialize(text, reraise=True) except Exception as e: err = str(e) if not text: self.value = -1 self.lbl_computed.setText( tr("AnyKeycodeDialog", "Enter an expression")) elif err: self.value = -1 self.lbl_computed.setText( tr("AnyKeycodeDialog", "Invalid input: {}").format(err)) elif isinstance(value, int): self.value = value self.lbl_computed.setText( tr("AnyKeycodeDialog", "Computed value: 0x{:X}").format(value)) else: self.value = -1 self.lbl_computed.setText(tr("AnyKeycodeDialog", "Invalid input")) self.buttons.button( QDialogButtonBox.Ok).setEnabled(0 <= self.value < 2**16)
def macro_deserialize_v2(data): """ Deserialize a single macro, protocol version 2 """ out = [] sequence = [] data = bytearray(data) while len(data) > 0: if data[0] == SS_QMK_PREFIX: if data[1] in [SS_TAP_CODE, SS_DOWN_CODE, SS_UP_CODE]: # append to previous *_CODE if it's the same type, otherwise create a new entry if len(sequence) > 0 and isinstance( sequence[-1], list) and sequence[-1][0] == data[1]: sequence[-1][1].append(data[2]) else: sequence.append([data[1], [data[2]]]) for x in range(3): data.pop(0) elif data[1] == SS_DELAY_CODE: # decode the delay delay = (data[2] - 1) + (data[3] - 1) * 255 sequence.append([SS_DELAY_CODE, delay]) for x in range(4): data.pop(0) else: # append to previous string if it is a string, otherwise create a new entry ch = chr(data[0]) if len(sequence) > 0 and isinstance(sequence[-1], str): sequence[-1] += ch else: sequence.append(ch) data.pop(0) for s in sequence: if isinstance(s, str): out.append(ActionText(s)) else: args = None if s[0] in [SS_TAP_CODE, SS_DOWN_CODE, SS_UP_CODE]: # map integer values to qmk keycodes args = [] for code in s[1]: keycode = Keycode.find_outer_keycode(code) if keycode: args.append(keycode) elif s[0] == SS_DELAY_CODE: args = s[1] if args is not None: cls = { SS_TAP_CODE: ActionTap, SS_DOWN_CODE: ActionDown, SS_UP_CODE: ActionUp, SS_DELAY_CODE: ActionDelay }[s[0]] out.append(cls(args)) return out
def on_output(self): if self.process.canReadLine(): line = bytes(self.process.readLine()).decode("utf-8") action, key = line.strip().split(":") code = Keycode.find_by_recorder_alias(key) if code is not None: action2cls = {"down": KeyDown, "up": KeyUp} self.keystroke.emit(action2cls[action](code))
def restore_layout(self, data): """ Restores saved layout """ data = json.loads(data.decode("utf-8")) # restore keymap for l, layer in enumerate(data["layout"]): for r, row in enumerate(layer): for c, code in enumerate(row): if (l, r, c) in self.layout: self.set_key(l, r, c, Keycode.deserialize(code)) # restore encoders for l, layer in enumerate(data["encoder_layout"]): for e, encoder in enumerate(layer): self.set_encoder(l, e, 0, Keycode.deserialize(encoder[0])) self.set_encoder(l, e, 1, Keycode.deserialize(encoder[1])) self.set_layout_options(data["layout_options"]) self.restore_macros(data.get("macro"))
def create_buttons(self, layout, keycodes): buttons = [] for keycode in keycodes: btn = SquareButton() btn.setRelSize(KEYCODE_BTN_RATIO) btn.setToolTip(Keycode.tooltip(keycode.code)) btn.clicked.connect( lambda st, k=keycode: self.keycode_changed.emit(k.code)) btn.keycode = keycode layout.addWidget(btn) buttons.append(btn) return buttons
def save_layout(self): """ Serializes current layout to a binary """ data = {"version": 1, "uid": self.keyboard_id} layout = [] for l in range(self.layers): layer = [] layout.append(layer) for r in range(self.rows): row = [] layer.append(row) for c in range(self.cols): val = self.layout.get((l, r, c), -1) row.append(Keycode.serialize(val)) encoder_layout = [] for l in range(self.layers): layer = [] for e in range(self.encoder_count): cw = (l, e, 0) ccw = (l, e, 1) layer.append([ Keycode.serialize(self.encoder_layout.get(cw, -1)), Keycode.serialize(self.encoder_layout.get(ccw, -1)) ]) encoder_layout.append(layer) data["layout"] = layout data["encoder_layout"] = encoder_layout data["layout_options"] = self.layout_options # TODO: macros should be serialized in a portable format instead of base64 string # i.e. use a custom structure (as keycodes numbers can change, etc) data["macro"] = base64.b64encode(self.macro).decode("utf-8") return json.dumps(data).encode("utf-8")
def macro_deserialize_v1(data): """ Deserialize a single macro, protocol version 1 """ out = [] sequence = [] data = bytearray(data) while len(data) > 0: if data[0] in [SS_TAP_CODE, SS_DOWN_CODE, SS_UP_CODE]: if len(data) < 2: break # append to previous *_CODE if it's the same type, otherwise create a new entry if len(sequence) > 0 and isinstance( sequence[-1], list) and sequence[-1][0] == data[0]: sequence[-1][1].append(data[1]) else: sequence.append([data[0], [data[1]]]) data.pop(0) data.pop(0) else: # append to previous string if it is a string, otherwise create a new entry ch = chr(data[0]) if len(sequence) > 0 and isinstance(sequence[-1], str): sequence[-1] += ch else: sequence.append(ch) data.pop(0) for s in sequence: if isinstance(s, str): out.append(ActionText(s)) else: # map integer values to qmk keycodes keycodes = [] for code in s[1]: keycode = Keycode.find_outer_keycode(code) if keycode: keycodes.append(keycode) cls = { SS_TAP_CODE: ActionTap, SS_DOWN_CODE: ActionDown, SS_UP_CODE: ActionUp }[s[0]] out.append(cls(keycodes)) return out
def recreate_buttons(self): for btn in self.buttons: btn.hide() btn.deleteLater() self.buttons = [] for keycode in self.keycodes: btn = SquareButton() btn.setWordWrap(self.word_wrap) btn.setRelSize(KEYCODE_BTN_RATIO) btn.setToolTip(Keycode.tooltip(keycode.code)) btn.clicked.connect( lambda st, k=keycode: self.keycode_changed.emit(k.code)) btn.keycode = keycode self.layout.addWidget(btn) self.buttons.append(btn) self.relabel_buttons() self.container.setVisible(len(self.buttons) > 0)
def __repr__(self): return "Tap({})".format(Keycode.label(self.keycode.code))
def restore_layout(self, data): """ Restores saved layout """ data = json.loads(data.decode("utf-8")) # restore keymap for l, layer in enumerate(data["layout"]): for r, row in enumerate(layer): for c, code in enumerate(row): if (l, r, c) in self.layout: self.set_key(l, r, c, Keycode.deserialize(code)) # restore encoders for l, layer in enumerate(data["encoder_layout"]): for e, encoder in enumerate(layer): self.set_encoder(l, e, 0, Keycode.deserialize(encoder[0])) self.set_encoder(l, e, 1, Keycode.deserialize(encoder[1])) self.set_layout_options(data["layout_options"]) self.restore_macros(data.get("macro")) for x, e in enumerate(data.get("tap_dance", [])): if x < self.tap_dance_count: e = (Keycode.deserialize(e[0]), Keycode.deserialize(e[1]), Keycode.deserialize(e[2]), Keycode.deserialize(e[3]), e[4]) self.tap_dance_set(x, e) for x, e in enumerate(data.get("combo", [])): if x < self.combo_count: e = (Keycode.deserialize(e[0]), Keycode.deserialize(e[1]), Keycode.deserialize(e[2]), Keycode.deserialize(e[3]), Keycode.deserialize(e[4])) self.combo_set(x, e) for qsid, value in data.get("settings", dict()).items(): from qmk_settings import QmkSettings qsid = int(qsid) if QmkSettings.is_qsid_supported(qsid): self.qmk_settings_set(qsid, value)
def save_layout(self): """ Serializes current layout to a binary """ data = {"version": 1, "uid": self.keyboard_id} layout = [] for l in range(self.layers): layer = [] layout.append(layer) for r in range(self.rows): row = [] layer.append(row) for c in range(self.cols): val = self.layout.get((l, r, c), -1) row.append(Keycode.serialize(val)) encoder_layout = [] for l in range(self.layers): layer = [] for e in range(self.encoder_count): cw = (l, e, 0) ccw = (l, e, 1) layer.append([ Keycode.serialize(self.encoder_layout.get(cw, -1)), Keycode.serialize(self.encoder_layout.get(ccw, -1)) ]) encoder_layout.append(layer) data["layout"] = layout data["encoder_layout"] = encoder_layout data["layout_options"] = self.layout_options data["macro"] = self.save_macro() data["vial_protocol"] = self.vial_protocol data["via_protocol"] = self.via_protocol tap_dance = [] for entry in self.tap_dance_entries: tap_dance.append( (Keycode.serialize(entry[0]), Keycode.serialize(entry[1]), Keycode.serialize(entry[2]), Keycode.serialize(entry[3]), entry[4])) data["tap_dance"] = tap_dance combo = [] for entry in self.combo_entries: combo.append(( Keycode.serialize(entry[0]), Keycode.serialize(entry[1]), Keycode.serialize(entry[2]), Keycode.serialize(entry[3]), Keycode.serialize(entry[4]), )) data["combo"] = combo data["settings"] = self.settings return json.dumps(data).encode("utf-8")
def get_label(self, code): """ Get label for a specific keycode """ if self.code_is_overriden(code): return self.keymap_override[Keycode.find_outer_keycode( code).qmk_id] return Keycode.label(code)
def code_is_overriden(self, code): """ Check whether a country-specific keymap overrides a code """ key = Keycode.find_outer_keycode(code) return key is not None and key.qmk_id in self.keymap_override
from keycodes import Keycode from keymap import french, german, hungarian, latam, norwegian, russian, spanish, swedish KEYMAPS = [("QWERTY", dict()), ("French (AZERTY)", french.keymap), ("German (QWERTZ)", german.keymap), ("Hungarian (QWERTZ)", hungarian.keymap), ("Latin American (QWERTY)", latam.keymap), ("Norwegian (QWERTY)", norwegian.keymap), ("Russian (ЙЦУКЕН)", russian.keymap), ("Spanish (QWERTY)", spanish.keymap), ("Swedish (QWERTY)", swedish.keymap)] # make sure that qmk IDs we used are all correct for name, keymap in KEYMAPS: for qmk_id in keymap.keys(): if Keycode.find_by_qmk_id(qmk_id) is None: raise RuntimeError( "Misconfigured - cannot find QMK keycode {}".format(qmk_id))
def on_tap_enter(self): self.add_action( ActionTapUI(self.container, ActionTap([Keycode.find_by_qmk_id("KC_ENTER")])))
# SPDX-License-Identifier: GPL-2.0-or-later import unittest from keycodes import Keycode from macro_key import KeyDown, KeyTap, KeyUp, KeyString from macro_optimizer import remove_repeats, replace_with_tap, replace_with_string KC_A = Keycode.find_by_qmk_id("KC_A") KC_B = Keycode.find_by_qmk_id("KC_B") KC_C = Keycode.find_by_qmk_id("KC_C") class TestMacro(unittest.TestCase): def test_remove_repeats(self): self.assertEqual(remove_repeats([KeyDown(KC_A), KeyDown(KC_A)]), [KeyDown(KC_A)]) self.assertEqual( remove_repeats([ KeyDown(KC_A), KeyDown(KC_B), KeyDown(KC_B), KeyDown(KC_C), KeyDown(KC_C) ]), [KeyDown(KC_A), KeyDown(KC_B), KeyDown(KC_C)]) # don't remove repeated taps self.assertEqual(remove_repeats([KeyTap(KC_A), KeyTap(KC_A)]), [KeyTap(KC_A), KeyTap(KC_A)])
def on_key(self, ev): code = Keycode.find_by_recorder_alias(ev.name) if code is not None: action2cls = {"down": KeyDown, "up": KeyUp} self.keystroke.emit(action2cls[ev.event_type](code))