qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

feature_userspace.md (12142B)


      1 # Userspace: Sharing Code Between Keymaps
      2 
      3 ::: warning
      4 Please note, userspace submissions to the upstream `qmk/qmk_firmware` repository are no longer being accepted. The userspace feature itself remains functional and can be configured locally.
      5 :::
      6 
      7 If you use more than one keyboard with a similar keymap, you might see the benefit in being able to share code between them. Create your own folder in `users/` named the same as your keymap (ideally your GitHub username, `<name>`) with the following structure:
      8 
      9 * `/users/<name>/` (added to the path automatically)
     10   * `readme.md` (optional, recommended)
     11   * `rules.mk` (included automatically)
     12   * `config.h` (included automatically)
     13   * `<name>.h` (optional)
     14   * `<name>.c` (optional)
     15   * `cool_rgb_stuff.c` (optional)
     16   * `cool_rgb_stuff.h` (optional)
     17 
     18 
     19 All this only happens when you build a keymap named `<name>`, like this:
     20 
     21     make planck:<name>
     22 
     23 For example,
     24 
     25     make planck:jack
     26 
     27 Will include the `/users/jack/` folder in the path, along with `/users/jack/rules.mk`.
     28 
     29 ::: warning
     30 This `name` can be [overridden](#override-default-userspace), if needed.
     31 :::
     32 
     33 ## `Rules.mk`
     34 
     35 The `rules.mk` is one of the two files that gets processed automatically.  This is how you add additional source files (such as  `<name>.c`) will be added when compiling.
     36 
     37 It's highly recommended that you use `<name>.c` as the default source file to be added. And to add it, you need to add it the SRC in `rules.mk` like this:
     38 
     39     SRC += <name>.c
     40 
     41 Additional files may be added in the same way - it's recommended you have one named `<name>`.c/.h to start off with, though.
     42 
     43 The `/users/<name>/rules.mk` file will be included in the build _after_ the `rules.mk` from your keymap. This allows you to have features in your userspace `rules.mk` that depend on individual QMK features that may or may not be available on a specific keyboard.
     44 
     45 For example, if you have RGB control features shared between all your keyboards that support RGB lighting, you can add support for that if the RGBLIGHT feature is enabled:
     46 ```make
     47 ifeq ($(strip $(RGBLIGHT_ENABLE)), yes)
     48   # Include my fancy rgb functions source here
     49   SRC += cool_rgb_stuff.c
     50 endif
     51 ```
     52 
     53 Alternatively, you can `define RGB_ENABLE` in your keymap's `rules.mk` and then check for the variable in your userspace's `rules.mk` like this:
     54 ```make
     55 ifdef RGB_ENABLE
     56   # Include my fancy rgb functions source here
     57   SRC += cool_rgb_stuff.c
     58 endif
     59 ```
     60 
     61 ### Override default userspace
     62 
     63 By default the userspace used will be the same as the keymap name. In some situations this isn't desirable. For instance, if you use the [layout](feature_layouts) feature you can't use the same name for different keymaps (e.g. ANSI and ISO). You can name your layouts `mylayout-ansi` and `mylayout-iso` and add the following line to your layout's `rules.mk`:
     64 
     65 ```
     66 USER_NAME := mylayout
     67 ```
     68 
     69 This is also useful if you have multiple different keyboards with different features physically present on the board (such as one with RGB Lights, and one with Audio, or different number of LEDs, or connected to a different PIN on the controller).
     70 
     71 ## Configuration Options (`config.h`)
     72 
     73 Additionally, `config.h` here will be processed like the same file in your keymap folder.  This is handled separately from the `<name>.h` file.
     74 
     75 The reason for this, is that `<name>.h` won't be added in time to add settings (such as `#define TAPPING_TERM 100`), and including the `<name.h>` file in any `config.h` files will result in compile issues.
     76 
     77 ::: warning
     78 You should use the `config.h` for [configuration options](config_options), and the `<name>.h` file for user or keymap specific settings (such as the enum for layer or keycodes)
     79 :::
     80 
     81 ## Readme (`readme.md`)
     82 
     83 Please include authorship (your name, GitHub username, email), and optionally [a license that's GPL compatible](https://www.gnu.org/licenses/license-list.html#GPLCompatibleLicenses).
     84 
     85 You can use this as a template:
     86 ```
     87 Copyright <year> <name> <email> @<github_username>
     88 
     89 This program is free software: you can redistribute it and/or modify
     90 it under the terms of the GNU General Public License as published by
     91 the Free Software Foundation, either version 2 of the License, or
     92 (at your option) any later version.
     93 
     94 This program is distributed in the hope that it will be useful,
     95 but WITHOUT ANY WARRANTY; without even the implied warranty of
     96 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     97 GNU General Public License for more details.
     98 
     99 You should have received a copy of the GNU General Public License
    100 along with this program.  If not, see <http://www.gnu.org/licenses/>.
    101 ```
    102 
    103 You'd want to replace the year, name, email and GitHub username with your info.
    104 
    105 Additionally, this is a good place to document your code, if you wish to share it with others.
    106 
    107 ## Build All Keyboards That Support a Specific Keymap
    108 
    109 Want to check all your keymaps build in a single command? You can run:
    110 
    111     make all:<name>
    112 
    113 For example,
    114 
    115     make all:jack
    116 
    117 This is ideal for when you want ensure everything compiles successfully when preparing a [_Pull request_](https://github.com/qmk/qmk_firmware/pulls).
    118 
    119 ## Examples
    120 
    121 For a brief example, checkout [`/users/_example/`](https://github.com/qmk/qmk_firmware/tree/master/users/_example).
    122 
    123 For more complicated examples, checkout the [`awesome-qmk` collection](https://github.com/qmk/awesome-qmk).
    124 
    125 
    126 ### Customized Functions
    127 
    128 QMK has a bunch of [functions](custom_quantum_functions) that have [`_quantum`, `_kb`, and `_user` versions](custom_quantum_functions#a-word-on-core-vs-keyboards-vs-keymap) that you can use.  You will pretty much always want to use the user version of these functions.  But the problem is that if you use them in your userspace, then you don't have a version that you can use in your keymap.
    129 
    130 However, you can actually add support for keymap version, so that you can use it in both your userspace and your keymap!
    131 
    132 
    133 For instance, let's look at the `layer_state_set_user()` function.  You can enable the [Tri Layer State](ref_functions#olkb-tri-layers) functionality on all of your boards, while also retaining the Tri Layer functionality in your `keymap.c` files.
    134 
    135 In your `<name.c>` file, you'd want to add this:
    136 ```c
    137 __attribute__ ((weak))
    138 layer_state_t layer_state_set_keymap (layer_state_t state) {
    139   return state;
    140 }
    141 
    142 layer_state_t layer_state_set_user (layer_state_t state) {
    143   state = update_tri_layer_state(state, 2, 3, 5);
    144   return layer_state_set_keymap (state);
    145 }
    146 ```
    147 The `__attribute__ ((weak))` part tells the compiler that this is a placeholder function that can then be replaced by a version in your `keymap.c`.  That way, you don't need to add it to your `keymap.c`, but if you do, you won't get any conflicts because the function is the same name.
    148 
    149 The `_keymap` part here doesn't matter, it just needs to be something other than `_quantum`, `_kb`, or `_user`, since those are already in use. So you could use `layer_state_set_mine`, `layer_state_set_fn`, or anything else.
    150 
    151 You can see a list of this and other common functions in [`template.c`](https://github.com/qmk/qmk_firmware/blob/master/users/drashna/template.c) in [`users/drashna`](https://github.com/qmk/qmk_firmware/tree/master/users/drashna).
    152 
    153 ### Custom Features
    154 
    155 Since the Userspace feature can support a staggering number of boards, you may have boards that you want to enable certain functionality for, but not for others. And you can actually create "features" that you can enable or disable in your own userspace.
    156 
    157 For instance, if you wanted to have a bunch of macros available, but only on certain boards (to save space), you could "hide" them being a `#ifdef MACROS_ENABLED`, and then enable it per board.  To do this, add this to your rules.mk
    158 ```make
    159 ifeq ($(strip $(MACROS_ENABLED)), yes)
    160     OPT_DEFS += -DMACROS_ENABLED
    161 endif
    162 ```
    163 The `OPT_DEFS` setting causes `MACROS_ENABLED` to be defined for your keyboards (note the `-D` in front of the name), and you could use `#ifdef MACROS_ENABLED` to check the status in your c/h files, and handle that code based on that.
    164 
    165 Then you add `MACROS_ENABLED = yes` to the `rules.mk` for you keymap to enable this feature and the code in your userspace.
    166 
    167 And in your `process_record_user` function, you'd do something like this:
    168 ```c
    169 bool process_record_user(uint16_t keycode, keyrecord_t *record) {
    170   switch (keycode) {
    171 #ifdef MACROS_ENABLED
    172   case MACRO1:
    173     if (!record->event.pressed) {
    174       SEND_STRING("This is macro 1!");
    175     }
    176     break;
    177   case MACRO2:
    178     if (!record->event.pressed) {
    179       SEND_STRING("This is macro 2!");
    180     }
    181     break;
    182 #endif
    183   }
    184   return true;
    185 }
    186 ```
    187 
    188 
    189 ### Consolidated Macros
    190 
    191 If you wanted to consolidate macros and other functions into your userspace for all of your keymaps, you can do that.  This builds upon the [Customized Functions](#customized-functions) example above. This lets you maintain a bunch of macros that are shared between the different keyboards, and allow for keyboard specific macros, too.
    192 
    193 First, you'd want to go through all of your `keymap.c` files and replace `process_record_user` with `process_record_keymap` instead.   This way, you can still use keyboard specific codes on those boards, and use your custom "global" keycodes as well.   You'll also want to replace `SAFE_RANGE` with `NEW_SAFE_RANGE` so that you won't have any overlapping keycodes
    194 
    195 Then add `#include "<name>.h"` to all of your keymap.c files.  This allows you to use these new keycodes without having to redefine them in each keymap.
    196 
    197 Once you've done that, you'll want to set the keycode definitions that you need to the `<name>.h`  file. For instance:
    198 ```c
    199 #pragma once
    200 
    201 #include "quantum.h"
    202 #include "action.h"
    203 #include "version.h"
    204 
    205 // Define all of
    206 enum custom_keycodes {
    207   KC_MAKE = SAFE_RANGE,
    208   NEW_SAFE_RANGE  //use "NEW_SAFE_RANGE" for keymap specific codes
    209 };
    210 ```
    211 
    212 Now you want to create the `<name>.c` file, and add this content to it:
    213 
    214 ```c
    215 #include "<name>.h"
    216 
    217 __attribute__ ((weak))
    218 bool process_record_keymap(uint16_t keycode, keyrecord_t *record) {
    219   return true;
    220 }
    221 
    222 bool process_record_user(uint16_t keycode, keyrecord_t *record) {
    223   switch (keycode) {
    224     case KC_MAKE:  // Compiles the firmware, and adds the flash command based on keyboard bootloader
    225             if (!record->event.pressed) {
    226             uint8_t temp_mod = get_mods();
    227             uint8_t temp_osm = get_oneshot_mods();
    228             clear_mods(); clear_oneshot_mods();
    229             SEND_STRING("make " QMK_KEYBOARD ":" QMK_KEYMAP);
    230     #ifndef FLASH_BOOTLOADER
    231             if ((temp_mod | temp_osm) & MOD_MASK_SHIFT)
    232     #endif
    233             {
    234                 SEND_STRING(":flash");
    235             }
    236             if ((temp_mod | temp_osm) & MOD_MASK_CTRL) {
    237                 SEND_STRING(" -j8 --output-sync");
    238             }
    239             tap_code(KC_ENT);
    240             set_mods(temp_mod);
    241         }
    242         break;
    243 
    244   }
    245   return process_record_keymap(keycode, record);
    246 }
    247 ```
    248 
    249 For boards that may not have a shift button (such as on a macro pad), we need a way to always include the bootloader option.  To do that, add the following to the `rules.mk` in your userspace folder:
    250 
    251 ```make
    252 ifeq ($(strip $(FLASH_BOOTLOADER)), yes)
    253     OPT_DEFS += -DFLASH_BOOTLOADER
    254 endif
    255 ```
    256 
    257 This will add a new `KC_MAKE` keycode that can be used in any of your keymaps.  And this keycode will output `make <keyboard>:<keymap>`, making frequent compiling easier.  And this will work with any keyboard and any keymap as it will output the current boards info, so that you don't have to type this out every time.
    258 
    259 Also, holding Shift will add the flash target (`:flash`) to the command.  Holding Control will add some commands that will speed up compiling time by processing multiple files at once.
    260 
    261 And for the boards that lack a shift key, or that you want to always attempt the flashing part, you can add `FLASH_BOOTLOADER = yes` to the `rules.mk` of that keymap.
    262 
    263 ::: tip
    264 This should flash the newly compiled firmware automatically, using the correct utility, based on the bootloader settings (or default to just generating the HEX file). However, it should be noted that this may not work on all systems. AVRDUDE doesn't work on WSL, namely.
    265 :::