implement fast and late reset for combos

This commit is contained in:
xs5871 2022-06-05 20:51:04 +00:00 committed by Kyle Brown
parent 229b7afcb0
commit 400042f799

View File

@ -4,22 +4,35 @@ from kmk.modules import Module
class Combo:
timeout = 50
fast_reset = False
per_key_timeout = False
_timeout = None
timeout = 50
_remaining = []
_timeout = None
def __init__(self, match, result, timeout=None, per_key_timeout=None):
def __init__(
self,
match,
result,
fast_reset=None,
per_key_timeout=None,
timeout=None,
):
'''
match: tuple of keys (KC.A, KC.B)
result: key KC.C
'''
self.match = match
self.result = result
if timeout:
self.timeout = timeout
if per_key_timeout:
if fast_reset is not None:
self.fast_reset = fast_reset
if per_key_timeout is not None:
self.per_key_timeout = per_key_timeout
if timeout is not None:
self.timeout = timeout
def __repr__(self):
return f'{self.__class__.__name__}({[k.code for k in self.match]})'
def matches(self, key):
raise NotImplementedError
@ -38,8 +51,9 @@ class Chord(Combo):
class Sequence(Combo):
timeout = 1000
fast_reset = True
per_key_timeout = True
timeout = 1000
def matches(self, key):
try:
@ -147,13 +161,57 @@ class Combos(Module):
if key in combo.match:
# Deactivate combo if it matches current key.
self.deactivate(keyboard, combo)
self.reset_combo(keyboard, combo)
if combo.fast_reset:
self.reset_combo(keyboard, combo)
self._key_buffer = []
else:
combo._remaining.insert(0, key)
self._matching.append(combo)
key = combo.result
break
# Don't propagate key-release events for keys that have been buffered.
# Append release events only if corresponding press is in buffer.
else:
# Non-active but matching combos can either activate on key release
# if they're the only match, or "un-match" the released key but stay
# matching if they're a repeatable combo.
for combo in self._matching.copy():
if key not in combo.match:
continue
# Combo matches, but first key released before timeout.
elif not combo._remaining:
keyboard.cancel_timeout(combo._timeout)
self._matching.remove(combo)
self.activate(keyboard, combo)
self._key_buffer = []
keyboard._send_hid()
self.deactivate(keyboard, combo)
if combo.fast_reset:
self.reset_combo(keyboard, combo)
else:
combo._remaining.insert(0, key)
self._matching.append(combo)
# Skip combos that allow tapping.
elif combo.fast_reset:
continue
# This was the last key released of a repeatable combo.
elif len(combo._remaining) == len(combo.match) - 1:
self._matching.remove(combo)
self.reset_combo(keyboard, combo)
self.send_key_buffer(keyboard)
self._key_buffer = []
# Anything between first and last key released.
else:
combo._remaining.insert(0, key)
# Don't propagate key-release events for keys that have been
# buffered. Append release events only if corresponding press is in
# buffer.
pressed = self._key_buffer.count((int_coord, key, True))
released = self._key_buffer.count((int_coord, key, False))
if (pressed - released) > 0:
@ -170,13 +228,8 @@ class Combos(Module):
if not combo._remaining:
self.activate(keyboard, combo)
if any([not pressed for (int_coord, key, pressed) in self._key_buffer]):
# At least one of the combo keys has already been released:
# "tap" the combo result.
keyboard._send_hid()
self.deactivate(keyboard, combo)
self.reset(keyboard)
self._key_buffer = []
self.reset(keyboard)
else:
if not self._matching:
# This was the last pending combo: flush key buffer.
@ -216,4 +269,5 @@ class Combos(Module):
def reset(self, keyboard):
self._matching = []
for combo in self.combos:
self.reset_combo(keyboard, combo)
if combo not in self._active:
self.reset_combo(keyboard, combo)