From 1004dc6e933bd05b4a152f98b403edaafd3cd761 Mon Sep 17 00:00:00 2001 From: xs5871 <60395129+xs5871@users.noreply.github.com> Date: Mon, 1 Jul 2024 20:51:07 +0000 Subject: [PATCH] Refactor key meta (#995) * Refactor key.meta: make_argumented_key * Refactor key.meta: dynamic sequences * Refactor key.meta: holdtap * Refactor key.meta: layers * Refactor key.meta: led * Refactor key.meta: macros * Refactor key.meta: midi * Refactor key.meta: oneshot * Refactor key.meta: pimoroni trackball * Refactor key.meta: rapid fire * Refactor key.meta: sticky keys * Refactor key.meta: sticky mod * Refactor key.meta: tapdance * Remove key.meta * Cleanup make_key/make_argumented_key calls --- docs/en/sticky_mod.md | 2 +- kmk/extensions/led.py | 36 ++++++------- kmk/extensions/media_keys.py | 29 ++++++----- kmk/keys.py | 79 ++++++++++------------------ kmk/modules/dynamic_sequences.py | 30 ++++++----- kmk/modules/holdtap.py | 46 +++++++++-------- kmk/modules/layers.py | 83 ++++++++++++++---------------- kmk/modules/macros.py | 85 ++++++++++++++++++------------- kmk/modules/midi.py | 48 +++++++++-------- kmk/modules/mouse_keys.py | 63 ++++++++--------------- kmk/modules/oneshot.py | 28 ++++++---- kmk/modules/pimoroni_trackball.py | 21 ++++---- kmk/modules/rapidfire.py | 30 ++++++----- kmk/modules/sticky_keys.py | 67 ++++++++++++------------ kmk/modules/sticky_mod.py | 19 +++---- kmk/modules/tapdance.py | 25 ++++----- tests/test_kmk_keys.py | 31 ++++------- tests/test_sticky_mod.py | 6 +-- 18 files changed, 349 insertions(+), 379 deletions(-) diff --git a/docs/en/sticky_mod.md b/docs/en/sticky_mod.md index 8b6dea8d8..eaab0d192 100644 --- a/docs/en/sticky_mod.md +++ b/docs/en/sticky_mod.md @@ -9,7 +9,7 @@ sticky_mod = StickyMod() keyboard.modules.append(sticky_mod) keyboard.keymap = [ [ - KC.SM(kc=KC.TAB, mod=KC.LALT), + KC.SM(key=KC.TAB, mod=KC.LALT), KC.SM(KC.TAB, KC.LSFT(KC.LALT)), ], ] diff --git a/kmk/extensions/led.py b/kmk/extensions/led.py index 3f417060f..1cdcbc508 100644 --- a/kmk/extensions/led.py +++ b/kmk/extensions/led.py @@ -2,16 +2,21 @@ from math import e, exp, pi, sin from kmk.extensions import Extension, InvalidExtensionEnvironment -from kmk.keys import make_argumented_key, make_key +from kmk.keys import Key, make_argumented_key, make_key from kmk.utils import clamp -class LEDKeyMeta: - def __init__(self, *leds): +class LEDKey(Key): + def __init__(self, *leds, brightness=None, **kwargs): + super().__init__(**kwargs) self.leds = leds self.brightness = None +def led_set_key(brightness, *leds): + return LEDKey(*leds, brightness=brightness) + + class AnimationModes: OFF = 0 STATIC = 1 @@ -63,22 +68,22 @@ def __init__( make_argumented_key( names=('LED_TOG',), - validator=self._led_key_validator, + constructor=LEDKey, on_press=self._key_led_tog, ) make_argumented_key( names=('LED_INC',), - validator=self._led_key_validator, + constructor=LEDKey, on_press=self._key_led_inc, ) make_argumented_key( names=('LED_DEC',), - validator=self._led_key_validator, + constructor=LEDKey, on_press=self._key_led_dec, ) make_argumented_key( names=('LED_SET',), - validator=self._led_set_key_validator, + constructor=led_set_key, on_press=self._key_led_set, ) make_key(names=('LED_ANI',), on_press=self._key_led_ani) @@ -216,17 +221,6 @@ def animate(self): else: self.off() - def _led_key_validator(self, *leds): - if leds: - for led in leds: - assert self._leds[led] - return LEDKeyMeta(*leds) - - def _led_set_key_validator(self, brightness, *leds): - meta = self._led_key_validator(*leds) - meta.brightness = brightness - return meta - def _key_led_tog(self, *args, **kwargs): if self.animation_mode == AnimationModes.STATIC_STANDBY: self.animation_mode = AnimationModes.STATIC @@ -236,13 +230,13 @@ def _key_led_tog(self, *args, **kwargs): self._enabled = not self._enabled def _key_led_inc(self, key, *args, **kwargs): - self.increase_brightness(leds=key.meta.leds) + self.increase_brightness(leds=key.leds) def _key_led_dec(self, key, *args, **kwargs): - self.decrease_brightness(leds=key.meta.leds) + self.decrease_brightness(leds=key.leds) def _key_led_set(self, key, *args, **kwargs): - self.set_brightness(percent=key.meta.brightness, leds=key.meta.leds) + self.set_brightness(percent=key.brightness, leds=key.leds) def _key_led_ani(self, *args, **kwargs): self.increase_ani() diff --git a/kmk/extensions/media_keys.py b/kmk/extensions/media_keys.py index 1d56029f3..19edda23c 100644 --- a/kmk/extensions/media_keys.py +++ b/kmk/extensions/media_keys.py @@ -15,18 +15,23 @@ def __init__(self): # adding the old Apple-specific consumer codes, but again, PRs welcome if the # lack of them impacts you. - make_key(code=0xE2, names=('AUDIO_MUTE', 'MUTE'), key_type=ConsumerKey) - make_key(code=0xE9, names=('AUDIO_VOL_UP', 'VOLU'), key_type=ConsumerKey) - make_key(code=0xEA, names=('AUDIO_VOL_DOWN', 'VOLD'), key_type=ConsumerKey) - make_key(code=0x6F, names=('BRIGHTNESS_UP', 'BRIU'), key_type=ConsumerKey) - make_key(code=0x70, names=('BRIGHTNESS_DOWN', 'BRID'), key_type=ConsumerKey) - make_key(code=0xB5, names=('MEDIA_NEXT_TRACK', 'MNXT'), key_type=ConsumerKey) - make_key(code=0xB6, names=('MEDIA_PREV_TRACK', 'MPRV'), key_type=ConsumerKey) - make_key(code=0xB7, names=('MEDIA_STOP', 'MSTP'), key_type=ConsumerKey) - make_key(code=0xCD, names=('MEDIA_PLAY_PAUSE', 'MPLY'), key_type=ConsumerKey) - make_key(code=0xB8, names=('MEDIA_EJECT', 'EJCT'), key_type=ConsumerKey) - make_key(code=0xB3, names=('MEDIA_FAST_FORWARD', 'MFFD'), key_type=ConsumerKey) - make_key(code=0xB4, names=('MEDIA_REWIND', 'MRWD'), key_type=ConsumerKey) + codes = ( + (0xE2, ('AUDIO_MUTE', 'MUTE')), + (0xE9, ('AUDIO_VOL_UP', 'VOLU')), + (0xEA, ('AUDIO_VOL_DOWN', 'VOLD')), + (0x6F, ('BRIGHTNESS_UP', 'BRIU')), + (0x70, ('BRIGHTNESS_DOWN', 'BRID')), + (0xB5, ('MEDIA_NEXT_TRACK', 'MNXT')), + (0xB6, ('MEDIA_PREV_TRACK', 'MPRV')), + (0xB7, ('MEDIA_STOP', 'MSTP')), + (0xCD, ('MEDIA_PLAY_PAUSE', 'MPLY')), + (0xB8, ('MEDIA_EJECT', 'EJCT')), + (0xB3, ('MEDIA_FAST_FORWARD', 'MFFD')), + (0xB4, ('MEDIA_REWIND', 'MRWD')), + ) + + for code, names in codes: + make_key(names=names, constructor=ConsumerKey, code=code) def on_runtime_enable(self, sandbox): return diff --git a/kmk/keys.py b/kmk/keys.py index 2fe304244..e98546e67 100644 --- a/kmk/keys.py +++ b/kmk/keys.py @@ -44,31 +44,28 @@ class AX: def maybe_make_key( - code: Optional[int], names: Tuple[str, ...], *args, **kwargs, ) -> Callable[[str], Key]: def closure(candidate): if candidate in names: - return make_key(code=code, names=names, *args, **kwargs) + return make_key(names=names, *args, **kwargs) return closure def maybe_make_argumented_key( - validator=lambda *validator_args, **validator_kwargs: object(), - names: Tuple[str, ...] = tuple(), # NOQA - *constructor_args, - **constructor_kwargs, + names: Tuple[str, ...], + constructor: [Key, Callable[[...], Key]], + **kwargs, ) -> Callable[[str], Key]: def closure(candidate): if candidate in names: return make_argumented_key( - validator, - names, - *constructor_args, - **constructor_kwargs, + names=names, + constructor=constructor, + **kwargs, ) return closure @@ -99,9 +96,9 @@ def maybe_make_alpha_key(candidate: str) -> Optional[Key]: candidate_upper = candidate.upper() if candidate_upper in ALL_ALPHAS: return make_key( - code=4 + ALL_ALPHAS.index(candidate_upper), names=(candidate_upper, candidate.lower()), - key_type=KeyboardKey, + constructor=KeyboardKey, + code=4 + ALL_ALPHAS.index(candidate_upper), ) @@ -113,9 +110,9 @@ def maybe_make_numeric_key(candidate: str) -> Optional[Key]: offset = ALL_NUMBER_ALIASES.index(candidate) return make_key( - code=30 + offset, names=(ALL_NUMBERS[offset], ALL_NUMBER_ALIASES[offset]), - key_type=KeyboardKey, + constructor=KeyboardKey, + code=30 + offset, ) @@ -137,7 +134,7 @@ def maybe_make_mod_key(candidate: str) -> Optional[Key]: for code, names in mods: if candidate in names: - return make_key(code=code, names=names, key_type=ModifierKey) + return make_key(names=names, constructor=ModifierKey, code=code) def maybe_make_more_ascii(candidate: str) -> Optional[Key]: @@ -162,7 +159,7 @@ def maybe_make_more_ascii(candidate: str) -> Optional[Key]: for code, names in codes: if candidate in names: - return make_key(code=code, names=names, key_type=KeyboardKey) + return make_key(names=names, constructor=KeyboardKey, code=code) def maybe_make_fn_key(candidate: str) -> Optional[Key]: @@ -195,7 +192,7 @@ def maybe_make_fn_key(candidate: str) -> Optional[Key]: for code, names in codes: if candidate in names: - return make_key(code=code, names=names, key_type=KeyboardKey) + return make_key(names=names, constructor=KeyboardKey, code=code) def maybe_make_navlock_key(candidate: str) -> Optional[Key]: @@ -224,7 +221,7 @@ def maybe_make_navlock_key(candidate: str) -> Optional[Key]: for code, names in codes: if candidate in names: - return make_key(code=code, names=names, key_type=KeyboardKey) + return make_key(names=names, constructor=KeyboardKey, code=code) def maybe_make_numpad_key(candidate: str) -> Optional[Key]: @@ -253,7 +250,7 @@ def maybe_make_numpad_key(candidate: str) -> Optional[Key]: for code, names in codes: if candidate in names: - return make_key(code=code, names=names, key_type=KeyboardKey) + return make_key(names=names, constructor=KeyboardKey, code=code) def maybe_make_shifted_key(candidate: str) -> Optional[Key]: @@ -284,7 +281,7 @@ def maybe_make_shifted_key(candidate: str) -> Optional[Key]: for code, names in codes: if candidate in names: return make_key( - code=code, names=names, key_type=ModifiedKey, modifier=KC.LSFT + names=names, constructor=ModifiedKey, code=code, modifier=KC.LSFT ) @@ -315,7 +312,7 @@ def maybe_make_international_key(candidate: str) -> Optional[Key]: for code, names in codes: if candidate in names: - return make_key(code=code, names=names, key_type=KeyboardKey) + return make_key(names=names, constructor=KeyboardKey, code=code) def maybe_make_firmware_key(candidate: str) -> Optional[Key]: @@ -341,13 +338,11 @@ def maybe_make_firmware_key(candidate: str) -> Optional[Key]: maybe_make_numeric_key, maybe_make_firmware_key, maybe_make_key( - None, ('BKDL',), on_press=handlers.bkdl_pressed, on_release=handlers.bkdl_released, ), maybe_make_key( - None, ('GESC', 'GRAVE_ESC'), on_press=handlers.gesc_pressed, on_release=handlers.gesc_released, @@ -444,9 +439,7 @@ def __init__( self, on_press: Callable[[object, Key, Keyboard, ...], None] = handlers.passthrough, on_release: Callable[[object, Key, Keyboard, ...], None] = handlers.passthrough, - meta: object = object(), ): - self.meta = meta self._on_press = on_press self._on_release = on_release @@ -463,8 +456,6 @@ def on_release(self, keyboard: Keyboard, coord_int: Optional[int] = None) -> Non class _DefaultKey(Key): '''Meta class implementing handlers for Keys with HID codes.''' - meta = None - def __init__(self, code: Optional[int] = None): self.code = code @@ -499,7 +490,6 @@ def __call__(self, key: Key) -> Key: class ModifiedKey(Key): - meta = None code = -1 def __init__(self, code: [Key, int], modifier: [ModifierKey]): @@ -545,18 +535,13 @@ class MouseKey(_DefaultKey): def make_key( - code: Optional[int] = None, - names: Tuple[str, ...] = tuple(), # NOQA - key_type: Key = Key, + names: Tuple[str, ...], + constructor: Key = Key, **kwargs, ) -> Key: ''' Create a new key, aliased by `names` in the KC lookup table. - If a code is not specified, the key is assumed to be a custom - internal key to be handled in a state callback rather than - sent directly to the OS. - Names are globally unique. If a later key is created with the same name as an existing entry in `KC`, it will overwrite the existing entry. @@ -566,10 +551,7 @@ def make_key( All **kwargs are passed to the Key constructor ''' - if code is None: - key = key_type(**kwargs) - else: - key = key_type(code, **kwargs) + key = constructor(**kwargs) for name in names: KC[name] = key @@ -580,20 +562,15 @@ def make_key( # Argumented keys are implicitly internal, so auto-gen of code # is almost certainly the best plan here def make_argumented_key( - validator: object = lambda *validator_args, **validator_kwargs: object(), - names: Tuple[str, ...] = tuple(), # NOQA - *constructor_args, - **constructor_kwargs, + names: Tuple[str, ...], + constructor: [Key, Callable[[...], Key]], + **_kwargs, ) -> Key: - def _argumented_key(*user_args, **user_kwargs) -> Key: - return Key( - *constructor_args, - meta=validator(*user_args, **user_kwargs), - **constructor_kwargs, - ) + def argumented_key(*args, **kwargs) -> Key: + return constructor(*args, **kwargs, **_kwargs) for name in names: - KC[name] = _argumented_key + KC[name] = argumented_key - return _argumented_key + return argumented_key diff --git a/kmk/modules/dynamic_sequences.py b/kmk/modules/dynamic_sequences.py index e633da576..7d1ce5b65 100644 --- a/kmk/modules/dynamic_sequences.py +++ b/kmk/modules/dynamic_sequences.py @@ -3,13 +3,14 @@ from collections import namedtuple -from kmk.keys import KC, make_argumented_key +from kmk.keys import KC, Key, make_argumented_key from kmk.kmktime import check_deadline, ticks_diff from kmk.modules import Module -class DSMeta: - def __init__(self, sequence_select=None): +class DynamicSequenceKey(Key): + def __init__(self, sequence_select=None, **kwargs): + super().__init__(**kwargs) self.sequence_select = sequence_select @@ -53,31 +54,32 @@ def __init__( # Create keycodes make_argumented_key( - validator=DSMeta, names=('RECORD_SEQUENCE',), on_press=self._record_sequence + names=('RECORD_SEQUENCE',), + constructor=DynamicSequenceKey, + on_press=self._record_sequence, ) make_argumented_key( - validator=DSMeta, names=('PLAY_SEQUENCE',), on_press=self._play_sequence + names=('PLAY_SEQUENCE',), + constructor=DynamicSequenceKey, + on_press=self._play_sequence, ) make_argumented_key( - validator=DSMeta, - names=( - 'SET_SEQUENCE', - 'STOP_SEQUENCE', - ), + names=('SET_SEQUENCE', 'STOP_SEQUENCE'), + constructor=DynamicSequenceKey, on_press=self._stop_sequence, ) make_argumented_key( - validator=DSMeta, names=('SET_SEQUENCE_REPETITIONS',), + constructor=DynamicSequenceKey, on_press=self._set_sequence_repetitions, ) make_argumented_key( - validator=DSMeta, names=('SET_SEQUENCE_INTERVAL',), + constructor=DynamicSequenceKey, on_press=self._set_sequence_interval, ) @@ -103,8 +105,8 @@ def _stop_sequence(self, key, keyboard, *args, **kwargs): self.status = SequenceStatus.STOPPED # Change sequences here because stop is always called - if key.meta.sequence_select is not None: - self.current_slot = self.sequences[key.meta.sequence_select] + if key.sequence_select is not None: + self.current_slot = self.sequences[key.sequence_select] # Configure repeat settings def _set_sequence_repetitions(self, key, keyboard, *args, **kwargs): diff --git a/kmk/modules/holdtap.py b/kmk/modules/holdtap.py index 135537b8f..3bd6cf49a 100644 --- a/kmk/modules/holdtap.py +++ b/kmk/modules/holdtap.py @@ -1,6 +1,6 @@ from micropython import const -from kmk.keys import make_argumented_key +from kmk.keys import Key, make_argumented_key from kmk.modules import Module from kmk.utils import Debug @@ -30,7 +30,7 @@ def __init__(self, timeout_key, *args, **kwargs): self.activated = ActivationType.PRESSED -class HoldTapKeyMeta: +class HoldTapKey(Key): def __init__( self, tap, @@ -39,7 +39,9 @@ def __init__( tap_interrupted=False, tap_time=None, repeat=HoldTapRepeat.NONE, + **kwargs, ): + super().__init__(**kwargs) self.tap = tap self.hold = hold self.prefer_hold = prefer_hold @@ -57,8 +59,8 @@ def __init__(self, _make_key=True): if _make_key: make_argumented_key( - validator=HoldTapKeyMeta, names=('HT',), + constructor=HoldTapKey, on_press=self.ht_pressed, on_release=self.ht_released, ) @@ -85,13 +87,13 @@ def process_key(self, keyboard, key, is_pressed, int_coord): continue # holdtap isn't interruptable, resolves on ht_release or timeout. - if not key.meta.tap_interrupted and not key.meta.prefer_hold: + if not key.tap_interrupted and not key.prefer_hold: append_buffer = is_pressed or self.key_buffer continue # holdtap is interrupted by another key event. - if (is_pressed and not key.meta.tap_interrupted) or ( - not is_pressed and key.meta.tap_interrupted and self.key_buffer + if (is_pressed and not key.tap_interrupted) or ( + not is_pressed and key.tap_interrupted and self.key_buffer ): keyboard.cancel_timeout(state.timeout_key) @@ -107,7 +109,7 @@ def process_key(self, keyboard, key, is_pressed, int_coord): # if interrupt on release: store interrupting keys until one of them # is released. - if key.meta.tap_interrupted and is_pressed: + if key.tap_interrupted and is_pressed: append_buffer = True # apply changes with 'side-effects' on key_states or the loop behaviour @@ -148,10 +150,10 @@ def ht_pressed(self, key, keyboard, *args, **kwargs): self.ht_activate_on_interrupt(key, keyboard, *args, **kwargs) return - if key.meta.tap_time is None: + if key.tap_time is None: tap_time = self.tap_time else: - tap_time = key.meta.tap_time + tap_time = key.tap_time timeout_key = keyboard.set_timeout( tap_time, lambda: self.on_tap_time_expired(key, keyboard, *args, **kwargs), @@ -166,17 +168,17 @@ def ht_released(self, key, keyboard, *args, **kwargs): state = self.key_states[key] keyboard.cancel_timeout(state.timeout_key) - repeat = key.meta.repeat & HoldTapRepeat.TAP + repeat = key.repeat & HoldTapRepeat.TAP if state.activated == ActivationType.HOLD_TIMEOUT: # release hold self.ht_deactivate_hold(key, keyboard, *args, **kwargs) - repeat = key.meta.repeat & HoldTapRepeat.HOLD + repeat = key.repeat & HoldTapRepeat.HOLD elif state.activated == ActivationType.INTERRUPTED: # release tap self.ht_deactivate_on_interrupt(key, keyboard, *args, **kwargs) - if key.meta.prefer_hold: - repeat = key.meta.repeat & HoldTapRepeat.HOLD + if key.prefer_hold: + repeat = key.repeat & HoldTapRepeat.HOLD elif state.activated == ActivationType.PRESSED: # press and release tap because key released within tap time self.ht_activate_tap(key, keyboard, *args, **kwargs) @@ -190,10 +192,10 @@ def ht_released(self, key, keyboard, *args, **kwargs): # don't delete the key state right now in this case if repeat: - if key.meta.tap_time is None: + if key.tap_time is None: tap_time = self.tap_time else: - tap_time = key.meta.tap_time + tap_time = key.tap_time state.timeout_key = keyboard.set_timeout( tap_time, lambda: self.key_states.pop(key) ) @@ -228,7 +230,7 @@ def send_key_buffer(self, keyboard): reprocess = False for int_coord, key, is_pressed in self.key_buffer: keyboard.resume_process_key(self, key, is_pressed, int_coord, reprocess) - if isinstance(key.meta, HoldTapKeyMeta): + if isinstance(key, HoldTapKey): reprocess = True self.key_buffer.clear() @@ -236,27 +238,27 @@ def send_key_buffer(self, keyboard): def ht_activate_hold(self, key, keyboard, *args, **kwargs): if debug.enabled: debug('ht_activate_hold') - keyboard.resume_process_key(self, key.meta.hold, True) + keyboard.resume_process_key(self, key.hold, True) def ht_deactivate_hold(self, key, keyboard, *args, **kwargs): if debug.enabled: debug('ht_deactivate_hold') - keyboard.resume_process_key(self, key.meta.hold, False) + keyboard.resume_process_key(self, key.hold, False) def ht_activate_tap(self, key, keyboard, *args, **kwargs): if debug.enabled: debug('ht_activate_tap') - keyboard.resume_process_key(self, key.meta.tap, True) + keyboard.resume_process_key(self, key.tap, True) def ht_deactivate_tap(self, key, keyboard, *args, **kwargs): if debug.enabled: debug('ht_deactivate_tap') - keyboard.resume_process_key(self, key.meta.tap, False) + keyboard.resume_process_key(self, key.tap, False) def ht_activate_on_interrupt(self, key, keyboard, *args, **kwargs): if debug.enabled: debug('ht_activate_on_interrupt') - if key.meta.prefer_hold: + if key.prefer_hold: self.ht_activate_hold(key, keyboard, *args, **kwargs) else: self.ht_activate_tap(key, keyboard, *args, **kwargs) @@ -264,7 +266,7 @@ def ht_activate_on_interrupt(self, key, keyboard, *args, **kwargs): def ht_deactivate_on_interrupt(self, key, keyboard, *args, **kwargs): if debug.enabled: debug('ht_deactivate_on_interrupt') - if key.meta.prefer_hold: + if key.prefer_hold: self.ht_deactivate_hold(key, keyboard, *args, **kwargs) else: self.ht_deactivate_tap(key, keyboard, *args, **kwargs) diff --git a/kmk/modules/layers.py b/kmk/modules/layers.py index a3c7128ee..17cf84a67 100644 --- a/kmk/modules/layers.py +++ b/kmk/modules/layers.py @@ -1,29 +1,18 @@ '''One layer isn't enough. Adds keys to get to more of them''' -from kmk.keys import KC, make_argumented_key -from kmk.modules.holdtap import HoldTap, HoldTapKeyMeta +from kmk.keys import KC, Key, make_argumented_key +from kmk.modules.holdtap import HoldTap, HoldTapKey from kmk.utils import Debug debug = Debug(__name__) -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, kc) +def lt_key(layer, key, prefer_hold=False, **kwargs): + return HoldTapKey(tap=key, hold=KC.MO(layer), prefer_hold=prefer_hold, **kwargs) -def layer_key_validator_lt(layer, kc, prefer_hold=False, **kwargs): - return HoldTapKeyMeta(tap=kc, hold=KC.MO(layer), prefer_hold=prefer_hold, **kwargs) - - -def layer_key_validator_tt(layer, prefer_hold=True, **kwargs): - return HoldTapKeyMeta( +def tt_key(layer, prefer_hold=True, **kwargs): + return HoldTapKey( tap=KC.TG(layer), hold=KC.MO(layer), prefer_hold=prefer_hold, @@ -31,10 +20,11 @@ def layer_key_validator_tt(layer, prefer_hold=True, **kwargs): ) -class LayerKeyMeta: - def __init__(self, layer, kc=None): +class LayerKey(Key): + def __init__(self, layer, key=None, **kwargs): + super().__init__(**kwargs) self.layer = layer - self.kc = kc + self.key = key class Layers(HoldTap): @@ -42,46 +32,51 @@ class Layers(HoldTap): _active_combo = None - def __init__( - self, - combo_layers=None, - ): + def __init__(self, combo_layers=None): # Layers super().__init__(_make_key=False) self.combo_layers = combo_layers make_argumented_key( - validator=layer_key_validator, names=('MO',), + constructor=LayerKey, on_press=self._mo_pressed, on_release=self._mo_released, ) make_argumented_key( - validator=layer_key_validator, names=('FD',), on_press=self._fd_pressed + names=('FD',), + constructor=LayerKey, + on_press=self._fd_pressed, ) make_argumented_key( - validator=layer_key_validator, names=('DF',), on_press=self._df_pressed + names=('DF',), + constructor=LayerKey, + on_press=self._df_pressed, ) make_argumented_key( - validator=layer_key_validator, names=('LM',), + constructor=LayerKey, on_press=self._lm_pressed, on_release=self._lm_released, ) make_argumented_key( - validator=layer_key_validator, names=('TG',), on_press=self._tg_pressed + names=('TG',), + constructor=LayerKey, + on_press=self._tg_pressed, ) make_argumented_key( - validator=layer_key_validator, names=('TO',), on_press=self._to_pressed + names=('TO',), + constructor=LayerKey, + on_press=self._to_pressed, ) make_argumented_key( - validator=layer_key_validator_lt, names=('LT',), + constructor=lt_key, on_press=self.ht_pressed, on_release=self.ht_released, ) make_argumented_key( - validator=layer_key_validator_tt, names=('TT',), + constructor=tt_key, on_press=self.ht_pressed, on_release=self.ht_released, ) @@ -90,48 +85,48 @@ def _fd_pressed(self, key, keyboard, *args, **kwargs): ''' Switches the top layer ''' - self.activate_layer(keyboard, key.meta.layer, idx=0) + self.activate_layer(keyboard, key.layer, idx=0) def _df_pressed(self, key, keyboard, *args, **kwargs): ''' Switches the default layer ''' - self.activate_layer(keyboard, key.meta.layer, idx=-1) + self.activate_layer(keyboard, key.layer, idx=-1) def _mo_pressed(self, key, keyboard, *args, **kwargs): ''' Momentarily activates layer, switches off when you let go ''' - self.activate_layer(keyboard, key.meta.layer) + self.activate_layer(keyboard, key.layer) def _mo_released(self, key, keyboard, *args, **kwargs): - self.deactivate_layer(keyboard, key.meta.layer) + self.deactivate_layer(keyboard, key.layer) def _lm_pressed(self, key, keyboard, *args, **kwargs): ''' As MO(layer) but with mod active ''' keyboard.hid_pending = True - keyboard.keys_pressed.add(key.meta.kc) - self.activate_layer(keyboard, key.meta.layer) + keyboard.keys_pressed.add(key.key) + self.activate_layer(keyboard, key.layer) def _lm_released(self, key, keyboard, *args, **kwargs): ''' As MO(layer) but with mod active ''' keyboard.hid_pending = True - keyboard.keys_pressed.discard(key.meta.kc) - self.deactivate_layer(keyboard, key.meta.layer) + keyboard.keys_pressed.discard(key.key) + self.deactivate_layer(keyboard, key.layer) def _tg_pressed(self, key, keyboard, *args, **kwargs): ''' Toggles the layer (enables it if not active, and vise versa) ''' # See mo_released for implementation details around this - if key.meta.layer in keyboard.active_layers: - self.deactivate_layer(keyboard, key.meta.layer) + if key.layer in keyboard.active_layers: + self.deactivate_layer(keyboard, key.layer) else: - self.activate_layer(keyboard, key.meta.layer) + self.activate_layer(keyboard, key.layer) def _to_pressed(self, key, keyboard, *args, **kwargs): ''' @@ -139,7 +134,7 @@ def _to_pressed(self, key, keyboard, *args, **kwargs): ''' self._active_combo = None keyboard.active_layers.clear() - self.activate_layer(keyboard, key.meta.layer) + self.activate_layer(keyboard, key.layer) def _print_debug(self, keyboard): if debug.enabled: diff --git a/kmk/modules/macros.py b/kmk/modules/macros.py index 483ea2ffe..6aa5312f6 100644 --- a/kmk/modules/macros.py +++ b/kmk/modules/macros.py @@ -1,6 +1,6 @@ from micropython import const -from kmk.keys import KC, make_argumented_key, make_key +from kmk.keys import KC, Key, make_argumented_key, make_key from kmk.modules import Module from kmk.scheduler import create_task from kmk.utils import Debug @@ -14,7 +14,7 @@ _ON_RELEASE = const(4) -class MacroMeta: +class MacroKey(Key): def __init__( self, *args, @@ -22,18 +22,28 @@ def __init__( on_hold=None, on_release=None, blocking=True, + _on_press=None, + _on_release=None, ): + super().__init__(on_press=_on_press, on_release=_on_release) + if on_press is not None: - self.on_press = on_press + self.on_press_macro = on_press else: - self.on_press = args - self.on_hold = on_hold - self.on_release = on_release + self.on_press_macro = args + self.on_hold_macro = on_hold + self.on_release_macro = on_release self.blocking = blocking self.state = _IDLE self._task = None +class UnicodeModeKey(Key): + def __init__(self, mode, **kwargs): + super().__init__(**kwargs) + self.mode = mode + + def Delay(delay): return lambda keyboard: delay @@ -156,24 +166,27 @@ def __init__(self, unicode_mode=UnicodeModeIBus, delay=10): self.delay = delay make_argumented_key( - validator=MacroMeta, names=('MACRO',), - on_press=self.on_press_macro, - on_release=self.on_release_macro, + constructor=MacroKey, + _on_press=self.on_press_macro, + _on_release=self.on_release_macro, ) make_key( names=('UC_MODE_IBUS',), - meta=UnicodeModeIBus, + constructor=UnicodeModeKey, + mode=UnicodeModeIBus, on_press=self.on_press_unicode_mode, ) make_key( names=('UC_MODE_MACOS',), - meta=UnicodeModeMacOS, + constructor=UnicodeModeKey, + mode=UnicodeModeMacOS, on_press=self.on_press_unicode_mode, ) make_key( names=('UC_MODE_WINC',), - meta=UnicodeModeWinC, + constructor=UnicodeModeKey, + mode=UnicodeModeWinC, on_press=self.on_press_unicode_mode, ) @@ -205,41 +218,41 @@ def on_powersave_disable(self, keyboard): return def on_press_unicode_mode(self, key, keyboard, *args, **kwargs): - self.unicode_mode = key.meta + self.unicode_mode = key.mode def on_press_macro(self, key, keyboard, *args, **kwargs): - key.meta.state = _ON_PRESS + key.state = _ON_PRESS self.process_macro_async(keyboard, key) def on_release_macro(self, key, keyboard, *args, **kwargs): - key.meta.state = _RELEASE - if key.meta._task is None: + key.state = _RELEASE + if key._task is None: self.process_macro_async(keyboard, key) def process_macro_async(self, keyboard, key, _iter=None): # There's no active macro iterator: select the next one. if _iter is None: - key.meta._task = None + key._task = None - if key.meta.state == _ON_PRESS: - if key.meta.blocking: + if key.state == _ON_PRESS: + if key.blocking: self._active.append(key) - if (macro := key.meta.on_press) is None: - key.meta.state = _ON_HOLD + if (macro := key.on_press_macro) is None: + key.state = _ON_HOLD elif debug.enabled: debug('on_press') - if key.meta.state == _ON_HOLD: - if (macro := key.meta.on_hold) is None: + if key.state == _ON_HOLD: + if (macro := key.on_hold_macro) is None: return elif debug.enabled: debug('on_hold') - if key.meta.state == _RELEASE: - key.meta.state = _ON_RELEASE + if key.state == _RELEASE: + key.state = _ON_RELEASE - if key.meta.state == _ON_RELEASE: - if (macro := key.meta.on_release) is None: + if key.state == _ON_RELEASE: + if (macro := key.on_release_macro) is None: macro = () elif debug.enabled: debug('on_release') @@ -259,30 +272,30 @@ def process_macro_async(self, keyboard, key, _iter=None): except StopIteration: _iter = None delay = 0 - key.meta._task = None + key._task = None - if key.meta.state == _ON_PRESS: - key.meta.state = _ON_HOLD + if key.state == _ON_PRESS: + key.state = _ON_HOLD - elif key.meta.state == _ON_RELEASE: + elif key.state == _ON_RELEASE: if debug.enabled: debug('deactivate') - key.meta.state = _IDLE - if key.meta.blocking: + key.state = _IDLE + if key.blocking: self._active.remove(key) self.send_key_buffer(keyboard) return # Schedule the next step. # Reuse existing task objects and save a couple of bytes and cycles for the gc. - if key.meta._task: - task = key.meta._task + if key._task: + task = key._task else: def task(): self.process_macro_async(keyboard, key, _iter) - key.meta._task = create_task(task, after_ms=delay) + key._task = create_task(task, after_ms=delay) def send_key_buffer(self, keyboard): if not self.key_buffer or self._active: diff --git a/kmk/modules/midi.py b/kmk/modules/midi.py index d32ce6440..2cee603c0 100644 --- a/kmk/modules/midi.py +++ b/kmk/modules/midi.py @@ -8,53 +8,64 @@ from adafruit_midi.start import Start from adafruit_midi.stop import Stop -from kmk.keys import make_argumented_key +from kmk.keys import Key, make_argumented_key from kmk.modules import Module -class midiNoteValidator: - def __init__(self, note=69, velocity=64, channel=None): - self.note = note - self.velocity = velocity - self.channel = channel +class MidiKey(Key): + def __init__(self, *args, command, channel=None, **kwargs): + super().__init__(**kwargs) + self.on_press_msg = command(*args, channel=channel) + self.on_release_msg = None + + +def midi_note_key(note=69, velocity=127, channel=None, **kwargs): + key = MidiKey(note, velocity, command=NoteOn, channel=channel, **kwargs) + key.on_release_msg = NoteOff(note, velocity, channel=channel) + return key class MidiKeys(Module): def __init__(self): make_argumented_key( names=('MIDI_CC',), - validator=ControlChange, + constructor=MidiKey, + command=ControlChange, on_press=self.on_press, ) make_argumented_key( names=('MIDI_NOTE',), - validator=midiNoteValidator, - on_press=self.note_on, - on_release=self.note_off, + constructor=midi_note_key, + on_press=self.on_press, + on_release=self.on_release, ) make_argumented_key( names=('MIDI_PB',), - validator=PitchBend, + constructor=MidiKey, + command=PitchBend, on_press=self.on_press, ) make_argumented_key( names=('MIDI_PC',), - validator=ProgramChange, + constructor=MidiKey, + command=ProgramChange, on_press=self.on_press, ) make_argumented_key( names=('MIDI_START',), - validator=Start, + constructor=MidiKey, + command=Start, on_press=self.on_press, ) make_argumented_key( names=('MIDI_STOP',), - validator=Stop, + constructor=MidiKey, + command=Stop, on_press=self.on_press, ) @@ -94,10 +105,7 @@ def send(self, message): self.midi.send(message) def on_press(self, key, keyboard, *args, **kwargs): - self.send(key.meta) - - def note_on(self, key, keyboard, *args, **kwargs): - self.send(NoteOn(key.meta.note, key.meta.velocity, channel=key.meta.channel)) + self.send(key.on_press_msg) - def note_off(self, key, keyboard, *args, **kwargs): - self.send(NoteOff(key.meta.note, key.meta.velocity, channel=key.meta.channel)) + def on_release(self, key, keyboard, *args, **kwargs): + self.send(key.on_release_msg) diff --git a/kmk/modules/mouse_keys.py b/kmk/modules/mouse_keys.py index e89682dab..7ee98dc69 100644 --- a/kmk/modules/mouse_keys.py +++ b/kmk/modules/mouse_keys.py @@ -21,51 +21,28 @@ def __init__(self, max_speed=10, acc_interval=20, move_step=1): self.acc_interval = acc_interval self.move_step = move_step - make_key(code=0x01, names=('MB_LMB',), key_type=MouseKey) - make_key(code=0x02, names=('MB_RMB',), key_type=MouseKey) - make_key(code=0x04, names=('MB_MMB',), key_type=MouseKey) - make_key(code=0x08, names=('MB_BTN4',), key_type=MouseKey) - make_key(code=0x10, names=('MB_BTN5',), key_type=MouseKey) - make_key( - names=('MW_UP',), - on_press=self._mw_up_press, - on_release=self._mw_up_release, + codes = ( + (0x01, ('MB_LMB',)), + (0x02, ('MB_RMB',)), + (0x04, ('MB_MMB',)), + (0x08, ('MB_BTN4',)), + (0x10, ('MB_BTN5',)), ) - make_key( - names=('MW_DOWN', 'MW_DN'), - on_press=self._mw_down_press, - on_release=self._mw_down_release, - ) - make_key( - names=('MW_LEFT', 'MW_LT'), - on_press=self._mw_left_press, - on_release=self._mw_left_release, - ) - make_key( - names=('MW_RIGHT', 'MW_RT'), - on_press=self._mw_right_press, - on_release=self._mw_right_release, - ) - make_key( - names=('MS_UP',), - on_press=self._ms_up_press, - on_release=self._ms_up_release, - ) - make_key( - names=('MS_DOWN', 'MS_DN'), - on_press=self._ms_down_press, - on_release=self._ms_down_release, - ) - make_key( - names=('MS_LEFT', 'MS_LT'), - on_press=self._ms_left_press, - on_release=self._ms_left_release, - ) - make_key( - names=('MS_RIGHT', 'MS_RT'), - on_press=self._ms_right_press, - on_release=self._ms_right_release, + for code, names in codes: + make_key(names=names, constructor=MouseKey, code=code) + + keys = ( + (('MW_UP',), self._mw_up_press, self._mw_up_release), + (('MW_DOWN', 'MW_DN'), self._mw_down_press, self._mw_down_release), + (('MW_LEFT', 'MW_LT'), self._mw_left_press, self._mw_left_release), + (('MW_RIGHT', 'MW_RT'), self._mw_right_press, self._mw_right_release), + (('MS_UP',), self._ms_up_press, self._ms_up_release), + (('MS_DOWN', 'MS_DN'), self._ms_down_press, self._ms_down_release), + (('MS_LEFT', 'MS_LT'), self._ms_left_press, self._ms_left_release), + (('MS_RIGHT', 'MS_RT'), self._ms_right_press, self._ms_right_release), ) + for names, on_press, on_release in keys: + make_key(names=names, on_press=on_press, on_release=on_release) def during_bootup(self, keyboard): self._task = create_task( diff --git a/kmk/modules/oneshot.py b/kmk/modules/oneshot.py index d7dcf317e..098212975 100644 --- a/kmk/modules/oneshot.py +++ b/kmk/modules/oneshot.py @@ -1,14 +1,22 @@ from kmk.keys import make_argumented_key -from kmk.modules.holdtap import ActivationType, HoldTap, HoldTapKeyMeta -from kmk.modules.layers import LayerKeyMeta +from kmk.modules.holdtap import ActivationType, HoldTap, HoldTapKey +from kmk.modules.layers import LayerKey from kmk.utils import Debug debug = Debug(__name__) -class OneShotKeyMeta(HoldTapKeyMeta): - def __init__(self, kc, tap_time=None): - super().__init__(tap=kc, hold=kc, prefer_hold=False, tap_time=tap_time) +class OneShotKey(HoldTapKey): + def __init__(self, kc, tap_time=None, **kwargs): + super().__init__( + tap=kc, + hold=kc, + prefer_hold=False, + tap_interrupted=False, + tap_time=tap_time, + repeat=0, + **kwargs, + ) class OneShot(HoldTap): @@ -17,8 +25,8 @@ class OneShot(HoldTap): def __init__(self): super().__init__() make_argumented_key( - validator=OneShotKeyMeta, names=('OS', 'ONESHOT'), + constructor=OneShotKey, on_press=self.osk_pressed, on_release=self.osk_released, ) @@ -32,14 +40,14 @@ def process_key(self, keyboard, current_key, is_pressed, int_coord): if key == current_key: continue - if (isinstance(current_key.meta, OneShotKeyMeta)) or ( - isinstance(current_key.meta, LayerKeyMeta) + if (isinstance(current_key, OneShotKey)) or ( + isinstance(current_key, LayerKey) ): keyboard.cancel_timeout(state.timeout_key) - if key.meta.tap_time is None: + if key.tap_time is None: tap_time = self.tap_time else: - tap_time = key.meta.tap_time + tap_time = key.tap_time state.timeout_key = keyboard.set_timeout( tap_time, lambda k=key: self.on_tap_time_expired(k, keyboard), diff --git a/kmk/modules/pimoroni_trackball.py b/kmk/modules/pimoroni_trackball.py index 4f20b519e..7fb2d60ab 100644 --- a/kmk/modules/pimoroni_trackball.py +++ b/kmk/modules/pimoroni_trackball.py @@ -8,7 +8,7 @@ import struct from adafruit_pixelbuf import PixelBuf -from kmk.keys import AX, KC, make_argumented_key, make_key +from kmk.keys import AX, KC, Key, make_argumented_key, make_key from kmk.kmktime import PeriodicTimer from kmk.modules import Module from kmk.utils import Debug @@ -50,15 +50,6 @@ debug = Debug(__name__) -class TrackballHandlerKeyMeta: - def __init__(self, handler=0): - self.handler = handler - - -def layer_key_validator(handler): - return TrackballHandlerKeyMeta(handler=handler) - - class TrackballMode: '''Behaviour mode of trackball: mouse movement or vertical scroll''' @@ -73,6 +64,12 @@ class ScrollDirection: REVERSE = const(1) +class TrackballHandlerKey(Key): + def __init__(self, handler=TrackballMode.MOUSE_MODE, **kwargs): + super().__init__(**kwargs) + self.handler = handler + + class TrackballHandler: def handle(self, keyboard, trackball, x, y, switch, state): raise NotImplementedError @@ -171,8 +168,8 @@ def __init__( ) make_argumented_key( - validator=layer_key_validator, names=('TB_HANDLER', 'TB_H'), + constructor=TrackballHandlerKey, on_press=self._tb_handler_press, ) @@ -285,7 +282,7 @@ def _i2c_rdwr(self, data, length=0): self._i2c_bus.unlock() def _tb_handler_press(self, key, keyboard, *args, **kwargs): - self.activate_handler(key.meta.handler) + self.activate_handler(key.handler) def _tb_handler_next_press(self, key, keyboard, *args, **kwargs): self.next_handler() diff --git a/kmk/modules/rapidfire.py b/kmk/modules/rapidfire.py index 50720eaeb..498d0c692 100644 --- a/kmk/modules/rapidfire.py +++ b/kmk/modules/rapidfire.py @@ -1,20 +1,22 @@ from random import randint -from kmk.keys import make_argumented_key +from kmk.keys import Key, make_argumented_key from kmk.modules import Module -class RapidFireMeta: +class RapidFireKey(Key): def __init__( self, - kc, + key, interval=100, timeout=200, enable_interval_randomization=False, randomization_magnitude=15, toggle=False, + *kwargs, ): - self.kc = kc + super().__init__(**kwargs) + self.key = key self.interval = interval self.timeout = timeout self.enable_interval_randomization = enable_interval_randomization @@ -29,24 +31,24 @@ class RapidFire(Module): def __init__(self): make_argumented_key( - validator=RapidFireMeta, names=('RF',), + constructor=RapidFireKey, on_press=self._rf_pressed, on_release=self._rf_released, ) def _get_repeat(self, key): - if key.meta.enable_interval_randomization: - return key.meta.interval + randint( - -key.meta.randomization_magnitude, key.meta.randomization_magnitude + if key.enable_interval_randomization: + return key.interval + randint( + -key.randomization_magnitude, key.randomization_magnitude ) - return key.meta.interval + return key.interval def _on_timer_timeout(self, key, keyboard): - keyboard.tap_key(key.meta.kc) + keyboard.tap_key(key.key) if key in self._waiting_keys: self._waiting_keys.remove(key) - if key.meta.toggle and key not in self._toggled_keys: + if key.toggle and key not in self._toggled_keys: self._toggled_keys.append(key) self._active_keys[key] = keyboard.set_timeout( self._get_repeat(key), lambda: self._on_timer_timeout(key, keyboard) @@ -57,11 +59,11 @@ def _rf_pressed(self, key, keyboard, *args, **kwargs): self._toggled_keys.remove(key) self._deactivate_key(key, keyboard) return - if key.meta.timeout > 0: - keyboard.tap_key(key.meta.kc) + if key.timeout > 0: + keyboard.tap_key(key.key) self._waiting_keys.append(key) self._active_keys[key] = keyboard.set_timeout( - key.meta.timeout, lambda: self._on_timer_timeout(key, keyboard) + key.timeout, lambda: self._on_timer_timeout(key, keyboard) ) else: self._on_timer_timeout(key, keyboard) diff --git a/kmk/modules/sticky_keys.py b/kmk/modules/sticky_keys.py index f071bfa7d..eaeae3a63 100644 --- a/kmk/modules/sticky_keys.py +++ b/kmk/modules/sticky_keys.py @@ -1,6 +1,6 @@ from micropython import const -from kmk.keys import make_argumented_key +from kmk.keys import Key, make_argumented_key from kmk.modules import Module from kmk.utils import Debug @@ -14,8 +14,9 @@ _SK_STICKY = const(4) -class StickyKeyMeta: - def __init__(self, key, defer_release=False, retap_cancel=True): +class StickyKey(Key): + def __init__(self, key, defer_release=False, retap_cancel=True, **kwargs): + super().__init__(**kwargs) self.key = key self.defer_release = defer_release self.timeout = None @@ -29,8 +30,8 @@ def __init__(self, release_after=1000): self.release_after = release_after make_argumented_key( - validator=StickyKeyMeta, names=('SK', 'STICKY'), + constructor=StickyKey, on_press=self.on_press, on_release=self.on_release, ) @@ -66,22 +67,20 @@ def process_key(self, keyboard, current_key, is_pressed, int_coord): # instead of `process_key` to avoid race conditions / causal # reordering when resetting timeouts. if ( - isinstance(current_key.meta, StickyKeyMeta) - or current_key.meta.__class__.__name__ == 'TapDanceKeyMeta' - or current_key.meta.__class__.__name__ == 'HoldTapKeyMeta' - or current_key.meta.__class__.__name__ == 'LayerTapKeyMeta' + isinstance(current_key, StickyKey) + or current_key.__class__.__name__ == 'TapDanceKey' + or current_key.__class__.__name__ == 'HoldTapKey' + or current_key.__class__.__name__ == 'LayerTapKeyMeta' ): continue - meta = key.meta - - if meta.state == _SK_PRESSED and is_pressed: - meta.state = _SK_HOLD - elif meta.state == _SK_RELEASED and is_pressed: - meta.state = _SK_STICKY - elif meta.state == _SK_STICKY: + if key.state == _SK_PRESSED and is_pressed: + key.state = _SK_HOLD + elif key.state == _SK_RELEASED and is_pressed: + key.state = _SK_STICKY + elif key.state == _SK_STICKY: # Defer sticky release until last other key is released. - if meta.defer_release: + if key.defer_release: if not is_pressed and len(keyboard._coordkeys_pressed) <= 1: self.deactivate(keyboard, key) # Release sticky key; if it's a new key pressed: delay @@ -96,7 +95,7 @@ def process_key(self, keyboard, current_key, is_pressed, int_coord): return current_key def set_timeout(self, keyboard, key): - key.meta.timeout = keyboard.set_timeout( + key.timeout = keyboard.set_timeout( self.release_after, lambda: self.on_release_after(keyboard, key), ) @@ -104,16 +103,14 @@ def set_timeout(self, keyboard, key): def on_press(self, key, keyboard, *args, **kwargs): # Let sticky keys stack while renewing timeouts. for sk in self.active_keys: - keyboard.cancel_timeout(sk.meta.timeout) + keyboard.cancel_timeout(sk.timeout) # If active sticky key is tapped again, cancel. - if key.meta.retap_cancel and ( - key.meta.state == _SK_RELEASED or key.meta.state == _SK_STICKY - ): + if key.retap_cancel and (key.state == _SK_RELEASED or key.state == _SK_STICKY): self.deactivate(keyboard, key) # Reset on repeated taps. - elif key.meta.state != _SK_IDLE: - key.meta.state = _SK_PRESSED + elif key.state != _SK_IDLE: + key.state = _SK_PRESSED else: self.activate(keyboard, key) @@ -123,35 +120,35 @@ def on_press(self, key, keyboard, *args, **kwargs): def on_release(self, key, keyboard, *args, **kwargs): # No interrupt or timeout happend, mark key as RELEASED, ready to get # STICKY. - if key.meta.state == _SK_PRESSED: - key.meta.state = _SK_RELEASED + if key.state == _SK_PRESSED: + key.state = _SK_RELEASED # Key in HOLD state is handled like a regular release. - elif key.meta.state == _SK_HOLD: + elif key.state == _SK_HOLD: for sk in self.active_keys.copy(): - keyboard.cancel_timeout(sk.meta.timeout) + keyboard.cancel_timeout(sk.timeout) self.deactivate(keyboard, sk) def on_release_after(self, keyboard, key): # Key is still pressed but nothing else happend: set to HOLD. - if key.meta.state == _SK_PRESSED: + if key.state == _SK_PRESSED: for sk in self.active_keys: - key.meta.state = _SK_HOLD - keyboard.cancel_timeout(sk.meta.timeout) + key.state = _SK_HOLD + keyboard.cancel_timeout(sk.timeout) # Key got released but nothing else happend: deactivate. - elif key.meta.state == _SK_RELEASED: + elif key.state == _SK_RELEASED: for sk in self.active_keys.copy(): self.deactivate(keyboard, sk) def activate(self, keyboard, key): if debug.enabled: debug('activate') - key.meta.state = _SK_PRESSED + key.state = _SK_PRESSED self.active_keys.insert(0, key) - keyboard.resume_process_key(self, key.meta.key, True) + keyboard.resume_process_key(self, key.key, True) def deactivate(self, keyboard, key): if debug.enabled: debug('deactivate') - key.meta.state = _SK_IDLE + key.state = _SK_IDLE self.active_keys.remove(key) - keyboard.resume_process_key(self, key.meta.key, False) + keyboard.resume_process_key(self, key.key, False) diff --git a/kmk/modules/sticky_mod.py b/kmk/modules/sticky_mod.py index 73ad569df..0896b14fd 100644 --- a/kmk/modules/sticky_mod.py +++ b/kmk/modules/sticky_mod.py @@ -1,10 +1,11 @@ -from kmk.keys import make_argumented_key +from kmk.keys import Key, make_argumented_key from kmk.modules import Module -class StickyModMeta: - def __init__(self, kc, mod): - self.kc = kc +class StickyModKey(Key): + def __init__(self, key, mod, **kwargs): + super().__init__(**kwargs) + self.key = key self.mod = mod @@ -14,7 +15,7 @@ def __init__(self): self._active_key = None make_argumented_key( names=('SM',), - validator=StickyModMeta, + constructor=StickyModKey, on_press=self.sm_pressed, on_release=self.sm_released, ) @@ -48,16 +49,16 @@ def after_matrix_scan(self, keyboard): return def release_key(self, keyboard, key): - keyboard.process_key(key.meta.mod, False) + keyboard.process_key(key.mod, False) self._active = False self._active_key = None def sm_pressed(self, key, keyboard, *args, **kwargs): - keyboard.process_key(key.meta.mod, True) - keyboard.process_key(key.meta.kc, True) + keyboard.process_key(key.mod, True) + keyboard.process_key(key.key, True) self._active_key = key def sm_released(self, key, keyboard, *args, **kwargs): - keyboard.process_key(key.meta.kc, False) + keyboard.process_key(key.key, False) self._active_key = key self._active = True diff --git a/kmk/modules/tapdance.py b/kmk/modules/tapdance.py index 5b276ab76..43cbd9e36 100644 --- a/kmk/modules/tapdance.py +++ b/kmk/modules/tapdance.py @@ -1,19 +1,20 @@ -from kmk.keys import KC, make_argumented_key -from kmk.modules.holdtap import ActivationType, HoldTap, HoldTapKeyMeta +from kmk.keys import KC, Key, make_argumented_key +from kmk.modules.holdtap import ActivationType, HoldTap, HoldTapKey -class TapDanceKeyMeta: - def __init__(self, *keys, tap_time=None): +class TapDanceKey(Key): + def __init__(self, *keys, tap_time=None, **kwargs): ''' Any key in the tapdance sequence that is not already a holdtap key gets converted to a holdtap key with identical tap and hold - meta attributes. + attributes. ''' + super().__init__(**kwargs) self.tap_time = tap_time self.keys = [] for key in keys: - if not isinstance(key.meta, HoldTapKeyMeta): + if not isinstance(key, HoldTapKey): ht_key = KC.HT( tap=key, hold=key, @@ -31,8 +32,8 @@ class TapDance(HoldTap): def __init__(self): super().__init__(_make_key=False) make_argumented_key( - validator=TapDanceKeyMeta, names=('TD',), + constructor=TapDanceKey, on_press=self.td_pressed, on_release=self.td_released, ) @@ -40,7 +41,7 @@ def __init__(self): self.td_counts = {} def process_key(self, keyboard, key, is_pressed, int_coord): - if isinstance(key.meta, TapDanceKeyMeta): + if isinstance(key, TapDanceKey): if key in self.td_counts: return key @@ -64,14 +65,14 @@ def td_pressed(self, key, keyboard, *args, **kwargs): # active tap dance if key in self.td_counts: count = self.td_counts[key] - kc = key.meta.keys[count] + kc = key.keys[count] keyboard.cancel_timeout(self.key_states[kc].timeout_key) count += 1 # Tap dance reached the end of the list: send last tap in sequence # and start from the beginning. - if count >= len(key.meta.keys): + if count >= len(key.keys): self.key_states[kc].activated = ActivationType.RELEASED self.on_tap_time_expired(kc, keyboard) count = 0 @@ -82,7 +83,7 @@ def td_pressed(self, key, keyboard, *args, **kwargs): else: count = 0 - current_key = key.meta.keys[count] + current_key = key.keys[count] self.ht_pressed(current_key, keyboard, *args, **kwargs) self.td_counts[key] = count @@ -93,7 +94,7 @@ def td_pressed(self, key, keyboard, *args, **kwargs): def td_released(self, key, keyboard, *args, **kwargs): try: - kc = key.meta.keys[self.td_counts[key]] + kc = key.keys[self.td_counts[key]] except KeyError: return state = self.key_states[kc] diff --git a/tests/test_kmk_keys.py b/tests/test_kmk_keys.py index b4d339445..8f277e36e 100644 --- a/tests/test_kmk_keys.py +++ b/tests/test_kmk_keys.py @@ -130,12 +130,9 @@ def test_invalid_key_lower(self): def test_custom_key(self): created = make_key( - KC.N2.code, - names=( - 'EURO', - '€', - ), - key_type=ModifiedKey, + names=('EURO', '€'), + constructor=ModifiedKey, + code=KC.N2.code, modifier=KC.LSFT(KC.ROPT), ) assert created is KC.get('EURO') @@ -174,12 +171,9 @@ def test_invalid_key_lower(self): def test_custom_key(self): created = make_key( - KC['N2'].code, - names=( - 'EURO', - '€', - ), - key_type=ModifiedKey, + names=('EURO', '€'), + constructor=ModifiedKey, + code=KC['N2'].code, modifier=KC.LSFT(KC.ROPT), ) assert created is KC['EURO'] @@ -223,12 +217,9 @@ def test_invalid_key_lower(self): def test_custom_key(self): created = make_key( - KC.get('N2').code, - names=( - 'EURO', - '€', - ), - key_type=ModifiedKey, + names=('EURO', '€'), + constructor=ModifiedKey, + code=KC.get('N2').code, modifier=KC.LSFT(KC.ROPT), ) assert created is KC.get('EURO') @@ -250,8 +241,8 @@ def setUp(self): KC.clear() def test_make_key_new_instance(self): - key1 = make_key(code=1, key_type=KeyboardKey) - key2 = make_key(code=1, key_type=KeyboardKey) + key1 = make_key(names=(), constructor=KeyboardKey, code=1) + key2 = make_key(names=(), constructor=KeyboardKey, code=1) assert key1 is not key2 assert key1.code == key2.code diff --git a/tests/test_sticky_mod.py b/tests/test_sticky_mod.py index 03236700d..809bf2e63 100644 --- a/tests/test_sticky_mod.py +++ b/tests/test_sticky_mod.py @@ -17,12 +17,12 @@ def test_basic_kmk_keyboard(self): KC.B, KC.MO(1), KC.LT(1, KC.C), - KC.SM(kc=KC.TAB, mod=KC.LCTL(KC.LSFT)), + KC.SM(key=KC.TAB, mod=KC.LCTL(KC.LSFT)), KC.F, ], [ - KC.SM(kc=KC.TAB, mod=KC.LGUI), - KC.SM(kc=KC.TAB, mod=KC.LSFT(KC.LGUI)), + KC.SM(key=KC.TAB, mod=KC.LGUI), + KC.SM(key=KC.TAB, mod=KC.LSFT(KC.LGUI)), KC.TRNS, KC.B, KC.N5,