implement hold-tap interrupt on other key tap (i.e. release)

This commit is contained in:
xs5871 2022-01-29 21:11:57 +00:00 committed by Kyle Brown
parent 5cae17c9f3
commit a685618480
3 changed files with 63 additions and 22 deletions

View File

@ -11,7 +11,7 @@ def key_seq_sleep_validator(ms):
return KeySeqSleepMeta(ms) return KeySeqSleepMeta(ms)
def layer_key_validator(layer, kc=None): def layer_key_validator(layer, kc=None, tap_time=None):
''' '''
Validates the syntax (but not semantics) of a layer key call. We won't Validates the syntax (but not semantics) of a layer key call. We won't
have access to the keymap here, so we can't verify much of anything useful have access to the keymap here, so we can't verify much of anything useful
@ -19,14 +19,24 @@ def layer_key_validator(layer, kc=None):
existing is mostly that Python will catch extraneous args/kwargs and error existing is mostly that Python will catch extraneous args/kwargs and error
out. out.
''' '''
return LayerKeyMeta(layer=layer, kc=kc) return LayerKeyMeta(
layer=layer, kc=kc, prefer_hold=False, tap_interrupted=False, tap_time=tap_time
)
def mod_tap_validator(kc, mods=None, prefer_hold=True, tap_time=None): def mod_tap_validator(
kc, mods=None, prefer_hold=True, tap_interrupted=False, tap_time=None
):
''' '''
Validates that mod tap keys are correctly used Validates that mod tap keys are correctly used
''' '''
return ModTapKeyMeta(kc=kc, mods=mods, prefer_hold=prefer_hold, tap_time=tap_time) return ModTapKeyMeta(
kc=kc,
mods=mods,
prefer_hold=prefer_hold,
tap_interrupted=tap_interrupted,
tap_time=tap_time,
)
def tap_dance_key_validator(*codes): def tap_dance_key_validator(*codes):

View File

@ -21,6 +21,7 @@ class HoldTap(Module):
tap_time = 300 tap_time = 300
def __init__(self): def __init__(self):
self.key_buffer = set()
self.key_states = {} self.key_states = {}
def during_bootup(self, keyboard): def during_bootup(self, keyboard):
@ -33,19 +34,36 @@ class HoldTap(Module):
return return
def process_key(self, keyboard, key, is_pressed): def process_key(self, keyboard, key, is_pressed):
'''Before other key down decide to send tap kc down.''' '''Handle holdtap being interrupted by another key press/release.'''
current_key = key current_key = key
for key, state in self.key_states.items(): for key, state in self.key_states.items():
if key == current_key: if key == current_key:
continue continue
if is_pressed: if state.activated != ActivationType.NOT_ACTIVATED:
if state.activated == ActivationType.NOT_ACTIVATED: continue
# press tap because interrupted by other key
# holdtap is interrupted by another key event.
if (is_pressed and not key.meta.tap_interrupted) or (
not is_pressed and key.meta.tap_interrupted and self.key_buffer
):
keyboard.cancel_timeout(state.timeout_key)
self.key_states[key].activated = ActivationType.INTERRUPTED self.key_states[key].activated = ActivationType.INTERRUPTED
self.ht_activate_on_interrupt( self.ht_activate_on_interrupt(
key, keyboard, *state.args, **state.kwargs key, keyboard, *state.args, **state.kwargs
) )
keyboard._send_hid() keyboard._send_hid()
self.send_key_buffer(keyboard)
# if interrupt on release: store interrupting keys until one of them
# is released.
if key.meta.tap_interrupted:
if is_pressed:
self.key_buffer.add(current_key)
current_key = None
return current_key return current_key
def before_hid_send(self, keyboard): def before_hid_send(self, keyboard):
@ -91,6 +109,7 @@ class HoldTap(Module):
False, False,
lambda: self.ht_deactivate_tap(key, keyboard, *args, **kwargs), lambda: self.ht_deactivate_tap(key, keyboard, *args, **kwargs),
) )
self.send_key_buffer(keyboard)
del self.key_states[key] del self.key_states[key]
return keyboard return keyboard
@ -103,6 +122,13 @@ class HoldTap(Module):
# press hold because timer expired after tap time # press hold because timer expired after tap time
self.key_states[key].activated = ActivationType.HOLD_TIMEOUT self.key_states[key].activated = ActivationType.HOLD_TIMEOUT
self.ht_activate_hold(key, keyboard, *args, **kwargs) self.ht_activate_hold(key, keyboard, *args, **kwargs)
self.send_key_buffer(keyboard)
def send_key_buffer(self, keyboard):
for key in self.key_buffer:
key.on_press(keyboard)
keyboard._send_hid()
self.key_buffer.clear()
def ht_activate_hold(self, key, keyboard, *args, **kwargs): def ht_activate_hold(self, key, keyboard, *args, **kwargs):
pass pass

View File

@ -11,21 +11,26 @@ class AttrDict(dict):
return self[key] return self[key]
class LayerKeyMeta: class HoldTapKeyMeta:
def __init__(self, layer, kc=None, tap_time=None): def __init__(self, kc=None, prefer_hold=True, tap_interrupted=False, tap_time=None):
self.layer = layer
self.kc = kc self.kc = kc
self.tap_time = tap_time
class ModTapKeyMeta:
def __init__(self, kc=None, mods=None, prefer_hold=True, tap_time=None):
self.prefer_hold = prefer_hold self.prefer_hold = prefer_hold
self.kc = kc self.tap_interrupted = tap_interrupted
self.mods = mods
self.tap_time = tap_time self.tap_time = tap_time
class LayerKeyMeta(HoldTapKeyMeta):
def __init__(self, layer, **kwargs):
super().__init__(**kwargs)
self.layer = layer
class ModTapKeyMeta(HoldTapKeyMeta):
def __init__(self, kc=None, mods=None, **kwargs):
super().__init__(kc=kc, **kwargs)
self.mods = mods
class KeySequenceMeta: class KeySequenceMeta:
def __init__(self, seq): def __init__(self, seq):
self.seq = seq self.seq = seq