From fb29c0ae53e32fb049516fcbe0f7863882ca9173 Mon Sep 17 00:00:00 2001 From: Drashna Jaelre Date: Sat, 17 Sep 2022 00:50:54 -0700 Subject: [Core] Add getreuer's Autocorrect feature to core (#15699) Co-authored-by: Albert Y <76888457+filterpaper@users.noreply.github.com> --- quantum/process_keycode/process_autocorrect.c | 287 ++++++++++++++++++++++++++ 1 file changed, 287 insertions(+) create mode 100644 quantum/process_keycode/process_autocorrect.c (limited to 'quantum/process_keycode/process_autocorrect.c') diff --git a/quantum/process_keycode/process_autocorrect.c b/quantum/process_keycode/process_autocorrect.c new file mode 100644 index 0000000000..abae5e7811 --- /dev/null +++ b/quantum/process_keycode/process_autocorrect.c @@ -0,0 +1,287 @@ +// Copyright 2021 Google LLC +// Copyright 2021 @filterpaper +// SPDX-License-Identifier: Apache-2.0 +// Original source: https://getreuer.info/posts/keyboards/autocorrection + +#include "process_autocorrect.h" +#include +#include "keycode_config.h" + +#if __has_include("autocorrect_data.h") +# include "autocorrect_data.h" +#else +# pragma message "Autocorrect is using the default library." +# include "autocorrect_data_default.h" +#endif + +static uint8_t typo_buffer[AUTOCORRECT_MAX_LENGTH] = {KC_SPC}; +static uint8_t typo_buffer_size = 1; + +/** + * @brief function for querying the enabled state of autocorrect + * + * @return true if enabled + * @return false if disabled + */ +bool autocorrect_is_enabled(void) { + return keymap_config.autocorrect_enable; +} + +/** + * @brief Enables autocorrect and saves state to eeprom + * + */ +void autocorrect_enable(void) { + keymap_config.autocorrect_enable = true; + eeconfig_update_keymap(keymap_config.raw); +} + +/** + * @brief Disables autocorrect and saves state to eeprom + * + */ +void autocorrect_disable(void) { + keymap_config.autocorrect_enable = false; + typo_buffer_size = 0; + eeconfig_update_keymap(keymap_config.raw); +} + +/** + * @brief Toggles autocorrect's status and save state to eeprom + * + */ +void autocorrect_toggle(void) { + keymap_config.autocorrect_enable = !keymap_config.autocorrect_enable; + typo_buffer_size = 0; + eeconfig_update_keymap(keymap_config.raw); +} + +/** + * @brief handler for determining if autocorrect should process keypress + * + * @param keycode Keycode registered by matrix press, per keymap + * @param record keyrecord_t structure + * @param typo_buffer_size passed along to allow resetting of autocorrect buffer + * @param mods allow processing of mod status + * @return true Allow autocorection + * @return false Stop processing and escape from autocorrect. + */ +__attribute__((weak)) bool process_autocorrect_user(uint16_t *keycode, keyrecord_t *record, uint8_t *typo_buffer_size, uint8_t *mods) { + // See quantum_keycodes.h for reference on these matched ranges. + switch (*keycode) { + // Exclude these keycodes from processing. + case KC_LSFT: + case KC_RSFT: + case KC_CAPS: + case QK_TO ... QK_ONE_SHOT_LAYER_MAX: + case QK_LAYER_TAP_TOGGLE ... QK_LAYER_MOD_MAX: + case QK_ONE_SHOT_MOD ... QK_ONE_SHOT_MOD_MAX: + return false; + + // Mask for base keycode from shifted keys. + case QK_LSFT ... QK_LSFT + 255: + case QK_RSFT ... QK_RSFT + 255: + if (*keycode >= QK_LSFT && *keycode <= (QK_LSFT + 255)) { + *mods |= MOD_LSFT; + } else { + *mods |= MOD_RSFT; + } + *keycode &= 0xFF; // Get the basic keycode. + return true; +#ifndef NO_ACTION_TAPPING + // Exclude tap-hold keys when they are held down + // and mask for base keycode when they are tapped. + case QK_LAYER_TAP ... QK_LAYER_TAP_MAX: +# ifdef NO_ACTION_LAYER + // Exclude Layer Tap, if layers are disabled + // but action tapping is still enabled. + return false; +# endif + case QK_MOD_TAP ... QK_MOD_TAP_MAX: + // Exclude hold keycode + if (!record->tap.count) { + return false; + } + *keycode &= 0xFF; + break; +#else + case QK_MOD_TAP ... QK_MOD_TAP_MAX: + case QK_LAYER_TAP ... QK_LAYER_TAP_MAX: + // Exclude if disabled + return false; +#endif + // Exclude swap hands keys when they are held down + // and mask for base keycode when they are tapped. + case QK_SWAP_HANDS ... QK_SWAP_HANDS_MAX: +#ifdef SWAP_HANDS_ENABLE + if (*keycode >= 0x56F0 || !record->tap.count) { + return false; + } + *keycode &= 0xFF; + break; +#else + // Exclude if disabled + return false; +#endif + } + + // Disable autocorrect while a mod other than shift is active. + if ((*mods & ~MOD_MASK_SHIFT) != 0) { + *typo_buffer_size = 0; + return false; + } + + return true; +} + +/** + * @brief handling for when autocorrection has been triggered + * + * @param backspaces number of characters to remove + * @param str pointer to PROGMEM string to replace mistyped seletion with + * @return true apply correction + * @return false user handled replacement + */ +__attribute__((weak)) bool apply_autocorrect(uint8_t backspaces, const char *str) { + return true; +} + +/** + * @brief Process handler for autocorrect feature + * + * @param keycode Keycode registered by matrix press, per keymap + * @param record keyrecord_t structure + * @return true Continue processing keycodes, and send to host + * @return false Stop processing keycodes, and don't send to host + */ +bool process_autocorrect(uint16_t keycode, keyrecord_t *record) { + uint8_t mods = get_mods(); +#ifndef NO_ACTION_ONESHOT + mods |= get_oneshot_mods(); +#endif + + if ((keycode >= AUTOCORRECT_ON && keycode <= AUTOCORRECT_TOGGLE) && record->event.pressed) { + if (keycode == AUTOCORRECT_ON) { + autocorrect_enable(); + } else if (keycode == AUTOCORRECT_OFF) { + autocorrect_disable(); + } else if (keycode == AUTOCORRECT_TOGGLE) { + autocorrect_toggle(); + } else { + return true; + } + + return false; + } + + if (!keymap_config.autocorrect_enable) { + typo_buffer_size = 0; + return true; + } + + if (!record->event.pressed) { + return true; + } + + // autocorrect keycode verification and extraction + if (!process_autocorrect_user(&keycode, record, &typo_buffer_size, &mods)) { + return true; + } + + // keycode buffer check + switch (keycode) { + case KC_A ... KC_Z: + // process normally + break; + case KC_1 ... KC_0: + case KC_TAB ... KC_SEMICOLON: + case KC_GRAVE ... KC_SLASH: + // Set a word boundary if space, period, digit, etc. is pressed. + keycode = KC_SPC; + break; + case KC_ENTER: + // Behave more conservatively for the enter key. Reset, so that enter + // can't be used on a word ending. + typo_buffer_size = 0; + keycode = KC_SPC; + break; + case KC_BSPC: + // Remove last character from the buffer. + if (typo_buffer_size > 0) { + --typo_buffer_size; + } + return true; + case KC_QUOTE: + // Treat " (shifted ') as a word boundary. + if ((mods & MOD_MASK_SHIFT) != 0) { + keycode = KC_SPC; + } + break; + default: + // Clear state if some other non-alpha key is pressed. + typo_buffer_size = 0; + return true; + } + + // Rotate oldest character if buffer is full. + if (typo_buffer_size >= AUTOCORRECT_MAX_LENGTH) { + memmove(typo_buffer, typo_buffer + 1, AUTOCORRECT_MAX_LENGTH - 1); + typo_buffer_size = AUTOCORRECT_MAX_LENGTH - 1; + } + + // Append `keycode` to buffer. + typo_buffer[typo_buffer_size++] = keycode; + // Return if buffer is smaller than the shortest word. + if (typo_buffer_size < AUTOCORRECT_MIN_LENGTH) { + return true; + } + + // Check for typo in buffer using a trie stored in `autocorrect_data`. + uint16_t state = 0; + uint8_t code = pgm_read_byte(autocorrect_data + state); + for (int8_t i = typo_buffer_size - 1; i >= 0; --i) { + uint8_t const key_i = typo_buffer[i]; + + if (code & 64) { // Check for match in node with multiple children. + code &= 63; + for (; code != key_i; code = pgm_read_byte(autocorrect_data + (state += 3))) { + if (!code) return true; + } + // Follow link to child node. + state = (pgm_read_byte(autocorrect_data + state + 1) | pgm_read_byte(autocorrect_data + state + 2) << 8); + // Check for match in node with single child. + } else if (code != key_i) { + return true; + } else if (!(code = pgm_read_byte(autocorrect_data + (++state)))) { + ++state; + } + + // Stop if `state` becomes an invalid index. This should not normally + // happen, it is a safeguard in case of a bug, data corruption, etc. + if (state >= DICTIONARY_SIZE) { + return true; + } + + code = pgm_read_byte(autocorrect_data + state); + + if (code & 128) { // A typo was found! Apply autocorrect. + const uint8_t backspaces = (code & 63) + !record->event.pressed; + if (apply_autocorrect(backspaces, (char const *)(autocorrect_data + state + 1))) { + for (uint8_t i = 0; i < backspaces; ++i) { + tap_code(KC_BSPC); + } + send_string_P((char const *)(autocorrect_data + state + 1)); + } + + if (keycode == KC_SPC) { + typo_buffer[0] = KC_SPC; + typo_buffer_size = 1; + return true; + } else { + typo_buffer_size = 0; + return false; + } + } + } + return true; +} -- cgit v1.2.3 From 4f63be4d1d44c7d4a154c14a982186e0ca27ceba Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Tue, 25 Oct 2022 06:07:34 +0100 Subject: Remove some assumptions on sequential keycode ranges (#18838) --- keyboards/moonlander/moonlander.c | 11 +++++++++-- keyboards/planck/ez/ez.c | 11 +++++++++-- quantum/process_keycode/process_autocorrect.c | 9 +++++++-- 3 files changed, 25 insertions(+), 6 deletions(-) (limited to 'quantum/process_keycode/process_autocorrect.c') diff --git a/keyboards/moonlander/moonlander.c b/keyboards/moonlander/moonlander.c index 8bde17b79e..29d13af3ee 100644 --- a/keyboards/moonlander/moonlander.c +++ b/keyboards/moonlander/moonlander.c @@ -316,8 +316,15 @@ led_config_t g_led_config = { { #ifdef AUDIO_ENABLE bool music_mask_kb(uint16_t keycode) { switch (keycode) { - case QK_LAYER_TAP ... QK_ONE_SHOT_LAYER_MAX: - case QK_LAYER_TAP_TOGGLE ... QK_LAYER_MOD_MAX: + case QK_LAYER_TAP ... QK_LAYER_TAP_MAX: + case QK_TO ... QK_TO_MAX: + case QK_MOMENTARY ... QK_MOMENTARY_MAX: + case QK_DEF_LAYER ... QK_DEF_LAYER_MAX: + case QK_TOGGLE_LAYER ... QK_TOGGLE_LAYER_MAX: + case QK_ONE_SHOT_LAYER ... QK_ONE_SHOT_LAYER_MAX: + case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX: + case QK_LAYER_MOD ... QK_LAYER_MOD_MAX: + case QK_ONE_SHOT_MOD ... QK_ONE_SHOT_MOD_MAX: case QK_MOD_TAP ... QK_MOD_TAP_MAX: case AU_ON ... MUV_DE: case QK_BOOT: diff --git a/keyboards/planck/ez/ez.c b/keyboards/planck/ez/ez.c index 01157e106b..84eb725c90 100644 --- a/keyboards/planck/ez/ez.c +++ b/keyboards/planck/ez/ez.c @@ -318,8 +318,15 @@ bool process_record_kb(uint16_t keycode, keyrecord_t *record) { #ifdef AUDIO_ENABLE bool music_mask_kb(uint16_t keycode) { switch (keycode) { - case QK_LAYER_TAP ... QK_ONE_SHOT_LAYER_MAX: - case QK_LAYER_TAP_TOGGLE ... QK_LAYER_MOD_MAX: + case QK_LAYER_TAP ... QK_LAYER_TAP_MAX: + case QK_TO ... QK_TO_MAX: + case QK_MOMENTARY ... QK_MOMENTARY_MAX: + case QK_DEF_LAYER ... QK_DEF_LAYER_MAX: + case QK_TOGGLE_LAYER ... QK_TOGGLE_LAYER_MAX: + case QK_ONE_SHOT_LAYER ... QK_ONE_SHOT_LAYER_MAX: + case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX: + case QK_LAYER_MOD ... QK_LAYER_MOD_MAX: + case QK_ONE_SHOT_MOD ... QK_ONE_SHOT_MOD_MAX: case QK_MOD_TAP ... QK_MOD_TAP_MAX: case AU_ON ... MUV_DE: case QK_BOOT: diff --git a/quantum/process_keycode/process_autocorrect.c b/quantum/process_keycode/process_autocorrect.c index abae5e7811..f19ca4a90b 100644 --- a/quantum/process_keycode/process_autocorrect.c +++ b/quantum/process_keycode/process_autocorrect.c @@ -73,8 +73,13 @@ __attribute__((weak)) bool process_autocorrect_user(uint16_t *keycode, keyrecord case KC_LSFT: case KC_RSFT: case KC_CAPS: - case QK_TO ... QK_ONE_SHOT_LAYER_MAX: - case QK_LAYER_TAP_TOGGLE ... QK_LAYER_MOD_MAX: + case QK_TO ... QK_TO_MAX: + case QK_MOMENTARY ... QK_MOMENTARY_MAX: + case QK_DEF_LAYER ... QK_DEF_LAYER_MAX: + case QK_TOGGLE_LAYER ... QK_TOGGLE_LAYER_MAX: + case QK_ONE_SHOT_LAYER ... QK_ONE_SHOT_LAYER_MAX: + case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX: + case QK_LAYER_MOD ... QK_LAYER_MOD_MAX: case QK_ONE_SHOT_MOD ... QK_ONE_SHOT_MOD_MAX: return false; -- cgit v1.2.3 From 2ff1d852909daaaf94433fab202e7bf94274d67b Mon Sep 17 00:00:00 2001 From: Ryan Date: Fri, 28 Oct 2022 22:21:24 +1100 Subject: Normalise Autocorrect keycodes (#18893) --- docs/feature_autocorrect.md | 14 +++++++------- docs/keycodes.md | 10 ++++++++++ quantum/process_keycode/process_autocorrect.c | 8 ++++---- quantum/quantum_keycodes.h | 12 ++++++------ users/drashna/keyrecords/process_records.h | 2 +- 5 files changed, 28 insertions(+), 18 deletions(-) (limited to 'quantum/process_keycode/process_autocorrect.c') diff --git a/docs/feature_autocorrect.md b/docs/feature_autocorrect.md index 480131e5fc..e042027c0f 100644 --- a/docs/feature_autocorrect.md +++ b/docs/feature_autocorrect.md @@ -22,7 +22,7 @@ AUTOCORRECT_ENABLE = yes Additionally, you will need a library for autocorrection. A small sample library is included by default, so that you can get up and running right away, but you can provide a customized library. -By default, autocorrect is disabled. To enable it, you need to use the `AUTOCORRECT_TOGGLE` keycode to enable it. The status is stored in persistent memory, so you shouldn't need to enabled it again. +By default, autocorrect is disabled. To enable it, you need to use the `AC_TOGG` keycode to enable it. The status is stored in persistent memory, so you shouldn't need to enabled it again. ## Customizing autocorrect library :id=customizing-autocorrect-library @@ -94,15 +94,15 @@ Occasionally you might actually want to type a typo (for instance, while editing This works because the autocorrection implementation doesn’t understand hotkeys, so it resets itself whenever a modifier other than shift is held. -Additionally, you can use the `AUTOCORRECT_TOGGLE` keycode to toggle the on/off status for Autocorrect. +Additionally, you can use the `AC_TOGG` keycode to toggle the on/off status for Autocorrect. ### Keycodes :id=keycodes -|Keycode | Short keycode | Description | -|---------------------|---------------|------------------------------------------------| -|`AUTOCORRECT_ON` | `CRT_ON` | Turns on the Autocorrect feature. | -|`AUTOCORRECT_OFF` | `CRT_OFF` | Turns off the Autocorrect feature. | -|`AUTOCORRECT_TOGGLE` | `CRT_TOG` | Toggles the status of the Autocorrect feature. | +|Keycode |Aliases |Description | +|-----------------------|---------|----------------------------------------------| +|`QK_AUTOCORRECT_ON` |`AC_ON` |Turns on the Autocorrect feature. | +|`QK_AUTOCORRECT_OFF` |`AC_OFF` |Turns off the Autocorrect feature. | +|`QK_AUTOCORRECT_TOGGLE`|`AC_TOGG`|Toggles the status of the Autocorrect feature.| ## User Callback Functions diff --git a/docs/keycodes.md b/docs/keycodes.md index 0de0ce56b4..4aa54e1c20 100644 --- a/docs/keycodes.md +++ b/docs/keycodes.md @@ -247,6 +247,16 @@ See also: [Audio](feature_audio.md) |`MU_TOG` | |Toggles Music Mode | |`MU_MOD` | |Cycles through the music modes | +## Autocorrect :id=autocorrect + +See also: [Autocorrect](feature_autocorrect.md) + +|Key |Aliases |Description | +|-----------------------|---------|----------------------------------------------| +|`QK_AUTOCORRECT_ON` |`AC_ON` |Turns on the Autocorrect feature. | +|`QK_AUTOCORRECT_OFF` |`AC_OFF` |Turns off the Autocorrect feature. | +|`QK_AUTOCORRECT_TOGGLE`|`AC_TOGG`|Toggles the status of the Autocorrect feature.| + ## Backlighting :id=backlighting See also: [Backlighting](feature_backlight.md) diff --git a/quantum/process_keycode/process_autocorrect.c b/quantum/process_keycode/process_autocorrect.c index f19ca4a90b..e3fcbc34ae 100644 --- a/quantum/process_keycode/process_autocorrect.c +++ b/quantum/process_keycode/process_autocorrect.c @@ -165,12 +165,12 @@ bool process_autocorrect(uint16_t keycode, keyrecord_t *record) { mods |= get_oneshot_mods(); #endif - if ((keycode >= AUTOCORRECT_ON && keycode <= AUTOCORRECT_TOGGLE) && record->event.pressed) { - if (keycode == AUTOCORRECT_ON) { + if ((keycode >= QK_AUTOCORRECT_ON && keycode <= QK_AUTOCORRECT_TOGGLE) && record->event.pressed) { + if (keycode == QK_AUTOCORRECT_ON) { autocorrect_enable(); - } else if (keycode == AUTOCORRECT_OFF) { + } else if (keycode == QK_AUTOCORRECT_OFF) { autocorrect_disable(); - } else if (keycode == AUTOCORRECT_TOGGLE) { + } else if (keycode == QK_AUTOCORRECT_TOGGLE) { autocorrect_toggle(); } else { return true; diff --git a/quantum/quantum_keycodes.h b/quantum/quantum_keycodes.h index 9d332ffb11..0be090e48b 100644 --- a/quantum/quantum_keycodes.h +++ b/quantum/quantum_keycodes.h @@ -611,9 +611,9 @@ enum quantum_keycodes { UNICODE_MODE_EMACS, - AUTOCORRECT_ON, - AUTOCORRECT_OFF, - AUTOCORRECT_TOGGLE, + QK_AUTOCORRECT_ON, + QK_AUTOCORRECT_OFF, + QK_AUTOCORRECT_TOGGLE, MAGIC_TOGGLE_BACKSLASH_BACKSPACE, @@ -740,9 +740,9 @@ enum quantum_keycodes { #define EH_LEFT MAGIC_EE_HANDS_LEFT #define EH_RGHT MAGIC_EE_HANDS_RIGHT -#define CRT_ON AUTOCORRECT_ON -#define CRT_OFF AUTOCORRECT_OFF -#define CRT_TOG AUTOCORRECT_TOGGLE +#define AC_ON QK_AUTOCORRECT_ON +#define AC_OFF QK_AUTOCORRECT_OFF +#define AC_TOGG QK_AUTOCORRECT_TOGGLE // Velocikey #define VK_TOGG QK_VELOCIKEY_TOGGLE diff --git a/users/drashna/keyrecords/process_records.h b/users/drashna/keyrecords/process_records.h index cae3620fe4..2ee7551648 100644 --- a/users/drashna/keyrecords/process_records.h +++ b/users/drashna/keyrecords/process_records.h @@ -125,7 +125,7 @@ bool process_record_unicode(uint16_t keycode, keyrecord_t *record); #define MG_NKRO MAGIC_TOGGLE_NKRO -#define AUTO_CTN AUTOCORRECT_TOGGLE +#define AUTO_CTN QK_AUTOCORRECT_TOGGLE /* Custom Keycodes for Diablo 3 layer But since TD() doesn't work when tap dance is disabled -- cgit v1.2.3 From a7b2f4233ca2062037303e783ced30d9376c9443 Mon Sep 17 00:00:00 2001 From: Sergey Vlasov Date: Mon, 7 Nov 2022 00:39:05 +0300 Subject: Fix keycode parameter extraction to match the new DD keycodes (#18977) * Add macros to extract parameters from keycode values Implement both encoding and decoding for keycodes like TO(layer) or LM(layer, mod) in one place, so that the decoding won't get out of sync with the encoding. While at it, fix some macros for creating keycode values that did not apply the appropriate masks to parameters (and therefore could allow the result to be out of range if a wrong parameter was passed). * keymap_common: Use extraction macros for keycodes * pointing_device_auto_mouse: Use extraction macros for keycodes Fixes #18970. * process_autocorrect: Use extraction macros for keycodes * process_caps_word: Use extraction macros for keycodes (Also fix a minor bug - SH_TG was not handled properly) * process_leader: Use extraction macros for keycodes (Technically the code is not 100% correct, because it always assumes that the LT() or MT() action was a tap, but it's a separate issue that already existed before the keycode changes.) * process_unicode: Use extraction macros for keycodes * process_unicodemap: Use extraction macros for keycodes --- quantum/keymap_common.c | 28 +++++++++++----------- .../pointing_device/pointing_device_auto_mouse.c | 16 ++++++++----- quantum/process_keycode/process_autocorrect.c | 17 +++++++++---- quantum/process_keycode/process_caps_word.c | 12 ++++++---- quantum/process_keycode/process_leader.c | 6 +++-- quantum/process_keycode/process_unicode.c | 2 +- quantum/process_keycode/process_unicodemap.c | 10 ++++---- quantum/quantum_keycodes.h | 28 ++++++++++++++++++++-- 8 files changed, 81 insertions(+), 38 deletions(-) (limited to 'quantum/process_keycode/process_autocorrect.c') diff --git a/quantum/keymap_common.c b/quantum/keymap_common.c index a5997711f2..c4336440f9 100644 --- a/quantum/keymap_common.c +++ b/quantum/keymap_common.c @@ -74,64 +74,64 @@ action_t action_for_keycode(uint16_t keycode) { case QK_MODS ... QK_MODS_MAX:; // Has a modifier // Split it up - action.code = ACTION_MODS_KEY(keycode >> 8, keycode & 0xFF); // adds modifier to key + action.code = ACTION_MODS_KEY(QK_MODS_GET_MODS(keycode), QK_MODS_GET_BASIC_KEYCODE(keycode)); // adds modifier to key break; #ifndef NO_ACTION_LAYER case QK_LAYER_TAP ... QK_LAYER_TAP_MAX: - action.code = ACTION_LAYER_TAP_KEY((keycode >> 0x8) & 0xF, keycode & 0xFF); + action.code = ACTION_LAYER_TAP_KEY(QK_LAYER_TAP_GET_LAYER(keycode), QK_LAYER_TAP_GET_TAP_KEYCODE(keycode)); break; case QK_TO ... QK_TO_MAX:; // Layer set "GOTO" - action_layer = keycode & 0xFF; + action_layer = QK_TO_GET_LAYER(keycode); action.code = ACTION_LAYER_GOTO(action_layer); break; case QK_MOMENTARY ... QK_MOMENTARY_MAX:; // Momentary action_layer - action_layer = keycode & 0xFF; + action_layer = QK_MOMENTARY_GET_LAYER(keycode); action.code = ACTION_LAYER_MOMENTARY(action_layer); break; case QK_DEF_LAYER ... QK_DEF_LAYER_MAX:; // Set default action_layer - action_layer = keycode & 0xFF; + action_layer = QK_DEF_LAYER_GET_LAYER(keycode); action.code = ACTION_DEFAULT_LAYER_SET(action_layer); break; case QK_TOGGLE_LAYER ... QK_TOGGLE_LAYER_MAX:; // Set toggle - action_layer = keycode & 0xFF; + action_layer = QK_TOGGLE_LAYER_GET_LAYER(keycode); action.code = ACTION_LAYER_TOGGLE(action_layer); break; #endif #ifndef NO_ACTION_ONESHOT case QK_ONE_SHOT_LAYER ... QK_ONE_SHOT_LAYER_MAX:; // OSL(action_layer) - One-shot action_layer - action_layer = keycode & 0xFF; + action_layer = QK_ONE_SHOT_LAYER_GET_LAYER(keycode); action.code = ACTION_LAYER_ONESHOT(action_layer); break; case QK_ONE_SHOT_MOD ... QK_ONE_SHOT_MOD_MAX:; // OSM(mod) - One-shot mod - mod = mod_config(keycode & 0x1F); + mod = mod_config(QK_ONE_SHOT_MOD_GET_MODS(keycode)); action.code = ACTION_MODS_ONESHOT(mod); break; #endif #ifndef NO_ACTION_LAYER case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX: - action.code = ACTION_LAYER_TAP_TOGGLE(keycode & 0xFF); + action.code = ACTION_LAYER_TAP_TOGGLE(QK_LAYER_TAP_TOGGLE_GET_LAYER(keycode)); break; case QK_LAYER_MOD ... QK_LAYER_MOD_MAX: - mod = mod_config(keycode & 0x1F); - action_layer = (keycode >> 5) & 0xF; + mod = mod_config(QK_LAYER_MOD_GET_MODS(keycode)); + action_layer = QK_LAYER_MOD_GET_LAYER(keycode); action.code = ACTION_LAYER_MODS(action_layer, mod); break; #endif #ifndef NO_ACTION_TAPPING case QK_MOD_TAP ... QK_MOD_TAP_MAX: - mod = mod_config((keycode >> 0x8) & 0x1F); - action.code = ACTION_MODS_TAP_KEY(mod, keycode & 0xFF); + mod = mod_config(QK_MOD_TAP_GET_MODS(keycode)); + action.code = ACTION_MODS_TAP_KEY(mod, QK_MOD_TAP_GET_TAP_KEYCODE(keycode)); break; #endif #ifdef SWAP_HANDS_ENABLE case QK_SWAP_HANDS ... QK_SWAP_HANDS_MAX: - action.code = ACTION(ACT_SWAP_HANDS, keycode & 0xff); + action.code = ACTION(ACT_SWAP_HANDS, QK_SWAP_HANDS_GET_TAP_KEYCODE(keycode)); break; #endif diff --git a/quantum/pointing_device/pointing_device_auto_mouse.c b/quantum/pointing_device/pointing_device_auto_mouse.c index edffd44787..5e78817c7c 100644 --- a/quantum/pointing_device/pointing_device_auto_mouse.c +++ b/quantum/pointing_device/pointing_device_auto_mouse.c @@ -266,16 +266,20 @@ bool process_auto_mouse(uint16_t keycode, keyrecord_t* record) { case QK_MODS ... QK_MODS_MAX: break; // TO((AUTO_MOUSE_TARGET_LAYER))------------------------------------------------------------------------------- - case QK_TO ... QK_TO_MAX: // same proccessing as next + case QK_TO ... QK_TO_MAX: + if (QK_TO_GET_LAYER(keycode) == (AUTO_MOUSE_TARGET_LAYER)) { + if (!(record->event.pressed)) auto_mouse_toggle(); + } + break; // TG((AUTO_MOUSE_TARGET_LAYER))------------------------------------------------------------------------------- case QK_TOGGLE_LAYER ... QK_TOGGLE_LAYER_MAX: - if ((keycode & 0xff) == (AUTO_MOUSE_TARGET_LAYER)) { + if (QK_TOGGLE_LAYER_GET_LAYER(keycode) == (AUTO_MOUSE_TARGET_LAYER)) { if (!(record->event.pressed)) auto_mouse_toggle(); } break; // MO((AUTO_MOUSE_TARGET_LAYER))------------------------------------------------------------------------------- case QK_MOMENTARY ... QK_MOMENTARY_MAX: - if ((keycode & 0xff) == (AUTO_MOUSE_TARGET_LAYER)) { + if (QK_MOMENTARY_GET_LAYER(keycode) == (AUTO_MOUSE_TARGET_LAYER)) { auto_mouse_keyevent(record->event.pressed); } // DF --------------------------------------------------------------------------------------------------------- @@ -288,14 +292,14 @@ bool process_auto_mouse(uint16_t keycode, keyrecord_t* record) { break; // LM((AUTO_MOUSE_TARGET_LAYER), mod)-------------------------------------------------------------------------- case QK_LAYER_MOD ... QK_LAYER_MOD_MAX: - if (((keycode >> 8) & 0x0f) == (AUTO_MOUSE_TARGET_LAYER)) { + if (QK_LAYER_MOD_GET_LAYER(keycode) == (AUTO_MOUSE_TARGET_LAYER)) { auto_mouse_keyevent(record->event.pressed); } break; // TT((AUTO_MOUSE_TARGET_LAYER))--------------------------------------------------------------------------- # ifndef NO_ACTION_TAPPING case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX: - if ((keycode & 0xff) == (AUTO_MOUSE_TARGET_LAYER)) { + if (QK_LAYER_TAP_TOGGLE_GET_LAYER(keycode) == (AUTO_MOUSE_TARGET_LAYER)) { auto_mouse_keyevent(record->event.pressed); # if TAPPING_TOGGLE != 0 if (record->tap.count == TAPPING_TOGGLE) { @@ -312,7 +316,7 @@ bool process_auto_mouse(uint16_t keycode, keyrecord_t* record) { // LT((AUTO_MOUSE_TARGET_LAYER), kc)--------------------------------------------------------------------------- case QK_LAYER_TAP ... QK_LAYER_TAP_MAX: if (!record->tap.count) { - if (((keycode >> 8) & 0x0f) == (AUTO_MOUSE_TARGET_LAYER)) { + if (QK_LAYER_TAP_GET_LAYER(keycode) == (AUTO_MOUSE_TARGET_LAYER)) { auto_mouse_keyevent(record->event.pressed); } break; diff --git a/quantum/process_keycode/process_autocorrect.c b/quantum/process_keycode/process_autocorrect.c index e3fcbc34ae..8aeebf0e06 100644 --- a/quantum/process_keycode/process_autocorrect.c +++ b/quantum/process_keycode/process_autocorrect.c @@ -91,7 +91,7 @@ __attribute__((weak)) bool process_autocorrect_user(uint16_t *keycode, keyrecord } else { *mods |= MOD_RSFT; } - *keycode &= 0xFF; // Get the basic keycode. + *keycode = QK_MODS_GET_BASIC_KEYCODE(*keycode); // Get the basic keycode. return true; #ifndef NO_ACTION_TAPPING // Exclude tap-hold keys when they are held down @@ -101,13 +101,20 @@ __attribute__((weak)) bool process_autocorrect_user(uint16_t *keycode, keyrecord // Exclude Layer Tap, if layers are disabled // but action tapping is still enabled. return false; +# else + // Exclude hold keycode + if (!record->tap.count) { + return false; + } + *keycode = QK_LAYER_TAP_GET_TAP_KEYCODE(*keycode); + break; # endif case QK_MOD_TAP ... QK_MOD_TAP_MAX: // Exclude hold keycode if (!record->tap.count) { return false; } - *keycode &= 0xFF; + *keycode = QK_MOD_TAP_GET_TAP_KEYCODE(*keycode); break; #else case QK_MOD_TAP ... QK_MOD_TAP_MAX: @@ -119,10 +126,12 @@ __attribute__((weak)) bool process_autocorrect_user(uint16_t *keycode, keyrecord // and mask for base keycode when they are tapped. case QK_SWAP_HANDS ... QK_SWAP_HANDS_MAX: #ifdef SWAP_HANDS_ENABLE - if (*keycode >= 0x56F0 || !record->tap.count) { + // Note: IS_SWAP_HANDS_KEYCODE() actually tests for the special action keycodes like SH_TG, SH_TT, ..., + // which currently overlap the SH_T(kc) range. + if (IS_SWAP_HANDS_KEYCODE(*keycode) || !record->tap.count) { return false; } - *keycode &= 0xFF; + *keycode = QK_SWAP_HANDS_GET_TAP_KEYCODE(*keycode); break; #else // Exclude if disabled diff --git a/quantum/process_keycode/process_caps_word.c b/quantum/process_keycode/process_caps_word.c index 7a83f7bf49..4c0217eba7 100644 --- a/quantum/process_keycode/process_caps_word.c +++ b/quantum/process_keycode/process_caps_word.c @@ -109,7 +109,7 @@ bool process_caps_word(uint16_t keycode, keyrecord_t* record) { // * Otherwise stop Caps Word. case QK_MOD_TAP ... QK_MOD_TAP_MAX: if (record->tap.count == 0) { // Mod-tap key is held. - const uint8_t mods = (keycode >> 8) & 0x1f; + const uint8_t mods = QK_MOD_TAP_GET_MODS(keycode); switch (mods) { case MOD_LSFT: keycode = KC_LSFT; @@ -127,7 +127,7 @@ bool process_caps_word(uint16_t keycode, keyrecord_t* record) { return true; } } else { - keycode &= 0xff; + keycode = QK_MOD_TAP_GET_TAP_KEYCODE(keycode); } break; @@ -137,16 +137,18 @@ bool process_caps_word(uint16_t keycode, keyrecord_t* record) { if (record->tap.count == 0) { return true; } - keycode &= 0xff; + keycode = QK_LAYER_TAP_GET_TAP_KEYCODE(keycode); break; #endif // NO_ACTION_TAPPING #ifdef SWAP_HANDS_ENABLE case QK_SWAP_HANDS ... QK_SWAP_HANDS_MAX: - if (keycode > 0x56F0 || record->tap.count == 0) { + // Note: IS_SWAP_HANDS_KEYCODE() actually tests for the special action keycodes like SH_TG, SH_TT, ..., + // which currently overlap the SH_T(kc) range. + if (IS_SWAP_HANDS_KEYCODE(keycode) || record->tap.count == 0) { return true; } - keycode &= 0xff; + keycode = QK_SWAP_HANDS_GET_TAP_KEYCODE(keycode); break; #endif // SWAP_HANDS_ENABLE } diff --git a/quantum/process_keycode/process_leader.c b/quantum/process_keycode/process_leader.c index 7e6f3ad73c..b74b4927a8 100644 --- a/quantum/process_keycode/process_leader.c +++ b/quantum/process_keycode/process_leader.c @@ -54,8 +54,10 @@ bool process_leader(uint16_t keycode, keyrecord_t *record) { # endif // LEADER_NO_TIMEOUT { # ifndef LEADER_KEY_STRICT_KEY_PROCESSING - if ((keycode >= QK_MOD_TAP && keycode <= QK_MOD_TAP_MAX) || (keycode >= QK_LAYER_TAP && keycode <= QK_LAYER_TAP_MAX)) { - keycode = keycode & 0xFF; + if (IS_QK_MOD_TAP(keycode)) { + keycode = QK_MOD_TAP_GET_TAP_KEYCODE(keycode); + } else if (IS_QK_LAYER_TAP(keycode)) { + keycode = QK_LAYER_TAP_GET_TAP_KEYCODE(keycode); } # endif // LEADER_KEY_STRICT_KEY_PROCESSING if (leader_sequence_size < ARRAY_SIZE(leader_sequence)) { diff --git a/quantum/process_keycode/process_unicode.c b/quantum/process_keycode/process_unicode.c index 99cc2f5f26..1ec76245a3 100644 --- a/quantum/process_keycode/process_unicode.c +++ b/quantum/process_keycode/process_unicode.c @@ -21,7 +21,7 @@ bool process_unicode(uint16_t keycode, keyrecord_t *record) { if (record->event.pressed) { if (keycode >= QK_UNICODE && keycode <= QK_UNICODE_MAX) { - register_unicode(keycode & 0x7FFF); + register_unicode(QK_UNICODE_GET_CODE_POINT(keycode)); } } return true; diff --git a/quantum/process_keycode/process_unicodemap.c b/quantum/process_keycode/process_unicodemap.c index 979d773b05..195c093e6e 100644 --- a/quantum/process_keycode/process_unicodemap.c +++ b/quantum/process_keycode/process_unicodemap.c @@ -24,7 +24,7 @@ __attribute__((weak)) uint16_t unicodemap_index(uint16_t keycode) { if (keycode >= QK_UNICODEMAP_PAIR) { // Keycode is a pair: extract index based on Shift / Caps Lock state - uint16_t index = keycode - QK_UNICODEMAP_PAIR; + uint16_t index; uint8_t mods = get_mods() | get_weak_mods(); #ifndef NO_ACTION_ONESHOT @@ -34,13 +34,15 @@ __attribute__((weak)) uint16_t unicodemap_index(uint16_t keycode) { bool shift = mods & MOD_MASK_SHIFT; bool caps = host_keyboard_led_state().caps_lock; if (shift ^ caps) { - index >>= 7; + index = QK_UNICODEMAP_PAIR_GET_SHIFTED_INDEX(keycode); + } else { + index = QK_UNICODEMAP_PAIR_GET_UNSHIFTED_INDEX(keycode); } - return index & 0x7F; + return index; } else { // Keycode is a regular index - return keycode - QK_UNICODEMAP; + return QK_UNICODEMAP_GET_INDEX(keycode); } } diff --git a/quantum/quantum_keycodes.h b/quantum/quantum_keycodes.h index ec69fadbab..6b0e890792 100644 --- a/quantum/quantum_keycodes.h +++ b/quantum/quantum_keycodes.h @@ -39,6 +39,10 @@ #define QK_UNICODEMAP_PAIR_MAX 0xFFFF // clang-format on +// Generic decoding for the whole QK_MODS range +#define QK_MODS_GET_MODS(kc) (((kc) >> 8) & 0x1F) +#define QK_MODS_GET_BASIC_KEYCODE(kc) ((kc)&0xFF) + // Keycode modifiers & aliases #define LCTL(kc) (QK_LCTL | (kc)) #define LSFT(kc) (QK_LSFT | (kc)) @@ -80,33 +84,46 @@ // GOTO layer - 32 layer max #define TO(layer) (QK_TO | ((layer)&0x1F)) +#define QK_TO_GET_LAYER(kc) ((kc)&0x1F) // Momentary switch layer - 32 layer max #define MO(layer) (QK_MOMENTARY | ((layer)&0x1F)) +#define QK_MOMENTARY_GET_LAYER(kc) ((kc)&0x1F) // Set default layer - 32 layer max #define DF(layer) (QK_DEF_LAYER | ((layer)&0x1F)) +#define QK_DEF_LAYER_GET_LAYER(kc) ((kc)&0x1F) // Toggle to layer - 32 layer max #define TG(layer) (QK_TOGGLE_LAYER | ((layer)&0x1F)) +#define QK_TOGGLE_LAYER_GET_LAYER(kc) ((kc)&0x1F) // One-shot layer - 32 layer max #define OSL(layer) (QK_ONE_SHOT_LAYER | ((layer)&0x1F)) +#define QK_ONE_SHOT_LAYER_GET_LAYER(kc) ((kc)&0x1F) // L-ayer M-od: Momentary switch layer with modifiers active - 16 layer max #define LM(layer, mod) (QK_LAYER_MOD | (((layer)&0xF) << 5) | ((mod)&0x1F)) +#define QK_LAYER_MOD_GET_LAYER(kc) (((kc) >> 5) & 0xF) +#define QK_LAYER_MOD_GET_MODS(kc) ((kc)&0x1F) // One-shot mod #define OSM(mod) (QK_ONE_SHOT_MOD | ((mod)&0x1F)) +#define QK_ONE_SHOT_MOD_GET_MODS(kc) ((kc)&0x1F) // Layer tap-toggle - 32 layer max #define TT(layer) (QK_LAYER_TAP_TOGGLE | ((layer)&0x1F)) +#define QK_LAYER_TAP_TOGGLE_GET_LAYER(kc) ((kc)&0x1F) // L-ayer, T-ap - 256 keycode max, 16 layer max #define LT(layer, kc) (QK_LAYER_TAP | (((layer)&0xF) << 8) | ((kc)&0xFF)) +#define QK_LAYER_TAP_GET_LAYER(kc) (((kc) >> 8) & 0xF) +#define QK_LAYER_TAP_GET_TAP_KEYCODE(kc) ((kc)&0xFF) // M-od, T-ap - 256 keycode max #define MT(mod, kc) (QK_MOD_TAP | (((mod)&0x1F) << 8) | ((kc)&0xFF)) +#define QK_MOD_TAP_GET_MODS(kc) (((kc) >> 8) & 0x1F) +#define QK_MOD_TAP_GET_TAP_KEYCODE(kc) ((kc)&0xFF) #define LCTL_T(kc) MT(MOD_LCTL, kc) #define RCTL_T(kc) MT(MOD_RCTL, kc) @@ -161,12 +178,19 @@ // Unicode aliases // UNICODE_ENABLE - Allows Unicode input up to 0x7FFF #define UC(c) (QK_UNICODE | (c)) +#define QK_UNICODE_GET_CODE_POINT(kc) ((kc)&0x7FFF) + // UNICODEMAP_ENABLE - Allows Unicode input up to 0x10FFFF, requires unicode_map -#define X(i) (QK_UNICODEMAP | (i)) +#define X(i) (QK_UNICODEMAP | ((i)&0x3FFF)) +#define QK_UNICODEMAP_GET_INDEX(kc) ((kc)&0x3FFF) + #define XP(i, j) (QK_UNICODEMAP_PAIR | ((i)&0x7F) | (((j)&0x7F) << 7)) // 127 max i and j +#define QK_UNICODEMAP_PAIR_GET_UNSHIFTED_INDEX(kc) ((kc)&0x7F) +#define QK_UNICODEMAP_PAIR_GET_SHIFTED_INDEX(kc) (((kc) >> 7) & 0x7F) // Swap Hands -#define SH_T(kc) (QK_SWAP_HANDS | (kc)) +#define SH_T(kc) (QK_SWAP_HANDS | ((kc)&0xFF)) +#define QK_SWAP_HANDS_GET_TAP_KEYCODE(kc) ((kc)&0xFF) // MIDI aliases #define MIDI_TONE_MIN MI_C -- cgit v1.2.3