Completely overhaul the entire MatrixScanner and KEY_UP/DOWN_EVENT system for efficiency. Less delay() on HID sends. Speed is only BARELY slower than QMK now.
This commit is contained in:
		| @@ -5,21 +5,5 @@ class AbstractMatrixScanner(): | ||||
|     def __init__(self, cols, rows, active_layers, diode_orientation=DiodeOrientation.COLUMNS): | ||||
|         raise NotImplementedError('Abstract implementation') | ||||
|  | ||||
|     def _normalize_matrix(self, matrix): | ||||
|         ''' | ||||
|         We always want to internally look at a keyboard as a list of rows, | ||||
|         where a "row" is a list of keycodes (columns). | ||||
|  | ||||
|         This will convert DiodeOrientation.COLUMNS matrix scans into a | ||||
|         ROWS scan, so we never have to think about these things again. | ||||
|         ''' | ||||
|         if self.diode_orientation == DiodeOrientation.ROWS: | ||||
|             return matrix | ||||
|  | ||||
|         return [ | ||||
|             [col[col_entry] for col in matrix] | ||||
|             for col_entry in range(max(len(col) for col in matrix)) | ||||
|         ] | ||||
|  | ||||
|     def raw_scan(self): | ||||
|     def scan_for_pressed(self): | ||||
|         raise NotImplementedError('Abstract implementation') | ||||
|   | ||||
| @@ -100,55 +100,28 @@ def macro_complete_event(): | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def matrix_changed(new_matrix): | ||||
| def matrix_changed(new_pressed): | ||||
|     def _key_pressed(dispatch, get_state): | ||||
|         dispatch(new_matrix_event(new_pressed)) | ||||
|  | ||||
|         state = get_state() | ||||
|         # Temporarily preserve a reference to the old event | ||||
|         # We do fake Redux around here because microcontrollers | ||||
|         # aren't exactly RAM or CPU powerhouses - the state does | ||||
|         # mutate in place. Unfortunately this makes reasoning | ||||
|         # about code a bit messier and really hurts one of the | ||||
|         # selling points of Redux. Former development versions | ||||
|         # of KMK created new InternalState copies every single | ||||
|         # time the state changed, but it was sometimes slow. | ||||
|         old_matrix = state.matrix | ||||
|         old_keys_pressed = state.keys_pressed | ||||
|  | ||||
|         dispatch(new_matrix_event(new_matrix)) | ||||
|         if state.hid_pending: | ||||
|             dispatch(hid_report_event()) | ||||
|  | ||||
|         with get_state() as new_state: | ||||
|             for ridx, row in enumerate(new_state.matrix): | ||||
|                 for cidx, col in enumerate(row): | ||||
|                     if col != old_matrix[ridx][cidx]: | ||||
|                         if col: | ||||
|                             dispatch(key_down_event( | ||||
|                                 row=ridx, | ||||
|                                 col=cidx, | ||||
|                             )) | ||||
|                         else: | ||||
|                             dispatch(key_up_event( | ||||
|                                 row=ridx, | ||||
|                                 col=cidx, | ||||
|                             )) | ||||
|         if Keycodes.KMK.KC_RESET in state.keys_pressed: | ||||
|             try: | ||||
|                 import machine | ||||
|                 machine.bootloader() | ||||
|             except ImportError: | ||||
|                 logger.warning('Tried to reset to bootloader, but not supported on this chip?') | ||||
|  | ||||
|         with get_state() as new_state: | ||||
|             if old_keys_pressed != new_state.keys_pressed: | ||||
|                 dispatch(hid_report_event()) | ||||
|         if state.macro_pending: | ||||
|             macro = state.macro_pending | ||||
|  | ||||
|             if Keycodes.KMK.KC_RESET in new_state.keys_pressed: | ||||
|                 try: | ||||
|                     import machine | ||||
|                     machine.bootloader() | ||||
|                 except ImportError: | ||||
|                     logger.warning('Tried to reset to bootloader, but not supported on this chip?') | ||||
|             for event in macro(state): | ||||
|                 dispatch(event) | ||||
|  | ||||
|         with get_state() as new_state: | ||||
|             if new_state.macro_pending: | ||||
|                 macro = new_state.macro_pending | ||||
|  | ||||
|                 for event in macro(new_state): | ||||
|                     dispatch(event) | ||||
|  | ||||
|                 dispatch(macro_complete_event()) | ||||
|             dispatch(macro_complete_event()) | ||||
|  | ||||
|     return _key_pressed | ||||
|   | ||||
| @@ -4,7 +4,7 @@ from kmk.common.event_defs import KEY_DOWN_EVENT, KEY_UP_EVENT | ||||
| from kmk.common.keycodes import Keycodes, RawKeycodes | ||||
|  | ||||
|  | ||||
| def process_internal_key_event(state, action, changed_key, logger=None): | ||||
| def process_internal_key_event(state, action_type, changed_key, logger=None): | ||||
|     if logger is None: | ||||
|         logger = logging.getLogger(__name__) | ||||
|  | ||||
| @@ -14,71 +14,58 @@ def process_internal_key_event(state, action, changed_key, logger=None): | ||||
|     # objects | ||||
|  | ||||
|     if changed_key.code == RawKeycodes.KC_DF: | ||||
|         return df(state, action, changed_key, logger=logger) | ||||
|         return df(state, action_type, changed_key, logger=logger) | ||||
|     elif changed_key.code == RawKeycodes.KC_MO: | ||||
|         return mo(state, action, changed_key, logger=logger) | ||||
|         return mo(state, action_type, changed_key, logger=logger) | ||||
|     elif changed_key.code == RawKeycodes.KC_TG: | ||||
|         return tg(state, action, changed_key, logger=logger) | ||||
|         return tg(state, action_type, changed_key, logger=logger) | ||||
|     elif changed_key.code == RawKeycodes.KC_TO: | ||||
|         return to(state, action, changed_key, logger=logger) | ||||
|         return to(state, action_type, changed_key, logger=logger) | ||||
|     elif changed_key.code == Keycodes.KMK.KC_GESC.code: | ||||
|         return grave_escape(state, action, logger=logger) | ||||
|         return grave_escape(state, action_type, logger=logger) | ||||
|     elif changed_key.code == RawKeycodes.KC_UC_MODE: | ||||
|         return unicode_mode(state, action, changed_key, logger=logger) | ||||
|         return unicode_mode(state, action_type, changed_key, logger=logger) | ||||
|     else: | ||||
|         return state | ||||
|  | ||||
|  | ||||
| def grave_escape(state, action, logger): | ||||
|     if action[0] == KEY_DOWN_EVENT: | ||||
| def grave_escape(state, action_type, logger): | ||||
|     if action_type == KEY_DOWN_EVENT: | ||||
|         for key in state.keys_pressed: | ||||
|             if key in {Keycodes.Modifiers.KC_LSHIFT, Keycodes.Modifiers.KC_RSHIFT}: | ||||
|                 # if Shift is held, return KC_GRAVE which will become KC_TILDE on OS level | ||||
|                 return state.update( | ||||
|                     keys_pressed=( | ||||
|                         state.keys_pressed | {Keycodes.Common.KC_GRAVE} | ||||
|                     ), | ||||
|                 ) | ||||
|             elif key in {Keycodes.Modifiers.KC_LGUI, Keycodes.Modifiers.KC_RGUI}: | ||||
|                 # if GUI is held, return KC_GRAVE | ||||
|                 return state.update( | ||||
|                     keys_pressed=( | ||||
|                         state.keys_pressed | {Keycodes.Common.KC_GRAVE} | ||||
|                     ), | ||||
|                 ) | ||||
|             if key in { | ||||
|                 Keycodes.Modifiers.KC_LSHIFT, Keycodes.Modifiers.KC_RSHIFT, | ||||
|                 Keycodes.Modifiers.KC_LGUI, Keycodes.Modifiers.KC_RGUI, | ||||
|             }: | ||||
|                 # if Shift is held, KC_GRAVE will become KC_TILDE on OS level | ||||
|                 state.keys_pressed.add(Keycodes.Common.KC_GRAVE) | ||||
|                 return state | ||||
|  | ||||
|             # else return KC_ESC | ||||
|             return state.update( | ||||
|                 keys_pressed=( | ||||
|                     state.keys_pressed | {Keycodes.Common.KC_ESCAPE} | ||||
|                 ), | ||||
|             ) | ||||
|             state.keys_pressed.add(Keycodes.Common.KC_ESCAPE) | ||||
|             return state | ||||
|  | ||||
|     elif action[0] == KEY_UP_EVENT: | ||||
|         return state.update( | ||||
|             keys_pressed=frozenset( | ||||
|                 key for key in state.keys_pressed | ||||
|                 if key not in {Keycodes.Common.KC_ESCAPE, Keycodes.Common.KC_GRAVE} | ||||
|             ), | ||||
|         ) | ||||
|     elif action_type == KEY_UP_EVENT: | ||||
|         state.keys_pressed.discard(Keycodes.Common.KC_ESCAPE) | ||||
|         state.keys_pressed.discard(Keycodes.Common.KC_GRAVE) | ||||
|         return state | ||||
|  | ||||
|  | ||||
| def df(state, action, changed_key, logger): | ||||
| def df(state, action_type, changed_key, logger): | ||||
|     """Switches the default layer""" | ||||
|     if action[0] == KEY_DOWN_EVENT: | ||||
|     if action_type == KEY_DOWN_EVENT: | ||||
|         state.active_layers[0] = changed_key.layer | ||||
|  | ||||
|     return state | ||||
|  | ||||
|  | ||||
| def mo(state, action, changed_key, logger): | ||||
| def mo(state, action_type, changed_key, logger): | ||||
|     """Momentarily activates layer, switches off when you let go""" | ||||
|     if action[0] == KEY_UP_EVENT: | ||||
|     if action_type == KEY_UP_EVENT: | ||||
|         state.active_layers = [ | ||||
|             layer for layer in state.active_layers | ||||
|             if layer != changed_key.layer | ||||
|         ] | ||||
|     elif action[0] == KEY_DOWN_EVENT: | ||||
|     elif action_type == KEY_DOWN_EVENT: | ||||
|         state.active_layers.append(changed_key.layer) | ||||
|  | ||||
|     return state | ||||
| @@ -92,9 +79,9 @@ def lt(layer, kc): | ||||
|     """Momentarily activates layer if held, sends kc if tapped""" | ||||
|  | ||||
|  | ||||
| def tg(state, action, changed_key, logger): | ||||
| def tg(state, action_type, changed_key, logger): | ||||
|     """Toggles the layer (enables it if not active, and vise versa)""" | ||||
|     if action[0] == KEY_DOWN_EVENT: | ||||
|     if action_type == KEY_DOWN_EVENT: | ||||
|         if changed_key.layer in state.active_layers: | ||||
|             state.active_layers = [ | ||||
|                 layer for layer in state.active_layers | ||||
| @@ -106,9 +93,9 @@ def tg(state, action, changed_key, logger): | ||||
|     return state | ||||
|  | ||||
|  | ||||
| def to(state, action, changed_key, logger): | ||||
| def to(state, action_type, changed_key, logger): | ||||
|     """Activates layer and deactivates all other layers""" | ||||
|     if action[0] == KEY_DOWN_EVENT: | ||||
|     if action_type == KEY_DOWN_EVENT: | ||||
|         state.active_layers = [changed_key.layer] | ||||
|  | ||||
|     return state | ||||
| @@ -118,8 +105,8 @@ def tt(layer): | ||||
|     """Momentarily activates layer if held, toggles it if tapped repeatedly""" | ||||
|  | ||||
|  | ||||
| def unicode_mode(state, action, changed_key, logger): | ||||
|     if action[0] == KEY_DOWN_EVENT: | ||||
| def unicode_mode(state, action_type, changed_key, logger): | ||||
|     if action_type == KEY_DOWN_EVENT: | ||||
|         state.unicode_mode = changed_key.mode | ||||
|  | ||||
|     return state | ||||
|   | ||||
| @@ -52,9 +52,9 @@ class ReduxStore: | ||||
|  | ||||
|  | ||||
| class InternalState: | ||||
|     modifiers_pressed = frozenset() | ||||
|     keys_pressed = frozenset() | ||||
|     keys_pressed = set() | ||||
|     macro_pending = None | ||||
|     hid_pending = False | ||||
|     unicode_mode = UnicodeModes.NOOP | ||||
|     keymap = [] | ||||
|     row_pins = [] | ||||
| @@ -76,7 +76,6 @@ class InternalState: | ||||
|     def to_dict(self, verbose=False): | ||||
|         ret = { | ||||
|             'keys_pressed': self.keys_pressed, | ||||
|             'modifiers_pressed': self.modifiers_pressed, | ||||
|             'active_layers': self.active_layers, | ||||
|             'unicode_mode': self.unicode_mode, | ||||
|         } | ||||
| @@ -134,83 +133,60 @@ def kmk_reducer(state=None, action=None, logger=None): | ||||
|         return state | ||||
|  | ||||
|     if action[0] == NEW_MATRIX_EVENT: | ||||
|         return state.update( | ||||
|             matrix=action[1], | ||||
|         ) | ||||
|         matrix_keys_pressed = { | ||||
|             find_key_in_map(state, row, col) | ||||
|             for row, col in action[1] | ||||
|         } | ||||
|  | ||||
|         pressed = matrix_keys_pressed - state.keys_pressed | ||||
|         released = state.keys_pressed - matrix_keys_pressed | ||||
|  | ||||
|         if not pressed and not released: | ||||
|             return state | ||||
|  | ||||
|         for changed_key in released: | ||||
|             if not changed_key: | ||||
|                 continue | ||||
|  | ||||
|             elif isinstance(changed_key, KMKMacro): | ||||
|                 if changed_key.keyup: | ||||
|                     state.macro_pending = changed_key.keyup | ||||
|  | ||||
|             elif changed_key.code >= FIRST_KMK_INTERNAL_KEYCODE: | ||||
|                 state = process_internal_key_event(state, KEY_UP_EVENT, changed_key, logger=logger) | ||||
|  | ||||
|         for changed_key in pressed: | ||||
|             if not changed_key: | ||||
|                 continue | ||||
|  | ||||
|             elif isinstance(changed_key, KMKMacro): | ||||
|                 if changed_key.keydown: | ||||
|                     state.macro_pending = changed_key.keydown | ||||
|  | ||||
|             elif changed_key.code >= FIRST_KMK_INTERNAL_KEYCODE: | ||||
|                 state = process_internal_key_event( | ||||
|                     state, | ||||
|                     KEY_DOWN_EVENT, | ||||
|                     changed_key, | ||||
|                     logger=logger, | ||||
|                 ) | ||||
|  | ||||
|         state.matrix = action[1] | ||||
|         state.keys_pressed |= pressed | ||||
|         state.keys_pressed -= released | ||||
|         state.hid_pending = True | ||||
|  | ||||
|         return state | ||||
|  | ||||
|     if action[0] == KEYCODE_UP_EVENT: | ||||
|         return state.update( | ||||
|             keys_pressed=frozenset( | ||||
|                 key for key in state.keys_pressed if key != action[1] | ||||
|             ), | ||||
|         ) | ||||
|         state.keys_pressed.discard(action[1]) | ||||
|         state.hid_pending = True | ||||
|         return state | ||||
|  | ||||
|     if action[0] == KEYCODE_DOWN_EVENT: | ||||
|         return state.update( | ||||
|             keys_pressed=( | ||||
|                 state.keys_pressed | {action[1]} | ||||
|             ), | ||||
|         ) | ||||
|  | ||||
|     if action[0] == KEY_UP_EVENT: | ||||
|         row = action[1] | ||||
|         col = action[2] | ||||
|  | ||||
|         changed_key = find_key_in_map(state, row, col) | ||||
|  | ||||
|         logger.debug('Detected change to key: {}'.format(changed_key)) | ||||
|  | ||||
|         if not changed_key: | ||||
|             return state | ||||
|  | ||||
|         if isinstance(changed_key, KMKMacro): | ||||
|             if changed_key.keyup: | ||||
|                 return state.update( | ||||
|                     macro_pending=changed_key.keyup, | ||||
|                 ) | ||||
|  | ||||
|             return state | ||||
|  | ||||
|         newstate = state.update( | ||||
|             keys_pressed=frozenset( | ||||
|                 key for key in state.keys_pressed if key != changed_key | ||||
|             ), | ||||
|         ) | ||||
|  | ||||
|         if changed_key.code >= FIRST_KMK_INTERNAL_KEYCODE: | ||||
|             return process_internal_key_event(newstate, action, changed_key, logger=logger) | ||||
|  | ||||
|         return newstate | ||||
|  | ||||
|     if action[0] == KEY_DOWN_EVENT: | ||||
|         row = action[1] | ||||
|         col = action[2] | ||||
|  | ||||
|         changed_key = find_key_in_map(state, row, col) | ||||
|  | ||||
|         logger.debug('Detected change to key: {}'.format(changed_key)) | ||||
|  | ||||
|         if not changed_key: | ||||
|             return state | ||||
|  | ||||
|         if isinstance(changed_key, KMKMacro): | ||||
|             if changed_key.keydown: | ||||
|                 return state.update( | ||||
|                     macro_pending=changed_key.keydown, | ||||
|                 ) | ||||
|  | ||||
|             return state | ||||
|  | ||||
|         newstate = state.update( | ||||
|             keys_pressed=( | ||||
|                 state.keys_pressed | {changed_key} | ||||
|             ), | ||||
|         ) | ||||
|  | ||||
|         if changed_key.code >= FIRST_KMK_INTERNAL_KEYCODE: | ||||
|             return process_internal_key_event(newstate, action, changed_key, logger=logger) | ||||
|  | ||||
|         return newstate | ||||
|         state.keys_pressed.add(action[1]) | ||||
|         state.hid_pending = True | ||||
|         return state | ||||
|  | ||||
|     if action[0] == INIT_FIRMWARE_EVENT: | ||||
|         return state.update( | ||||
| @@ -230,6 +206,7 @@ def kmk_reducer(state=None, action=None, logger=None): | ||||
|     # into KEY_UP_EVENT and KEY_DOWN_EVENT, but for now it's nice to separate | ||||
|     # this out for debugging's sake. | ||||
|     if action[0] == HID_REPORT_EVENT: | ||||
|         state.hid_pending = False | ||||
|         return state | ||||
|  | ||||
|     if action[0] == MACRO_COMPLETE_EVENT: | ||||
|   | ||||
		Reference in New Issue
	
	Block a user