Skip to content

Commit

Permalink
Merge remote-tracking branch 'qmk/develop' into develop
Browse files Browse the repository at this point in the history
* qmk/develop:
  More headroom. (qmk#15302)
  More headroom. (qmk#15301)
  Documentation typo fix (qmk#15298)
  New feature: `DYNAMIC_TAPPING_TERM_ENABLE` (qmk#11036)
  Tidy up adjustable ws2812 timing (qmk#15299)
  Add ifndef to WS2812 timing constraints (qmk#14678)
  Add Retro Shift (Auto Shift for Tap Hold via Retro Tapping) and Custom Auto Shifts (qmk#11059)
  • Loading branch information
Carlos Cardoso committed Nov 25, 2021
2 parents e04f8a1 + 08b7f8b commit f4a446b
Show file tree
Hide file tree
Showing 25 changed files with 877 additions and 163 deletions.
1 change: 1 addition & 0 deletions builddefs/generic_features.mk
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ GENERIC_FEATURES = \
TAP_DANCE \
VELOCIKEY \
WPM \
DYNAMIC_TAPPING_TERM \

define HANDLE_GENERIC_FEATURE
# $$(info "Processing: $1_ENABLE $2.c")
Expand Down
1 change: 1 addition & 0 deletions builddefs/show_options.mk
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ OTHER_OPTION_NAMES = \
UNICODE_COMMON \
AUTO_SHIFT_ENABLE \
AUTO_SHIFT_MODIFIERS \
DYNAMIC_TAPPING_TERM_ENABLE \
COMBO_ENABLE \
KEY_LOCK_ENABLE \
KEY_OVERRIDE_ENABLE \
Expand Down
2 changes: 2 additions & 0 deletions docs/config_options.md
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,8 @@ Use these to enable or disable building certain features. The more you have enab
* Disables usb suspend check after keyboard startup. Usually the keyboard waits for the host to wake it up before any tasks are performed. This is useful for split keyboards as one half will not get a wakeup call but must send commands to the master.
* `DEFERRED_EXEC_ENABLE`
* Enables deferred executor support -- timed delays before callbacks are invoked. See [deferred execution](custom_quantum_functions.md#deferred-execution) for more information.
* `DYNAMIC_TAPPING_TERM_ENABLE`
* Allows to configure the global tapping term on the fly.
## USB Endpoint Limitations
Expand Down
176 changes: 162 additions & 14 deletions docs/feature_auto_shift.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,26 @@ down will repeat the shifted key, though this can be disabled with
once then immediately (within `TAPPING_TERM`) hold it down again (this works
with the shifted value as well if auto-repeat is disabled).

There are also the `get_auto_shift_repeat` and `get_auto_shift_no_auto_repeat`
functions for more granular control. Neither will have an effect unless
`AUTO_SHIFT_REPEAT_PER_KEY` or `AUTO_SHIFT_NO_AUTO_REPEAT_PER_KEY` respectively
are defined.

## Are There Limitations to Auto Shift?

Yes, unfortunately.

You will have characters that are shifted when you did not intend on shifting, and
other characters you wanted shifted, but were not. This simply comes down to
practice. As we get in a hurry, we think we have hit the key long enough for a
shifted version, but we did not. On the other hand, we may think we are tapping
the keys, but really we have held it for a little longer than anticipated.

Additionally, with keyrepeat the desired shift state can get mixed up. It will
always 'belong' to the last key pressed. For example, keyrepeating a capital
and then tapping something lowercase (whether or not it's an Auto Shift key)
will result in the capital's *key* still being held, but shift not.
1. You will have characters that are shifted when you did not intend on shifting, and
other characters you wanted shifted, but were not. This simply comes down to
practice. As we get in a hurry, we think we have hit the key long enough for a
shifted version, but we did not. On the other hand, we may think we are tapping
the keys, but really we have held it for a little longer than anticipated.
2. Additionally, with keyrepeat the desired shift state can get mixed up. It will
always 'belong' to the last key pressed. For example, keyrepeating a capital
and then tapping something lowercase (whether or not it's an Auto Shift key)
will result in the capital's *key* still being held, but shift not.
3. Auto Shift does not apply to Tap Hold keys. For automatic shifting of Tap Hold
keys see [Retro Shift](#retro-shift).

## How Do I Enable Auto Shift?

Expand Down Expand Up @@ -96,6 +102,34 @@ quicker than normal and you will be set.
?> Auto Shift has three special keys that can help you get this value right very quick. See "Auto Shift Setup" for more details!
For more granular control of this feature, you can add the following to your `config.h`:
```c
#define AUTO_SHIFT_TIMEOUT_PER_KEY
```

You can then add the following function to your keymap:

```c
uint16_t get_autoshift_timeout(uint16_t keycode, keyrecord_t *record) {
switch(keycode) {
case AUTO_SHIFT_NUMERIC:
return 2 * get_generic_autoshift_timeout();
case AUTO_SHIFT_SPECIAL:
return get_generic_autoshift_timeout() + 50;
case AUTO_SHIFT_ALPHA:
default:
return get_generic_autoshift_timeout();
}
}
```
Note that you cannot override individual keys that are in one of those groups
if you are using them; trying to add a case for `KC_A` in the above example will
not compile as `AUTO_SHIFT_ALPHA` is there. A possible solution is a second switch
above to handle individual keys with no default case and only referencing the
groups in the below fallback switch.
### NO_AUTO_SHIFT_SPECIAL (simple define)
Do not Auto Shift special keys, which include -\_, =+, [{, ]}, ;:, '", ,<, .>,
Expand All @@ -109,11 +143,24 @@ Do not Auto Shift numeric keys, zero through nine.
Do not Auto Shift alpha characters, which include A through Z.
### Auto Shift Per Key
### Auto Shift Per Key
This is a function that allows you to determine which keys shold be autoshifted, much like the tap-hold keys.
There are functions that allows you to determine which keys shold be autoshifted, much like the tap-hold keys.
The default function looks like this:
The first of these, used to simply add a key to Auto Shift, is `get_custom_auto_shifted_key`:
```c
bool get_custom_auto_shifted_key(uint16_t keycode, keyrecord_t *record) {
switch(keycode) {
case KC_DOT:
return true;
default:
return false;
}
}
```

For more granular control, there is `get_auto_shifted_key`. The default function looks like this:

```c
bool get_auto_shifted_key(uint16_t keycode, keyrecord_t *record) {
Expand All @@ -131,9 +178,10 @@ bool get_auto_shifted_key(uint16_t keycode, keyrecord_t *record) {
# endif
return true;
}
return false;
return get_custom_auto_shifted_key(keycode, record);
}
```
This functionality is enabled by default, and does not need a define.
### AUTO_SHIFT_REPEAT (simple define)
Expand All @@ -144,6 +192,106 @@ Enables keyrepeat.
Disables automatically keyrepeating when `AUTO_SHIFT_TIMEOUT` is exceeded.
## Custom Shifted Values
Especially on small keyboards, the default shifted value for many keys is not
optimal. To provide more customizability, there are two user-definable
functions, `autoshift_press/release_user`. These register or unregister the
correct value for the passed key. Below is an example adding period to Auto
Shift and making its shifted value exclamation point. Make sure to use weak
mods - setting real would make any keys following it use their shifted values
as if you were holding the key. Clearing of modifiers is handled by Auto Shift,
and the OS-sent shift value if keyrepeating multiple keys is always that of
the last key pressed (whether or not it's an Auto Shift key).
You can also have non-shifted keys for the shifted values (or even no shifted
value), just don't set a shift modifier!
```c
bool get_custom_auto_shifted_key(uint16_t keycode, keyrecord_t *record) {
switch(keycode) {
case KC_DOT:
return true;
default:
return false;
}
}
void autoshift_press_user(uint16_t keycode, bool shifted, keyrecord_t *record) {
switch(keycode) {
case KC_DOT:
register_code16((!shifted) ? KC_DOT : KC_EXLM);
break;
default:
if (shifted) {
add_weak_mods(MOD_BIT(KC_LSFT));
}
// & 0xFF gets the Tap key for Tap Holds, required when using Retro Shift
register_code16((IS_RETRO(keycode)) ? keycode & 0xFF : keycode);
}
}
void autoshift_release_user(uint16_t keycode, bool shifted, keyrecord_t *record) {
switch(keycode) {
case KC_DOT:
unregister_code16((!shifted) ? KC_DOT : KC_EXLM);
break;
default:
// & 0xFF gets the Tap key for Tap Holds, required when using Retro Shift
// The IS_RETRO check isn't really necessary here, always using
// keycode & 0xFF would be fine.
unregister_code16((IS_RETRO(keycode)) ? keycode & 0xFF : keycode);
}
}
```

## Retro Shift

Holding and releasing a Tap Hold key without pressing another key will ordinarily
result in only the hold. With `retro shift` enabled this action will instead
produce a shifted version of the tap keycode on release.

It does not require [Retro Tapping](tap_hold.md#retro-tapping) to be enabled, and
if both are enabled the state of `retro tapping` will only apply if the tap keycode
is not matched by Auto Shift. `RETRO_TAPPING_PER_KEY` and its corresponding
function, however, are checked before `retro shift` is applied.

To enable `retro shift`, add the following to your `config.h`:

```c
#define RETRO_SHIFT
```

If `RETRO_SHIFT` is defined to a value, hold times greater than that value will
not produce a tap on release for Mod Taps, and instead triggers the hold action.
This enables modifiers to be held for combining with mouse clicks without
generating taps on release. For example:

```c
#define RETRO_SHIFT 500
```
This value (if set) must be greater than one's `TAPPING_TERM`, as the key press
must be designated as a 'hold' by `process_tapping` before we send the modifier.
There is no such limitation in regards to `AUTO_SHIFT_TIMEOUT` for normal keys.
### Retro Shift and Tap Hold Configurations
Tap Hold Configurations work a little differently when using Retro Shift.
Referencing `TAPPING_TERM` makes little sense, as holding longer would result in
shifting one of the keys.
`IGNORE_MOD_TAP_INTERRUPT` changes *only* rolling from a mod tap (releasing it
first), sending both keys instead of the modifier on the second. Its effects on
nested presses are ignored.
As nested taps were changed to act as though `PERMISSIVE_HOLD` is set unless only
`IGNORE_MOD_TAP_INTERRUPT` is (outside of Retro Shift), and Retro Shift ignores
`IGNORE_MOD_TAP_INTERRUPT`, `PERMISSIVE_HOLD` has no effect on Mod Taps.
Nested taps will *always* act as though the `TAPPING_TERM` was exceeded for both
Mod and Layer Tap keys.
## Using Auto Shift Setup
This will enable you to define three keys temporarily to increase, decrease and report your `AUTO_SHIFT_TIMEOUT`.
Expand Down
10 changes: 10 additions & 0 deletions docs/keycodes.md
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,16 @@ See also: [Mod-Tap](mod_tap.md)
|`MEH_T(kc)` | |Left Control, Shift and Alt when held, `kc` when tapped |
|`HYPR_T(kc)` |`ALL_T(kc)` |Left Control, Shift, Alt and GUI when held, `kc` when tapped - more info [here](https://brettterpstra.com/2012/12/08/a-useful-caps-lock-key/)|

## Tapping Term Keys :id=tapping-term-keys

See also: [Dynamic Tapping Term](tap_hold#dynamic-tapping-term)

| Key | Description |
|-------------|------------------------------------------------------------------------------------------------------------------------|
| `DT_PRNT` | "Dynamic Tapping Term Print": Types the current tapping term, in milliseconds |
| `DT_UP` | "Dynamic Tapping Term Up": Increases the current tapping term by `DYNAMIC_TAPPING_TERM_INCREMENT`ms (5ms by default) |
| `DT_DOWN` | "Dynamic Tapping Term Down": Decreases the current tapping term by `DYNAMIC_TAPPING_TERM_INCREMENT`ms (5ms by default) |

## RGB Lighting :id=rgb-lighting

See also: [RGB Lighting](feature_rgblight.md)
Expand Down
84 changes: 83 additions & 1 deletion docs/tap_hold.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ These options let you modify the behavior of the Tap-Hold keys.

## Tapping Term

The crux of all of the following features is the tapping term setting. This determines what is a tap and what is a hold. And the exact timing for this to feel natural can vary from keyboard to keyboard, from switch to switch, and from key to key.
The crux of all of the following features is the tapping term setting. This determines what is a tap and what is a hold. The exact timing for this to feel natural can vary from keyboard to keyboard, from switch to switch, and from key to key.

?> `DYNAMIC_TAPPING_TERM_ENABLE` enables three special keys that can help you quickly find a comfortable tapping term for you. See "Dynamic Tapping Term" for more details.

You can set the global time for this by adding the following setting to your `config.h`:

Expand Down Expand Up @@ -36,6 +38,82 @@ uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) {
}
```
### Dynamic Tapping Term :id=dynamic-tapping-term
`DYNAMIC_TAPPING_TERM_ENABLE` is a feature you can enable in `rules.mk` that lets you use three special keys in your keymap to configure the tapping term on the fly.
| Key | Description |
|-------------|------------------------------------------------------------------------------------------------------------------------|
| `DT_PRNT` | "Dynamic Tapping Term Print": Types the current tapping term, in milliseconds |
| `DT_UP` | "Dynamic Tapping Term Up": Increases the current tapping term by `DYNAMIC_TAPPING_TERM_INCREMENT`ms (5ms by default) |
| `DT_DOWN` | "Dynamic Tapping Term Down": Decreases the current tapping term by `DYNAMIC_TAPPING_TERM_INCREMENT`ms (5ms by default) |
Set the tapping term as usual with `#define TAPPING_TERM <value>` in `config.h` and add `DYNAMIC_TAPPING_TERM_ENABLE = yes` in `rules.mk`. Then, place the above three keys somewhere in your keymap and flash the new firmware onto your board.
Now, you can try using your dual-role keys, such as layer-taps and mod-taps, and use `DT_DOWN` and `DT_UP` to adjust the tapping term immediately. If you find that you frequently trigger the modifier of your mod-tap(s) by accident for example, that's a sign that your tapping term may be too low so tap `DT_UP` a few times to increase the tapping term until that no longer happens. On the flip side, if you get superfluous characters when you actually intended to momentarily activate a layer, tap `DT_DOWN` to lower the tapping term. Do note that these keys affect the *global* tapping term, you cannot change the tapping term of a specific key on the fly.
Once you're satisfied with the current tapping term value, open `config.h` and replace whatever value you first wrote for the tapping term by the output of the `DT_PRNT` key.
It's important to update `TAPPING_TERM` with the new value because the adjustments made using `DT_UP` and `DT_DOWN` are not persistent.
The value by which the tapping term increases or decreases when you tap `DT_UP` and `DT_DOWN` can be configured in `config.h` with `#define DYNAMIC_TAPPING_TERM_INCREMENT <new value>`. Note that the tapping term is *not* modified when holding down the tap term keys so if you need to, for example, decrease the current tapping term by 50ms, you cannot just press down and hold `DT_DOWN`; you will have to tap it 10 times in a row with the default increment of 5ms.
If you need more flexibility, nothing prevents you from defining your own custom keys to dynamically change the tapping term.
```c
enum custom_dynamic_tapping_term_keys = {
DT_UP_50 = SAFE_RANGE,
DT_DOWN_50,
DT_UP_X2,
DT_DOWN_X2,
}
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
switch (keycode) {
case DT_UP_50:
if (record->event.pressed) {
g_tapping_term += 50;
}
break;
case DT_DOWN_50:
if (record->event.pressed) {
g_tapping_term -= 50;
}
break;
case DT_UP_X2:
if (record->event.pressed) {
g_tapping_term *= 2;
}
break;
case DT_DOWN_X2:
if (record->event.pressed) {
g_tapping_term /= 2;
}
break;
}
return true;
};
```

In order for this feature to be effective if you use per-key tapping terms, you need to make a few changes to the syntax of the `get_tapping_term` function. All you need to do is replace every occurrence of `TAPPING_TERM` in the `get_tapping_term` function by lowercase `g_tapping_term`. If you don't do that, you will still see the value typed by `DT_PRNT` go up and down as you configure the tapping term on the fly but you won't feel those changes as they don't get applied. If you can go as low as 10ms and still easily trigger the tap function of a dual-role key, that's a sign that you forgot to make the necessary changes to your `get_tapping_term` function.

For instance, here's how the example `get_tapping_term` shown earlier should look like after the transformation:

```c
uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) {
switch (keycode) {
case SFT_T(KC_SPC):
return g_tapping_term + 1250;
case LT(1, KC_GRV):
return 130;
default:
return g_tapping_term;
}
}
```
The reason being that `TAPPING_TERM` is a macro that expands to a constant integer and thus cannot be changed at runtime whereas `g_tapping_term` is a variable whose value can be changed at runtime. If you want, you can temporarily enable `DYNAMIC_TAPPING_TERM_ENABLE` to find a suitable tapping term value and then disable that feature and revert back to using the classic syntax for per-key tapping term settings.
## Tap-Or-Hold Decision Modes
The code which decides between the tap and hold actions of dual-role keys supports three different modes, in increasing order of preference for the hold action:
Expand Down Expand Up @@ -268,6 +346,10 @@ bool get_retro_tapping(uint16_t keycode, keyrecord_t *record) {
}
```
### Retro Shift
[Auto Shift,](feature_auto_shift.md) has its own version of `retro tapping` called `retro shift`. It is extremely similar to `retro tapping`, but holding the key past `AUTO_SHIFT_TIMEOUT` results in the value it sends being shifted. Other configurations also affect it differently; see [here](feature_auto_shift.md#retro-shift) for more information.
## Why do we include the key record for the per key functions?
One thing that you may notice is that we include the key record for all of the "per key" functions, and may be wondering why we do that.
Expand Down
1 change: 1 addition & 0 deletions docs/understanding_qmk.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ The `process_record()` function itself is deceptively simple, but hidden within
* [`bool process_combo(uint16_t keycode, keyrecord_t *record)`](https://github.com/qmk/qmk_firmware/blob/e1203a222bb12ab9733916164a000ef3ac48da93/quantum/process_keycode/process_combo.c#L115)
* [`bool process_printer(uint16_t keycode, keyrecord_t *record)`](https://github.com/qmk/qmk_firmware/blob/e1203a222bb12ab9733916164a000ef3ac48da93/quantum/process_keycode/process_printer.c#L77)
* [`bool process_auto_shift(uint16_t keycode, keyrecord_t *record)`](https://github.com/qmk/qmk_firmware/blob/e1203a222bb12ab9733916164a000ef3ac48da93/quantum/process_keycode/process_auto_shift.c#L94)
* `bool process_dynamic_tapping_term(uint16_t keycode, keyrecord_t *record)`
* [`bool process_terminal(uint16_t keycode, keyrecord_t *record)`](https://github.com/qmk/qmk_firmware/blob/e1203a222bb12ab9733916164a000ef3ac48da93/quantum/process_keycode/process_terminal.c#L264)
* [Identify and process Quantum-specific keycodes](https://github.com/qmk/qmk_firmware/blob/e1203a222bb12ab9733916164a000ef3ac48da93/quantum/quantum.c#L291)
Expand Down
Loading

0 comments on commit f4a446b

Please sign in to comment.