From e5e342d39716340eadcb55ae9d2060d9747032d6 Mon Sep 17 00:00:00 2001 From: Ilya Zhuravlev Date: Thu, 24 Dec 2020 09:25:59 -0500 Subject: [PATCH] macro_recorder: basic macro optimizer --- src/main/python/macro_key.py | 25 +++++++++++++++++---- src/main/python/macro_optimizer.py | 35 ++++++++++++++++++++++++++++++ src/main/python/macro_recorder.py | 2 ++ src/main/python/test/test_macro.py | 22 +++++++++++++++++++ 4 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 src/main/python/macro_optimizer.py create mode 100644 src/main/python/test/test_macro.py diff --git a/src/main/python/macro_key.py b/src/main/python/macro_key.py index 499ba6e..f0e557c 100644 --- a/src/main/python/macro_key.py +++ b/src/main/python/macro_key.py @@ -2,7 +2,12 @@ from keycodes import keycode_label -class KeyDown: +class BasicAction: + + pass + + +class KeyDown(BasicAction): def __init__(self, keycode): self.keycode = keycode @@ -10,8 +15,11 @@ class KeyDown: def __repr__(self): return "Down({})".format(keycode_label(self.keycode.code)) + def __eq__(self, other): + return isinstance(other, KeyDown) and other.keycode == self.keycode -class KeyUp: + +class KeyUp(BasicAction): def __init__(self, keycode): self.keycode = keycode @@ -19,8 +27,11 @@ class KeyUp: def __repr__(self): return "Up({})".format(keycode_label(self.keycode.code)) + def __eq__(self, other): + return isinstance(other, KeyUp) and other.keycode == self.keycode -class KeyTap: + +class KeyTap(BasicAction): def __init__(self, keycode): self.keycode = keycode @@ -28,11 +39,17 @@ class KeyTap: def __repr__(self): return "Tap({})".format(keycode_label(self.keycode.code)) + def __eq__(self, other): + return isinstance(other, KeyTap) and other.keycode == self.keycode -class KeyString: + +class KeyString(BasicAction): def __init__(self, string): self.string = string def __repr__(self): return "SendString({})".format(self.string) + + def __eq__(self, other): + return isinstance(other, KeyString) and other.string == self.string diff --git a/src/main/python/macro_optimizer.py b/src/main/python/macro_optimizer.py new file mode 100644 index 0000000..6e822dd --- /dev/null +++ b/src/main/python/macro_optimizer.py @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +from macro_key import KeyUp, KeyDown, KeyTap + + +def remove_repeats(sequence): + """ Removes exact repetition, i.e. two Down or two Up of the same key """ + out = [] + for k in sequence: + if out and (isinstance(k, KeyDown) or isinstance(k, KeyUp)) and k == out[-1]: + continue + out.append(k) + return out + + +def replace_with_tap(sequence): + """ Replaces a sequence of Down/Up with a Tap """ + out = [] + sequence = sequence[:] + while len(sequence) > 0: + if len(sequence) >= 2 and isinstance(sequence[0], KeyDown) and isinstance(sequence[1], KeyUp) and \ + sequence[0].keycode == sequence[1].keycode: + key = KeyTap(sequence[0].keycode) + out.append(key) + sequence.pop(0) + sequence.pop(0) + else: + out.append(sequence[0]) + sequence.pop(0) + return out + + +def macro_optimize(sequence): + sequence = remove_repeats(sequence) + sequence = replace_with_tap(sequence) + return sequence diff --git a/src/main/python/macro_recorder.py b/src/main/python/macro_recorder.py index 7348420..0f818f5 100644 --- a/src/main/python/macro_recorder.py +++ b/src/main/python/macro_recorder.py @@ -9,6 +9,7 @@ from fbs_runtime.application_context import is_frozen from basic_editor import BasicEditor from keycodes import KEYCODES_BASIC from macro_key import KeyDown, KeyUp +from macro_optimizer import macro_optimize from util import tr from vial_device import VialKeyboard @@ -202,6 +203,7 @@ class MacroRecorder(BasicEditor): self.recorder.start() def on_stop(self): + self.keystrokes = macro_optimize(self.keystrokes) print(self.keystrokes) def on_keystroke(self, keystroke): diff --git a/src/main/python/test/test_macro.py b/src/main/python/test/test_macro.py new file mode 100644 index 0000000..c042e08 --- /dev/null +++ b/src/main/python/test/test_macro.py @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +import unittest + +from keycodes import find_keycode +from macro_key import KeyDown, KeyTap +from macro_optimizer import remove_repeats + + +KC_A = find_keycode(0x04) +KC_B = find_keycode(0x05) +KC_C = find_keycode(0x06) + + +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)])