From 17094a29888b0de01821967cb6fa75680fc96abb Mon Sep 17 00:00:00 2001 From: Josh Klar Date: Fri, 12 Jul 2019 16:38:50 -0700 Subject: [PATCH] Clean some code up; force GC on every cycle --- kmk/firmware.py | 58 +++++++++++++++++++++-------------- kmk/internal_state.py | 1 - kmk/key_validators.py | 32 +++++++++++++++++++ kmk/keys.py | 71 +++---------------------------------------- 4 files changed, 72 insertions(+), 90 deletions(-) create mode 100644 kmk/key_validators.py diff --git a/kmk/firmware.py b/kmk/firmware.py index 8e37cb7..dba794d 100644 --- a/kmk/firmware.py +++ b/kmk/firmware.py @@ -50,6 +50,7 @@ import kmk.internal_state # isort:skip # Thanks for sticking around. Now let's do real work, starting below +from kmk.kmktime import sleep_ms from kmk.util import intify_coordinate as ic @@ -156,13 +157,18 @@ class Firmware: ) def _print_debug_cycle(self, init=False): + pre_alloc = gc.mem_alloc() + pre_free = gc.mem_free() + if self.debug_enabled: if init: print('KMKInit()') print(self) print(self._state) - print('GCStats(alloc={} free={})'.format( + print('GCStats(pre_alloc={} pre_free={} alloc={} free={})'.format( + pre_alloc, + pre_free, gc.mem_alloc(), gc.mem_free(), )) @@ -222,21 +228,8 @@ class Firmware: self.uart.write('DEB') self.uart.write(message, '\n') - def _master_half(self): - if self.is_master is not None: - return self.is_master - - # Working around https://github.com/adafruit/circuitpython/issues/1769 - try: - self._hid_helper_inst.create_report([]).send() - self.is_master = True - except OSError: - self.is_master = False - - return self.is_master - def init_uart(self, pin, timeout=20): - if self._master_half(): + if self.is_master: return busio.UART(tx=None, rx=pin, timeout=timeout) else: return busio.UART(tx=pin, rx=None, timeout=timeout) @@ -250,13 +243,27 @@ class Firmware: self._hid_helper_inst = self.hid_helper() # Split keyboard Init - if self.split_flip and not self._master_half(): - self.col_pins = list(reversed(self.col_pins)) + if self.split_type is not None: + try: + # Working around https://github.com/adafruit/circuitpython/issues/1769 + self._hid_helper_inst.create_report([]).send() + self.is_master = True - if self.split_side == "Left": - self.split_master_left = self._master_half() - elif self.split_side == "Right": - self.split_master_left = not self._master_half() + # Sleep 2s so master portion doesn't "appear" to boot quicker than + # dependent portions (which will take ~2s to time out on the HID send) + sleep_ms(2000) + except OSError: + self.is_master = False + + if self.split_flip and not self.is_master: + self.col_pins = list(reversed(self.col_pins)) + + if self.split_side == "Left": + self.split_master_left = self.is_master + elif self.split_side == "Right": + self.split_master_left = not self.is_master + else: + self.is_master = True if self.uart_pin is not None: self.uart = self.init_uart(self.uart_pin) @@ -278,12 +285,17 @@ class Firmware: if not isinstance(k, tuple): del self.leader_dictionary[k] + gc.collect() self._print_debug_cycle(init=True) while True: + # Generally speaking, the less stuff GC has to clean out, + # the faster it'll run. Start every cycle with a clean + # garbage bin to avoid random hiccups during keypress handling + gc.collect() state_changed = False - if self.split_type is not None and self._master_half: + if self.split_type is not None and self.is_master: update = self._receive_from_slave() if update is not None: self._handle_matrix_report(update) @@ -292,7 +304,7 @@ class Firmware: update = self.matrix.scan_for_changes() if update is not None: - if self._master_half(): + if self.is_master: self._handle_matrix_report(update) state_changed = True else: diff --git a/kmk/internal_state.py b/kmk/internal_state.py index 292ff11..a50e399 100644 --- a/kmk/internal_state.py +++ b/kmk/internal_state.py @@ -144,7 +144,6 @@ class InternalState: return self.process_key(kc_changed, is_pressed, int_coord, (row, col)) def process_key(self, key, is_pressed, coord_int=None, coord_raw=None): - if self.tapping and not isinstance(key.meta, TapDanceKeyMeta): self._process_tap_dance(key, is_pressed) else: diff --git a/kmk/key_validators.py b/kmk/key_validators.py new file mode 100644 index 0000000..bff9bfd --- /dev/null +++ b/kmk/key_validators.py @@ -0,0 +1,32 @@ +from kmk.types import (KeySeqSleepMeta, LayerKeyMeta, ModTapKeyMeta, + TapDanceKeyMeta, UnicodeModeKeyMeta) + + +def key_seq_sleep_validator(ms): + return KeySeqSleepMeta(ms) + + +def layer_key_validator(layer, kc=None): + ''' + Validates the syntax (but not semantics) of a layer key call. We won't + have access to the keymap here, so we can't verify much of anything useful + here (like whether the target layer actually exists). The spirit of this + existing is mostly that Python will catch extraneous args/kwargs and error + out. + ''' + return LayerKeyMeta(layer=layer, kc=kc) + + +def mod_tap_validator(kc, mods=None): + ''' + Validates that mod tap keys are correctly used + ''' + return ModTapKeyMeta(kc=kc, mods=mods) + + +def tap_dance_key_validator(*codes): + return TapDanceKeyMeta(codes) + + +def unicode_mode_key_validator(mode): + return UnicodeModeKeyMeta(mode) diff --git a/kmk/keys.py b/kmk/keys.py index 2571b76..9607454 100644 --- a/kmk/keys.py +++ b/kmk/keys.py @@ -1,11 +1,11 @@ -import gc - import kmk.handlers.layers as layers import kmk.handlers.modtap as modtap import kmk.handlers.stock as handlers from kmk.consts import UnicodeMode -from kmk.types import (AttrDict, KeySeqSleepMeta, LayerKeyMeta, ModTapKeyMeta, - TapDanceKeyMeta, UnicodeModeKeyMeta) +from kmk.key_validators import (key_seq_sleep_validator, layer_key_validator, + mod_tap_validator, tap_dance_key_validator, + unicode_mode_key_validator) +from kmk.types import AttrDict, UnicodeModeKeyMeta FIRST_KMK_INTERNAL_KEY = 1000 NEXT_AVAILABLE_KEY = 1000 @@ -367,8 +367,6 @@ def make_argumented_key( return _argumented_key -gc.collect() - # Modifiers make_mod_key(code=0x01, names=('LEFT_CONTROL', 'LCTRL', 'LCTL')) make_mod_key(code=0x02, names=('LEFT_SHIFT', 'LSHIFT', 'LSFT')) @@ -383,8 +381,6 @@ make_mod_key(code=0x07, names=('MEH',)) # HYPR = LCTL | LALT | LSFT | LGUI make_mod_key(code=0x0F, names=('HYPER', 'HYPR')) -gc.collect() - # Basic ASCII letters make_key(code=4, names=('A',)) make_key(code=5, names=('B',)) @@ -413,8 +409,6 @@ make_key(code=27, names=('X',)) make_key(code=28, names=('Y',)) make_key(code=29, names=('Z',)) -gc.collect() - # Numbers # Aliases to play nicely with AttrDict, since KC.1 isn't a valid # attribute key in Python, but KC.N1 is @@ -429,8 +423,6 @@ make_key(code=37, names=('8', 'N8')) make_key(code=38, names=('9', 'N9')) make_key(code=39, names=('0', 'N0')) -gc.collect() - # More ASCII standard keys make_key(code=40, names=('ENTER', 'ENT', "\n")) make_key(code=41, names=('ESCAPE', 'ESC')) @@ -449,8 +441,6 @@ make_key(code=54, names=('COMMA', 'COMM', ',')) make_key(code=55, names=('DOT', '.')) make_key(code=56, names=('SLASH', 'SLSH')) -gc.collect() - # Function Keys make_key(code=58, names=('F1',)) make_key(code=59, names=('F2',)) @@ -477,8 +467,6 @@ make_key(code=113, names=('F22',)) make_key(code=114, names=('F23',)) make_key(code=115, names=('F24',)) -gc.collect() - # Lock Keys, Navigation, etc. make_key(code=57, names=('CAPS_LOCK', 'CAPSLOCK', 'CLCK', 'CAPS')) # FIXME: Investigate whether this key actually works, and @@ -501,8 +489,6 @@ make_key(code=80, names=('LEFT',)) make_key(code=81, names=('DOWN',)) make_key(code=82, names=('UP',)) -gc.collect() - # Numpad make_key(code=83, names=('NUM_LOCK', 'NUMLOCK', 'NLCK')) # FIXME: Investigate whether this key actually works, and @@ -528,8 +514,6 @@ make_key(code=103, names=('KP_EQUAL', 'PEQL', 'NUMPAD_EQUAL')) make_key(code=133, names=('KP_COMMA', 'PCMM', 'NUMPAD_COMMA')) make_key(code=134, names=('KP_EQUAL_AS400', 'NUMPAD_EQUAL_AS400')) -gc.collect() - # Making life better for folks on tiny keyboards especially: exposes # the "shifted" keys as raw keys. Under the hood we're still # sending Shift+(whatever key is normally pressed) to get these, so @@ -556,8 +540,6 @@ make_shifted_key('COMMA', names=('LEFT_ANGLE_BRACKET', 'LABK', '<')) make_shifted_key('DOT', names=('RIGHT_ANGLE_BRACKET', 'RABK', '>')) make_shifted_key('SLSH', names=('QUESTION', 'QUES', '?')) -gc.collect() - # International make_key(code=50, names=('NONUS_HASH', 'NUHS')) make_key(code=100, names=('NONUS_BSLASH', 'NUBS')) @@ -582,8 +564,6 @@ make_key(code=150, names=('LANG7',)) make_key(code=151, names=('LANG8',)) make_key(code=152, names=('LANG9',)) -gc.collect() - # Consumer ("media") keys. Most known keys aren't supported here. A much # longer list used to exist in this file, but the codes were almost certainly # incorrect, conflicting with each other, or otherwise "weird". We'll add them @@ -605,8 +585,6 @@ make_consumer_key(code=184, names=('MEDIA_EJECT', 'EJCT')) # 0xB8 make_consumer_key(code=179, names=('MEDIA_FAST_FORWARD', 'MFFD')) # 0xB3 make_consumer_key(code=180, names=('MEDIA_REWIND', 'MRWD')) # 0xB4 -gc.collect() - # Internal, diagnostic, or auxiliary/enhanced keys # NO and TRNS are functionally identical in how they (don't) mutate @@ -631,18 +609,6 @@ make_key( on_release=handlers.passthrough, ) - -def layer_key_validator(layer, kc=None): - ''' - Validates the syntax (but not semantics) of a layer key call. We won't - have access to the keymap here, so we can't verify much of anything useful - here (like whether the target layer actually exists). The spirit of this - existing is mostly that Python will catch extraneous args/kwargs and error - out. - ''' - return LayerKeyMeta(layer=layer, kc=kc) - - # Layers make_argumented_key( validator=layer_key_validator, @@ -684,15 +650,6 @@ make_argumented_key( on_release=layers.tt_released, ) - -def mod_tap_validator(kc, mods=None): - ''' - Validates that mod tap keys are correctly used - ''' - return ModTapKeyMeta(kc=kc, mods=mods) - - -# ModTap make_argumented_key( validator=mod_tap_validator, names=('MT',), @@ -700,14 +657,6 @@ make_argumented_key( on_release=modtap.mt_released, ) - -gc.collect() - - -def key_seq_sleep_validator(ms): - return KeySeqSleepMeta(ms) - - # A dummy key to trigger a sleep_ms call in a sequence of other keys in a # simple sequence macro. make_argumented_key( @@ -716,8 +665,6 @@ make_argumented_key( on_press=handlers.sleep_pressed, ) - -# Switch unicode modes at runtime make_key( names=('UC_MODE_NOOP', 'UC_DISABLE'), meta=UnicodeModeKeyMeta(UnicodeMode.NOOP), @@ -738,22 +685,14 @@ make_key( meta=UnicodeModeKeyMeta(UnicodeMode.WINC), on_press=handlers.uc_mode_pressed, ) - - -def unicode_mode_key_validator(mode): - return UnicodeModeKeyMeta(mode) - - make_argumented_key( validator=unicode_mode_key_validator, names=('UC_MODE',), on_press=handlers.uc_mode_pressed, ) - -# Tap Dance make_argumented_key( - validator=lambda *codes: TapDanceKeyMeta(codes), + validator=tap_dance_key_validator, names=('TAP_DANCE', 'TD'), on_press=handlers.td_pressed, on_release=handlers.td_released,