I have been working on a feature I call 'key override' over on my fork of QMK. I think it could be an interesting addition to the main repo that a lot of people would love to use.
Feature Request Type
Description
This feature is already implemented. View the diff here.
See the draft of the documentation here.
The feature works as follows: You can define custom actions for arbitrary modifier+key combinations: if modifiers x + key y are pressed, send modifiers w + key z instead of modifiers x + key y. This feature basically gives you the power of layers without having to use dedicated layer keys, allowing you to use modifier keys instead.
A very simple example of one way I apply this feature: On my keyboard I use a key override that sends the delete key when shift + backspace is pressed.
When the necessary keys are pressed (in the above example: shift + backspace), the override is 'activated' and the override key (+ optionally extra modifiers) is sent in the keyboard report (delete), while the trigger key (backspace) is removed from the keyboard report. The trigger modifiers may be removed from the keyboard report upon activation of an override (customizable, in this example shift is removed from the report). The override is 'deactivated' in certain customizable situations, like when one of the trigger keys (shift or backspace) is lifted. The override key is then removed from the keyboard report and whichever keys are still held down may be re-added to the keyboard report (this is customizable).
Some more examples where I use this feature:
- I have a 65% board, so the ^ key, which in the German layout is usually left of the 1, is taken up by the esc key. Hence I have an override that maps the combination ctrl + esc to ^. The shifted version of this key types °, which works with this key override too: press ctrl + shift + esc to type °. Key overrides make the grave escape feature obsolete and even overcome the caveats of the grave escape feature.
- I have dedicated vol up/down keys, but no dedicated brightness up/down keys. Ctrl + vol up does brightness up, alt + vol up does a small increment of vol up, ctrl + alt + vol up does a small increment of brightness up. Same for vol/brightness down.
- I also have a 'next track' key, but no 'previous track' key. Ctrl + 'next track' does 'previous track'.
See below for a more complex example.
I only have one fn key on my keyboard (due to space limitations), so instead of having to limit myself to the fn key for custom actions I can use all modifier keys (and combinations thereof) to trigger custom actions/keycodes. Moreover, one might want to change the behavior of a common shortcut (e.g. cmd/ctrl+z), and can do so purely using the key override feature rather than having to install extra software on their PC OS.
Overrides have very fine grained control: You control which modifiers need to be pressed down to activate the override, you can control exactly which modifier+key combination is sent as a replacement. You control if the override is deactivated if certain other keys are pressed down while the override is active, and more.
It seems there is a lot of demand for this type of feature (see here, here, here, #108, #1495), but no solid solution has yet been implemented (#2900, #4795). I think my implementation of key overrides covers a lot of use cases by being very flexible, yet easy to use for beginners, while also being well engineered and tested.
Difference to Combos
The two main differences to combos are the following:
- There is no tapping term involved. The key combinations do not need to be pressed within a small period of time. Instead, since it relies on modifiers, it works like normal keyboard shortcuts: E.g. hold down control, and eventually press another key to do action x.
- It is mainly focused on modifier + key combinations (although it can also create custom actions for a single key press without modifiers). This feature cannot be used to create a custom action for when two or more basic keys are held down simultaneously — this is what combos should be used for.
In addition, with the fine grained options for when an override is activated and deactivated, this feature really gives you maximum control over overrides in a way what combos does not.
Implementation
This feature is already implemented in my fork, and is already quite clean. It is gated behind a rule KEY_OVERRIDE_ENABLE, and I tried following most conventions of other features. I have tested and refined it quite a bit over the last ~6 months, but it is certainly not perfect and I would still need to write docs, perhaps unit tests, etc before it could go into the main repo I am deliberately not opening a PR because I first want to hear the reactions to this pitch.
A clean diff of mainline QMK <-> my fork can be viewed here. The feature is mostly implemented in the files key_override.h/c, with small supporting modifications in core QMK/TMK files.
The code includes a few changes in the QMK/TMK core, most notably adding a new bitfield of weak modifiers used exclusively for overrides, and a bit field of suppressed mods, which is used to eliminate modifiers that are held down from the keyboard report (see here). This is to be able to replace arbitrary modifier+key combinations that are physically held down with other arbitrary modifier+key combinations part of an override.
Overrides are defined as structs that are pointed to from an array containing all active overrides. The fields of the key_override_t struct are well documented in the file key_override.h. See below for an example.
Examples
My use of key overrides can be seen in my keymap here, which includes all the examples mentioned here.
Here is an example of the shift + backspace = delete override, created with the macro ko_make_basic:
const key_override_t backspace_delete_override = ko_make_basic(MOD_MASK_SHIFT, KC_BSPACE, KC_DELETE);
// Null terminated array defines all overrides.
const key_override_t **key_overrides = (const key_override_t *[]){ &backspace_delete_override, NULL };
To simplify the creation of key overrides, the macros ko_make_xxx exist, which simplify the creation of key overrides by not having to create the full key_override_t struct with all its fields manually. For the simpler macros, like ko_make_basic, the most 'standard' options are used. Most people would not have to come to grips with all the options of a key override and could rely on the simple macros, while advanced users can tinker and fine-tune as much as they want by manually defining a key_override_t struct.
Complex Example
Perhaps the most complex example is this: I use a German layout, so I have umlaut keys (ö, ä and ü) in the places where UK/US keyboards have braces/brackets. Brackets are hidden somewhere in alt + number combinations in the German layout, which can be quite inconvenient to reach when having to type many brackets while programming. I rarely use umlauts when I program, so I use key overrides to map brackets to these keys. Using the key override feature I map the ö and ä key to { and } (when no modifiers are pressed!). When shift is held these keys type [ and ]. When control is held they go back to their normal state of ö and ä, even when other modifiers are held (like shift to get Ö and Ä).
This is really hard to get right because typing a {, for example, is done by sending alt + 8 in the German layout. Hence pressing the ö key sends alt + 8 in the keyboard report. Now add the shift key and using a key override, a [ is typed, which is done by sending alt + 5 in the keyboard report. Now if the ctrl key is also held down (in addition to shift and ö already being held), the ö key goes back to its original use, hence shift + ö will be sent in the keyboard report. Imagine holding ö and ä down randomly in combination with mashing and holding different modifiers repeatedly. This creates a lot of complexity in terms of transitions between active overrides. Without a careful implementation you might end up with ghosted stuck keys or wrong characters being typed. I actually tried implementing these umlaut -> bracket overrides manually using custom keycodes and handling them in process_record_user. Even after a decent amount of work I could still find edge cases where stuck or incorrect keys would be sent in the keyboard report. Using key overrides I have not encountered any seriously wrong behavior in edge cases, and the desired behavior is quite easy to define.
I have been working on a feature I call 'key override' over on my fork of QMK. I think it could be an interesting addition to the main repo that a lot of people would love to use.
Feature Request Type
Description
This feature is already implemented. View the diff here.
See the draft of the documentation here.
The feature works as follows: You can define custom actions for arbitrary modifier+key combinations: if
modifiers x+key yare pressed, sendmodifiers w+key zinstead ofmodifiers x+key y. This feature basically gives you the power of layers without having to use dedicated layer keys, allowing you to use modifier keys instead.A very simple example of one way I apply this feature: On my keyboard I use a key override that sends the delete key when shift + backspace is pressed.
When the necessary keys are pressed (in the above example: shift + backspace), the override is 'activated' and the override key (+ optionally extra modifiers) is sent in the keyboard report (delete), while the trigger key (backspace) is removed from the keyboard report. The trigger modifiers may be removed from the keyboard report upon activation of an override (customizable, in this example shift is removed from the report). The override is 'deactivated' in certain customizable situations, like when one of the trigger keys (shift or backspace) is lifted. The override key is then removed from the keyboard report and whichever keys are still held down may be re-added to the keyboard report (this is customizable).
Some more examples where I use this feature:
See below for a more complex example.
I only have one fn key on my keyboard (due to space limitations), so instead of having to limit myself to the fn key for custom actions I can use all modifier keys (and combinations thereof) to trigger custom actions/keycodes. Moreover, one might want to change the behavior of a common shortcut (e.g. cmd/ctrl+z), and can do so purely using the key override feature rather than having to install extra software on their PC OS.
Overrides have very fine grained control: You control which modifiers need to be pressed down to activate the override, you can control exactly which modifier+key combination is sent as a replacement. You control if the override is deactivated if certain other keys are pressed down while the override is active, and more.
It seems there is a lot of demand for this type of feature (see here, here, here, #108, #1495), but no solid solution has yet been implemented (#2900, #4795). I think my implementation of key overrides covers a lot of use cases by being very flexible, yet easy to use for beginners, while also being well engineered and tested.
Difference to Combos
The two main differences to combos are the following:
In addition, with the fine grained options for when an override is activated and deactivated, this feature really gives you maximum control over overrides in a way what combos does not.
Implementation
This feature is already implemented in my fork, and is already quite clean. It is gated behind a rule
KEY_OVERRIDE_ENABLE, and I tried following most conventions of other features. I have tested and refined it quite a bit over the last ~6 months, but it is certainly not perfect and I would still need to write docs, perhaps unit tests, etc before it could go into the main repo I am deliberately not opening a PR because I first want to hear the reactions to this pitch.A clean diff of mainline QMK <-> my fork can be viewed here. The feature is mostly implemented in the files
key_override.h/c, with small supporting modifications in core QMK/TMK files.The code includes a few changes in the QMK/TMK core, most notably adding a new bitfield of weak modifiers used exclusively for overrides, and a bit field of suppressed mods, which is used to eliminate modifiers that are held down from the keyboard report (see here). This is to be able to replace arbitrary modifier+key combinations that are physically held down with other arbitrary modifier+key combinations part of an override.
Overrides are defined as structs that are pointed to from an array containing all active overrides. The fields of the
key_override_tstruct are well documented in the file key_override.h. See below for an example.Examples
My use of key overrides can be seen in my keymap here, which includes all the examples mentioned here.
Here is an example of the shift + backspace = delete override, created with the macro
ko_make_basic:To simplify the creation of key overrides, the macros
ko_make_xxxexist, which simplify the creation of key overrides by not having to create the fullkey_override_tstruct with all its fields manually. For the simpler macros, likeko_make_basic, the most 'standard' options are used. Most people would not have to come to grips with all the options of a key override and could rely on the simple macros, while advanced users can tinker and fine-tune as much as they want by manually defining akey_override_tstruct.Complex Example
Perhaps the most complex example is this: I use a German layout, so I have umlaut keys (ö, ä and ü) in the places where UK/US keyboards have braces/brackets. Brackets are hidden somewhere in alt + number combinations in the German layout, which can be quite inconvenient to reach when having to type many brackets while programming. I rarely use umlauts when I program, so I use key overrides to map brackets to these keys. Using the key override feature I map the ö and ä key to { and } (when no modifiers are pressed!). When shift is held these keys type [ and ]. When control is held they go back to their normal state of ö and ä, even when other modifiers are held (like shift to get Ö and Ä).
This is really hard to get right because typing a {, for example, is done by sending alt + 8 in the German layout. Hence pressing the ö key sends alt + 8 in the keyboard report. Now add the shift key and using a key override, a [ is typed, which is done by sending alt + 5 in the keyboard report. Now if the ctrl key is also held down (in addition to shift and ö already being held), the ö key goes back to its original use, hence shift + ö will be sent in the keyboard report. Imagine holding ö and ä down randomly in combination with mashing and holding different modifiers repeatedly. This creates a lot of complexity in terms of transitions between active overrides. Without a careful implementation you might end up with ghosted stuck keys or wrong characters being typed. I actually tried implementing these umlaut -> bracket overrides manually using custom keycodes and handling them in
process_record_user. Even after a decent amount of work I could still find edge cases where stuck or incorrect keys would be sent in the keyboard report. Using key overrides I have not encountered any seriously wrong behavior in edge cases, and the desired behavior is quite easy to define.