From 0a61cbd4afe18de33a24a5ff8c395345eefa577a Mon Sep 17 00:00:00 2001 From: Christian Tu Date: Sun, 19 Sep 2021 14:14:13 +0200 Subject: [PATCH 1/5] change ModTap to allow for activating mod on hold, rolling key presses and fix stuck mod --- kmk/kmk_keyboard.py | 2 +- kmk/modules/layers.py | 131 +++++++++++++++--------------------------- kmk/modules/modtap.py | 129 +++++++++++++++++++++++++++++++++-------- 3 files changed, 151 insertions(+), 111 deletions(-) diff --git a/kmk/kmk_keyboard.py b/kmk/kmk_keyboard.py index acb199c..b292d0c 100644 --- a/kmk/kmk_keyboard.py +++ b/kmk/kmk_keyboard.py @@ -258,7 +258,7 @@ class KMKKeyboard: self._timeouts[timeout_key] = callback return timeout_key - def _cancel_timeout(self, timeout_key): + def cancel_timeout(self, timeout_key): if timeout_key in self._timeouts: del self._timeouts[timeout_key] diff --git a/kmk/modules/layers.py b/kmk/modules/layers.py index 416bc27..3586299 100644 --- a/kmk/modules/layers.py +++ b/kmk/modules/layers.py @@ -1,34 +1,25 @@ '''One layer isn't enough. Adds keys to get to more of them''' -from micropython import const - from kmk.key_validators import layer_key_validator from kmk.keys import make_argumented_key -from kmk.kmktime import accurate_ticks, accurate_ticks_diff -from kmk.modules import Module +from kmk.modules.modtap import HoldTap -class LayerType: - '''Defines layer type values for readability''' +def curry(fn, *args, **kwargs): + def curried(*fn_args, **fn_kwargs): + merged_args = args + fn_args + merged_kwargs = kwargs.copy() + merged_kwargs.update(fn_kwargs) + return fn(*merged_args, **merged_kwargs) - MO = const(0) - DF = const(1) - LM = const(2) - LT = const(3) - TG = const(4) - TT = const(5) + return curried -class Layers(Module): +class Layers(HoldTap): '''Gives access to the keys used to enable the layer system''' def __init__(self): # Layers - self.start_time = { - LayerType.LT: None, - LayerType.TG: None, - LayerType.TT: None, - LayerType.LM: None, - } + super().__init__() make_argumented_key( validator=layer_key_validator, names=('MO',), @@ -47,8 +38,8 @@ class Layers(Module): make_argumented_key( validator=layer_key_validator, names=('LT',), - on_press=self._lt_pressed, - on_release=self._lt_released, + on_press=curry(self.ht_pressed, key_type='LT'), + on_release=curry(self.ht_released, key_type='LT'), ) make_argumented_key( validator=layer_key_validator, names=('TG',), on_press=self._tg_pressed @@ -59,31 +50,10 @@ class Layers(Module): make_argumented_key( validator=layer_key_validator, names=('TT',), - on_press=self._tt_pressed, - on_release=self._tt_released, + on_press=curry(self.ht_pressed, key_type='TT'), + on_release=curry(self.ht_released, key_type='TT'), ) - def during_bootup(self, keyboard): - return - - def before_matrix_scan(self, keyboard): - return - - def after_matrix_scan(self, keyboard): - return - - def before_hid_send(self, keyboard): - return - - def after_hid_send(self, keyboard): - return - - def on_powersave_enable(self, keyboard): - return - - def on_powersave_disable(self, keyboard): - return - def _df_pressed(self, key, keyboard, *args, **kwargs): ''' Switches the default layer @@ -129,24 +99,6 @@ class Layers(Module): keyboard.keys_pressed.discard(key.meta.kc) self._mo_released(key, keyboard, *args, **kwargs) - def _lt_pressed(self, key, keyboard, *args, **kwargs): - # Sets the timer start and acts like MO otherwise - self.start_time[LayerType.LT] = accurate_ticks() - self._mo_pressed(key, keyboard, *args, **kwargs) - - def _lt_released(self, key, keyboard, *args, **kwargs): - # On keyup, check timer, and press key if needed. - if self.start_time[LayerType.LT] and ( - accurate_ticks_diff( - accurate_ticks(), self.start_time[LayerType.LT], keyboard.tap_time - ) - ): - keyboard.hid_pending = True - keyboard.tap_key(key.meta.kc) - - self._mo_released(key, keyboard, *args, **kwargs) - self.start_time[LayerType.LT] = None - def _tg_pressed(self, key, keyboard, *args, **kwargs): ''' Toggles the layer (enables it if not active, and vise versa) @@ -165,27 +117,36 @@ class Layers(Module): keyboard.active_layers.clear() keyboard.active_layers.insert(0, key.meta.layer) - def _tt_pressed(self, key, keyboard, *args, **kwargs): - ''' - Momentarily activates layer if held, toggles it if tapped repeatedly - ''' - if self.start_time[LayerType.TT] is None: - # Sets the timer start and acts like MO otherwise - self.start_time[LayerType.TT] = accurate_ticks() - self._mo_pressed(key, keyboard, *args, **kwargs) - elif accurate_ticks_diff( - accurate_ticks(), self.start_time[LayerType.TT], keyboard.tap_time - ): - self.start_time[LayerType.TT] = None - self._tg_pressed(key, keyboard, *args, **kwargs) - return - return + def ht_activate_hold(self, key, keyboard, *args, **kwargs): + self._mo_pressed(key, keyboard, *args, **kwargs) - def _tt_released(self, key, keyboard, *args, **kwargs): - if self.start_time[LayerType.TT] is None or not accurate_ticks_diff( - accurate_ticks(), self.start_time[LayerType.TT], keyboard.tap_time - ): - # On first press, works like MO. On second press, does nothing unless let up within - # time window, then acts like TG. - self.start_time[LayerType.TT] = None - self._mo_released(key, keyboard, *args, **kwargs) + def ht_deactivate_hold(self, key, keyboard, *args, **kwargs): + self._mo_released(key, keyboard, *args, **kwargs) + + def ht_activate_tap(self, key, keyboard, *args, **kwargs): + key_type = kwargs['key_type'] + if key_type == 'LT': + keyboard.hid_pending = True + keyboard.keys_pressed.add(key.meta.kc) + elif key_type == 'TT': + self._tg_pressed(key, keyboard, *args, **kwargs) + + def ht_deactivate_tap(self, key, keyboard, *args, **kwargs): + key_type = kwargs['key_type'] + if key_type == 'LT': + keyboard.hid_pending = True + keyboard.keys_pressed.discard(key.meta.kc) + + def ht_activate_on_interrupt(self, key, keyboard, *args, **kwargs): + key_type = kwargs['key_type'] + if key_type == 'LT': + self.ht_activate_tap(key, keyboard, *args, **kwargs) + elif key_type == 'TT': + self.ht_activate_hold(key, keyboard, *args, **kwargs) + + def ht_deactivate_on_interrupt(self, key, keyboard, *args, **kwargs): + key_type = kwargs['key_type'] + if key_type == 'LT': + self.ht_deactivate_tap(key, keyboard, *args, **kwargs) + elif key_type == 'TT': + self.ht_deactivate_hold(key, keyboard, *args, **kwargs) diff --git a/kmk/modules/modtap.py b/kmk/modules/modtap.py index 5402899..eaa83c2 100644 --- a/kmk/modules/modtap.py +++ b/kmk/modules/modtap.py @@ -1,18 +1,19 @@ from kmk.key_validators import mod_tap_validator from kmk.keys import make_argumented_key -from kmk.kmktime import accurate_ticks, accurate_ticks_diff from kmk.modules import Module -class ModTap(Module): +class HoldTapKeyState: + def __init__(self, timeout_key, *args, **kwargs): + self.timeout_key = timeout_key + self.args = args + self.kwargs = kwargs + self.activated = False + + +class HoldTap(Module): def __init__(self): - self._mod_tap_timer = None - make_argumented_key( - validator=mod_tap_validator, - names=('MT',), - on_press=self.mt_pressed, - on_release=self.mt_released, - ) + self.key_states = {} def during_bootup(self, keyboard): return @@ -21,6 +22,17 @@ class ModTap(Module): return def after_matrix_scan(self, keyboard): + '''Before other key down decide to send tap kc down.''' + if self.matrix_detected_press(keyboard): + for key, state in self.key_states.items(): + if not state.activated: + # press tap because interrupted by other key + self.key_states[key].activated = 'interrupt' + self.ht_activate_on_interrupt( + key, keyboard, *state.args, **state.kwargs + ) + if keyboard.hid_pending: + keyboard._send_hid() return def before_hid_send(self, keyboard): @@ -35,23 +47,90 @@ class ModTap(Module): def on_powersave_disable(self, keyboard): return - def mt_pressed(self, key, keyboard, *args, **kwargs): - '''Sets the timer start and acts like a modifier otherwise''' + def matrix_detected_press(self, keyboard): + return (keyboard.matrix_update is not None and keyboard.matrix_update[2]) or ( + keyboard.secondary_matrix_update is not None + and keyboard.secondary_matrix_update[2] + ) + + def ht_pressed(self, key, keyboard, *args, **kwargs): + '''Do nothing yet, action resolves when key is released, timer expires or other key is pressed.''' + timeout_key = keyboard.set_timeout( + keyboard.tap_time, + lambda: self.on_tap_time_expired(key, keyboard, *args, **kwargs), + ) + self.key_states[key] = HoldTapKeyState(timeout_key, *args, **kwargs) + return keyboard + + def ht_released(self, key, keyboard, *args, **kwargs): + '''On keyup, release mod or tap key.''' + if key in self.key_states: + state = self.key_states[key] + keyboard.cancel_timeout(state.timeout_key) + if state.activated == 'hold': + # release hold + self.ht_deactivate_hold(key, keyboard, *args, **kwargs) + elif state.activated == 'interrupt': + # release tap + self.ht_deactivate_on_interrupt(key, keyboard, *args, **kwargs) + else: + # press and release tap because key released within tap time + self.ht_activate_tap(key, keyboard, *args, **kwargs) + keyboard.set_timeout( + False, + lambda: self.ht_deactivate_tap(key, keyboard, *args, **kwargs), + ) + del self.key_states[key] + return keyboard + + def on_tap_time_expired(self, key, keyboard, *args, **kwargs): + '''When tap time expires activate mod if key is still being pressed.''' + if key in self.key_states and not self.key_states[key].activated: + # press hold because timer expired after tap time + self.key_states[key].activated = 'hold' + self.ht_activate_hold(key, keyboard, *args, **kwargs) + + def ht_activate_hold(self, key, keyboard, *args, **kwargs): + pass + + def ht_deactivate_hold(self, key, keyboard, *args, **kwargs): + pass + + def ht_activate_tap(self, key, keyboard, *args, **kwargs): + pass + + def ht_deactivate_tap(self, key, keyboard, *args, **kwargs): + pass + + def ht_activate_on_interrupt(self, key, keyboard, *args, **kwargs): + self.ht_activate_tap(key, keyboard, *args, **kwargs) + + def ht_deactivate_on_interrupt(self, key, keyboard, *args, **kwargs): + self.ht_deactivate_tap(key, keyboard, *args, **kwargs) + + +class ModTap(HoldTap): + def __init__(self): + super().__init__() + make_argumented_key( + validator=mod_tap_validator, + names=('MT',), + on_press=self.ht_pressed, + on_release=self.ht_released, + ) + + def ht_activate_hold(self, key, keyboard, *args, **kwargs): + keyboard.hid_pending = True keyboard.keys_pressed.add(key.meta.mods) - self._mod_tap_timer = accurate_ticks() - return keyboard - - def mt_released(self, key, keyboard, *args, **kwargs): - '''On keyup, check timer, and press key if needed.''' + def ht_deactivate_hold(self, key, keyboard, *args, **kwargs): + keyboard.hid_pending = True keyboard.keys_pressed.discard(key.meta.mods) - if self._mod_tap_timer and ( - accurate_ticks_diff( - accurate_ticks(), self._mod_tap_timer, keyboard.tap_time - ) - ): - keyboard.hid_pending = True - keyboard.tap_key(key.meta.kc) - self._mod_tap_timer = None - return keyboard + def ht_activate_tap(self, key, keyboard, *args, **kwargs): + keyboard.hid_pending = True + keyboard.keys_pressed.add(key.meta.kc) + + def ht_deactivate_tap(self, key, keyboard, *args, **kwargs): + keyboard.hid_pending = True + keyboard.keys_pressed.discard(key.meta.kc) From 4e938ef6b64ccf0b1bd19d418309f8c8bc2edd85 Mon Sep 17 00:00:00 2001 From: Christian Tu Date: Sat, 25 Sep 2021 10:48:43 +0200 Subject: [PATCH 2/5] use const values instead of magic strings --- kmk/modules/layers.py | 31 ++++++++++++++++++++----------- kmk/modules/modtap.py | 25 ++++++++++++++++++------- 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/kmk/modules/layers.py b/kmk/modules/layers.py index 3586299..2cea357 100644 --- a/kmk/modules/layers.py +++ b/kmk/modules/layers.py @@ -1,9 +1,18 @@ '''One layer isn't enough. Adds keys to get to more of them''' +from micropython import const + from kmk.key_validators import layer_key_validator from kmk.keys import make_argumented_key from kmk.modules.modtap import HoldTap +class LayerType: + '''Defines layer types to be passed on as on_press and on_release kwargs where needed''' + + LT = const(0) + TT = const(1) + + def curry(fn, *args, **kwargs): def curried(*fn_args, **fn_kwargs): merged_args = args + fn_args @@ -38,8 +47,8 @@ class Layers(HoldTap): make_argumented_key( validator=layer_key_validator, names=('LT',), - on_press=curry(self.ht_pressed, key_type='LT'), - on_release=curry(self.ht_released, key_type='LT'), + on_press=curry(self.ht_pressed, key_type=LayerType.LT), + on_release=curry(self.ht_released, key_type=LayerType.LT), ) make_argumented_key( validator=layer_key_validator, names=('TG',), on_press=self._tg_pressed @@ -50,8 +59,8 @@ class Layers(HoldTap): make_argumented_key( validator=layer_key_validator, names=('TT',), - on_press=curry(self.ht_pressed, key_type='TT'), - on_release=curry(self.ht_released, key_type='TT'), + on_press=curry(self.ht_pressed, key_type=LayerType.TT), + on_release=curry(self.ht_released, key_type=LayerType.TT), ) def _df_pressed(self, key, keyboard, *args, **kwargs): @@ -125,28 +134,28 @@ class Layers(HoldTap): def ht_activate_tap(self, key, keyboard, *args, **kwargs): key_type = kwargs['key_type'] - if key_type == 'LT': + if key_type == LayerType.LT: keyboard.hid_pending = True keyboard.keys_pressed.add(key.meta.kc) - elif key_type == 'TT': + elif key_type == LayerType.TT: self._tg_pressed(key, keyboard, *args, **kwargs) def ht_deactivate_tap(self, key, keyboard, *args, **kwargs): key_type = kwargs['key_type'] - if key_type == 'LT': + if key_type == LayerType.LT: keyboard.hid_pending = True keyboard.keys_pressed.discard(key.meta.kc) def ht_activate_on_interrupt(self, key, keyboard, *args, **kwargs): key_type = kwargs['key_type'] - if key_type == 'LT': + if key_type == LayerType.LT: self.ht_activate_tap(key, keyboard, *args, **kwargs) - elif key_type == 'TT': + elif key_type == LayerType.TT: self.ht_activate_hold(key, keyboard, *args, **kwargs) def ht_deactivate_on_interrupt(self, key, keyboard, *args, **kwargs): key_type = kwargs['key_type'] - if key_type == 'LT': + if key_type == LayerType.LT: self.ht_deactivate_tap(key, keyboard, *args, **kwargs) - elif key_type == 'TT': + elif key_type == LayerType.TT: self.ht_deactivate_hold(key, keyboard, *args, **kwargs) diff --git a/kmk/modules/modtap.py b/kmk/modules/modtap.py index eaa83c2..e619386 100644 --- a/kmk/modules/modtap.py +++ b/kmk/modules/modtap.py @@ -1,14 +1,22 @@ +from micropython import const + from kmk.key_validators import mod_tap_validator from kmk.keys import make_argumented_key from kmk.modules import Module +class ActivationType: + NOT_ACTIVATED = const(0) + HOLD_TIMEOUT = const(1) + INTERRUPTED = const(2) + + class HoldTapKeyState: def __init__(self, timeout_key, *args, **kwargs): self.timeout_key = timeout_key self.args = args self.kwargs = kwargs - self.activated = False + self.activated = ActivationType.NOT_ACTIVATED class HoldTap(Module): @@ -25,9 +33,9 @@ class HoldTap(Module): '''Before other key down decide to send tap kc down.''' if self.matrix_detected_press(keyboard): for key, state in self.key_states.items(): - if not state.activated: + if state.activated == ActivationType.NOT_ACTIVATED: # press tap because interrupted by other key - self.key_states[key].activated = 'interrupt' + self.key_states[key].activated = ActivationType.INTERRUPTED self.ht_activate_on_interrupt( key, keyboard, *state.args, **state.kwargs ) @@ -67,10 +75,10 @@ class HoldTap(Module): if key in self.key_states: state = self.key_states[key] keyboard.cancel_timeout(state.timeout_key) - if state.activated == 'hold': + if state.activated == ActivationType.HOLD_TIMEOUT: # release hold self.ht_deactivate_hold(key, keyboard, *args, **kwargs) - elif state.activated == 'interrupt': + elif state.activated == ActivationType.INTERRUPTED: # release tap self.ht_deactivate_on_interrupt(key, keyboard, *args, **kwargs) else: @@ -85,9 +93,12 @@ class HoldTap(Module): def on_tap_time_expired(self, key, keyboard, *args, **kwargs): '''When tap time expires activate mod if key is still being pressed.''' - if key in self.key_states and not self.key_states[key].activated: + if ( + key in self.key_states + and self.key_states[key].activated == ActivationType.NOT_ACTIVATED + ): # press hold because timer expired after tap time - self.key_states[key].activated = 'hold' + self.key_states[key].activated = ActivationType.HOLD_TIMEOUT self.ht_activate_hold(key, keyboard, *args, **kwargs) def ht_activate_hold(self, key, keyboard, *args, **kwargs): From 209acc94b28d9f061e30a95e73ad1601cc13d2cd Mon Sep 17 00:00:00 2001 From: Christian Tu Date: Sat, 25 Sep 2021 10:54:45 +0200 Subject: [PATCH 3/5] TT toggles layer state on key hold --- kmk/modules/layers.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/kmk/modules/layers.py b/kmk/modules/layers.py index 2cea357..d669aef 100644 --- a/kmk/modules/layers.py +++ b/kmk/modules/layers.py @@ -127,10 +127,18 @@ class Layers(HoldTap): keyboard.active_layers.insert(0, key.meta.layer) def ht_activate_hold(self, key, keyboard, *args, **kwargs): - self._mo_pressed(key, keyboard, *args, **kwargs) + key_type = kwargs['key_type'] + if key_type == LayerType.LT: + self._mo_released(key, keyboard, *args, **kwargs) + elif key_type == LayerType.TT: + self._tg_pressed(key, keyboard, *args, **kwargs) def ht_deactivate_hold(self, key, keyboard, *args, **kwargs): - self._mo_released(key, keyboard, *args, **kwargs) + key_type = kwargs['key_type'] + if key_type == LayerType.LT: + self._mo_released(key, keyboard, *args, **kwargs) + elif key_type == LayerType.TT: + self._tg_pressed(key, keyboard, *args, **kwargs) def ht_activate_tap(self, key, keyboard, *args, **kwargs): key_type = kwargs['key_type'] From 99577357f6d53448dea4e7249456a198893192b2 Mon Sep 17 00:00:00 2001 From: Christian Tu Date: Sat, 25 Sep 2021 11:07:33 +0200 Subject: [PATCH 4/5] move HoldTap to its own file --- kmk/modules/holdtap.py | 118 ++++++++++++++++++++++++++++++++++++++++ kmk/modules/layers.py | 2 +- kmk/modules/modtap.py | 119 +---------------------------------------- 3 files changed, 120 insertions(+), 119 deletions(-) create mode 100644 kmk/modules/holdtap.py diff --git a/kmk/modules/holdtap.py b/kmk/modules/holdtap.py new file mode 100644 index 0000000..77dd4e9 --- /dev/null +++ b/kmk/modules/holdtap.py @@ -0,0 +1,118 @@ +from micropython import const + +from kmk.modules import Module + + +class ActivationType: + NOT_ACTIVATED = const(0) + HOLD_TIMEOUT = const(1) + INTERRUPTED = const(2) + + +class HoldTapKeyState: + def __init__(self, timeout_key, *args, **kwargs): + self.timeout_key = timeout_key + self.args = args + self.kwargs = kwargs + self.activated = ActivationType.NOT_ACTIVATED + + +class HoldTap(Module): + def __init__(self): + self.key_states = {} + + def during_bootup(self, keyboard): + return + + def before_matrix_scan(self, keyboard): + return + + def after_matrix_scan(self, keyboard): + '''Before other key down decide to send tap kc down.''' + if self.matrix_detected_press(keyboard): + for key, state in self.key_states.items(): + if state.activated == ActivationType.NOT_ACTIVATED: + # press tap because interrupted by other key + self.key_states[key].activated = ActivationType.INTERRUPTED + self.ht_activate_on_interrupt( + key, keyboard, *state.args, **state.kwargs + ) + if keyboard.hid_pending: + keyboard._send_hid() + return + + def before_hid_send(self, keyboard): + return + + def after_hid_send(self, keyboard): + return + + def on_powersave_enable(self, keyboard): + return + + def on_powersave_disable(self, keyboard): + return + + def matrix_detected_press(self, keyboard): + return (keyboard.matrix_update is not None and keyboard.matrix_update[2]) or ( + keyboard.secondary_matrix_update is not None + and keyboard.secondary_matrix_update[2] + ) + + def ht_pressed(self, key, keyboard, *args, **kwargs): + '''Do nothing yet, action resolves when key is released, timer expires or other key is pressed.''' + timeout_key = keyboard.set_timeout( + keyboard.tap_time, + lambda: self.on_tap_time_expired(key, keyboard, *args, **kwargs), + ) + self.key_states[key] = HoldTapKeyState(timeout_key, *args, **kwargs) + return keyboard + + def ht_released(self, key, keyboard, *args, **kwargs): + '''On keyup, release mod or tap key.''' + if key in self.key_states: + state = self.key_states[key] + keyboard.cancel_timeout(state.timeout_key) + if state.activated == ActivationType.HOLD_TIMEOUT: + # release hold + self.ht_deactivate_hold(key, keyboard, *args, **kwargs) + elif state.activated == ActivationType.INTERRUPTED: + # release tap + self.ht_deactivate_on_interrupt(key, keyboard, *args, **kwargs) + else: + # press and release tap because key released within tap time + self.ht_activate_tap(key, keyboard, *args, **kwargs) + keyboard.set_timeout( + False, + lambda: self.ht_deactivate_tap(key, keyboard, *args, **kwargs), + ) + del self.key_states[key] + return keyboard + + def on_tap_time_expired(self, key, keyboard, *args, **kwargs): + '''When tap time expires activate mod if key is still being pressed.''' + if ( + key in self.key_states + and self.key_states[key].activated == ActivationType.NOT_ACTIVATED + ): + # press hold because timer expired after tap time + self.key_states[key].activated = ActivationType.HOLD_TIMEOUT + self.ht_activate_hold(key, keyboard, *args, **kwargs) + + def ht_activate_hold(self, key, keyboard, *args, **kwargs): + pass + + def ht_deactivate_hold(self, key, keyboard, *args, **kwargs): + pass + + def ht_activate_tap(self, key, keyboard, *args, **kwargs): + pass + + def ht_deactivate_tap(self, key, keyboard, *args, **kwargs): + pass + + def ht_activate_on_interrupt(self, key, keyboard, *args, **kwargs): + self.ht_activate_tap(key, keyboard, *args, **kwargs) + + def ht_deactivate_on_interrupt(self, key, keyboard, *args, **kwargs): + self.ht_deactivate_tap(key, keyboard, *args, **kwargs) diff --git a/kmk/modules/layers.py b/kmk/modules/layers.py index d669aef..c369476 100644 --- a/kmk/modules/layers.py +++ b/kmk/modules/layers.py @@ -3,7 +3,7 @@ from micropython import const from kmk.key_validators import layer_key_validator from kmk.keys import make_argumented_key -from kmk.modules.modtap import HoldTap +from kmk.modules.holdtap import HoldTap class LayerType: diff --git a/kmk/modules/modtap.py b/kmk/modules/modtap.py index e619386..6b31a73 100644 --- a/kmk/modules/modtap.py +++ b/kmk/modules/modtap.py @@ -1,123 +1,6 @@ -from micropython import const - from kmk.key_validators import mod_tap_validator from kmk.keys import make_argumented_key -from kmk.modules import Module - - -class ActivationType: - NOT_ACTIVATED = const(0) - HOLD_TIMEOUT = const(1) - INTERRUPTED = const(2) - - -class HoldTapKeyState: - def __init__(self, timeout_key, *args, **kwargs): - self.timeout_key = timeout_key - self.args = args - self.kwargs = kwargs - self.activated = ActivationType.NOT_ACTIVATED - - -class HoldTap(Module): - def __init__(self): - self.key_states = {} - - def during_bootup(self, keyboard): - return - - def before_matrix_scan(self, keyboard): - return - - def after_matrix_scan(self, keyboard): - '''Before other key down decide to send tap kc down.''' - if self.matrix_detected_press(keyboard): - for key, state in self.key_states.items(): - if state.activated == ActivationType.NOT_ACTIVATED: - # press tap because interrupted by other key - self.key_states[key].activated = ActivationType.INTERRUPTED - self.ht_activate_on_interrupt( - key, keyboard, *state.args, **state.kwargs - ) - if keyboard.hid_pending: - keyboard._send_hid() - return - - def before_hid_send(self, keyboard): - return - - def after_hid_send(self, keyboard): - return - - def on_powersave_enable(self, keyboard): - return - - def on_powersave_disable(self, keyboard): - return - - def matrix_detected_press(self, keyboard): - return (keyboard.matrix_update is not None and keyboard.matrix_update[2]) or ( - keyboard.secondary_matrix_update is not None - and keyboard.secondary_matrix_update[2] - ) - - def ht_pressed(self, key, keyboard, *args, **kwargs): - '''Do nothing yet, action resolves when key is released, timer expires or other key is pressed.''' - timeout_key = keyboard.set_timeout( - keyboard.tap_time, - lambda: self.on_tap_time_expired(key, keyboard, *args, **kwargs), - ) - self.key_states[key] = HoldTapKeyState(timeout_key, *args, **kwargs) - return keyboard - - def ht_released(self, key, keyboard, *args, **kwargs): - '''On keyup, release mod or tap key.''' - if key in self.key_states: - state = self.key_states[key] - keyboard.cancel_timeout(state.timeout_key) - if state.activated == ActivationType.HOLD_TIMEOUT: - # release hold - self.ht_deactivate_hold(key, keyboard, *args, **kwargs) - elif state.activated == ActivationType.INTERRUPTED: - # release tap - self.ht_deactivate_on_interrupt(key, keyboard, *args, **kwargs) - else: - # press and release tap because key released within tap time - self.ht_activate_tap(key, keyboard, *args, **kwargs) - keyboard.set_timeout( - False, - lambda: self.ht_deactivate_tap(key, keyboard, *args, **kwargs), - ) - del self.key_states[key] - return keyboard - - def on_tap_time_expired(self, key, keyboard, *args, **kwargs): - '''When tap time expires activate mod if key is still being pressed.''' - if ( - key in self.key_states - and self.key_states[key].activated == ActivationType.NOT_ACTIVATED - ): - # press hold because timer expired after tap time - self.key_states[key].activated = ActivationType.HOLD_TIMEOUT - self.ht_activate_hold(key, keyboard, *args, **kwargs) - - def ht_activate_hold(self, key, keyboard, *args, **kwargs): - pass - - def ht_deactivate_hold(self, key, keyboard, *args, **kwargs): - pass - - def ht_activate_tap(self, key, keyboard, *args, **kwargs): - pass - - def ht_deactivate_tap(self, key, keyboard, *args, **kwargs): - pass - - def ht_activate_on_interrupt(self, key, keyboard, *args, **kwargs): - self.ht_activate_tap(key, keyboard, *args, **kwargs) - - def ht_deactivate_on_interrupt(self, key, keyboard, *args, **kwargs): - self.ht_deactivate_tap(key, keyboard, *args, **kwargs) +from kmk.modules.holdtap import HoldTap class ModTap(HoldTap): From 199ec1d914ebb5a39176c5298c4b7dc4a246f5d4 Mon Sep 17 00:00:00 2001 From: Christian Tu Date: Sat, 25 Sep 2021 11:20:45 +0200 Subject: [PATCH 5/5] fix last merge --- kmk/modules/layers.py | 1 - kmk/modules/modtap.py | 2 -- 2 files changed, 3 deletions(-) diff --git a/kmk/modules/layers.py b/kmk/modules/layers.py index b67e8d3..c369476 100644 --- a/kmk/modules/layers.py +++ b/kmk/modules/layers.py @@ -1,6 +1,5 @@ '''One layer isn't enough. Adds keys to get to more of them''' from micropython import const -from supervisor import ticks_ms from kmk.key_validators import layer_key_validator from kmk.keys import make_argumented_key diff --git a/kmk/modules/modtap.py b/kmk/modules/modtap.py index 43de0fe..6b31a73 100644 --- a/kmk/modules/modtap.py +++ b/kmk/modules/modtap.py @@ -1,5 +1,3 @@ -from supervisor import ticks_ms - from kmk.key_validators import mod_tap_validator from kmk.keys import make_argumented_key from kmk.modules.holdtap import HoldTap