New to QMK, or in for a refresher? In this series of tutorials on QMK Basics, you’ll be brought up to speed on a number of features that help you customize your keyboard just the way you want it.
When fitting in your keymap onto a smaller keyboard than you’re used to, finding enough keys can be difficult. Even if you’ve got plenty of keys at your disposal, making some often used keys more accessible can provide you with more comfort and efficiency.
The QMK framework offers some creative ways to use your modifier keys so that they’re not just modifiers, but also a key of your choosing.
When is the last time you tapped a modifier key like Control, Shift or Alt? Modifier keys are often used in conjunction with another key, so you’ll often hold the key instead of tapping it. With the various keycodes I’ll describe below, you can put those modifier keys to use.
Table of Contents
Mod-tap
To quote the documentation:
The Mod-Tap key MT(mod, kc) acts like a modifier when held, and like a regular keycode when tapped. In other words, you can have a key that sends Escape when you tap it, but functions as a Control or Shift key when you hold it down.
There are many shortcuts available for the MT keycode that are configured with the right modifier, so you’ll only need to pick the keycode you want. Some of those keycodes are LCTL_T(kc), which is Left Control when held and sends kc when tapped.
Limitations of Mod-tap
Shifted keycodes won’t work. They are keycodes that send, for example, a # by themselves instead of having to press SHIFT + 3. Mod-tap ignores modifiers sent with the keycode, so MT(MOD_LSFT,KC_HASH) would send a 3 when pressed instead of a #.
The keycodes MT(mod, kc) and LT(layer, kc) only support basic keycodes.
There also isn’t a way to execute custom functions with Mod-Tap. The Mod-tap function directly registers the keycode with register_code, and doesn’t call process_record_user.
We can emulate a mod-tap action by manually keeping track of whether a key was tapped or held in the process_record_user function. In this example, let’s send the shifted key # manually, something that mod-tap can’t do for us.
You can add #define TAPPING_TERM 200 to your config.h. Any tap that’s shorter than 200ms will be a tap, anything longer will be held. You can pick a duration of your choice, 200 is the default.
Define a custom keycode by adding the custom_keycodes array. Our keycode will be named MY_HASH.
Place the custom keycode somewhere in your keymap. In this example, we only have a single key, so deciding where to put it is easy.
Now to edit the process_record_user function. First, add a variable to hold a timer value in. We make it static so the value is saved over time: normally, when a function like this is over, all the variables are emptied. This time, we want to remember at what time MY_HASH was pressed, so static remembers the value for us.
Add the below switch statement. You may already have such a switch statement, if so, you can place a new case in the existing statement.
When MY_HASH is pressed, we log the time to the my_hash_timer variable. We also register the keycode KC_LCTL, you can replace this with your held modifier of choice. This means that the modifier is in effect the moment you press the key.
When MY_HASH is released, we unregister KC_LCTL, so the modifier won’t be active anymore. Then, we check the timer: if it’s smaller than TAPPING_TERM, it’s a tap action and we will send the character we want: #. Instead of using TAPPING_TERM, you can enter a duration in milliseconds, anything below it will be a tap, anything more will be held.
#include QMK_KEYBOARD_H
enum custom_keycodes {
MY_HASH = SAFE_RANGE
};
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[0] = LAYOUT( /* Base */
MY_HASH \
),
};
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
static uint16_t my_hash_timer;
switch (keycode) {
case MY_HASH:
if(record->event.pressed) {
my_hash_timer = timer_read();
register_code(KC_LCTL); // Change the key to be held here
} else {
unregister_code(KC_LCTL); // Change the key that was held here, too!
if (timer_elapsed(my_hash_timer) < TAPPING_TERM) {
SEND_STRING("#"); // Change the character(s) to be sent on tap here
}
}
return false; // We handled this keypress
}
return true; // We didn't handle other keypresses
}
Space cadet
QMK has two features that are very much alike Mod-tap: Space Cadet Shift and Space Cadet Shift Enter. These features are much like Mod-tap, but have the ability to send specific shifted keycodes.
Space Cadet Shift sends ( when the left shift is tapped, and ) when the right shift is tapped. When the shift keys are held, they’re simply shift. The Space Cadet shift keys have the keycodes KC_LSPO and KC_RSPC for left and right shift respectively.
Space Cadet Shift Enter acts as a shift key when held and sends Enter when tapped. Its keycode is KC_SFTENT.
Both Space Cadet Shift and Space Cadet Shift Enter conflict with the Command feature, since they all use the same timer. If you decide to use the keycodes for Space Cadet, disable Command by adding COMMAND_ENABLE = no to your rules.mk.
Layer keys
Like MT(mod, kc), there exists a key to enter a layer when held, but which sends a keycode when tapped: LT(layer, kc). LT(layer, kc) activates layer momentarily when held, and sends the given keycode when pressed.
It has the same limitations as MT(mod, kc): it only supports basic keycodes, so no shifted keys and no user functions.
Customizing the tapping term
Under Behaviors that can be configured, there’s an interesting entry: #define TAPPING_TERM 200. The tapping term defines for how long in milliseconds you need to hold a key before the tap becomes a hold.
You can add the above line to your config.h.
Examples
There are many ways people use Mod-tap and Space Cadet. Some of those ways aren’t as obvious when starting out, but you may end up liking them in your keymap. Here are some examples of how people use tap and hold actions.
Home-row modifiers
In the thread What are your favorite QMK hacks?, user Canatella explains how to use mod-tap for home-row modifier keys. Canatella didn’t post a keymap, but here’s a possible application:
You can accomplish the above with the following keycodes, from left to right:
MOD_LGUI(KC_A) for the left Windows/Command/Meta key;
Kauyon Kais has shared his custom keyboard build, which makes use of a number of featurs, including tap and hold modifiers. In his keymap, you can find the full source code.
Here are some of the keycodes used to make the tap and hold keys work:
MT(MOD_LSFT, KC_BSPC) for backspace on tap and shift on hold;
LT(_FUN, KC_ENT) for enter on tap and switching to the _FUN layer on hold;
LT(_SYB, KC_SPC) for space on tap and switching to the _SYB layer on hold.
Reusing some often used keys like shift, enter and space is a very clever way to regain some key estate. It also places more emphasis on the thumbs. Thumbs are generally stronger than the outer fingers, which should provide more comfort.
Conclusion
You can put your modifier and layer keys to use by also assigning a tap action to them. This also works the other way around: You can make ordinary keys function as a modifier or layer key when held.
Just know that only basic keycodes are supported for tap and hold keys. That’s still a rather long list, so there are plenty of possibilities to explore!
Further reading
There are more ways than #define TAPPING_TERM 200 to configure the way QMK handles your tap and hold keys:
Permissive hold: Makes tap and hold keys work better for fast typists, or when you’ve set TAPPING_TERM to a high value.
Ignore Mod Tap Interrupt: Alters the behaviour of tap and hold keys in a similar but different way than Permissive hold.
Tapping Force Hold: Allows you to have the given keycode repeat when tapping and then holding the modifier key for a second time.
Retro Tapping: Sends the keycode instead of the modifier when you have held the modifier without pressing another key.
New to QMK, or in for a refresher? In this series of tutorials on QMK Basics, you’ll be brought up to speed on a number of features that help you customize your keyboard just the way you want it.
When fitting in your keymap onto a smaller keyboard than you’re used to, finding enough keys can be difficult. Even if you’ve got plenty of keys at your disposal, making some often used keys more accessible can provide you with more comfort and efficiency.
The QMK framework offers some creative ways to use your modifier keys so that they’re not just modifiers, but also a key of your choosing.
When is the last time you tapped a modifier key like Control, Shift or Alt? Modifier keys are often used in conjunction with another key, so you’ll often hold the key instead of tapping it. With the various keycodes I’ll describe below, you can put those modifier keys to use.
Table of Contents
Mod-tap
To quote the documentation:
There are many shortcuts available for the
MT
keycode that are configured with the right modifier, so you’ll only need to pick the keycode you want. Some of those keycodes areLCTL_T(kc)
, which is Left Control when held and sendskc
when tapped.Limitations of Mod-tap
Shifted keycodes won’t work. They are keycodes that send, for example, a
#
by themselves instead of having to pressSHIFT + 3
. Mod-tap ignores modifiers sent with the keycode, soMT(MOD_LSFT,KC_HASH)
would send a3
when pressed instead of a#
.Here is some background to this issue: a pull request by dpapavas and this thread by Fin_Complete on Reddit.
There also isn’t a way to execute custom functions with Mod-Tap. The Mod-tap function directly registers the keycode with
register_code
, and doesn’t callprocess_record_user
.A workaround for mod-tap
MechMerlin shared a very interesting code snippet in which he emulates the mod-tap behavior, adding the ability to perform custom functions. He makes various videos on keyboards and adventures with them at his YouTube channel.
We can emulate a mod-tap action by manually keeping track of whether a key was tapped or held in the
process_record_user
function. In this example, let’s send the shifted key#
manually, something that mod-tap can’t do for us.#define TAPPING_TERM 200
to yourconfig.h
. Any tap that’s shorter than 200ms will be a tap, anything longer will be held. You can pick a duration of your choice, 200 is the default.custom_keycodes
array. Our keycode will be namedMY_HASH
.process_record_user
function. First, add a variable to hold a timer value in. We make it static so the value is saved over time: normally, when a function like this is over, all the variables are emptied. This time, we want to remember at what timeMY_HASH
was pressed, sostatic
remembers the value for us.switch
statement. You may already have such a switch statement, if so, you can place a new case in the existing statement.MY_HASH
is pressed, we log the time to themy_hash_timer
variable. We also register the keycodeKC_LCTL
, you can replace this with your held modifier of choice. This means that the modifier is in effect the moment you press the key.MY_HASH
is released, we unregisterKC_LCTL
, so the modifier won’t be active anymore. Then, we check the timer: if it’s smaller thanTAPPING_TERM
, it’s a tap action and we will send the character we want:#
. Instead of usingTAPPING_TERM
, you can enter a duration in milliseconds, anything below it will be a tap, anything more will be held.Space cadet
QMK has two features that are very much alike Mod-tap: Space Cadet Shift and Space Cadet Shift Enter. These features are much like Mod-tap, but have the ability to send specific shifted keycodes.
Space Cadet Shift sends
(
when the left shift is tapped, and)
when the right shift is tapped. When the shift keys are held, they’re simply shift. The Space Cadet shift keys have the keycodesKC_LSPO
andKC_RSPC
for left and right shift respectively.Space Cadet Shift Enter acts as a shift key when held and sends
Enter
when tapped. Its keycode isKC_SFTENT
.Both Space Cadet Shift and Space Cadet Shift Enter conflict with the Command feature, since they all use the same timer. If you decide to use the keycodes for Space Cadet, disable Command by adding
COMMAND_ENABLE = no
to yourrules.mk
.Layer keys
Like
MT(mod, kc)
, there exists a key to enter a layer when held, but which sends a keycode when tapped:LT(layer, kc)
.LT(layer, kc)
activates layer momentarily when held, and sends the given keycode when pressed.It has the same limitations as
MT(mod, kc)
: it only supports basic keycodes, so no shifted keys and no user functions.Customizing the tapping term
Under Behaviors that can be configured, there’s an interesting entry:
#define TAPPING_TERM 200
. The tapping term defines for how long in milliseconds you need to hold a key before the tap becomes a hold.You can add the above line to your
config.h
.Examples
There are many ways people use Mod-tap and Space Cadet. Some of those ways aren’t as obvious when starting out, but you may end up liking them in your keymap. Here are some examples of how people use tap and hold actions.
Home-row modifiers
In the thread What are your favorite QMK hacks?, user Canatella explains how to use mod-tap for home-row modifier keys. Canatella didn’t post a keymap, but here’s a possible application:
You can accomplish the above with the following keycodes, from left to right:
MOD_LGUI(KC_A)
for the left Windows/Command/Meta key;MOD_LALT(KC_S)
for the left alt key;MOD_LCTL(KC_D)
for the left control key;MOD_LSFT(KC_F)
for the left shift key;LT(LOWER, KC_G)
for the lower layer key.Check out the Mod-Tap documentation for more keycodes.
Doubling up on thumb-keys
Kauyon Kais has shared his custom keyboard build, which makes use of a number of featurs, including tap and hold modifiers. In his keymap, you can find the full source code.
Here are some of the keycodes used to make the tap and hold keys work:
MT(MOD_LSFT, KC_BSPC)
for backspace on tap and shift on hold;LT(_FUN, KC_ENT)
for enter on tap and switching to the_FUN
layer on hold;LT(_SYB, KC_SPC)
for space on tap and switching to the_SYB
layer on hold.Reusing some often used keys like shift, enter and space is a very clever way to regain some key estate. It also places more emphasis on the thumbs. Thumbs are generally stronger than the outer fingers, which should provide more comfort.
Conclusion
You can put your modifier and layer keys to use by also assigning a tap action to them. This also works the other way around: You can make ordinary keys function as a modifier or layer key when held.
Just know that only basic keycodes are supported for tap and hold keys. That’s still a rather long list, so there are plenty of possibilities to explore!
Further reading
There are more ways than
#define TAPPING_TERM 200
to configure the way QMK handles your tap and hold keys:TAPPING_TERM
to a high value.Share this post: