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