implement fast and late reset for combos
This commit is contained in:
		@@ -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)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user