From 280f981bd956cba5c0fed533dec2a161ceb16c59 Mon Sep 17 00:00:00 2001 From: Ilya Zhuravlev Date: Wed, 24 Mar 2021 00:28:15 -0400 Subject: [PATCH] Support saving and restoring macros into .vil --- src/main/python/keyboard_comm.py | 32 ++++++++++++++++---- src/main/python/macro_action.py | 51 +++++++++++++++++++++++++++++++- 2 files changed, 77 insertions(+), 6 deletions(-) diff --git a/src/main/python/keyboard_comm.py b/src/main/python/keyboard_comm.py index 61dd50c..71c228e 100644 --- a/src/main/python/keyboard_comm.py +++ b/src/main/python/keyboard_comm.py @@ -383,8 +383,11 @@ class Keyboard: return json.dumps(data).encode("utf-8") def save_macro(self): - # TODO: decouple macros from GUI, should be able to serialize/deserialize just with keyboard interface - return [] + macros = self.macros_deserialize(self.macro) + out = [] + for macro in macros: + out.append([act.save() for act in macro]) + return out def restore_layout(self, data): """ Restores saved layout """ @@ -407,10 +410,29 @@ class Keyboard: self.set_layout_options(data["layout_options"]) self.restore_macros(data.get("macro")) - def restore_macro(self, macro): - if not isinstance(macro, list): + def restore_macros(self, macros): + if not isinstance(macros, list): return - print(macro) + tag_to_action = { + "down": ActionDown, + "up": ActionUp, + "tap": ActionTap, + "text": ActionText, + "delay": ActionDelay, + } + full_macro = [] + for macro in macros: + actions = [] + for act in macro: + if act[0] in tag_to_action: + obj = tag_to_action[act[0]]() + obj.restore(act) + actions.append(obj) + full_macro.append(actions) + if len(full_macro) < self.macro_count: + full_macro += [[] for x in range(self.macro_count - len(full_macro))] + full_macro = full_macro[:self.macro_count] + self.set_macro(self.macros_serialize(full_macro)) def reset(self): self.usb_send(self.dev, struct.pack("B", 0xB)) diff --git a/src/main/python/macro_action.py b/src/main/python/macro_action.py index c5bd2d5..4b58b3d 100644 --- a/src/main/python/macro_action.py +++ b/src/main/python/macro_action.py @@ -1,6 +1,8 @@ # SPDX-License-Identifier: GPL-2.0-or-later import struct +from keycodes import Keycode + SS_QMK_PREFIX = 1 SS_TAP_CODE = 1 @@ -10,11 +12,23 @@ SS_DELAY_CODE = 4 class BasicAction: - pass + + tag = "unknown" + + def save(self): + return [self.tag] + + def restore(self, act): + if self.tag != act[0]: + raise RuntimeError("cannot restore {}: expected tag={} got tag={}".format( + self, self.tag, act[0] + )) class ActionText(BasicAction): + tag = "text" + def __init__(self, text=""): super().__init__() self.text = text @@ -22,9 +36,18 @@ class ActionText(BasicAction): def serialize(self, vial_protocol): return self.text.encode("utf-8") + def save(self): + return super().save() + [self.text] + + def restore(self, act): + super().restore(act) + self.text = act[1] + class ActionSequence(BasicAction): + tag = "unknown-sequence" + def __init__(self, sequence=None): super().__init__() if sequence is None: @@ -43,27 +66,46 @@ class ActionSequence(BasicAction): out += struct.pack("B", k.code) return out + def save(self): + out = super().save() + for k in self.sequence: + out.append(k.qmk_id) + return out + + def restore(self, act): + super().restore(act) + for qmk_id in act[1:]: + self.sequence.append(Keycode.find_by_qmk_id(qmk_id)) + class ActionDown(ActionSequence): + tag = "down" + def serialize_prefix(self): return b"\x02" class ActionUp(ActionSequence): + tag = "up" + def serialize_prefix(self): return b"\x03" class ActionTap(ActionSequence): + tag = "tap" + def serialize_prefix(self): return b"\x01" class ActionDelay(BasicAction): + tag = "delay" + def __init__(self, delay=0): super().__init__() self.delay = delay @@ -73,3 +115,10 @@ class ActionDelay(BasicAction): raise RuntimeError("ActionDelay can only be used with vial_protocol>=2") delay = self.delay return struct.pack("BBBB", SS_QMK_PREFIX, SS_DELAY_CODE, (delay % 255) + 1, (delay // 255) + 1) + + def save(self): + return super().save() + [self.delay] + + def restore(self, act): + super().restore(act) + self.delay = act[1]