diff options
| -rw-r--r-- | docs/feature_advanced_keycodes.md | 135 | ||||
| -rw-r--r-- | docs/feature_macros.md | 2 |
2 files changed, 136 insertions, 1 deletions
diff --git a/docs/feature_advanced_keycodes.md b/docs/feature_advanced_keycodes.md index 745308b29..75b7fef89 100644 --- a/docs/feature_advanced_keycodes.md +++ b/docs/feature_advanced_keycodes.md | |||
| @@ -23,6 +23,141 @@ These allow you to combine a modifier with a keycode. When pressed, the keydown | |||
| 23 | 23 | ||
| 24 | You can also chain them, for example `LCTL(LALT(KC_DEL))` or `C(A(KC_DEL))` makes a key that sends Control+Alt+Delete with a single keypress. | 24 | You can also chain them, for example `LCTL(LALT(KC_DEL))` or `C(A(KC_DEL))` makes a key that sends Control+Alt+Delete with a single keypress. |
| 25 | 25 | ||
| 26 | # Checking Modifier State :id=checking-modifier-state | ||
| 27 | |||
| 28 | The current modifier state can mainly be accessed with two functions: `get_mods()` for normal modifiers and modtaps and `get_oneshot_mods()` for one-shot modifiers (unless they're held, in which case they act like normal modifier keys). | ||
| 29 | |||
| 30 | The presence of one or more specific modifiers in the current modifier state can be detected by ANDing the modifier state with a mod mask corresponding to the set of modifiers you want to match for. The reason why bitwise operators are used is that the modifier state is stored as a single byte in the format (GASC)<sub>R</sub>(GASC)<sub>L</sub>. | ||
| 31 | |||
| 32 | Thus, to give an example, `01000010` would be the internal representation of LShift+RAlt. | ||
| 33 | For more information on bitwise operators in C, click [here](https://en.wikipedia.org/wiki/Bitwise_operations_in_C) to open the Wikipedia page on the topic. | ||
| 34 | |||
| 35 | In practice, this means that you can check whether a given modifier is active with `get_mods() & MOD_BIT(KC_<modifier>)` (see the [list of modifier keycodes](keycodes_basic.md#modifiers)) or with `get_mods() & MOD_MASK_<modifier>` if the difference between left and right hand modifiers is not important and you want to match both. Same thing can be done for one-shot modifiers if you replace `get_mods()` with `get_oneshot_mods()`. | ||
| 36 | |||
| 37 | To check that *only* a specific set of mods is active at a time, AND the modifier state and your desired mod mask as explained above and compare the result to the mod mask itself: `get_mods() & <mod mask> == <mod mask>`. | ||
| 38 | |||
| 39 | For example, let's say you want to trigger a piece of custom code if one-shot left control and one-shot left shift are on but every other one-shot mods are off. To do so, you can compose the desired mod mask by combining the mod bits for left control and shift with `(MOD_BIT(KC_LCTL) | MOD_BIT(KC_LSFT))` and then plug it in: `get_oneshot_mods & (MOD_BIT(KC_LCTL) | MOD_BIT(KC_LSFT)) == (MOD_BIT(KC_LCTL) | MOD_BIT(KC_LSFT))`. Using `MOD_MASK_CS` instead for the mod bitmask would have forced you to press four modifier keys (both versions of control and shift) to fulfill the condition. | ||
| 40 | |||
| 41 | The full list of mod masks is as follows: | ||
| 42 | |||
| 43 | | Mod Mask Name | Matching Modifiers | | ||
| 44 | |--------------------|------------------------------------------------| | ||
| 45 | | `MOD_MASK_CTRL` | LCTRL , RCTRL | | ||
| 46 | | `MOD_MASK_SHIFT` | LSHIFT , RSHIFT | | ||
| 47 | | `MOD_MASK_ALT` | LALT , RALT | | ||
| 48 | | `MOD_MASK_GUI` | LGUI , RGUI | | ||
| 49 | | `MOD_MASK_CS` | CTRL , SHIFT | | ||
| 50 | | `MOD_MASK_CA` | (L/R)CTRL , (L/R)ALT | | ||
| 51 | | `MOD_MASK_CG` | (L/R)CTRL , (L/R)GUI | | ||
| 52 | | `MOD_MASK_SA` | (L/R)SHIFT , (L/R)ALT | | ||
| 53 | | `MOD_MASK_SG` | (L/R)SHIFT , (L/R)GUI | | ||
| 54 | | `MOD_MASK_AG` | (L/R)ALT , (L/R)GUI | | ||
| 55 | | `MOD_MASK_CSA` | (L/R)CTRL , (L/R)SHIFT , (L/R)ALT | | ||
| 56 | | `MOD_MASK_CSG` | (L/R)CTRL , (L/R)SHIFT , (L/R)GUI | | ||
| 57 | | `MOD_MASK_CAG` | (L/R)CTRL , (L/R)ALT , (L/R)GUI | | ||
| 58 | | `MOD_MASK_SAG` | (L/R)SHIFT , (L/R)ALT , (L/R)GUI | | ||
| 59 | | `MOD_MASK_CSAG` | (L/R)CTRL , (L/R)SHIFT , (L/R)ALT , (L/R)GUI | | ||
| 60 | |||
| 61 | Aside from accessing the currently active modifiers using `get_mods()`, there exists some other functions you can use to modify the modifier state, where the `mods` argument refers to the modifiers bitmask. | ||
| 62 | |||
| 63 | * `add_mods(mods)`: Enable `mods` without affecting any other modifiers | ||
| 64 | * `register_mods(mods)`: Like `add_mods` but send a keyboard report immediately. | ||
| 65 | * `del_mods(mods)`: Disable `mods` without affecting any other modifiers | ||
| 66 | * `unregister_mods(mods)`: Like `del_mods` but send a keyboard report immediately. | ||
| 67 | * `set_mods(mods)`: Overwrite current modifier state with `mods` | ||
| 68 | * `clear_mods()`: Reset the modifier state by disabling all modifiers | ||
| 69 | |||
| 70 | Similarly, in addition to `get_oneshot_mods()`, there also exists these functions for one-shot mods: | ||
| 71 | |||
| 72 | * `add_oneshot_mods(mods)`: Enable `mods` without affecting any other one-shot modifiers | ||
| 73 | * `del_oneshot_mods(mods)`: Disable `mods` without affecting any other one-shot modifiers | ||
| 74 | * `set_oneshot_mods(mods)`: Overwrite current one-shot modifier state with `mods` | ||
| 75 | * `clear_oneshot_mods()`: Reset the one-shot modifier state by disabling all one-shot modifiers | ||
| 76 | |||
| 77 | ## Examples :id=examples | ||
| 78 | |||
| 79 | The following examples use [advanced macro functions](feature_macros.md#advanced-macro-functions) which you can read more about in the [documentation page on macros](feature_macros.md). | ||
| 80 | |||
| 81 | ### Alt + Escape for Alt + Tab :id=alt-escape-for-alt-tab | ||
| 82 | |||
| 83 | Simple example where chording Left Alt with `KC_ESC` makes it behave like `KC_TAB` for alt-tabbing between applications. This example strictly checks if only Left Alt is active, meaning you can't do Alt+Shift+Esc to switch between applications in reverse order. Also keep in mind that this removes the ability to trigger the actual Alt+Escape keyboard shortcut, though it keeps the ability to do AltGr+Escape. | ||
| 84 | |||
| 85 | ```c | ||
| 86 | bool process_record_user(uint16_t keycode, keyrecord_t *record) { | ||
| 87 | switch (keycode) { | ||
| 88 | |||
| 89 | case KC_ESC: | ||
| 90 | // Detect the activation of only Left Alt | ||
| 91 | if ((get_mods() & MOD_BIT(KC_LALT)) == MOD_BIT(KC_LALT)) { | ||
| 92 | if (record->event.pressed) { | ||
| 93 | // No need to register KC_LALT because it's already active. | ||
| 94 | // The Alt modifier will apply on this KC_TAB. | ||
| 95 | register_code(KC_TAB); | ||
| 96 | } else { | ||
| 97 | unregister_code(KC_TAB); | ||
| 98 | } | ||
| 99 | // Do not let QMK process the keycode further | ||
| 100 | return false; | ||
| 101 | } | ||
| 102 | // Else, let QMK process the KC_ESC keycode as usual | ||
| 103 | return true; | ||
| 104 | |||
| 105 | } | ||
| 106 | return true; | ||
| 107 | }; | ||
| 108 | ``` | ||
| 109 | |||
| 110 | ### Shift + Backspace for Delete :id=shift-backspace-for-delete | ||
| 111 | |||
| 112 | Advanced example where the original behaviour of shift is cancelled when chorded with `KC_BSPC` and is instead fully replaced by `KC_DEL`. Two main variables are created to make this work well: `mod_state` and `delkey_registered`. The first one stores the modifier state and is used to restore it after registering `KC_DEL`. The second variable is a boolean variable (true or false) which keeps track of the status of `KC_DEL` to manage the release of the whole Backspace/Delete key correctly. | ||
| 113 | |||
| 114 | As opposed to the previous example, this doesn't use strict modifier checking. Pressing `KC_BSPC` while one or two shifts are active is enough to trigger this custom code, regardless of the state of other modifiers. That approach offers some perks: Ctrl+Shift+Backspace lets us delete the next word (Ctrl+Delete) and Ctrl+Alt+Shift+Backspace lets us execute the Ctrl+Alt+Del keyboard shortcut. | ||
| 115 | |||
| 116 | ```c | ||
| 117 | // Initialize variable holding the binary | ||
| 118 | // representation of active modifiers. | ||
| 119 | uint8_t mod_state; | ||
| 120 | bool process_record_user(uint16_t keycode, keyrecord_t *record) { | ||
| 121 | // Store the current modifier state in the variable for later reference | ||
| 122 | mod_state = get_mods(); | ||
| 123 | switch (keycode) { | ||
| 124 | |||
| 125 | case KC_BSPC: | ||
| 126 | { | ||
| 127 | // Initialize a boolean variable that keeps track | ||
| 128 | // of the delete key status: registered or not? | ||
| 129 | static bool delkey_registered; | ||
| 130 | if (record->event.pressed) { | ||
| 131 | // Detect the activation of either shift keys | ||
| 132 | if (mod_state & MOD_MASK_SHIFT) { | ||
| 133 | // First temporarily canceling both shifts so that | ||
| 134 | // shift isn't applied to the KC_DEL keycode | ||
| 135 | del_mods(MOD_MASK_SHIFT); | ||
| 136 | register_code(KC_DEL); | ||
| 137 | // Update the boolean variable to reflect the status of KC_DEL | ||
| 138 | delkey_registered = true; | ||
| 139 | // Reapplying modifier state so that the held shift key(s) | ||
| 140 | // still work even after having tapped the Backspace/Delete key. | ||
| 141 | set_mods(mod_state); | ||
| 142 | return false; | ||
| 143 | } | ||
| 144 | } else { // on release of KC_BSPC | ||
| 145 | // In case KC_DEL is still being sent even after the release of KC_BSPC | ||
| 146 | if (delkey_registered) { | ||
| 147 | unregister_code(KC_DEL); | ||
| 148 | delkey_registered = false; | ||
| 149 | return false; | ||
| 150 | } | ||
| 151 | } | ||
| 152 | // Let QMK process the KC_BSPC keycode as usual outside of shift | ||
| 153 | return true; | ||
| 154 | } | ||
| 155 | |||
| 156 | } | ||
| 157 | return true; | ||
| 158 | }; | ||
| 159 | ``` | ||
| 160 | |||
| 26 | # Legacy Content :id=legacy-content | 161 | # Legacy Content :id=legacy-content |
| 27 | 162 | ||
| 28 | This page used to encompass a large set of features. We have moved many sections that used to be part of this page to their own pages. Everything below this point is simply a redirect so that people following old links on the web find what they're looking for. | 163 | This page used to encompass a large set of features. We have moved many sections that used to be part of this page to their own pages. Everything below this point is simply a redirect so that people following old links on the web find what they're looking for. |
diff --git a/docs/feature_macros.md b/docs/feature_macros.md index 6e69ad642..3660f3775 100644 --- a/docs/feature_macros.md +++ b/docs/feature_macros.md | |||
| @@ -209,7 +209,7 @@ SEND_STRING(".."SS_TAP(X_END)); | |||
| 209 | 209 | ||
| 210 | There are some functions you may find useful in macro-writing. Keep in mind that while you can write some fairly advanced code within a macro, if your functionality gets too complex you may want to define a custom keycode instead. Macros are meant to be simple. | 210 | There are some functions you may find useful in macro-writing. Keep in mind that while you can write some fairly advanced code within a macro, if your functionality gets too complex you may want to define a custom keycode instead. Macros are meant to be simple. |
| 211 | 211 | ||
| 212 | ?> You can also use the functions described in [Useful functions](ref_functions.md) for additional functionality. For example `reset_keyboard()` allows you to reset the keyboard as part of a macro. | 212 | ?> You can also use the functions described in [Useful function](ref_functions.md) and [Checking modifier state](feature_advanced_keycodes#checking-modifier-state) for additional functionality. For example, `reset_keyboard()` allows you to reset the keyboard as part of a macro and `get_mods() & MOD_MASK_SHIFT` lets you check for the existence of active shift modifiers. |
| 213 | 213 | ||
| 214 | ### `record->event.pressed` | 214 | ### `record->event.pressed` |
| 215 | 215 | ||
