aboutsummaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/feature_advanced_keycodes.md135
-rw-r--r--docs/feature_macros.md2
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
24You 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. 24You 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
28The 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
30The 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
32Thus, to give an example, `01000010` would be the internal representation of LShift+RAlt.
33For 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
35In 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
37To 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
39For 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
41The 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
61Aside 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
70Similarly, 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
79The 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
83Simple 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
86bool 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
112Advanced 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
114As 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.
119uint8_t mod_state;
120bool 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
28This 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. 163This 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
210There 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. 210There 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