QMK: Custom shift keys (2024)

Pascal Getreuer, 2021-10-30 (updated 2024-02-29)

  • Overview
  • Implementation
  • Customization
  • Compared to Key Overrides
  • Explanation
  • Acknowledgements

← More about keyboards

Overview

A frequently asked question about QMK is how to change what a keytypes when it is shifted. For instance, how to make a key with“inverted” shifting such that it types : when pressednormally and ; when pressed shifted. Or how to implement“programmer” layouts having keys that type symbols normally and type thedigits when pressed shifted.

QMK: Custom shift keys (1)

It’s surprisingly tricky to get a custom shift key implemented justright. I’ve seen a lot of proposed solutions, and tried a few thingsmyself. Some subtle gotchas:

  • Key repeating. When you hold a key down longenough, it normally repeats the character. E.g. you may want thisrepeating behavior to type a row of stars ****************without having to tap the * key for each star.

  • Rolled presses. Real typing is not always clean,individual key presses, especially in fast typing. You may press downone key, then while it is held, begin pressing another key—a “roll”across the keys. A common failure mode is a rolled press involvingcustom shift keys causes a key to get “stuck” until it is pressedagain.

  • Interoperating with QMK features. Does thecustom shift key implementation support shifting when done as a one-shotmod? Or with a mod-tap? Auto Shift? Space Cadet Shift?

Implementation

Here is my solution. It correctly handles key repeating and rolledpresses, and I’ve tested that it works predictably in combination withone-shot mods, mod-taps, and Space Cadet Shift. It does not work withAuto Shift. To get the analogous effect with Auto Shift, use AutoShift’s customshifted values configuration.

Step 1: In your keymap.c, define atable of “custom_shift_key_t” structs. Each row defines onekey. The keycode is the keycode as it appears in yourlayout and determines what is typed normally. Theshifted_keycode is what you want the key to type whenshifted. (See the QMK keycodesdocumentation for possible keycodes.)

Here is an example. The first row in my table has a .(KC_DOT) key that types ?(KC_QUES) when pressed shifted.

#include "features/custom_shift_keys.h"const custom_shift_key_t custom_shift_keys[] = { {KC_DOT , KC_QUES}, // Shift . is ? {KC_COMM, KC_EXLM}, // Shift , is ! {KC_MINS, KC_EQL }, // Shift - is = {KC_COLN, KC_SCLN}, // Shift : is ; };uint8_t NUM_CUSTOM_SHIFT_KEYS = sizeof(custom_shift_keys) / sizeof(custom_shift_key_t);

Special cases:

  • For tap-hold keys, write the full mod-tap orlayer-tap keycode in the first column and the shifted keycode in thesecond column. Suppose we have a mod-tap key that is dot .on tap and Ctrl on hold, and we want to customize the shifted tap actionto be question mark ?, then its entry would be:

    {LCTL_T(KC_DOT), KC_QUES}, // Shift . is ?
  • Shift has no effect: It is allowed to put thesame keycode in both columns to say that Shift has no effect on thatkey. For example, if we want the minus key - to type- regardless of Shift, its entry would be:

    {KC_MINS, KC_MINS}, // Shift - is - (Shift has no effect)

Step 2: Handle custom shift keys from yourprocess_record_user() function like so:

bool process_record_user(uint16_t keycode, keyrecord_t* record) { if (!process_custom_shift_keys(keycode, record)) { return false; } // Your macros ... return true;}

Step 3: In your rules.mk file, add

SRC += features/custom_shift_keys.c

Step 4: In the directory containing yourkeymap.c, create a features subdirectory andcopy custom_shift_keys.hand custom_shift_keys.cthere. This is the meat of the implementation.

custom_shift_keys.h

// Copyright 2021 Google LLC.// SPDX-License-Identifier: Apache-2.0#pragma once#include QMK_KEYBOARD_Htypedef struct { uint16_t keycode; uint16_t shifted_keycode;} custom_shift_key_t;extern const custom_shift_key_t custom_shift_keys[];extern uint8_t NUM_CUSTOM_SHIFT_KEYS;bool process_custom_shift_keys(uint16_t keycode, keyrecord_t *record);

custom_shift_keys.c

// Copyright 2021-2024 Google LLC.// SPDX-License-Identifier: Apache-2.0#include "custom_shift_keys.h"bool process_custom_shift_keys(uint16_t keycode, keyrecord_t *record) { static uint16_t registered_keycode = KC_NO; // If a custom shift key is registered, then this event is either // releasing it or manipulating another key at the same time. Either way, // we release the currently registered key. if (registered_keycode != KC_NO) { unregister_code16(registered_keycode); registered_keycode = KC_NO; } if (record->event.pressed) { // Press event. const uint8_t saved_mods = get_mods();#ifndef NO_ACTION_ONESHOT const uint8_t mods = saved_mods | get_weak_mods() | get_oneshot_mods();#else const uint8_t mods = saved_mods | get_weak_mods();#endif // NO_ACTION_ONESHOT if ((mods & MOD_MASK_SHIFT) != 0 // Shift is held.#if CUSTOM_SHIFT_KEYS_NEGMODS != 0 // Nothing in CUSTOM_SHIFT_KEYS_NEGMODS is held. && (mods & (CUSTOM_SHIFT_KEYS_NEGMODS)) == 0#endif // CUSTOM_SHIFT_KEYS_NEGMODS != 0 ) { // Continue default handling if this is a tap-hold key being held. if ((IS_QK_MOD_TAP(keycode) || IS_QK_LAYER_TAP(keycode)) && record->tap.count == 0) { return true; } // Search for a custom shift key whose keycode is `keycode`. for (int i = 0; i < NUM_CUSTOM_SHIFT_KEYS; ++i) { if (keycode == custom_shift_keys[i].keycode) { registered_keycode = custom_shift_keys[i].shifted_keycode; if (IS_QK_MODS(registered_keycode) && // Should key be shifted? (QK_MODS_GET_MODS(registered_keycode) & MOD_LSFT) != 0) { register_code16(registered_keycode); // If so, press directly. } else { // If not, cancel shift mods, press the key, and restore mods. del_weak_mods(MOD_MASK_SHIFT);#ifndef NO_ACTION_ONESHOT del_oneshot_mods(MOD_MASK_SHIFT);#endif // NO_ACTION_ONESHOT unregister_mods(MOD_MASK_SHIFT); register_code16(registered_keycode); set_mods(mods); } return false; } } } } return true; // Continue with default handling.}

Troubleshooting: If your keymap fails to build, alikely reason is that your QMK installation needs to be updated. If youhave the qmk_firmware git repo cloned locally, do agit pull. Or see Updatingyour master branch for more details.

Customization

By default, custom shift keys are applied whenever a shift mod isactive, including in combination with non-shift mods. The non-shift modsremain active with the shifted tap action. For instance with the entry{KC_DOT, KC_QUES}, pressing Ctrl +Shift + . sends Ctrl + ?. To disablecustom shift keys with certain mods, defineCUSTOM_SHIFT_KEYS_NEGMODS in your config.h using theMOD_MASK_<modifier> constants orMOD_BIT(KC_<modifier>) as describedhere. Examples:

// Don't apply custom shift keys when a Ctrl key is held.#define CUSTOM_SHIFT_KEYS_NEGMODS MOD_MASK_CTRL// Don't apply custom shift keys together with right Alt (AltGr).#define CUSTOM_SHIFT_KEYS_NEGMODS MOD_BIT(KC_RALT)

Or to enable custom shift keys only with shift mods, add inconfig.h:

// Don't apply custom shift keys when any non-shift mod is held.#define CUSTOM_SHIFT_KEYS_NEGMODS ~MOD_MASK_SHIFT

Compared to Key Overrides

QMK’s KeyOverrides feature “overrides” the keys sent for specifiedmodifier-key combinations. In particular, it can be used to implementcustom shift keys. Add “KEY_OVERRIDE_ENABLE = yes” inrules.mk to enable it, then the example above is analogously implementedas:

const key_override_t dot_key_override = ko_make_basic(MOD_MASK_SHIFT, KC_DOT, KC_QUES); // Shift . is ?const key_override_t comm_key_override = ko_make_basic(MOD_MASK_SHIFT, KC_COMM, KC_EXLM); // Shift , is !const key_override_t mins_key_override = ko_make_basic(MOD_MASK_SHIFT, KC_MINS, KC_EQL); // Shift - is =const key_override_t coln_key_override = ko_make_basic(MOD_MASK_SHIFT, KC_COLN, KC_SCLN); // Shift : is ;const key_override_t** key_overrides = (const key_override_t*[]){ &dot_key_override, &comm_key_override, &mins_key_override, &coln_key_override, NULL};

Advantages of custom_shift_keys:

  • Costs less firmware space: custom_shift_keys adds192 bytes to my keymap vs. 1956 bytes for Key Overrides (building withLTO enabled).

  • Simpler configuration syntax.

Advantages of Key Overrides:

  • Easily enabled, since it is part of QMK.

  • More general and configurable.

If you are already using Key Overrides for other purposes or have acouple kilobytes to spare, it is a great solution.

Explanation

The registered_keycode variable is the keycode of thecustom shift key that is currently pressed or otherwiseKC_NO. Only one custom key can be pressed at a time.Attempting to hold multiple custom shift keys releases all but the lastone.

On each press or release of any key:

  1. If registered_keycode is not KC_NO, werelease the currently active custom shift key(unregister_code16). To avoid stuck keys, this is alwaysthe right thing to do: either the event is releasing the active customshift key (so we should release it), or it is a rolled pressmanipulating another key while the active custom shift key is still held(so again, we should release it).

  2. In the loop, we check whether the current key event is pressing acustom shift key. If so, we clear the shift mods, press the appropriatekey depending on whether shift was held (register_code16),and restore the mods.

Acknowledgements

Thanks a bunch to @wheredoesyourmindgo on GitHuband u/uolot on Reddit for feedback and improvements to make custom shiftkeys better.

← More about keyboards

QMK: Custom shift keys (2024)
Top Articles
SI:AM | Matt Waldron Is Singlehandedly Keeping the Knuckleball Alive
Padres' Matt Waldron Is Bringing The Knuckleball Back To MLB
Nullreferenceexception 7 Days To Die
Whas Golf Card
Access-A-Ride – ACCESS NYC
Mate Me If You May Sapir Englard Pdf
Craigslist Mpls Mn Apartments
Hotels Near 500 W Sunshine St Springfield Mo 65807
Samsung 9C8
Music Archives | Hotel Grand Bach - Hotel GrandBach
Amateur Lesbian Spanking
Unit 1 Lesson 5 Practice Problems Answer Key
Oppenheimer Showtimes Near Cinemark Denton
Rachel Griffin Bikini
Gem City Surgeons Miami Valley South
Google Flights Missoula
Sonic Fan Games Hq
Driving Directions To Bed Bath & Beyond
Vintage Stock Edmond Ok
Wgu Academy Phone Number
Getmnapp
Prep Spotlight Tv Mn
Meta Carevr
Bend Missed Connections
Play It Again Sports Forsyth Photos
How to Use Craigslist (with Pictures) - wikiHow
Obsidian Guard's Skullsplitter
Club Keno Drawings
Dubois County Barter Page
Restaurants Near Calvary Cemetery
Bt33Nhn
Clark County Ky Busted Newspaper
Ny Post Front Page Cover Today
Reading Craigslist Pa
Jewish Federation Of Greater Rochester
Wisconsin Women's Volleyball Team Leaked Pictures
Hometown Pizza Sheridan Menu
Frommer's Philadelphia &amp; the Amish Country (2007) (Frommer's Complete) - PDF Free Download
Me Tv Quizzes
Electronic Music Duo Daft Punk Announces Split After Nearly 3 Decades
3 bis 4 Saison-Schlafsack - hier online kaufen bei Outwell
Payrollservers.us Webclock
Brother Bear Tattoo Ideas
Fluffy Jacket Walmart
Joblink Maine
Canvas Elms Umd
Erica Mena Net Worth Forbes
Craigslist Psl
Parks And Rec Fantasy Football Names
8663831604
Convert Celsius to Kelvin
OSF OnCall Urgent Care treats minor illnesses and injuries
Latest Posts
Article information

Author: Geoffrey Lueilwitz

Last Updated:

Views: 6024

Rating: 5 / 5 (80 voted)

Reviews: 87% of readers found this page helpful

Author information

Name: Geoffrey Lueilwitz

Birthday: 1997-03-23

Address: 74183 Thomas Course, Port Micheal, OK 55446-1529

Phone: +13408645881558

Job: Global Representative

Hobby: Sailing, Vehicle restoration, Rowing, Ghost hunting, Scrapbooking, Rugby, Board sports

Introduction: My name is Geoffrey Lueilwitz, I am a zealous, encouraging, sparkling, enchanting, graceful, faithful, nice person who loves writing and wants to share my knowledge and understanding with you.