diff --git a/kmk/firmware.py b/kmk/firmware.py index 389321d..bf4c134 100644 --- a/kmk/firmware.py +++ b/kmk/firmware.py @@ -95,30 +95,41 @@ class Firmware: print("Firin' lazers. Keyboard is booted.") while True: - update = self.matrix.scan_for_changes() - if update is not None: + matrix_report = self.matrix.scan_for_changes() + state_changed = False + + if matrix_report is not None: self._state.matrix_changed( - update[0], - update[1], - update[2], + matrix_report[0], + matrix_report[1], + matrix_report[2], ) - if self._state.hid_pending: - self._send_hid() + state_changed = True - if self.debug_enabled: - print('New State: {}'.format(self._state._to_dict())) + if self._state.hid_pending: + self._send_hid() + old_timeouts_len = len(self._state.timeouts) self._state.process_timeouts() + new_timeouts_len = len(self._state.timeouts) + + if old_timeouts_len != new_timeouts_len: + state_changed = True for key in self._state.pending_keys: self._send_key(key) self._state.pending_key_handled() + state_changed = True if self._state.macro_pending: for key in self._state.macro_pending(self): self._send_key(key) self._state.resolve_macro() + state_changed = True + + if self.debug_enabled and state_changed: + print('New State: {}'.format(self._state._to_dict())) gc.collect() diff --git a/kmk/internal_state.py b/kmk/internal_state.py index cdb82e3..5352604 100644 --- a/kmk/internal_state.py +++ b/kmk/internal_state.py @@ -1,5 +1,6 @@ from kmk.consts import LeaderMode -from kmk.keycodes import FIRST_KMK_INTERNAL_KEYCODE, Keycodes, RawKeycodes +from kmk.keycodes import (FIRST_KMK_INTERNAL_KEYCODE, Keycodes, RawKeycodes, + TapDanceKeycode) from kmk.kmktime import sleep_ms, ticks_diff, ticks_ms from kmk.util import intify_coordinate @@ -30,6 +31,7 @@ class InternalState: timeouts = {} tapping = False tap_dance_counts = {} + tap_side_effects = {} def __init__(self, config): self.config = config @@ -108,7 +110,7 @@ class InternalState: print('No key accessible for col, row: {}, {}'.format(row, col)) return self - if self.tapping: + if self.tapping and not isinstance(kc_changed, TapDanceKeycode): self._process_tap_dance(kc_changed, is_pressed) else: if is_pressed: @@ -327,18 +329,7 @@ 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 + return self._process_tap_dance(changed_key, is_pressed) def _process_tap_dance(self, changed_key, is_pressed): if is_pressed: @@ -346,28 +337,47 @@ class InternalState: changed_key not in self.tap_dance_counts or not self.tap_dance_counts[changed_key] ): - self._end_tap_dance() + self.tap_dance_counts[changed_key] = 1 + self.set_timeout(500, lambda: self._end_tap_dance(changed_key)) + self.tapping = True else: self.tap_dance_counts[changed_key] += 1 - if self.tap_dance_counts[changed_key] == len(changed_key.codes): - self._end_tap_dance() + if changed_key not in self.tap_side_effects: + self.tap_side_effects[changed_key] = None + else: + if ( + self.tap_side_effects[changed_key] is not None or + self.tap_dance_counts[changed_key] == len(changed_key.codes) + ): + self._end_tap_dance(changed_key) return self - def _end_tap_dance(self): - k = self.tapping + def _end_tap_dance(self, td_key): + v = self.tap_dance_counts[td_key] - 1 - if not k: - # already handled elsewhere? - return self + if v >= 0: + if td_key in self.keys_pressed: + key_to_press = td_key.codes[v] + self.keys_pressed.add(key_to_press) + self.tap_side_effects[td_key] = key_to_press + self.hid_pending = True + else: + if self.tap_side_effects[td_key]: + self.keys_pressed.discard(self.tap_side_effects[td_key]) + self.tap_side_effects[td_key] = None + self.hid_pending = True + self._cleanup_tap_dance(td_key) + else: + self.pending_keys.append(td_key.codes[v]) + self._cleanup_tap_dance(td_key) - 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 _cleanup_tap_dance(self, td_key): + self.tap_dance_counts[td_key] = 0 + self.tapping = any(count > 0 for count in self.tap_dance_counts.values()) return self def _begin_leader_mode(self):