diff --git a/kmk/internal_state.py b/kmk/internal_state.py index a3f130f..b27454b 100644 --- a/kmk/internal_state.py +++ b/kmk/internal_state.py @@ -107,9 +107,9 @@ class InternalState: self._process_tap_dance(key, is_pressed) else: if is_pressed: - key.on_press(self, coord_int, coord_raw) + key._on_press(self, coord_int, coord_raw) else: - key.on_release(self, coord_int, coord_raw) + key._on_release(self, coord_int, coord_raw) if self.config.leader_mode % 2 == 1: self._process_leader_mode() diff --git a/kmk/keys.py b/kmk/keys.py index b226f38..544c5be 100644 --- a/kmk/keys.py +++ b/kmk/keys.py @@ -35,8 +35,12 @@ class Key: self.no_press = bool(no_press) self.no_release = bool(no_press) - self._on_press = on_press - self._on_release = on_release + self._pre_press_handlers = [] + self._post_press_handlers = [] + self._pre_release_handlers = [] + self._post_release_handlers = [] + self._handle_press = on_press + self._handle_release = on_release self.meta = meta def __call__(self, no_press=None, no_release=None): @@ -53,11 +57,136 @@ class Key: def __repr__(self): return 'Key(code={}, has_modifiers={})'.format(self.code, self.has_modifiers) - def on_press(self, state, coord_int, coord_raw): - return self._on_press(self, state, KC, coord_int, coord_raw) + def _on_press(self, state, coord_int, coord_raw): + for fn in self._pre_press_handlers: + fn(self, state, KC, coord_int, coord_raw) - def on_release(self, state, coord_int, coord_raw): - return self._on_release(self, state, KC, coord_int, coord_raw) + ret = self._handle_press(self, state, KC, coord_int, coord_raw) + + for fn in self._post_press_handlers: + fn(self, state, KC, coord_int, coord_raw) + + return ret + + def _on_release(self, state, coord_int, coord_raw): + for fn in self._pre_release_handlers: + fn(self, state, KC, coord_int, coord_raw) + + ret = self._handle_release(self, state, KC, coord_int, coord_raw) + + for fn in self._post_release_handlers: + fn(self, state, KC, coord_int, coord_raw) + + return ret + + def clone(self): + ''' + Return a shallow clone of the current key without any pre/post press/release + handlers attached. Almost exclusively useful for creating non-colliding keys + to use such handlers. + ''' + + return type(self)( + code=self.code, + has_modifiers=self.has_modifiers, + no_press=self.no_press, + no_release=self.no_release, + on_press=self._handle_press, + on_release=self._handle_release, + meta=self.meta, + ) + + def before_press_handler(self, fn): + ''' + Attach a callback to be run prior to the on_press handler for this key. + Receives the following: + + - self (this Key instance) + - state (the current InternalState) + - KC (the global KC lookup table, for convenience) + - coord_int (an internal integer representation of the matrix coordinate + for the pressed key - this is likely not useful to end users, but is + provided for consistency with the internal handlers) + - coord_raw (an X,Y tuple of the matrix coordinate - also likely not useful) + + The return value of the provided callback is discarded. Exceptions are _not_ + caught, and will likely crash KMK if not handled within your function. + + These handlers are run in attachment order: handlers provided by earlier + calls of this method will be executed before those provided by later calls. + ''' + + self._pre_press_handlers.append(fn) + return self + + def after_press_handler(self, fn): + ''' + Attach a callback to be run after the on_release handler for this key. + Receives the following: + + - self (this Key instance) + - state (the current InternalState) + - KC (the global KC lookup table, for convenience) + - coord_int (an internal integer representation of the matrix coordinate + for the pressed key - this is likely not useful to end users, but is + provided for consistency with the internal handlers) + - coord_raw (an X,Y tuple of the matrix coordinate - also likely not useful) + + The return value of the provided callback is discarded. Exceptions are _not_ + caught, and will likely crash KMK if not handled within your function. + + These handlers are run in attachment order: handlers provided by earlier + calls of this method will be executed before those provided by later calls. + ''' + + self._post_press_handlers.append(fn) + return self + + def before_release_handler(self, fn): + ''' + Attach a callback to be run prior to the on_release handler for this + key. Receives the following: + + - self (this Key instance) + - state (the current InternalState) + - KC (the global KC lookup table, for convenience) + - coord_int (an internal integer representation of the matrix coordinate + for the pressed key - this is likely not useful to end users, but is + provided for consistency with the internal handlers) + - coord_raw (an X,Y tuple of the matrix coordinate - also likely not useful) + + The return value of the provided callback is discarded. Exceptions are _not_ + caught, and will likely crash KMK if not handled within your function. + + These handlers are run in attachment order: handlers provided by earlier + calls of this method will be executed before those provided by later calls. + ''' + + self._pre_release_handlers.append(fn) + return self + + def after_release_handler(self, fn): + ''' + Attach a callback to be run after the on_release handler for this key. + Receives the following: + + - self (this Key instance) + - state (the current InternalState) + - KC (the global KC lookup table, for convenience) + - coord_int (an internal integer representation of the matrix coordinate + for the pressed key - this is likely not useful to end users, but is + provided for consistency with the internal handlers) + - coord_raw (an X,Y tuple of the matrix coordinate - also likely not useful) + + The return value of the provided callback is discarded. Exceptions are _not_ + caught, and will likely crash KMK if not handled within your function. + + These handlers are run in attachment order: handlers provided by earlier + calls of this method will be executed before those provided by later calls. + ''' + + self._post_release_handlers.append(fn) + return self class ModifierKey(Key): diff --git a/user_keymaps/klardotsh/klarank_featherm4.py b/user_keymaps/klardotsh/klarank_featherm4.py index 249dfd7..5abb66b 100644 --- a/user_keymaps/klardotsh/klarank_featherm4.py +++ b/user_keymaps/klardotsh/klarank_featherm4.py @@ -89,8 +89,16 @@ def shrek_is_life(*args, **kwargs): print('⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⠻⠿⠿⠿⠿⠛⠉') +# Spew shrek when hitting a fully custom key +# This won't modify internal state at all SHREK_IS_LOVE = make_key(on_press=shrek_is_life) +# Also spew shrek every time I try to use Alt. It's a dev board, after all. +KC.LALT.before_press_handler(shrek_is_life) + +# But also give me a normal alt if I want it. Shrek isn't ALWAYS life. +BORING_ALT = KC.LALT.clone() + keyboard.keymap = [ [ @@ -117,7 +125,7 @@ keyboard.keymap = [ [ [KC.GRV, KC.EXLM, KC.AT, KC.HASH, KC.DLR, KC.PERC, KC.CIRC, KC.AMPR, KC.ASTR, KC.LPRN, KC.RPRN, KC.SLSH], [KC.TAB, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, KC.MINS], - [KC.LGUI, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx], + [KC.LGUI, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, BORING_ALT], [KC.LCTL, KC.DBG, HELLA_TD, xxxxxxx, _______, _______, xxxxxxx, xxxxxxx, KC.MUTE, KC.VOLD, KC.VOLU, xxxxxxx], ], ]