Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Key Overrides #409

Open
rrotter opened this issue Apr 20, 2022 · 4 comments
Open

Add support for Key Overrides #409

rrotter opened this issue Apr 20, 2022 · 4 comments

Comments

@rrotter
Copy link
Contributor

rrotter commented Apr 20, 2022

Key Overrides in QMK allow remapping a key based upon which mods the user is holding. This allows, for example, switching the shift layer to produce different symbols, or swapping a base key with its shifted variant.

Docs for QMK implementation

@xs5871
Copy link
Collaborator

xs5871 commented May 25, 2022

Could be done with combos. We're missing some knobs for combos anyway and I'm currently working on making this possible as a side effect.

@kilipan
Copy link

kilipan commented Mar 14, 2023

I've written this for my own usage, maybe someone more versed in python object orientation and kmk's internal logic could refactor it into something like a module? (in an oop-implementation you'd also probably get rid of the need for a global variable)

from kmk.keys import KC, make_key


mods_before_modmorph = set()
def modmorph(names = {'DUMMY_KEY',}, default_kc = KC.NO, morphed_kc = KC.NO, triggers = {KC.LSHIFT, KC.RSHIFT}):
    def _pressed(key, state, KC, *args, **kwargs):
        global mods_before_modmorph
        mods_before_modmorph = triggers.intersection(state.keys_pressed)
        # if a trigger is held, morph key
        if mods_before_modmorph:
            state._send_hid()
            for mod_kc in mods_before_modmorph:
                # discard triggering mods so morphed key is unaffected by them
                state.keys_pressed.discard(mod_kc)
            state.keys_pressed.add(morphed_kc)
            state.hid_pending = True
            return state
        # else return default keycode
        state.keys_pressed.add(default_kc)
        state.hid_pending = True
        return state
    def _released(key, state, KC, *args, **kwargs):
        if {morphed_kc,}.intersection(state.keys_pressed):
            state.keys_pressed.discard(morphed_kc)
            for mod_kc in mods_before_modmorph:
                # re-add previously discarded shift so normal typing isn't impacted
                state.keys_pressed.add(mod_kc)
        else:
            state.keys_pressed.discard(default_kc)
        state.hid_pending = True
        return state
    modmorph_key = make_key(names=names, on_press=_pressed,
                            on_release=_released)
    return modmorph_key

Define a mod morph / key override for e.g. a comma/semicolon key like this:

modmorph({'CSEM', 'COMMA_SEMICOLON'}, KC.COMMA, KC.SEMICOLON)

Then simply use the chosen keycode (here: KC.CSEM or KC.COMMA_SEMICOLON) in your keymap.

[Edits: Improve clarity]

@ShreddingGuitars
Copy link

Came across this thread while looking for a way to add custom behaviour on certain keys while holding shift. Based on the Generally Recommended section of the Keys docs and the way overrides are handled for KC.GESC, I wrote the code below, which seems to work fine for me.

class CustomShiftKey(Key):
    def __init__(self, key, shift_key):
        self.key = key
        self.shift_key = shift_key

    def on_press(self, keyboard, coord_int=None):
        SHIFT_TRIGGERS = {KC.LSFT, KC.RSFT}
        
        if SHIFT_TRIGGERS.intersection(keyboard.keys_pressed):
            keyboard._send_hid()
            keyboard.keys_pressed.add(self.shift_key)
            keyboard.hid_pending = True
            return keyboard
        
        keyboard.keys_pressed.add(self.key)
        keyboard.hid_pending = True
        return keyboard
        

    def on_release(self, keyboard, coord_int=None):
        keyboard.keys_pressed.discard(self.key)
        keyboard.keys_pressed.discard(self.shift_key)
        keyboard.hid_pending = True
        return keyboard


KC_LPAK = CustomShiftKey(KC.LPRN, KC.LABK)  # '(', or '<' while holding shift

@xs5871
Copy link
Collaborator

xs5871 commented Dec 23, 2024

KC.GESC, shifted ANSII keys, and similar all have naive implementations that do not take anything else that happens while you type into account. They are known to be buggy and will produce occasional undesired output.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants