Ejemplo n.º 1
0
    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")
Ejemplo n.º 2
0
    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()
Ejemplo n.º 3
0
    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()
Ejemplo n.º 4
0
 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))
Ejemplo n.º 5
0
 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)
Ejemplo n.º 6
0
    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()
Ejemplo n.º 7
0
    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)
Ejemplo n.º 8
0
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
Ejemplo n.º 9
0
 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))
Ejemplo n.º 10
0
    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"))
Ejemplo n.º 11
0
    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
Ejemplo n.º 12
0
    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")
Ejemplo n.º 13
0
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
Ejemplo n.º 14
0
    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)
Ejemplo n.º 15
0
 def __repr__(self):
     return "Tap({})".format(Keycode.label(self.keycode.code))
Ejemplo n.º 16
0
    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)
Ejemplo n.º 17
0
    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")
Ejemplo n.º 18
0
 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)
Ejemplo n.º 19
0
 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
Ejemplo n.º 20
0
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))
Ejemplo n.º 21
0
 def on_tap_enter(self):
     self.add_action(
         ActionTapUI(self.container,
                     ActionTap([Keycode.find_by_qmk_id("KC_ENTER")])))
Ejemplo n.º 22
0
# 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)])
Ejemplo n.º 23
0
 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))