← More about keyboards

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:

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:

  1. 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.

  2. Then begin typing to get capitalized letters.

  3. 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) {
        caps_word_set(true);  // Activate Caps Word!
      }
      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:
      add_weak_mods(MOD_BIT(KC_LSFT));  // Apply shift to the next key.
      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:

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 az, 09, -, _, 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!

← More about keyboards