Skip to content

Commit

Permalink
Enhancement of WPM feature (qmk#11727)
Browse files Browse the repository at this point in the history
  • Loading branch information
drashna authored Apr 19, 2021
1 parent 8b9f146 commit 07a70af
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 20 deletions.
67 changes: 52 additions & 15 deletions docs/feature_wpm.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,62 @@
# Word Per Minute (WPM) Calculcation

The WPM feature uses time between keystrokes to compute a rolling average words
per minute rate and makes this available for various uses.
The WPM feature uses time between keystrokes to compute a rolling average words per minute rate and makes this available for various uses.

Enable the WPM system by adding this to your `rules.mk`:

WPM_ENABLE = yes

For split keyboards using soft serial, the computed WPM
score will be available on the master AND slave half.
For split keyboards using soft serial, the computed WPM score will be available on the master AND slave half.

## Public Functions

`uint8_t get_current_wpm(void);`
This function returns the current WPM as an unsigned integer.
## Configuration

|Define |Default | Description |
|-----------------------------|--------------|------------------------------------------------------------------------------------------|
|`WPM_SMOOTHING` |`0.0487` | Sets the smoothing to about 40 keystrokes |
|`WPM_ESTIMATED_WORD_SIZE` |`5` | This is the value used when estimating average word size (for regression and normal use) |
|`WPM_ALLOW_COUNT_REGRESSOIN` |_Not defined_ | If defined allows the WPM to be decreased when hitting Delete or Backspace |
## Public Functions

## Customized keys for WPM calc

By default, the WPM score only includes letters, numbers, space and some
punctuation. If you want to change the set of characters considered as part of
the WPM calculation, you can implement `wpm_keycode_user(uint16_t keycode)`
and return true for any characters you would like included in the calculation,
or false to not count that particular keycode.
|Function |Description |
|--------------------------|--------------------------------------------------|
|`get_current_wpm(void)` | Returns the current WPM as a value between 0-255 |
|`set_current_wpm(x)` | Sets the current WPM to `x` (between 0-255) |

## Callbacks

By default, the WPM score only includes letters, numbers, space and some punctuation. If you want to change the set of characters considered as part of the WPM calculation, you can implement your own `bool wpm_keycode_user(uint16_t keycode)` and return true for any characters you would like included in the calculation, or false to not count that particular keycode.

For instance, the default is:

```c
bool wpm_keycode_user(uint16_t keycode) {
if ((keycode >= QK_MOD_TAP && keycode <= QK_MOD_TAP_MAX) || (keycode >= QK_LAYER_TAP && keycode <= QK_LAYER_TAP_MAX) || (keycode >= QK_MODS && keycode <= QK_MODS_MAX)) {
keycode = keycode & 0xFF;
} else if (keycode > 0xFF) {
keycode = 0;
}
if ((keycode >= KC_A && keycode <= KC_0) || (keycode >= KC_TAB && keycode <= KC_SLASH)) {
return true;
}

return false;
}
```
Additionally, if `WPM_ALLOW_COUNT_REGRESSION` is defined, there is the `uint8_t wpm_regress_count(uint16_t keycode)` function that allows you to decrease the WPM. This is useful if you want to be able to penalize certain keycodes (or even combinations).
__attribute__((weak)) uint8_t wpm_regress_count(uint16_t keycode) {
bool weak_modded = (keycode >= QK_LCTL && keycode < QK_LSFT) || (keycode >= QK_RCTL && keycode < QK_RSFT);
if ((keycode >= QK_MOD_TAP && keycode <= QK_MOD_TAP_MAX) || (keycode >= QK_LAYER_TAP && keycode <= QK_LAYER_TAP_MAX) || (keycode >= QK_MODS && keycode <= QK_MODS_MAX)) {
keycode = keycode & 0xFF;
} else if (keycode > 0xFF) {
keycode = 0;
}
if (((get_mods() | get_oneshot_mods()) & MOD_MASK_CTRL} || weak_modded) && (keycode == KC_DEL || keycode == KC_BSPC)) {
return WPM_ESTIMATED_WORD_SIZE;
}
if (keycode == KC_DEL || keycode == KC_BSPC) {
return 1;
}
}
36 changes: 31 additions & 5 deletions quantum/wpm.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,10 @@

// WPM Stuff
static uint8_t current_wpm = 0;
static uint8_t latest_wpm = 0;
static uint16_t wpm_timer = 0;

// This smoothing is 40 keystrokes
static const float wpm_smoothing = 0.0487;
static const float wpm_smoothing = WPM_SMOOTHING;

void set_current_wpm(uint8_t new_wpm) { current_wpm = new_wpm; }

Expand All @@ -46,19 +45,46 @@ __attribute__((weak)) bool wpm_keycode_user(uint16_t keycode) {
return false;
}

#ifdef WPM_ALLOW_COUNT_REGRESSION
__attribute__((weak)) uint8_t wpm_regress_count(uint16_t keycode) {
bool weak_modded = (keycode >= QK_LCTL && keycode < QK_LSFT) || (keycode >= QK_RCTL && keycode < QK_RSFT);

if ((keycode >= QK_MOD_TAP && keycode <= QK_MOD_TAP_MAX) || (keycode >= QK_LAYER_TAP && keycode <= QK_LAYER_TAP_MAX) || (keycode >= QK_MODS && keycode <= QK_MODS_MAX)) {
keycode = keycode & 0xFF;
} else if (keycode > 0xFF) {
keycode = 0;
}
if (keycode == KC_DEL || keycode == KC_BSPC) {
if (((get_mods() | get_oneshot_mods()) & MOD_MASK_CTRL) || weak_modded) {
return WPM_ESTIMATED_WORD_SIZE;
} else {
return 1;
}
} else {
return 0;
}
}
#endif

void update_wpm(uint16_t keycode) {
if (wpm_keycode(keycode)) {
if (wpm_timer > 0) {
latest_wpm = 60000 / timer_elapsed(wpm_timer) / 5;
current_wpm = (latest_wpm - current_wpm) * wpm_smoothing + current_wpm;
current_wpm += ((60000 / timer_elapsed(wpm_timer) / WPM_ESTIMATED_WORD_SIZE) - current_wpm) * wpm_smoothing;
}
wpm_timer = timer_read();
}
#ifdef WPM_ALLOW_COUNT_REGRESSION
uint8_t regress = wpm_regress_count(keycode);
if (regress) {
current_wpm -= regress;
wpm_timer = timer_read();
}
#endif
}

void decay_wpm(void) {
if (timer_elapsed(wpm_timer) > 1000) {
current_wpm = (0 - current_wpm) * wpm_smoothing + current_wpm;
current_wpm += (-current_wpm) * wpm_smoothing;
wpm_timer = timer_read();
}
}
12 changes: 12 additions & 0 deletions quantum/wpm.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,22 @@

#include "quantum.h"


#ifndef WPM_ESTIMATED_WORD_SIZE
# define WPM_ESTIMATED_WORD_SIZE 5
#endif
#ifndef WPM_SMOOTHING
# define WPM_SMOOTHING 0.0487
#endif

bool wpm_keycode(uint16_t keycode);
bool wpm_keycode_kb(uint16_t keycode);
bool wpm_keycode_user(uint16_t keycode);

#ifdef WPM_ALLOW_COUNT_REGRESSION
uint8_t wpm_regress_count(uint16_t keycode);
#endif

void set_current_wpm(uint8_t);
uint8_t get_current_wpm(void);
void update_wpm(uint16_t);
Expand Down

0 comments on commit 07a70af

Please sign in to comment.