From 35f2a692c38f98fcf594965b1b6b4933c1add1db Mon Sep 17 00:00:00 2001 From: xs5871 <60395129+xs5871@users.noreply.github.com> Date: Sat, 6 Jul 2024 17:00:30 +0000 Subject: [PATCH] Fix inconsistent macro state when double tapping (#1002) --- kmk/modules/macros.py | 26 ++++++----- tests/test_macros.py | 102 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 114 insertions(+), 14 deletions(-) diff --git a/kmk/modules/macros.py b/kmk/modules/macros.py index 6aa5312f6..59a0dd488 100644 --- a/kmk/modules/macros.py +++ b/kmk/modules/macros.py @@ -200,7 +200,9 @@ def after_matrix_scan(self, keyboard): return def process_key(self, keyboard, key, is_pressed, int_coord): - if not self._active or key in self._active: + # Passthrough if there are no active macros, or the key belongs to an + # active macro, or all active macros or non-blocking. + if not self._active or key in self._active or not self._active[-1].blocking: return key self.key_buffer.append((int_coord, key, is_pressed)) @@ -221,13 +223,19 @@ def on_press_unicode_mode(self, key, keyboard, *args, **kwargs): self.unicode_mode = key.mode def on_press_macro(self, key, keyboard, *args, **kwargs): - key.state = _ON_PRESS - self.process_macro_async(keyboard, key) + if key.state == _IDLE: + key.state = _ON_PRESS + self.process_macro_async(keyboard, key) + else: + self.key_buffer.append((args[1], key, True)) def on_release_macro(self, key, keyboard, *args, **kwargs): - key.state = _RELEASE - if key._task is None: - self.process_macro_async(keyboard, key) + if key.state == _ON_PRESS or key.state == _ON_HOLD: + key.state = _RELEASE + if key._task is None: + self.process_macro_async(keyboard, key) + else: + self.key_buffer.append((args[1], key, False)) def process_macro_async(self, keyboard, key, _iter=None): # There's no active macro iterator: select the next one. @@ -235,8 +243,7 @@ def process_macro_async(self, keyboard, key, _iter=None): key._task = None if key.state == _ON_PRESS: - if key.blocking: - self._active.append(key) + self._active.append(key) if (macro := key.on_press_macro) is None: key.state = _ON_HOLD elif debug.enabled: @@ -281,8 +288,7 @@ def process_macro_async(self, keyboard, key, _iter=None): if debug.enabled: debug('deactivate') key.state = _IDLE - if key.blocking: - self._active.remove(key) + self._active.remove(key) self.send_key_buffer(keyboard) return diff --git a/tests/test_macros.py b/tests/test_macros.py index 15057eb74..2f555895e 100644 --- a/tests/test_macros.py +++ b/tests/test_macros.py @@ -34,10 +34,12 @@ def setUp(self): KC.MACRO(on_release='r'), KC.MACRO(on_press='p', on_hold='h', on_release='r'), KC.MACRO('bar', blocking=False), + KC.MACRO(on_press='q', on_hold='i', on_release='s', blocking=False), ] ], debug_enabled=False, ) + self.hold = self.kb.loop_delay_ms * 15 def test_0(self): self.kb.test( @@ -206,7 +208,7 @@ def test_8(self): def test_9(self): self.kb.test( '', - [(9, True), 15 * self.kb.loop_delay_ms, (9, False)], + [(9, True), self.hold, (9, False)], [{KC.H}, {}, {KC.H}, {}], ) @@ -217,20 +219,112 @@ def test_10(self): [{KC.R}, {}], ) - def test_11(self): + def test_11_0(self): self.kb.test( '', - [(11, True), 30 * self.kb.loop_delay_ms, (11, False)], + [(11, True), 2 * self.hold, (11, False)], [{KC.P}, {}, {KC.H}, {}, {KC.H}, {}, {KC.R}, {}], ) - def test_12(self): + def test_11_1(self): + self.kb.test( + '', + [(11, True), self.hold, (11, False), (11, True), (11, False)], + [{KC.P}, {}, {KC.H}, {}, {KC.R}, {}, {KC.P}, {}, {KC.R}, {}], + ) + + def test_11_2(self): + self.kb.test( + '', + [(11, True), (11, False), (11, True), (11, False)], + [{KC.P}, {}, {KC.R}, {}, {KC.P}, {}, {KC.R}, {}], + ) + + def test_11_3(self): + self.kb.test( + '', + [(4, True), (11, True), (4, False), self.hold, (11, False)], + [ + {KC.Y}, + {KC.P, KC.Y}, + {KC.Y}, + {KC.H, KC.Y}, + {KC.Y}, + {KC.R, KC.Y}, + {KC.Y}, + {}, + ], + ) + + def test_12_0(self): self.kb.test( '', [(12, True), (12, False), (4, True), (4, False)], [{KC.B}, {KC.B, KC.Y}, {KC.B}, {}, {KC.A}, {}, {KC.R}, {}], ) + def test_12_1(self): + self.kb.test( + '', + [(12, True), (12, False), (12, True), (12, False)], + [{KC.B}, {}, {KC.A}, {}, {KC.R}, {}, {KC.B}, {}, {KC.A}, {}, {KC.R}, {}], + ) + + def test_13_0(self): + self.kb.test( + '', + [(13, True), (13, False), (11, True), (11, False)], + [{KC.Q}, {KC.P, KC.Q}, {KC.P}, {}, {KC.S}, {KC.S, KC.R}, {KC.R}, {}], + ) + + def test_13_1(self): + self.kb.test( + '', + [(13, True), (11, True), (13, False), (11, False)], + [{KC.Q}, {KC.P, KC.Q}, {KC.P}, {}, {KC.S}, {KC.S, KC.R}, {KC.R}, {}], + ) + + def test_13_2(self): + self.kb.test( + '', + [(13, True), (11, True), self.hold, (11, False), (13, False)], + [ + {KC.Q}, + {KC.P, KC.Q}, + {KC.P}, + {}, + {KC.I}, + {KC.I, KC.H}, + {KC.H}, + {}, + {KC.S}, + {KC.S, KC.R}, + {KC.R}, + {}, + ], + ) + + def test_13_3(self): + self.kb.test( + '', + [(11, True), (11, False), (13, True), (13, False)], + [{KC.P}, {}, {KC.R}, {}, {KC.Q}, {}, {KC.S}, {}], + ) + + def test_13_4(self): + self.kb.test( + '', + [(11, True), (13, True), (11, False), (13, False)], + [{KC.P}, {}, {KC.R}, {}, {KC.Q}, {}, {KC.S}, {}], + ) + + def test_13_5(self): + self.kb.test( + '', + [(4, True), (13, True), (4, False), (13, False)], + [{KC.Y}, {KC.Q, KC.Y}, {KC.Q}, {}, {KC.S}, {}], + ) + class TestUnicodeModeKeys(unittest.TestCase): def setUp(self):