QMK: Caps Word
Pascal Getreuer
Overview
All-caps identifiers like “MOD_MASK_ALT
” are awkward to type.
Caps Lock would be the standard solution to this problem, but it is awkward: it needs a dedicated key to toggle it (an imposition on smaller keyboards), and we need to remember to toggle it off after typing the word. Or with normal shifting, we either perform finger gymnastics or need to stop typing in the middle of the word to release shift with one hand to switch to holding shift with the other hand. In my experience, this is a nuisance especially if your shift keys are mod-taps, as in home row mods.
Caps Word, implemented here, is a modern alternative to Caps Lock:
Caps Word is activated by pressing the left and right shift keys at the same time (see also the customization options below). This way you don’t need a dedicated key for using Caps Word.
Caps Word automatically disables itself at the end of the word, that is, once space or any key other than
a
–z
,0
–9
,-
,_
, or backspace is pressed (this is configurable, see below).
Compatibility: I’ve tested that this implementation works with one-shot mods and Space Cadet Shift, and it predictably handles key repeating.
Unlike some other QMK Caps Word implementations, this library does not use the Caps Lock (KC_CAPS
) keycode. It works even if you’re remapping Caps Lock at the OS level to Ctrl or something else, as Emacs and Vim users often do.
Add Caps Word to your keymap
Step 1: In your keymap.c
, call Caps Word from your process_record_user()
function as
#include "features/caps_word.h"
bool process_record_user(uint16_t keycode, keyrecord_t* record) {
if (!process_caps_word(keycode, record)) { return false; }
// Your macros ...
return true;
}
Step 2: In your rules.mk, add
SRC += features/caps_word.c
Step 3: In the directory containing your keymap.c
, create a features
subdirectory and copy caps_word.h and caps_word.c there.
Also, your keymap needs both a left and right shift key KC_LSFT
and KC_RSFT
or one-shot or mod-tap equivalents. Alternatively, use the customization options below to add another method of activating Caps Word.
Troubleshooting: Command
You might see a compile message “Caps Word and Command should not be enabled at the same time, since both use the Left Shift + Right Shift key combination.”
Many keyboards enable the Command feature, which by default is also activated using the Left Shift + Right Shift key combination. To avoid conflict, disable Command by adding in rules.mk:
COMMAND_ENABLE = no
Or configure Command to use another key combination like Left Ctrl + Right Ctrl by defining IS_COMMAND()
in config.h:
// Activate Command with Left Ctrl + Right Ctrl.
#define IS_COMMAND() (get_mods() == MOD_MASK_CTRL)
Using Caps Word
With the above flashed to your keyboard:
Activating: Press and release both left and right shift keys at the same time. If your shift keys are mod-taps, activate Caps Word by holding both shift mod-tap keys until the tapping term, then release them.
Then begin typing to get capitalized letters.
Disabling: Caps Word disables itself when the next word breaking key is typed.
If you want to explicitly stop Caps Word, press and release Ctrl or another modifier. Or in many programs, it may be acceptable to press space and then backspace. Any of these actions disables Caps Word.
Customization options
Idle timeout
Optionally, Caps Word may be configured to deactivate if the keyboard is idle for some time. This is useful to mitigate unintended shifting when you get interrupted or switch to the mouse while Caps Word is active. In your config.h, define CAPS_WORD_IDLE_TIMEOUT
with a time in milliseconds:
#define CAPS_WORD_IDLE_TIMEOUT 5000 // Turn off Caps Word after 5 seconds.
and in your keymap.c, define (or add to) matrix_scan_user()
as
void matrix_scan_user(void) {
caps_word_task();// Other tasks...
}
The default behavior (when CAPS_WORD_IDLE_TIMEOUT
isn’t set, or set to 0) is that Caps Word never times out, and in this case it isn’t necessary to call caps_word_task()
. Caps Word remains active indefinitely until a word breaking key is pressed.
Functions
Functions to manipulate Caps Word:
Function | Description |
---|---|
caps_word_set(active) |
Activates or deactivates Caps Word. |
caps_word_get() |
Returns whether Caps Word is currently active. |
These functions can be used to activate, deactivate, or toggle Caps Word from your keymap with a macro, combo, tap dance, or whatever means.
Activate Caps Word with a combo
In my keymap, I activate Caps Word with a combo of simultaneously pressing .
and C
:
enum combo_events {
CAPS_COMBO,// Other combos...
COMBO_LENGTH
};uint16_t COMBO_LEN = COMBO_LENGTH;
const uint16_t caps_combo[] PROGMEM = {KC_DOT, KC_C, COMBO_END};
combo_t key_combos[] = {
[CAPS_COMBO] = COMBO_ACTION(caps_combo),// Other combos...
};
void process_combo_event(uint16_t combo_index, bool pressed) {
switch(combo_index) {
case CAPS_COMBO:
if (pressed) {
// Activate Caps Word!
caps_word_set(true);
}break;
// Other combos...
} }
And if you haven’t yet enabled combos, add COMBO_ENABLE = yes
in rules.mk.
Configure which keys are “word breaking”
You can define the caps_word_press_user(uint16_t keycode)
callback to configure which keys should be shifted and which keys are considered “word breaking” and stop Caps Word.
The callback is called on every key press while Caps Word is active. When the key should be shifted (that is, a letter key), the callback should call add_weak_mods(MOD_BIT(KC_LSFT))
to shift the key. Returning true continues the current “word,” while returning false is “word breaking” and deactivates Caps Word. The default callback is
bool caps_word_press_user(uint16_t keycode) {
switch (keycode) {
// Keycodes that continue Caps Word, with shift applied.
case KC_A ... KC_Z:
case KC_MINS:
// Apply shift to the next key.
add_weak_mods(MOD_BIT(KC_LSFT)); return true;
// Keycodes that continue Caps Word, without shifting.
case KC_1 ... KC_0:
case KC_BSPC:
case KC_DEL:
case KC_UNDS:
return true;
default:
return false; // Deactivate Caps Word.
} }
To customize, copy the above function into your keymap.c and add or remove keycodes to the cases.
NOTE: Outside of this callback, you can use caps_word_set(false)
to deactivate Caps Word.
Representing the current Caps Word state
You can define caps_word_set_user(bool active)
to get callbacks when Caps Word turns on or off. This is useful to represent the current Caps Word state, e.g. by setting an LED or playing a sound. In your keymap, define
void caps_word_set_user(bool active) {
if (active) {
// Do something when Caps Word activates.
else {
} // Do something when Caps Word deactivates.
} }
For instance on the ZSA Moonlander, the following indicates the Caps Word state on LEDs 1 and 4:
#include "moonlander.h"
void keyboard_post_init_user(void) {
// I want to control the Moonlander's LEDs myself.
keyboard_config.led_level = false;
}
void caps_word_set_user(bool active) {
ML_LED_1(active);
ML_LED_4(active); }
Other implementations
I’m aware of a few other QMK implementations of Caps Word:
qmk/qmk_firmware#16588 is a pending pull request to add Caps Word to QMK as a core feature. It is functionally the same as the userspace implementation here, though there are a few differences in default settings and API naming.
andrewjrae’s Kyria keymap has a Caps Word implementation. Caps Word is done as a special case of a “Case Modes” library, which can also be used to conveniently type
snake_case_name
andcamelCaseName
identifiers.replicaJunction’s QMK users folder has a Caps Word implementation. There is also a matching “Num Word” library applying the same idea to using a number layer.
Beyond QMK, Caps Word is a core feature in ZMK.
Explanation
Here is a brief explanation of how this Caps Word implementation works. The code checks the mod bits on each key event, activating Caps Word when both left and right shifts are pressed.
While active, Caps Word automatically applies shift as a weak mod as needed so that letters are shifted and other keys are not. The word continues while caps_word_press_user
returns true (by default, for a
–z
, 0
–9
, -
, _
, and backspace). Additionally, most layer switch keys (MO
, TO
, TG
, TT
, OSL
, LT
) are ignored so that Caps Word may be used across layers. Any other key is considered “word breaking” and disables Caps Word.
Acknowledgements
Thanks to @drashna and @wheredoesyourmindgo on GitHub and u/uolot and u/Dutchnesss1 on Reddit for feedback, bug fixes, and improvements to make Caps Word better. I couldn’t have done it alone!