diff --git a/kmk/internal_state.py b/kmk/internal_state.py index 555fe39..cdb82e3 100644 --- a/kmk/internal_state.py +++ b/kmk/internal_state.py @@ -28,6 +28,8 @@ class InternalState: 'leader': None, } timeouts = {} + tapping = False + tap_dance_counts = {} def __init__(self, config): self.config = config @@ -45,6 +47,7 @@ class InternalState: Keycodes.KMK.KC_LEAD.code: self._kc_lead, Keycodes.KMK.KC_NO.code: self._kc_no, Keycodes.KMK.KC_DEBUG.code: self._kc_debug_mode, + RawKeycodes.KC_TAP_DANCE: self._kc_tap_dance, } def __repr__(self): @@ -57,6 +60,8 @@ class InternalState: 'leader_mode_history': self.leader_mode_history, 'leader_mode': self.config.leader_mode, 'start_time': self.start_time, + 'tapping': self.tapping, + 'tap_dance_counts': self.tap_dance_counts, } return ret @@ -103,24 +108,27 @@ class InternalState: print('No key accessible for col, row: {}, {}'.format(row, col)) return self - if is_pressed: - self.keys_pressed.add(kc_changed) - self.coord_keys_pressed[int_coord] = kc_changed + if self.tapping: + self._process_tap_dance(kc_changed, is_pressed) else: - self.keys_pressed.discard(kc_changed) - self.keys_pressed.discard(self.coord_keys_pressed[int_coord]) - self.coord_keys_pressed[int_coord] = None + if is_pressed: + self.keys_pressed.add(kc_changed) + self.coord_keys_pressed[int_coord] = kc_changed + else: + self.keys_pressed.discard(kc_changed) + self.keys_pressed.discard(self.coord_keys_pressed.get(int_coord, None)) + self.coord_keys_pressed[int_coord] = None - if kc_changed.code >= FIRST_KMK_INTERNAL_KEYCODE: - self._process_internal_key_event( - kc_changed, - is_pressed, - ) - else: - self.hid_pending = True + if kc_changed.code >= FIRST_KMK_INTERNAL_KEYCODE: + self._process_internal_key_event( + kc_changed, + is_pressed, + ) + else: + self.hid_pending = True - if self.config.leader_mode % 2 == 1: - self._process_leader_mode() + if self.config.leader_mode % 2 == 1: + self._process_leader_mode() return self @@ -318,6 +326,50 @@ class InternalState: return self + def _kc_tap_dance(self, changed_key, is_pressed): + if is_pressed: + self._begin_tap_dance(changed_key) + else: + self._end_tap_dance() + + return self + + def _begin_tap_dance(self, changed_key): + self.tap_dance_counts[changed_key] = 1 + self.set_timeout(500, self._end_tap_dance) + self.tapping = changed_key + return self + + def _process_tap_dance(self, changed_key, is_pressed): + if is_pressed: + if ( + changed_key not in self.tap_dance_counts or + not self.tap_dance_counts[changed_key] + ): + self._end_tap_dance() + else: + self.tap_dance_counts[changed_key] += 1 + + if self.tap_dance_counts[changed_key] == len(changed_key.codes): + self._end_tap_dance() + + return self + + def _end_tap_dance(self): + k = self.tapping + + if not k: + # already handled elsewhere? + return self + + v = self.tap_dance_counts[k] - 1 + + self.pending_keys.append(k.codes[min(v, len(k.codes) - 1)]) + self.tap_dance_counts[k] = 0 + self.tapping = False + + return self + def _begin_leader_mode(self): if self.config.leader_mode % 2 == 0: self.keys_pressed.discard(Keycodes.KMK.KC_LEAD) diff --git a/kmk/keycodes.py b/kmk/keycodes.py index 847e7d1..55d4443 100644 --- a/kmk/keycodes.py +++ b/kmk/keycodes.py @@ -80,6 +80,7 @@ class RawKeycodes: KC_MACRO = 1110 KC_MACRO_SLEEP_MS = 1111 + KC_TAP_DANCE = 1113 # These shouldn't have all the fancy shenanigans Keycode allows @@ -184,6 +185,13 @@ class Macro: return self.keyup() if self.keyup else None +class TapDanceKeycode: + code = RawKeycodes.KC_TAP_DANCE + + def __init__(self, *codes): + self.codes = codes + + class KeycodeCategory(type): @classmethod def to_dict(cls): @@ -551,6 +559,13 @@ class KMK(KeycodeCategory): def KC_MACRO_SLEEP_MS(ms): return MacroSleepKeycode(RawKeycodes.KC_MACRO_SLEEP_MS, ms) + @staticmethod + def KC_TAP_DANCE(*args): + return TapDanceKeycode(*args) + + +KMK.KC_TD = KMK.KC_TAP_DANCE + class Layers(KeycodeCategory): @staticmethod diff --git a/user_keymaps/klardotsh/klarank_featherm4.py b/user_keymaps/klardotsh/klarank_featherm4.py index 2cb1843..cfd0913 100644 --- a/user_keymaps/klardotsh/klarank_featherm4.py +++ b/user_keymaps/klardotsh/klarank_featherm4.py @@ -61,7 +61,7 @@ keyboard.keymap = [ [KC.GESC, KC.QUOT, KC.COMM, KC.DOT, KC.P, KC.Y, KC.F, KC.G, KC.C, KC.R, KC.L, KC.BSPC], [KC.TAB, KC.A, KC.O, KC.E, KC.U, KC.I, KC.D, KC.H, KC.T, KC.N, KC.S, KC.ENT], [KC.LGUI, KC.SCLN, KC.Q, KC.J, KC.K, KC.X, KC.B, KC.M, KC.W, KC.V, KC.Z, KC.LALT], - [KC.LCTL, KC.LEAD, KC.LSHIFT(KC.LGUI), KC.MO(2), KC.MO(3), KC.LSFT, KC.SPC, KC.MO(1), KC.LEFT, KC.DOWN, KC.UP, KC.RGHT], + [KC.LCTL, KC.TD(KC.A, KC.B), KC.LSHIFT(KC.LGUI), KC.MO(2), KC.MO(3), KC.LSFT, KC.SPC, KC.MO(1), KC.LEFT, KC.DOWN, KC.UP, KC.RGHT], ], [