diff --git a/docs/modtap.md b/docs/modtap.md new file mode 100644 index 0000000..f7ee5a3 --- /dev/null +++ b/docs/modtap.md @@ -0,0 +1,24 @@ +# ModTap +One key if you tap it, one or more modifiers if you hold it! + + +## Helpful examples +Just copy the example from New Keycode above your keymap and change KC.SOMETHING to the key that you want when tapped. +After that, just use the new keycode anywhere in your keymap. + +|New Keycode | Description | +|------------------------------------------------------------------|-----------------------------------------------------------------| +|LCTL = KC.MT(KC.LCTRL, kc=KC.SOMETHING) |`LCTRL` if held `kc` if tapped | +|LSFT = KC.MT(KC.LSFT, kc=KC.SOMETHING) |`LSHIFT` if held `kc` if tapped | +|LALT = KC.MT(KC.LALT, kc=KC.SOMETHING) |`LALT` if held `kc` if tapped | +|LGUI = KC.MT(KC.LGUI, kc=KC.SOMETHING) |`LGUI` if held `kc` if tapped | +|RCTL = KC.MT(KC.RCTRL, kc=KC.SOMETHING) |`RCTRL` if held `kc` if tapped | +|RSFT = KC.MT(KC.RSFT, kc=KC.SOMETHING) |`RSHIFT` if held `kc` if tapped | +|RALT = KC.MT(KC.RALT, kc=KC.SOMETHING) |`RALT` if held `kc` if tapped | +|RGUI = KC.MT(KC.RGUI, kc=KC.SOMETHING) |`RGUI` if held `kc` if tapped | +|SGUI = KC.MT(KC.LSHFT, KC.LGUI, kc=KC.SOMETHING) |`LSHIFT` and `LGUI` if held `kc` if tapped | +|LCA = KC.MT(KC.LCTRL, KC.LALT, kc=KC.SOMETHING) |`LCTRL` and `LALT` if held `kc` if tapped | +|LCAG = KC.MT(KC.LCTRL, KC.LALT, KC.LGUI, kc=KC.SOMETHING) |`LCTRL` and `LALT` and `LGUI` if held `kc` if tapped | +|MEH = KC.MT(KC.LCTRL, KC.LSFT, KC.LALT, kc=KC.SOMETHING) |`CTRL` and `LSHIFT` and `LALT` if held `kc` if tapped | +|HYPR = KC.MT(KC.LCTRL, KC.LSFT, KC.LALT, KC.LGUI, kc=KC.SOMETHING)|`LCTRL` and `LSHIFT` and `LALT` and `LGUI` if held `kc` if tapped| + diff --git a/kmk/firmware.py b/kmk/firmware.py index 1bfcb54..7bb3c14 100644 --- a/kmk/firmware.py +++ b/kmk/firmware.py @@ -22,9 +22,19 @@ import kmk.kmktime # isort:skip import kmk.types # isort:skip import kmk.util # isort:skip +import busio # isort:skip +import gc # isort:skip + +import supervisor # isort:skip +from kmk.consts import LeaderMode, UnicodeMode # isort:skip +from kmk.hid import USB_HID # isort:skip +from kmk.internal_state import InternalState # isort:skip +from kmk.keys import KC # isort:skip +from kmk.matrix import MatrixScanner # isort:skip + # Now handlers that will be used in keys later -import kmk.handlers.layers -import kmk.handlers.stock +import kmk.handlers.layers # isort:skip +import kmk.handlers.stock # isort:skip # Now stuff that depends on the above (and so on) import kmk.keys # isort:skip @@ -39,16 +49,6 @@ import kmk.internal_state # isort:skip # Thanks for sticking around. Now let's do real work, starting below -import busio -import gc - -import supervisor -from kmk.consts import LeaderMode, UnicodeMode -from kmk.hid import USB_HID -from kmk.internal_state import InternalState -from kmk.keys import KC -from kmk.matrix import MatrixScanner - class Firmware: debug_enabled = False diff --git a/kmk/handlers/modtap.py b/kmk/handlers/modtap.py new file mode 100644 index 0000000..a0a70d5 --- /dev/null +++ b/kmk/handlers/modtap.py @@ -0,0 +1,35 @@ +from kmk.kmktime import ticks_diff, ticks_ms + + +def mt_pressed(key, state, *args, **kwargs): + # Sets the timer start and acts like a modifier otherwise + state.keys_pressed.add(key.meta.mod1) + if key.meta.mod2: + state.keys_pressed.add(key.meta.mod2) + if key.meta.mod3: + state.keys_pressed.add(key.meta.mod3) + if key.meta.mod4: + state.keys_pressed.add(key.meta.mod4) + + state.start_time['mod_tap'] = ticks_ms() + return state + + +def mt_released(key, state, *args, **kwargs): + # On keyup, check timer, and press key if needed. + state.keys_pressed.discard(key.meta.mod1) + if key.meta.mod2: + state.keys_pressed.discard(key.meta.mod2) + if key.meta.mod3: + state.keys_pressed.discard(key.meta.mod3) + if key.meta.mod4: + state.keys_pressed.discard(key.meta.mod4) + timer_name = 'mod_tap' + if state.start_time[timer_name] and ( + ticks_diff(ticks_ms(), state.start_time[timer_name]) < state.config.tap_time + ): + state.hid_pending = True + state.tap_key(key.meta.kc) + + state.start_time[timer_name] = None + return state diff --git a/kmk/keys.py b/kmk/keys.py index 544c5be..3a757ec 100644 --- a/kmk/keys.py +++ b/kmk/keys.py @@ -1,9 +1,10 @@ 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, +from kmk.types import (AttrDict, KeySeqSleepMeta, LayerKeyMeta, ModTapKeyMeta, TapDanceKeyMeta, UnicodeModeKeyMeta) FIRST_KMK_INTERNAL_KEY = 1000 @@ -678,6 +679,22 @@ make_argumented_key( ) +def mod_tap_validator(mod1, mod2=None, mod3=None, mod4=None, kc=None): + ''' + Validates that mod tap keys are correctly used + ''' + return ModTapKeyMeta(mod1=mod1, mod2=mod2, mod3=mod3, mod4=mod4, kc=kc) + + +# ModTap +make_argumented_key( + validator=mod_tap_validator, + names=('MT',), + on_press=modtap.mt_pressed, + on_release=modtap.mt_released, +) + + gc.collect() diff --git a/kmk/types.py b/kmk/types.py index 123f806..80a3c3b 100644 --- a/kmk/types.py +++ b/kmk/types.py @@ -32,6 +32,15 @@ class LayerKeyMeta: self.kc = kc +class ModTapKeyMeta: + def __init__(self, mod1=None, mod2=None, mod3=None, mod4=None, kc=None): + self.mod1 = mod1 + self.mod2 = mod2 + self.mod3 = mod3 + self.mod4 = mod4 + self.kc = kc + + class KeySequenceMeta: def __init__(self, seq): self.seq = seq