← More about keyboards

Overview

One of QMK’s most empowering features is the ability to define macro buttons: a button, that when pressed, types a sequence of keys. This page is an assortment of practical QMK macros, collected from real users’ keymaps.

Obligatory warning: don’t use macros to type passwords or credit card numbers. Otherwise if someone has physical access to your keyboard, they can access this info.

Simple macros

First, to establish the basic pattern, here are instructions for implementing a couple simple macros. Add the following to your keymap.c file.

Step 1: Define keycodes for your macros:

enum custom_keycodes {
  UPDIR = SAFE_RANGE,
  EQ3X,
};

Step 2: Use these keycodes in your layouts, just as you would any other keycode:

[BASE] = LAYOUT(
  UPDIR  , KC_1   , KC_2   , KC_3   , KC_4   , KC_5   , ...

Step 3: In keymap.c, create (or add to) your process_record_user function to define the behavior for these buttons:

bool process_record_user(uint16_t keycode, keyrecord_t* record) {
  switch (keycode) {
    case UPDIR:  // Types ../ to go up a directory on the shell.
      if (record->event.pressed) {
        SEND_STRING("../");
      }
      return false;

    case EQ3X:  // Types triple equal ===
      if (record->event.pressed) {
        SEND_STRING("===");
      }
      return false;
  }
  return true;
}

See also the QMK macros documentation.

With the above implementation, pressing the UPDIR button on the keyboard has the effect of typing ../. Three characters for one button press! This might sound small, but from experience I can say this adds up, especially if it’s something you use a lot and if it’s awkward to type manually.

Use SEND_STRING to type a sequence of keys when the macro button is pressed. You can use this to blast out programming syntax like -> or === or whatever is common in the languages you use. I write a lot of C++ and use the Dvorak layout, in which the frequent C++ scope operator :: becomes an awkward double tap with the left pinky. So a well-used feature in my QMK keymap is a macro that types ::.

Macros are also useful for typing common strings, like your email or frequently typed commands, say SEND_STRING("git status").

You can also use tap_code(keycode) to tap an individual key. Or for even finer grained control, register_code(keycode) and unregister_code(keycode) to respectively hold and release a key.

Macros that respond to mods

Arrow macro, types -> or =>

For extra functionality, you can do a different action when the macro button is pressed with a mod. For instance, the following implements an ARROW button so that ARROW + shift types => and ARROW by itself types ->.

// Get current mod and one-shot mod states.
const uint8_t mods = get_mods();
const uint8_t oneshot_mods = get_oneshot_mods();

switch (keycode) {
  case ARROW:  // Arrow macro, types -> or =>.
    if (record->event.pressed) {
      if ((mods | oneshot_mods) & MOD_MASK_SHIFT) {  // Is shift held?
        del_mods(MOD_MASK_SHIFT);  // Temporarily delete shift.
        del_oneshot_mods(MOD_MASK_SHIFT);
        SEND_STRING("=>");
        set_mods(mods);            // Restore mods.
      } else {
        SEND_STRING("->");
      }
    }
    return false;
}

The tricky part is that shift or any other active mods will apply to the keys sent by SEND_STRING. So without treatment, the shift would turn the intended arrow => into +>. We get around this by temporarily deleting shift from the mod state. Alternatively, we can turn off all mods temporarily with

clear_mods();
clear_oneshot_mods();
// Do stuff...
set_mods(mods);

Braces macro, types [], {}, or <>

From draevin’s user directory, the following types [, then ], and taps the left arrow to position the cursor between the []. Or if shift is held, it does the same but with curly braces {}. Or with Ctrl, it does angle brackets <>.

const uint8_t mods = get_mods();
const uint8_t oneshot_mods = get_oneshot_mods();

switch (keycode) {
  case BRACES:  // Types [], {}, or <> and puts cursor between braces.
    if (record->event.pressed) {
      clear_mods();  // Temporarily disable mods.
      clear_oneshot_mods();
      if ((mods | oneshot_mods) & MOD_MASK_SHIFT) {
        SEND_STRING("{}");
      } else if ((mods | oneshot_mods) & MOD_MASK_CTRL) {
        SEND_STRING("<>");
      } else {
        SEND_STRING("[]");
      }
      tap_code(KC_LEFT);  // Move cursor between braces.
      set_mods(mods);  // Restore mods.
    }
    return false;
}

Macros with special keys

SEND_STRING is not limited to printable characters. To tap the left arrow key, for instance, do SEND_STRING(SS_TAP(X_LEFT)). Within SEND_STRING, the key name begins with X_ instead of KC_, otherwise keys have the same names as elsewhere.

Modifiers can be applied:

A few examples are described next.

Select word macro

Most text editors support Ctrl+left / Ctrl+right to move the cursor by words. From ajp10304’s user directory, this macro selects the current word. It types Ctrl+right to move to the end of the word, then Ctrl+Shift+left to select to the beginning of the word:

case SELWORD:  // Selects the current word under the cursor.
  if (record->event.pressed) {
    SEND_STRING(SS_LCTL(SS_TAP(X_RGHT) SS_LSFT(SS_TAP(X_LEFT))));
    // Mac users, change LCTL to LALT:
    // SEND_STRING(SS_LALT(SS_TAP(X_RGHT) SS_LSFT(SS_TAP(X_LEFT))));
  }
  return false;

See also my word selection post for an elaborated version of this macro.

Select line macro

This macro selects the current line, by tapping the home key and then Shift+end:

case SELLINE:  // Selects the current line.
  if (record->event.pressed) {
    SEND_STRING(SS_TAP(X_HOME) SS_LSFT(SS_TAP(X_END)));
    // Mac users, use:
    // SEND_STRING(SS_LCTL("a" SS_LSFT("e")));
  }
  return false;

Web search for current selection

Suppose you are in a web browser and have selected some text. This macro copies the text and does a web search for that text in a new tab.

case SRCHSEL:  // Searches the current selection in a new tab.
  if (record->event.pressed) {
    // Mac users, change LCTL to LGUI.
    SEND_STRING(SS_LCTL("ct") SS_DELAY(100) SS_LCTL("v") SS_TAP(X_ENTER));
  }
  return false;

In detail, it copies the selection (Ctrl+C), opens a new tab (Ctrl+T), pastes the selection into the omnibar (Ctrl+V), and finally taps enter to do the search. Since the browser might need a moment to open the new tab, SS_DELAY is used to wait briefly before pasting.

Other cool ideas

Next sentence macro

Inspired by precondition’s keymap, here is a “next sentence” macro. This is useful to flow between sentences. Having tried it, I admit it takes some practice to incorporate into your typing habits, but it packs a satisfying amount of action into a single button press. The macro types a period, space, then sets a one-shot mod so that the next key typed is shifted, to capitalize the first letter of the next sentence:

case NEXTSEN:  // Next sentence macro.
  if (record->event.pressed) {
    SEND_STRING(". ");
    add_oneshot_mods(MOD_BIT(KC_LSHIFT));  // Set one-shot mod for shift.
  }
  return false;

See also the approach taken in precondition’s keymap: there, the next sentence is triggered by double tapping the period button through a QMK tap dance, rather than using a macro.

Emojis

Julia Ebert’s blog notes that the way to type a multi-character emoji is to implement it as a macro. The following types a US flag emoji 🇺🇸:

case US_FLAG:  // Types US flag emoji.
  if (record->event.pressed) {
    send_unicode_hex_string("1F1FA 1F1F8");
  }
  return false;

Note: the above assumes UNICODEMAP_ENABLE = yes is set in the rules.mk file. You’ll also need to define UNICODE_SELECTED_MODES in config.h according to your OS.

← More about keyboards