From 784f8885bd6303f9ab813bfae12af29c120a58d2 Mon Sep 17 00:00:00 2001 From: Josh Klar Date: Sun, 30 Sep 2018 22:50:04 -0700 Subject: [PATCH 1/3] Remove a ton of dict lookups for minor perf gains --- kmk/common/event_defs.py | 88 +++++++++++++++++++-------------- kmk/common/internal_keycodes.py | 16 +++--- kmk/common/internal_state.py | 48 +++++++++--------- kmk/micropython/pyb_hid.py | 2 +- 4 files changed, 85 insertions(+), 69 deletions(-) diff --git a/kmk/common/event_defs.py b/kmk/common/event_defs.py index a8eb07f..f061f39 100644 --- a/kmk/common/event_defs.py +++ b/kmk/common/event_defs.py @@ -1,4 +1,5 @@ import logging +from collections import namedtuple from micropython import const @@ -16,31 +17,46 @@ MACRO_COMPLETE_EVENT = const(8) logger = logging.getLogger(__name__) +InitFirmware = namedtuple('InitFirmware', ( + 'type', + 'keymap', + 'row_pins', + 'col_pins', + 'diode_orientation', + 'unicode_mode', +)) + +KeyUpDown = namedtuple('KeyUpDown', ('type', 'row', 'col')) +KeycodeUpDown = namedtuple('KeycodeUpDown', ('type', 'keycode')) +NewMatrix = namedtuple('NewMatrix', ('type', 'matrix')) +BareEvent = namedtuple('BareEvent', ('type',)) + + def init_firmware(keymap, row_pins, col_pins, diode_orientation, unicode_mode): - return { - 'type': INIT_FIRMWARE_EVENT, - 'keymap': keymap, - 'row_pins': row_pins, - 'col_pins': col_pins, - 'diode_orientation': diode_orientation, - 'unicode_mode': unicode_mode, - } + return InitFirmware( + type=INIT_FIRMWARE_EVENT, + keymap=keymap, + row_pins=row_pins, + col_pins=col_pins, + diode_orientation=diode_orientation, + unicode_mode=unicode_mode, + ) def key_up_event(row, col): - return { - 'type': KEY_UP_EVENT, - 'row': row, - 'col': col, - } + return KeyUpDown( + type=KEY_UP_EVENT, + row=row, + col=col, + ) def key_down_event(row, col): - return { - 'type': KEY_DOWN_EVENT, - 'row': row, - 'col': col, - } + return KeyUpDown( + type=KEY_DOWN_EVENT, + row=row, + col=col, + ) def keycode_up_event(keycode): @@ -48,10 +64,10 @@ def keycode_up_event(keycode): Press a key by Keycode object, bypassing the keymap. Used mostly for macros. ''' - return { - 'type': KEYCODE_UP_EVENT, - 'keycode': keycode, - } + return KeycodeUpDown( + type=KEYCODE_UP_EVENT, + keycode=keycode, + ) def keycode_down_event(keycode): @@ -59,29 +75,29 @@ def keycode_down_event(keycode): Release a key by Keycode object, bypassing the keymap. Used mostly for macros. ''' - return { - 'type': KEYCODE_DOWN_EVENT, - 'keycode': keycode, - } + return KeycodeUpDown( + type=KEYCODE_DOWN_EVENT, + keycode=keycode, + ) def new_matrix_event(matrix): - return { - 'type': NEW_MATRIX_EVENT, - 'matrix': matrix, - } + return NewMatrix( + type=NEW_MATRIX_EVENT, + matrix=matrix, + ) def hid_report_event(): - return { - 'type': HID_REPORT_EVENT, - } + return BareEvent( + type=HID_REPORT_EVENT, + ) def macro_complete_event(): - return { - 'type': MACRO_COMPLETE_EVENT, - } + return BareEvent( + type=MACRO_COMPLETE_EVENT, + ) def matrix_changed(new_matrix): diff --git a/kmk/common/internal_keycodes.py b/kmk/common/internal_keycodes.py index 05d42ad..2dd903e 100644 --- a/kmk/common/internal_keycodes.py +++ b/kmk/common/internal_keycodes.py @@ -30,7 +30,7 @@ def process_internal_key_event(state, action, changed_key, logger=None): def grave_escape(state, action, logger): - if action['type'] == KEY_DOWN_EVENT: + if action[0] == 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 @@ -54,7 +54,7 @@ def grave_escape(state, action, logger): ), ) - elif action['type'] == KEY_UP_EVENT: + elif action[0] == KEY_UP_EVENT: return state.update( keys_pressed=frozenset( key for key in state.keys_pressed @@ -65,7 +65,7 @@ def grave_escape(state, action, logger): def df(state, action, changed_key, logger): """Switches the default layer""" - if action['type'] == KEY_DOWN_EVENT: + if action[0] == KEY_DOWN_EVENT: state.active_layers[0] = changed_key.layer return state @@ -73,12 +73,12 @@ def df(state, action, changed_key, logger): def mo(state, action, changed_key, logger): """Momentarily activates layer, switches off when you let go""" - if action['type'] == KEY_UP_EVENT: + if action[0] == KEY_UP_EVENT: state.active_layers = [ layer for layer in state.active_layers if layer != changed_key.layer ] - elif action['type'] == KEY_DOWN_EVENT: + elif action[0] == KEY_DOWN_EVENT: state.active_layers.append(changed_key.layer) return state @@ -94,7 +94,7 @@ def lt(layer, kc): def tg(state, action, changed_key, logger): """Toggles the layer (enables it if not active, and vise versa)""" - if action['type'] == KEY_DOWN_EVENT: + if action[0] == KEY_DOWN_EVENT: if changed_key.layer in state.active_layers: state.active_layers = [ layer for layer in state.active_layers @@ -108,7 +108,7 @@ def tg(state, action, changed_key, logger): def to(state, action, changed_key, logger): """Activates layer and deactivates all other layers""" - if action['type'] == KEY_DOWN_EVENT: + if action[0] == KEY_DOWN_EVENT: state.active_layers = [changed_key.layer] return state @@ -119,7 +119,7 @@ def tt(layer): def unicode_mode(state, action, changed_key, logger): - if action['type'] == KEY_DOWN_EVENT: + if action[0] == KEY_DOWN_EVENT: state.unicode_mode = changed_key.mode return state diff --git a/kmk/common/internal_state.py b/kmk/common/internal_state.py index b6bc9cb..935fa55 100644 --- a/kmk/common/internal_state.py +++ b/kmk/common/internal_state.py @@ -26,9 +26,9 @@ class ReduxStore: self.logger.debug('Finished thunk') return None - self.logger.debug('Dispatching action: Type {} >> {}'.format(action['type'], action)) + self.logger.debug('Dispatching action: Type {} >> {}'.format(action[0], action)) self.state = self.reducer(self.state, action, logger=self.logger) - self.logger.debug('Dispatching complete: Type {}'.format(action['type'])) + self.logger.debug('Dispatching complete: Type {}'.format(action[0])) self.logger.debug('New state: {}'.format(self.state)) @@ -133,28 +133,28 @@ def kmk_reducer(state=None, action=None, logger=None): return state - if action['type'] == NEW_MATRIX_EVENT: + if action[0] == NEW_MATRIX_EVENT: return state.update( - matrix=action['matrix'], + matrix=action[1], ) - if action['type'] == KEYCODE_UP_EVENT: + if action[0] == KEYCODE_UP_EVENT: return state.update( keys_pressed=frozenset( - key for key in state.keys_pressed if key != action['keycode'] + key for key in state.keys_pressed if key != action[1] ), ) - if action['type'] == KEYCODE_DOWN_EVENT: + if action[0] == KEYCODE_DOWN_EVENT: return state.update( keys_pressed=( - state.keys_pressed | {action['keycode']} + state.keys_pressed | {action[1]} ), ) - if action['type'] == KEY_UP_EVENT: - row = action['row'] - col = action['col'] + if action[0] == KEY_UP_EVENT: + row = action[1] + col = action[2] changed_key = find_key_in_map(state, row, col) @@ -182,9 +182,9 @@ def kmk_reducer(state=None, action=None, logger=None): return newstate - if action['type'] == KEY_DOWN_EVENT: - row = action['row'] - col = action['col'] + if action[0] == KEY_DOWN_EVENT: + row = action[1] + col = action[2] changed_key = find_key_in_map(state, row, col) @@ -212,16 +212,16 @@ def kmk_reducer(state=None, action=None, logger=None): return newstate - if action['type'] == INIT_FIRMWARE_EVENT: + if action[0] == INIT_FIRMWARE_EVENT: return state.update( - keymap=action['keymap'], - row_pins=action['row_pins'], - col_pins=action['col_pins'], - diode_orientation=action['diode_orientation'], - unicode_mode=action['unicode_mode'], + keymap=action.keymap, + row_pins=action.row_pins, + col_pins=action.col_pins, + diode_orientation=action.diode_orientation, + unicode_mode=action.unicode_mode, matrix=[ - [False for c in action['col_pins']] - for r in action['row_pins'] + [False for c in action.col_pins] + for r in action.row_pins ], ) @@ -229,10 +229,10 @@ def kmk_reducer(state=None, action=None, logger=None): # they should be doing things. This could/should arguably be folded back # into KEY_UP_EVENT and KEY_DOWN_EVENT, but for now it's nice to separate # this out for debugging's sake. - if action['type'] == HID_REPORT_EVENT: + if action[0] == HID_REPORT_EVENT: return state - if action['type'] == MACRO_COMPLETE_EVENT: + if action[0] == MACRO_COMPLETE_EVENT: return state.update(macro_pending=None) # On unhandled events, log and do not mutate state diff --git a/kmk/micropython/pyb_hid.py b/kmk/micropython/pyb_hid.py index 49e2977..98d733c 100644 --- a/kmk/micropython/pyb_hid.py +++ b/kmk/micropython/pyb_hid.py @@ -43,7 +43,7 @@ class HIDHelper: self.report_non_mods = memoryview(self._evt)[3:] def _subscription(self, state, action): - if action['type'] == HID_REPORT_EVENT: + if action[0] == HID_REPORT_EVENT: self.clear_all() consumer_key = None From b5457534bff9798c221b86a0569f23594f4aaf59 Mon Sep 17 00:00:00 2001 From: Josh Klar Date: Mon, 1 Oct 2018 00:31:45 -0700 Subject: [PATCH 2/3] 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. --- kmk/common/abstract/matrix_scanner.py | 18 +--- kmk/common/event_defs.py | 59 ++++-------- kmk/common/internal_keycodes.py | 79 +++++++--------- kmk/common/internal_state.py | 129 +++++++++++--------------- kmk/firmware.py | 12 +-- kmk/micropython/matrix.py | 35 +++---- kmk/micropython/pyb_hid.py | 5 +- 7 files changed, 126 insertions(+), 211 deletions(-) diff --git a/kmk/common/abstract/matrix_scanner.py b/kmk/common/abstract/matrix_scanner.py index 28561c9..6f324b7 100644 --- a/kmk/common/abstract/matrix_scanner.py +++ b/kmk/common/abstract/matrix_scanner.py @@ -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') diff --git a/kmk/common/event_defs.py b/kmk/common/event_defs.py index f061f39..e7102f9 100644 --- a/kmk/common/event_defs.py +++ b/kmk/common/event_defs.py @@ -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 diff --git a/kmk/common/internal_keycodes.py b/kmk/common/internal_keycodes.py index 2dd903e..54ef00f 100644 --- a/kmk/common/internal_keycodes.py +++ b/kmk/common/internal_keycodes.py @@ -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 diff --git a/kmk/common/internal_state.py b/kmk/common/internal_state.py index 935fa55..36c5601 100644 --- a/kmk/common/internal_state.py +++ b/kmk/common/internal_state.py @@ -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: diff --git a/kmk/firmware.py b/kmk/firmware.py index bff6c08..b97c1b0 100644 --- a/kmk/firmware.py +++ b/kmk/firmware.py @@ -18,7 +18,8 @@ class Firmware: logger = logging.getLogger(__name__) logger.setLevel(log_level) - self.cached_state = None + self.hydrated = False + self.store = ReduxStore(kmk_reducer, log_level=log_level) self.store.subscribe( lambda state, action: self._subscription(state, action), @@ -41,20 +42,17 @@ class Firmware: )) def _subscription(self, state, action): - if self.cached_state is None or any( - getattr(self.cached_state, k) != getattr(state, k) - for k in state.__dict__.keys() - ): + if not self.hydrated: self.matrix = MatrixScanner( state.col_pins, state.row_pins, state.diode_orientation, ) + self.hydrated = True def go(self): while True: - state = self.store.get_state() - update = self.matrix.scan_for_changes(state.matrix) + update = self.matrix.scan_for_pressed() if update: self.store.dispatch(update) diff --git a/kmk/micropython/matrix.py b/kmk/micropython/matrix.py index 3677201..6f69105 100644 --- a/kmk/micropython/matrix.py +++ b/kmk/micropython/matrix.py @@ -21,6 +21,7 @@ class MatrixScanner(AbstractMatrixScanner): self.rows = [machine.Pin(pin) for pin in rows] self.diode_orientation = diode_orientation self.active_layers = active_layers + self.last_pressed_len = 0 if self.diode_orientation == DiodeOrientation.COLUMNS: self.outputs = self.cols @@ -41,29 +42,23 @@ class MatrixScanner(AbstractMatrixScanner): pin.init(machine.Pin.IN, machine.Pin.PULL_DOWN) pin.off() - def _normalize_matrix(self, matrix): - return super()._normalize_matrix(matrix) + def scan_for_pressed(self): + pressed = [] - def raw_scan(self): - matrix = [] - - for opin in self.outputs: + for oidx, opin in enumerate(self.outputs): opin.value(1) - matrix.append([bool(ipin.value()) for ipin in self.inputs]) + + for iidx, ipin in enumerate(self.inputs): + if ipin.value(): + pressed.append( + (oidx, iidx) if self.diode_orientation == DiodeOrientation.ROWS else (iidx, oidx) # noqa + ) + opin.value(0) - return self._normalize_matrix(matrix) - - def scan_for_changes(self, old_matrix): - matrix = self.raw_scan() - - if any( - any( - col != old_matrix[ridx][cidx] - for cidx, col in enumerate(row) - ) - for ridx, row in enumerate(matrix) - ): - return matrix_changed(matrix) + if len(pressed) != self.last_pressed_len: + print('LEN PRESSED {} LEN LAST PRESSED {}'.format(len(pressed), self.last_pressed_len)) + self.last_pressed_len = len(pressed) + return matrix_changed(pressed) return None # The default, but for explicitness diff --git a/kmk/micropython/pyb_hid.py b/kmk/micropython/pyb_hid.py index 98d733c..4299814 100644 --- a/kmk/micropython/pyb_hid.py +++ b/kmk/micropython/pyb_hid.py @@ -6,6 +6,7 @@ from kmk.common.consts import HID_REPORT_STRUCTURE, HIDReportTypes from kmk.common.event_defs import HID_REPORT_EVENT from kmk.common.keycodes import (FIRST_KMK_INTERNAL_KEYCODE, ConsumerKeycode, ModifierKeycode) +from kmk.common.macros import KMKMacro def generate_pyb_hid_descriptor(): @@ -71,7 +72,7 @@ class HIDHelper: self.add_key(consumer_key) else: for key in state.keys_pressed: - if key.code >= FIRST_KMK_INTERNAL_KEYCODE: + if isinstance(key, KMKMacro) or key.code >= FIRST_KMK_INTERNAL_KEYCODE: continue if isinstance(key, ModifierKeycode): @@ -98,7 +99,7 @@ class HIDHelper: # It'd be real awesome if pyb.USB_HID.send/recv would support # uselect.poll or uselect.select to more safely determine when # it is safe to write to the host again... - delay(10) + delay(5) return self From f80873fdd0beafa8cf3c3e96d6a3d146627090be Mon Sep 17 00:00:00 2001 From: Josh Klar Date: Mon, 1 Oct 2018 01:07:57 -0700 Subject: [PATCH 3/3] Roll back the tuple indexing change (now access by dot notation), optimize GESC and fix a bug in how it interacts with the reducer --- kmk/common/internal_keycodes.py | 23 ++++++++++++----------- kmk/common/internal_state.py | 28 ++++++++++++---------------- kmk/micropython/matrix.py | 1 - kmk/micropython/pyb_hid.py | 2 +- 4 files changed, 25 insertions(+), 29 deletions(-) diff --git a/kmk/common/internal_keycodes.py b/kmk/common/internal_keycodes.py index 54ef00f..ec34ebc 100644 --- a/kmk/common/internal_keycodes.py +++ b/kmk/common/internal_keycodes.py @@ -3,6 +3,11 @@ import logging from kmk.common.event_defs import KEY_DOWN_EVENT, KEY_UP_EVENT from kmk.common.keycodes import Keycodes, RawKeycodes +GESC_TRIGGERS = { + Keycodes.Modifiers.KC_LSHIFT, Keycodes.Modifiers.KC_RSHIFT, + Keycodes.Modifiers.KC_LGUI, Keycodes.Modifiers.KC_RGUI, +} + def process_internal_key_event(state, action_type, changed_key, logger=None): if logger is None: @@ -31,19 +36,15 @@ def process_internal_key_event(state, action_type, changed_key, logger=None): 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, - 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 - state.keys_pressed.add(Keycodes.Common.KC_ESCAPE) + if any(key in GESC_TRIGGERS for key in state.keys_pressed): + # 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 + state.keys_pressed.add(Keycodes.Common.KC_ESCAPE) + return state + elif action_type == KEY_UP_EVENT: state.keys_pressed.discard(Keycodes.Common.KC_ESCAPE) state.keys_pressed.discard(Keycodes.Common.KC_GRAVE) diff --git a/kmk/common/internal_state.py b/kmk/common/internal_state.py index 36c5601..47972de 100644 --- a/kmk/common/internal_state.py +++ b/kmk/common/internal_state.py @@ -26,9 +26,9 @@ class ReduxStore: self.logger.debug('Finished thunk') return None - self.logger.debug('Dispatching action: Type {} >> {}'.format(action[0], action)) + self.logger.debug('Dispatching action: Type {} >> {}'.format(action.type, action)) self.state = self.reducer(self.state, action, logger=self.logger) - self.logger.debug('Dispatching complete: Type {}'.format(action[0])) + self.logger.debug('Dispatching complete: Type {}'.format(action.type)) self.logger.debug('New state: {}'.format(self.state)) @@ -132,10 +132,10 @@ def kmk_reducer(state=None, action=None, logger=None): return state - if action[0] == NEW_MATRIX_EVENT: + if action.type == NEW_MATRIX_EVENT: matrix_keys_pressed = { find_key_in_map(state, row, col) - for row, col in action[1] + for row, col in action.matrix } pressed = matrix_keys_pressed - state.keys_pressed @@ -171,45 +171,41 @@ def kmk_reducer(state=None, action=None, logger=None): logger=logger, ) - state.matrix = action[1] + state.matrix = action.matrix state.keys_pressed |= pressed state.keys_pressed -= released state.hid_pending = True return state - if action[0] == KEYCODE_UP_EVENT: - state.keys_pressed.discard(action[1]) + if action.type == KEYCODE_UP_EVENT: + state.keys_pressed.discard(action.keycode) state.hid_pending = True return state - if action[0] == KEYCODE_DOWN_EVENT: - state.keys_pressed.add(action[1]) + if action.type == KEYCODE_DOWN_EVENT: + state.keys_pressed.add(action.keycode) state.hid_pending = True return state - if action[0] == INIT_FIRMWARE_EVENT: + if action.type == INIT_FIRMWARE_EVENT: return state.update( keymap=action.keymap, row_pins=action.row_pins, col_pins=action.col_pins, diode_orientation=action.diode_orientation, unicode_mode=action.unicode_mode, - matrix=[ - [False for c in action.col_pins] - for r in action.row_pins - ], ) # HID events are non-mutating, used exclusively for listeners to know # they should be doing things. This could/should arguably be folded back # 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: + if action.type == HID_REPORT_EVENT: state.hid_pending = False return state - if action[0] == MACRO_COMPLETE_EVENT: + if action.type == MACRO_COMPLETE_EVENT: return state.update(macro_pending=None) # On unhandled events, log and do not mutate state diff --git a/kmk/micropython/matrix.py b/kmk/micropython/matrix.py index 6f69105..a89f6dd 100644 --- a/kmk/micropython/matrix.py +++ b/kmk/micropython/matrix.py @@ -57,7 +57,6 @@ class MatrixScanner(AbstractMatrixScanner): opin.value(0) if len(pressed) != self.last_pressed_len: - print('LEN PRESSED {} LEN LAST PRESSED {}'.format(len(pressed), self.last_pressed_len)) self.last_pressed_len = len(pressed) return matrix_changed(pressed) diff --git a/kmk/micropython/pyb_hid.py b/kmk/micropython/pyb_hid.py index 4299814..315fac4 100644 --- a/kmk/micropython/pyb_hid.py +++ b/kmk/micropython/pyb_hid.py @@ -44,7 +44,7 @@ class HIDHelper: self.report_non_mods = memoryview(self._evt)[3:] def _subscription(self, state, action): - if action[0] == HID_REPORT_EVENT: + if action.type == HID_REPORT_EVENT: self.clear_all() consumer_key = None