import logging from collections import namedtuple from micropython import const from kmk.common.keycodes import Keycodes KEY_UP_EVENT = const(1) KEY_DOWN_EVENT = const(2) INIT_FIRMWARE_EVENT = const(3) NEW_MATRIX_EVENT = const(4) HID_REPORT_EVENT = const(5) KEYCODE_UP_EVENT = const(6) KEYCODE_DOWN_EVENT = const(7) 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 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 KeyUpDown( type=KEY_UP_EVENT, row=row, col=col, ) def key_down_event(row, col): return KeyUpDown( type=KEY_DOWN_EVENT, row=row, col=col, ) def keycode_up_event(keycode): ''' Press a key by Keycode object, bypassing the keymap. Used mostly for macros. ''' return KeycodeUpDown( type=KEYCODE_UP_EVENT, keycode=keycode, ) def keycode_down_event(keycode): ''' Release a key by Keycode object, bypassing the keymap. Used mostly for macros. ''' return KeycodeUpDown( type=KEYCODE_DOWN_EVENT, keycode=keycode, ) def new_matrix_event(matrix): return NewMatrix( type=NEW_MATRIX_EVENT, matrix=matrix, ) def hid_report_event(): return BareEvent( type=HID_REPORT_EVENT, ) def macro_complete_event(): return BareEvent( type=MACRO_COMPLETE_EVENT, ) def matrix_changed(new_matrix): def _key_pressed(dispatch, get_state): 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)) 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, )) with get_state() as new_state: if old_keys_pressed != new_state.keys_pressed: dispatch(hid_report_event()) 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?') 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()) return _key_pressed