From 854547330704fb1b1f07d547d49728da8b92b2a3 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Tue, 31 May 2022 15:38:08 +1000 Subject: Removes terminal from QMK. (#17258) --- quantum/process_keycode/process_terminal.c | 330 ------------------------- quantum/process_keycode/process_terminal.h | 24 -- quantum/process_keycode/process_terminal_nop.h | 22 -- 3 files changed, 376 deletions(-) delete mode 100644 quantum/process_keycode/process_terminal.c delete mode 100644 quantum/process_keycode/process_terminal.h delete mode 100644 quantum/process_keycode/process_terminal_nop.h (limited to 'quantum/process_keycode') diff --git a/quantum/process_keycode/process_terminal.c b/quantum/process_keycode/process_terminal.c deleted file mode 100644 index da1c4d506f..0000000000 --- a/quantum/process_keycode/process_terminal.c +++ /dev/null @@ -1,330 +0,0 @@ -/* Copyright 2017 Jack Humbert - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "process_terminal.h" -#include -#include "version.h" -#include -#include - -#ifndef CMD_BUFF_SIZE -# define CMD_BUFF_SIZE 5 -#endif - -bool terminal_enabled = false; -char buffer[80] = ""; -char cmd_buffer[CMD_BUFF_SIZE][80]; -bool cmd_buffer_enabled = true; // replace with ifdef? -char newline[2] = "\n"; -char arguments[6][20]; -bool firstTime = true; - -short int current_cmd_buffer_pos = 0; // used for up/down arrows - keeps track of where you are in the command buffer - -__attribute__((weak)) const char terminal_prompt[8] = "> "; - -#ifdef AUDIO_ENABLE -# ifndef TERMINAL_SONG -# define TERMINAL_SONG SONG(TERMINAL_SOUND) -# endif -float terminal_song[][2] = TERMINAL_SONG; -# define TERMINAL_BELL() PLAY_SONG(terminal_song) -#else -# define TERMINAL_BELL() -#endif - -__attribute__((weak)) const char keycode_to_ascii_lut[58] = {0, 0, 0, 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 0, 0, 0, '\t', ' ', '-', '=', '[', ']', '\\', 0, ';', '\'', '`', ',', '.', '/'}; - -__attribute__((weak)) const char shifted_keycode_to_ascii_lut[58] = {0, 0, 0, 0, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', 0, 0, 0, '\t', ' ', '_', '+', '{', '}', '|', 0, ':', '\'', '~', '<', '>', '?'}; - -struct stringcase { - char *string; - void (*func)(void); -} typedef stringcase; - -void enable_terminal(void) { - terminal_enabled = true; - strcpy(buffer, ""); - memset(cmd_buffer, 0, CMD_BUFF_SIZE * 80); - for (int i = 0; i < 6; i++) - strcpy(arguments[i], ""); - // select all text to start over - // SEND_STRING(SS_LCTL("a")); - send_string(terminal_prompt); -} - -void disable_terminal(void) { - terminal_enabled = false; - SEND_STRING("\n"); -} - -void push_to_cmd_buffer(void) { - if (cmd_buffer_enabled) { - if (cmd_buffer == NULL) { - return; - } else { - if (firstTime) { - firstTime = false; - strcpy(cmd_buffer[0], buffer); - return; - } - - for (int i = CMD_BUFF_SIZE - 1; i > 0; --i) { - strncpy(cmd_buffer[i], cmd_buffer[i - 1], 80); - } - - strcpy(cmd_buffer[0], buffer); - - return; - } - } -} - -void terminal_about(void) { - SEND_STRING("QMK Firmware\n"); - SEND_STRING(" v"); - SEND_STRING(QMK_VERSION); - SEND_STRING("\n" SS_TAP(X_HOME) " Built: "); - SEND_STRING(QMK_BUILDDATE); - send_string(newline); -#ifdef TERMINAL_HELP - if (strlen(arguments[1]) != 0) { - SEND_STRING("You entered: "); - send_string(arguments[1]); - send_string(newline); - } -#endif -} - -void terminal_help(void); - -extern const uint16_t keymaps[][MATRIX_ROWS][MATRIX_COLS]; - -void terminal_keycode(void) { - if (strlen(arguments[1]) != 0 && strlen(arguments[2]) != 0 && strlen(arguments[3]) != 0) { - char keycode_dec[5]; - char keycode_hex[5]; - uint16_t layer = strtol(arguments[1], (char **)NULL, 10); - uint16_t row = strtol(arguments[2], (char **)NULL, 10); - uint16_t col = strtol(arguments[3], (char **)NULL, 10); - uint16_t keycode = pgm_read_word(&keymaps[layer][row][col]); - itoa(keycode, keycode_dec, 10); - itoa(keycode, keycode_hex, 16); - SEND_STRING("0x"); - send_string(keycode_hex); - SEND_STRING(" ("); - send_string(keycode_dec); - SEND_STRING(")\n"); - } else { -#ifdef TERMINAL_HELP - SEND_STRING("usage: keycode \n"); -#endif - } -} - -void terminal_keymap(void) { - if (strlen(arguments[1]) != 0) { - uint16_t layer = strtol(arguments[1], (char **)NULL, 10); - for (int r = 0; r < MATRIX_ROWS; r++) { - for (int c = 0; c < MATRIX_COLS; c++) { - uint16_t keycode = pgm_read_word(&keymaps[layer][r][c]); - char keycode_s[8]; - sprintf(keycode_s, "0x%04x,", keycode); - send_string(keycode_s); - } - send_string(newline); - } - } else { -#ifdef TERMINAL_HELP - SEND_STRING("usage: keymap \n"); -#endif - } -} - -void print_cmd_buff(void) { - /* without the below wait, a race condition can occur wherein the - buffer can be printed before it has been fully moved */ - wait_ms(250); - for (int i = 0; i < CMD_BUFF_SIZE; i++) { - char tmpChar = ' '; - itoa(i, &tmpChar, 10); - const char *tmpCnstCharStr = &tmpChar; // because sned_string wont take a normal char * - send_string(tmpCnstCharStr); - SEND_STRING(". "); - send_string(cmd_buffer[i]); - SEND_STRING("\n"); - } -} - -void flush_cmd_buffer(void) { - memset(cmd_buffer, 0, CMD_BUFF_SIZE * 80); - SEND_STRING("Buffer Cleared!\n"); -} - -stringcase terminal_cases[] = {{"about", terminal_about}, {"help", terminal_help}, {"keycode", terminal_keycode}, {"keymap", terminal_keymap}, {"flush-buffer", flush_cmd_buffer}, {"print-buffer", print_cmd_buff}, {"exit", disable_terminal}}; - -void terminal_help(void) { - SEND_STRING("commands available:\n "); - for (stringcase *case_p = terminal_cases; case_p != terminal_cases + sizeof(terminal_cases) / sizeof(terminal_cases[0]); case_p++) { - send_string(case_p->string); - SEND_STRING(" "); - } - send_string(newline); -} - -void command_not_found(void) { - wait_ms(50); // sometimes buffer isnt grabbed quick enough - SEND_STRING("command \""); - send_string(buffer); - SEND_STRING("\" not found\n"); -} - -void process_terminal_command(void) { - // we capture return bc of the order of events, so we need to manually send a newline - send_string(newline); - - char * pch; - uint8_t i = 0; - pch = strtok(buffer, " "); - while (pch != NULL) { - strcpy(arguments[i], pch); - pch = strtok(NULL, " "); - i++; - } - - bool command_found = false; - for (stringcase *case_p = terminal_cases; case_p != terminal_cases + sizeof(terminal_cases) / sizeof(terminal_cases[0]); case_p++) { - if (0 == strcmp(case_p->string, buffer)) { - command_found = true; - (*case_p->func)(); - break; - } - } - - if (!command_found) command_not_found(); - - if (terminal_enabled) { - strcpy(buffer, ""); - for (int i = 0; i < 6; i++) - strcpy(arguments[i], ""); - SEND_STRING(SS_TAP(X_HOME)); - send_string(terminal_prompt); - } -} -void check_pos(void) { - if (current_cmd_buffer_pos >= CMD_BUFF_SIZE) { // if over the top, move it back down to the top of the buffer so you can climb back down... - current_cmd_buffer_pos = CMD_BUFF_SIZE - 1; - } else if (current_cmd_buffer_pos < 0) { //...and if you fall under the bottom of the buffer, reset back to 0 so you can climb back up - current_cmd_buffer_pos = 0; - } -} - -bool process_terminal(uint16_t keycode, keyrecord_t *record) { - if (keycode == TERM_ON && record->event.pressed) { - enable_terminal(); - return false; - } - - if (terminal_enabled && record->event.pressed) { - if (keycode == TERM_OFF && record->event.pressed) { - disable_terminal(); - return false; - } - - if ((keycode >= QK_MOD_TAP && keycode <= QK_MOD_TAP_MAX) || (keycode >= QK_LAYER_TAP && keycode <= QK_LAYER_TAP_MAX)) { - keycode = keycode & 0xFF; - } - - if (keycode < 256) { - uint8_t str_len; - char char_to_add; - switch (keycode) { - case KC_ENTER: - case KC_KP_ENTER: - push_to_cmd_buffer(); - current_cmd_buffer_pos = 0; - process_terminal_command(); - return false; - break; - case KC_ESCAPE: - SEND_STRING("\n"); - enable_terminal(); - return false; - break; - case KC_BACKSPACE: - str_len = strlen(buffer); - if (str_len > 0) { - buffer[str_len - 1] = 0; - return true; - } else { - TERMINAL_BELL(); - return false; - } - break; - case KC_LEFT: - return false; - break; - case KC_RIGHT: - return false; - break; - case KC_UP: // 0 = recent - check_pos(); // check our current buffer position is valid - if (current_cmd_buffer_pos <= CMD_BUFF_SIZE - 1) { // once we get to the top, dont do anything - str_len = strlen(buffer); - for (int i = 0; i < str_len; ++i) { - send_string(SS_TAP(X_BSPACE)); // clear w/e is on the line already - // process_terminal(KC_BACKSPACE,record); - } - strncpy(buffer, cmd_buffer[current_cmd_buffer_pos], 80); - - send_string(buffer); - ++current_cmd_buffer_pos; // get ready to access the above cmd if up/down is pressed again - } - return false; - break; - case KC_DOWN: - check_pos(); - if (current_cmd_buffer_pos >= 0) { // once we get to the bottom, dont do anything - str_len = strlen(buffer); - for (int i = 0; i < str_len; ++i) { - send_string(SS_TAP(X_BSPACE)); // clear w/e is on the line already - // process_terminal(KC_BACKSPACE,record); - } - strncpy(buffer, cmd_buffer[current_cmd_buffer_pos], 79); - - send_string(buffer); - --current_cmd_buffer_pos; // get ready to access the above cmd if down/up is pressed again - } - return false; - break; - default: - if (keycode <= 58) { - char_to_add = 0; - if (get_mods() & (MOD_BIT(KC_LEFT_SHIFT) | MOD_BIT(KC_RIGHT_SHIFT))) { - char_to_add = shifted_keycode_to_ascii_lut[keycode]; - } else if (get_mods() == 0) { - char_to_add = keycode_to_ascii_lut[keycode]; - } - if (char_to_add != 0) { - strncat(buffer, &char_to_add, 1); - } - } - break; - } - } - } - return true; -} diff --git a/quantum/process_keycode/process_terminal.h b/quantum/process_keycode/process_terminal.h deleted file mode 100644 index 0159131e5b..0000000000 --- a/quantum/process_keycode/process_terminal.h +++ /dev/null @@ -1,24 +0,0 @@ -/* Copyright 2017 Jack Humbert - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include "quantum.h" - -extern const char keycode_to_ascii_lut[58]; -extern const char shifted_keycode_to_ascii_lut[58]; -extern const char terminal_prompt[8]; -bool process_terminal(uint16_t keycode, keyrecord_t *record); diff --git a/quantum/process_keycode/process_terminal_nop.h b/quantum/process_keycode/process_terminal_nop.h deleted file mode 100644 index 36e25320c5..0000000000 --- a/quantum/process_keycode/process_terminal_nop.h +++ /dev/null @@ -1,22 +0,0 @@ -/* Copyright 2017 Jack Humbert - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include "quantum.h" - -#define TERM_ON KC_NO -#define TERM_OFF KC_NO -- cgit v1.2.3 From 95d20e6d8bb1ffaf3024af793daf789ee0b75727 Mon Sep 17 00:00:00 2001 From: Pascal Getreuer <50221757+getreuer@users.noreply.github.com> Date: Sun, 5 Jun 2022 00:14:02 -0700 Subject: Fix and add unit tests for Caps Word to work with Unicode Map, Auto Shift, Retro Shift. (#17284) * Fix Caps Word and Unicode Map * Tests for Caps Word + Auto Shift and Unicode Map. * Fix formatting * Add additional keyboard report expectation macros This commit defines five test utilities, EXPECT_REPORT, EXPECT_UNICODE, EXPECT_EMPTY_REPORT, EXPECT_ANY_REPORT and EXPECT_NO_REPORT for use with TestDriver. EXPECT_REPORT sets a gmock expectation that a given keyboard report will be sent. For instance, EXPECT_REPORT(driver, (KC_LSFT, KC_A)); is shorthand for EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT, KC_A))); EXPECT_UNICODE sets a gmock expectation that a given Unicode code point will be sent using UC_LNX input mode. For instance for U+2013, EXPECT_UNICODE(driver, 0x2013); expects the sequence of keys: "Ctrl+Shift+U, 2, 0, 1, 3, space". EXPECT_EMPTY_REPORT sets a gmock expectation that a given keyboard report will be sent. For instance EXPECT_EMPTY_REPORT(driver); expects a single report without keypresses or modifiers. EXPECT_ANY_REPORT sets a gmock expectation that a arbitrary keyboard report will be sent, without matching its contents. For instance EXPECT_ANY_REPORT(driver).Times(1); expects a single arbitrary keyboard report will be sent. EXPECT_NO_REPORT sets a gmock expectation that no keyboard report will be sent at all. * Add tap_key() and tap_keys() to TestFixture. This commit adds a `tap_key(key)` method to TestFixture that taps a given KeymapKey, optionally with a specified delay between press and release. Similarly, the method `tap_keys(key_a, key_b, key_c)` taps a sequence of KeymapKeys. * Use EXPECT_REPORT, tap_keys, etc. in most tests. This commit uses EXPECT_REPORT, EXPECT_UNICODE, EXPECT_EMPTY_REPORT, EXPECT_NO_REPORT, tap_key() and tap_keys() test utilities from the previous two commits in most tests. Particularly the EXPECT_REPORT macro is frequently useful and makes a nice reduction in boilerplate needed to express many tests. Co-authored-by: David Kosorin --- quantum/process_keycode/process_unicode_common.c | 1 + 1 file changed, 1 insertion(+) (limited to 'quantum/process_keycode') diff --git a/quantum/process_keycode/process_unicode_common.c b/quantum/process_keycode/process_unicode_common.c index 652becbc9a..6cb2ba8dbb 100644 --- a/quantum/process_keycode/process_unicode_common.c +++ b/quantum/process_keycode/process_unicode_common.c @@ -95,6 +95,7 @@ __attribute__((weak)) void unicode_input_start(void) { unicode_saved_mods = get_mods(); // Save current mods clear_mods(); // Unregister mods to start from a clean state + clear_weak_mods(); switch (unicode_config.input_mode) { case UC_MAC: -- cgit v1.2.3 From 1706da9054f8c4aa77493062a8937a7b64970a9e Mon Sep 17 00:00:00 2001 From: Jouke Witteveen Date: Mon, 13 Jun 2022 22:12:55 +0200 Subject: tap-dance: Restructure code and document in more detail (#16394) --- quantum/process_keycode/process_tap_dance.c | 137 +++++++++++----------------- quantum/process_keycode/process_tap_dance.h | 33 +++---- 2 files changed, 70 insertions(+), 100 deletions(-) (limited to 'quantum/process_keycode') diff --git a/quantum/process_keycode/process_tap_dance.c b/quantum/process_keycode/process_tap_dance.c index db8df5f870..3270a1b000 100644 --- a/quantum/process_keycode/process_tap_dance.c +++ b/quantum/process_keycode/process_tap_dance.c @@ -15,12 +15,8 @@ */ #include "quantum.h" -#ifndef NO_ACTION_ONESHOT -uint8_t get_oneshot_mods(void); -#endif - -static uint16_t last_td; -static int16_t highest_td = -1; +static uint16_t active_td; +static uint16_t last_tap_time; void qk_tap_dance_pair_on_each_tap(qk_tap_dance_state_t *state, void *user_data) { qk_tap_dance_pair_t *pair = (qk_tap_dance_pair_t *)user_data; @@ -34,18 +30,14 @@ void qk_tap_dance_pair_on_each_tap(qk_tap_dance_state_t *state, void *user_data) void qk_tap_dance_pair_finished(qk_tap_dance_state_t *state, void *user_data) { qk_tap_dance_pair_t *pair = (qk_tap_dance_pair_t *)user_data; - if (state->count == 1) { - register_code16(pair->kc1); - } else if (state->count == 2) { - register_code16(pair->kc2); - } + register_code16(pair->kc1); } void qk_tap_dance_pair_reset(qk_tap_dance_state_t *state, void *user_data) { qk_tap_dance_pair_t *pair = (qk_tap_dance_pair_t *)user_data; - wait_ms(TAP_CODE_DELAY); if (state->count == 1) { + wait_ms(TAP_CODE_DELAY); unregister_code16(pair->kc1); } else if (state->count == 2) { unregister_code16(pair->kc2); @@ -87,23 +79,40 @@ static inline void _process_tap_dance_action_fn(qk_tap_dance_state_t *state, voi } static inline void process_tap_dance_action_on_each_tap(qk_tap_dance_action_t *action) { + action->state.count++; + action->state.weak_mods = get_mods(); + action->state.weak_mods |= get_weak_mods(); +#ifndef NO_ACTION_ONESHOT + action->state.oneshot_mods = get_oneshot_mods(); +#endif _process_tap_dance_action_fn(&action->state, action->user_data, action->fn.on_each_tap); } -static inline void process_tap_dance_action_on_dance_finished(qk_tap_dance_action_t *action) { - if (action->state.finished) return; - action->state.finished = true; - add_mods(action->state.oneshot_mods); - add_weak_mods(action->state.weak_mods); - send_keyboard_report(); - _process_tap_dance_action_fn(&action->state, action->user_data, action->fn.on_dance_finished); -} - static inline void process_tap_dance_action_on_reset(qk_tap_dance_action_t *action) { _process_tap_dance_action_fn(&action->state, action->user_data, action->fn.on_reset); - del_mods(action->state.oneshot_mods); del_weak_mods(action->state.weak_mods); +#ifndef NO_ACTION_ONESHOT + del_mods(action->state.oneshot_mods); +#endif send_keyboard_report(); + action->state = (const qk_tap_dance_state_t){0}; +} + +static inline void process_tap_dance_action_on_dance_finished(qk_tap_dance_action_t *action) { + if (!action->state.finished) { + action->state.finished = true; + add_weak_mods(action->state.weak_mods); +#ifndef NO_ACTION_ONESHOT + add_mods(action->state.oneshot_mods); +#endif + send_keyboard_report(); + _process_tap_dance_action_fn(&action->state, action->user_data, action->fn.on_dance_finished); + } + active_td = 0; + if (!action->state.pressed) { + // There will not be a key release event, so reset now. + process_tap_dance_action_on_reset(action); + } } void preprocess_tap_dance(uint16_t keycode, keyrecord_t *record) { @@ -111,51 +120,33 @@ void preprocess_tap_dance(uint16_t keycode, keyrecord_t *record) { if (!record->event.pressed) return; - if (highest_td == -1) return; - - for (int i = 0; i <= highest_td; i++) { - action = &tap_dance_actions[i]; - if (action->state.count) { - if (keycode == action->state.keycode && keycode == last_td) continue; - action->state.interrupted = true; - action->state.interrupting_keycode = keycode; - process_tap_dance_action_on_dance_finished(action); - reset_tap_dance(&action->state); - - // Tap dance actions can leave some weak mods active (e.g., if the tap dance is mapped to a keycode with - // modifiers), but these weak mods should not affect the keypress which interrupted the tap dance. - clear_weak_mods(); - } - } + if (!active_td || keycode == active_td) return; + + action = &tap_dance_actions[TD_INDEX(active_td)]; + action->state.interrupted = true; + action->state.interrupting_keycode = keycode; + process_tap_dance_action_on_dance_finished(action); + + // Tap dance actions can leave some weak mods active (e.g., if the tap dance is mapped to a keycode with + // modifiers), but these weak mods should not affect the keypress which interrupted the tap dance. + clear_weak_mods(); } bool process_tap_dance(uint16_t keycode, keyrecord_t *record) { - uint16_t idx = keycode - QK_TAP_DANCE; qk_tap_dance_action_t *action; switch (keycode) { case QK_TAP_DANCE ... QK_TAP_DANCE_MAX: - if ((int16_t)idx > highest_td) highest_td = idx; - action = &tap_dance_actions[idx]; + action = &tap_dance_actions[TD_INDEX(keycode)]; action->state.pressed = record->event.pressed; if (record->event.pressed) { - action->state.keycode = keycode; - action->state.count++; - action->state.timer = timer_read(); -#ifndef NO_ACTION_ONESHOT - action->state.oneshot_mods = get_oneshot_mods(); -#else - action->state.oneshot_mods = 0; -#endif - action->state.weak_mods = get_mods(); - action->state.weak_mods |= get_weak_mods(); + last_tap_time = timer_read(); process_tap_dance_action_on_each_tap(action); - - last_td = keycode; + active_td = action->state.finished ? 0 : keycode; } else { - if (action->state.count && action->state.finished) { - reset_tap_dance(&action->state); + if (action->state.finished) { + process_tap_dance_action_on_reset(action); } } @@ -166,35 +157,17 @@ bool process_tap_dance(uint16_t keycode, keyrecord_t *record) { } void tap_dance_task() { - if (highest_td == -1) return; - uint16_t tap_user_defined; - - for (uint8_t i = 0; i <= highest_td; i++) { - qk_tap_dance_action_t *action = &tap_dance_actions[i]; - if (action->custom_tapping_term > 0) { - tap_user_defined = action->custom_tapping_term; - } else { - tap_user_defined = GET_TAPPING_TERM(action->state.keycode, &(keyrecord_t){}); - } - if (action->state.count && timer_elapsed(action->state.timer) > tap_user_defined) { - process_tap_dance_action_on_dance_finished(action); - reset_tap_dance(&action->state); - } - } -} - -void reset_tap_dance(qk_tap_dance_state_t *state) { qk_tap_dance_action_t *action; - if (state->pressed) return; + if (!active_td || timer_elapsed(last_tap_time) <= GET_TAPPING_TERM(active_td, &(keyrecord_t){})) return; - action = &tap_dance_actions[state->keycode - QK_TAP_DANCE]; - - process_tap_dance_action_on_reset(action); + action = &tap_dance_actions[TD_INDEX(active_td)]; + if (!action->state.interrupted) { + process_tap_dance_action_on_dance_finished(action); + } +} - state->count = 0; - state->interrupted = false; - state->finished = false; - state->interrupting_keycode = 0; - last_td = 0; +void reset_tap_dance(qk_tap_dance_state_t *state) { + active_td = 0; + process_tap_dance_action_on_reset((qk_tap_dance_action_t *)state); } diff --git a/quantum/process_keycode/process_tap_dance.h b/quantum/process_keycode/process_tap_dance.h index d9ffb1e73d..d97900d96b 100644 --- a/quantum/process_keycode/process_tap_dance.h +++ b/quantum/process_keycode/process_tap_dance.h @@ -22,30 +22,27 @@ # include typedef struct { + uint16_t interrupting_keycode; uint8_t count; - uint8_t oneshot_mods; uint8_t weak_mods; - uint16_t keycode; - uint16_t interrupting_keycode; - uint16_t timer; - bool interrupted; - bool pressed; - bool finished; +# ifndef NO_ACTION_ONESHOT + uint8_t oneshot_mods; +# endif + bool pressed : 1; + bool finished : 1; + bool interrupted : 1; } qk_tap_dance_state_t; -# define TD(n) (QK_TAP_DANCE | ((n)&0xFF)) - typedef void (*qk_tap_dance_user_fn_t)(qk_tap_dance_state_t *state, void *user_data); typedef struct { + qk_tap_dance_state_t state; struct { qk_tap_dance_user_fn_t on_each_tap; qk_tap_dance_user_fn_t on_dance_finished; qk_tap_dance_user_fn_t on_reset; } fn; - qk_tap_dance_state_t state; - uint16_t custom_tapping_term; - void * user_data; + void *user_data; } qk_tap_dance_action_t; typedef struct { @@ -62,31 +59,31 @@ typedef struct { # define ACTION_TAP_DANCE_DOUBLE(kc1, kc2) \ { .fn = {qk_tap_dance_pair_on_each_tap, qk_tap_dance_pair_finished, qk_tap_dance_pair_reset}, .user_data = (void *)&((qk_tap_dance_pair_t){kc1, kc2}), } -# define ACTION_TAP_DANCE_DUAL_ROLE(kc, layer) \ +# define ACTION_TAP_DANCE_LAYER_MOVE(kc, layer) \ { .fn = {qk_tap_dance_dual_role_on_each_tap, qk_tap_dance_dual_role_finished, qk_tap_dance_dual_role_reset}, .user_data = (void *)&((qk_tap_dance_dual_role_t){kc, layer, layer_move}), } # define ACTION_TAP_DANCE_LAYER_TOGGLE(kc, layer) \ { .fn = {NULL, qk_tap_dance_dual_role_finished, qk_tap_dance_dual_role_reset}, .user_data = (void *)&((qk_tap_dance_dual_role_t){kc, layer, layer_invert}), } -# define ACTION_TAP_DANCE_LAYER_MOVE(kc, layer) ACTION_TAP_DANCE_DUAL_ROLE(kc, layer) - # define ACTION_TAP_DANCE_FN(user_fn) \ { .fn = {NULL, user_fn, NULL}, .user_data = NULL, } # define ACTION_TAP_DANCE_FN_ADVANCED(user_fn_on_each_tap, user_fn_on_dance_finished, user_fn_on_dance_reset) \ { .fn = {user_fn_on_each_tap, user_fn_on_dance_finished, user_fn_on_dance_reset}, .user_data = NULL, } -# define ACTION_TAP_DANCE_FN_ADVANCED_TIME(user_fn_on_each_tap, user_fn_on_dance_finished, user_fn_on_dance_reset, tap_specific_tapping_term) \ - { .fn = {user_fn_on_each_tap, user_fn_on_dance_finished, user_fn_on_dance_reset}, .user_data = NULL, .custom_tapping_term = tap_specific_tapping_term, } +# define TD(n) (QK_TAP_DANCE | TD_INDEX(n)) +# define TD_INDEX(code) ((code)&0xFF) +# define TAP_DANCE_KEYCODE(state) TD(((qk_tap_dance_action_t *)state) - tap_dance_actions) extern qk_tap_dance_action_t tap_dance_actions[]; +void reset_tap_dance(qk_tap_dance_state_t *state); + /* To be used internally */ void preprocess_tap_dance(uint16_t keycode, keyrecord_t *record); bool process_tap_dance(uint16_t keycode, keyrecord_t *record); void tap_dance_task(void); -void reset_tap_dance(qk_tap_dance_state_t *state); void qk_tap_dance_pair_on_each_tap(qk_tap_dance_state_t *state, void *user_data); void qk_tap_dance_pair_finished(qk_tap_dance_state_t *state, void *user_data); -- cgit v1.2.3 From 7060cb7b267e78ba693a77a3346c77c2d4209b01 Mon Sep 17 00:00:00 2001 From: precondition <57645186+precondition@users.noreply.github.com> Date: Thu, 23 Jun 2022 20:43:24 +0200 Subject: Refactor steno and add `STENO_PROTOCOL = [all|txbolt|geminipr]` (#17065) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Refactor steno into STENO_ENABLE_[ALL|GEMINI|BOLT] * Update stenography documentation * STENO_ENABLE_TXBOLT → STENO_ENABLE_BOLT TXBOLT is a better name but BOLT is more consistent with the pre-existing TX Bolt related constants, which all drop the "TX " prefix * Comments * STENO_ENABLE_[GEMINI|BOLT|ALL] → STENO_PROTOCOL = [geminipr|txbolt|all] * Add note on lacking V-USB support * Clear chord at the end of the switch(mode){send_steno_chord} block * Return true if NOEVENT * update_chord_xxx → add_xxx_key_to_chord * Enable the defines for all the protocols if STENO_PROTOCOL = all * Mention how to use `steno_set_mode` * Set the default steno protocol to "all" This is done so that existing keymaps invoking `steno_set_mode` don't all suddenly break * Add data driver equivalents for stenography feature * Document format of serial steno packets (Thanks dnaq) * Add missing comma --- quantum/process_keycode/process_steno.c | 312 +++++++++++++++++--------------- quantum/process_keycode/process_steno.h | 24 ++- 2 files changed, 188 insertions(+), 148 deletions(-) (limited to 'quantum/process_keycode') diff --git a/quantum/process_keycode/process_steno.c b/quantum/process_keycode/process_steno.c index 12ee898212..20b8b9db4b 100644 --- a/quantum/process_keycode/process_steno.c +++ b/quantum/process_keycode/process_steno.c @@ -1,4 +1,4 @@ -/* Copyright 2017 Joseph Wasson +/* Copyright 2017, 2022 Joseph Wasson, Vladislav Kucheriavykh * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,77 +15,118 @@ */ #include "process_steno.h" #include "quantum_keycodes.h" -#include "eeprom.h" #include "keymap_steno.h" -#include "virtser.h" #include +#ifdef VIRTSER_ENABLE +# include "virtser.h" +#endif +#ifdef STENO_ENABLE_ALL +# include "eeprom.h" +#endif -// TxBolt Codes -#define TXB_NUL 0 -#define TXB_S_L 0b00000001 -#define TXB_T_L 0b00000010 -#define TXB_K_L 0b00000100 -#define TXB_P_L 0b00001000 -#define TXB_W_L 0b00010000 -#define TXB_H_L 0b00100000 -#define TXB_R_L 0b01000001 -#define TXB_A_L 0b01000010 -#define TXB_O_L 0b01000100 -#define TXB_STR 0b01001000 -#define TXB_E_R 0b01010000 -#define TXB_U_R 0b01100000 -#define TXB_F_R 0b10000001 -#define TXB_R_R 0b10000010 -#define TXB_P_R 0b10000100 -#define TXB_B_R 0b10001000 -#define TXB_L_R 0b10010000 -#define TXB_G_R 0b10100000 -#define TXB_T_R 0b11000001 -#define TXB_S_R 0b11000010 -#define TXB_D_R 0b11000100 -#define TXB_Z_R 0b11001000 -#define TXB_NUM 0b11010000 - -#define TXB_GRP0 0b00000000 -#define TXB_GRP1 0b01000000 -#define TXB_GRP2 0b10000000 -#define TXB_GRP3 0b11000000 -#define TXB_GRPMASK 0b11000000 - -#define TXB_GET_GROUP(code) ((code & TXB_GRPMASK) >> 6) - -#define BOLT_STATE_SIZE 4 -#define GEMINI_STATE_SIZE 6 -#define MAX_STATE_SIZE GEMINI_STATE_SIZE - -static uint8_t state[MAX_STATE_SIZE] = {0}; -static uint8_t chord[MAX_STATE_SIZE] = {0}; -static int8_t pressed = 0; +// All steno keys that have been pressed to form this chord, +// stored in MAX_STROKE_SIZE groups of 8-bit arrays. +static uint8_t chord[MAX_STROKE_SIZE] = {0}; +// The number of physical keys actually being held down. +// This is not always equal to the number of 1 bits in `chord` because it is possible to +// simultaneously press down four keys, then release three of those four keys and then press yet +// another key while the fourth finger is still holding down its key. +// At the end of this scenario given as an example, `chord` would have five bits set to 1 but +// `n_pressed_keys` would be set to 2 because there are only two keys currently being pressed down. +static int8_t n_pressed_keys = 0; + +#ifdef STENO_ENABLE_ALL static steno_mode_t mode; - -static const uint8_t boltmap[64] PROGMEM = {TXB_NUL, TXB_NUM, TXB_NUM, TXB_NUM, TXB_NUM, TXB_NUM, TXB_NUM, TXB_S_L, TXB_S_L, TXB_T_L, TXB_K_L, TXB_P_L, TXB_W_L, TXB_H_L, TXB_R_L, TXB_A_L, TXB_O_L, TXB_STR, TXB_STR, TXB_NUL, TXB_NUL, TXB_NUL, TXB_STR, TXB_STR, TXB_E_R, TXB_U_R, TXB_F_R, TXB_R_R, TXB_P_R, TXB_B_R, TXB_L_R, TXB_G_R, TXB_T_R, TXB_S_R, TXB_D_R, TXB_NUM, TXB_NUM, TXB_NUM, TXB_NUM, TXB_NUM, TXB_NUM, TXB_Z_R}; - -#ifdef STENO_COMBINEDMAP -/* Used to look up when pressing the middle row key to combine two consonant or vowel keys */ -static const uint16_t combinedmap_first[] PROGMEM = {STN_S1, STN_TL, STN_PL, STN_HL, STN_FR, STN_PR, STN_LR, STN_TR, STN_DR, STN_A, STN_E}; -static const uint16_t combinedmap_second[] PROGMEM = {STN_S2, STN_KL, STN_WL, STN_RL, STN_RR, STN_BR, STN_GR, STN_SR, STN_ZR, STN_O, STN_U}; +#elif defined(STENO_ENABLE_GEMINI) +static const steno_mode_t mode = STENO_MODE_GEMINI; +#elif defined(STENO_ENABLE_BOLT) +static const steno_mode_t mode = STENO_MODE_BOLT; #endif -static void steno_clear_state(void) { - memset(state, 0, sizeof(state)); +static inline void steno_clear_chord(void) { memset(chord, 0, sizeof(chord)); } -static void send_steno_state(uint8_t size, bool send_empty) { - for (uint8_t i = 0; i < size; ++i) { - if (chord[i] || send_empty) { -#ifdef VIRTSER_ENABLE +#ifdef STENO_ENABLE_GEMINI + +# ifdef VIRTSER_ENABLE +void send_steno_chord_gemini(void) { + // Set MSB to 1 to indicate the start of packet + chord[0] |= 0x80; + for (uint8_t i = 0; i < GEMINI_STROKE_SIZE; ++i) { + virtser_send(chord[i]); + } +} +# else +# pragma message "VIRTSER_ENABLE = yes is required for Gemini PR to work properly out of the box!" +# endif // VIRTSER_ENABLE + +/** + * @precondition: `key` is pressed + */ +bool add_gemini_key_to_chord(uint8_t key) { + // Although each group of the packet is 8 bits long, the MSB is reserved + // to indicate whether that byte is the first byte of the packet (MSB=1) + // or one of the remaining five bytes of the packet (MSB=0). + // As a consequence, only 7 out of the 8 bits are left to be used as a bit array + // for the steno keys of that group. + const int group_idx = key / 7; + const int intra_group_idx = key - group_idx * 7; + // The 0th steno key of the group has bit=0b01000000, the 1st has bit=0b00100000, etc. + const uint8_t bit = 1 << (6 - intra_group_idx); + chord[group_idx] |= bit; + return false; +} +#endif // STENO_ENABLE_GEMINI + +#ifdef STENO_ENABLE_BOLT + +# define TXB_GRP0 0b00000000 +# define TXB_GRP1 0b01000000 +# define TXB_GRP2 0b10000000 +# define TXB_GRP3 0b11000000 +# define TXB_GRPMASK 0b11000000 + +# define TXB_GET_GROUP(code) ((code & TXB_GRPMASK) >> 6) + +static const uint8_t boltmap[64] PROGMEM = {TXB_NUL, TXB_NUM, TXB_NUM, TXB_NUM, TXB_NUM, TXB_NUM, TXB_NUM, TXB_S_L, TXB_S_L, TXB_T_L, TXB_K_L, TXB_P_L, TXB_W_L, TXB_H_L, TXB_R_L, TXB_A_L, TXB_O_L, TXB_STR, TXB_STR, TXB_NUL, TXB_NUL, TXB_NUL, TXB_STR, TXB_STR, TXB_E_R, TXB_U_R, TXB_F_R, TXB_R_R, TXB_P_R, TXB_B_R, TXB_L_R, TXB_G_R, TXB_T_R, TXB_S_R, TXB_D_R, TXB_NUM, TXB_NUM, TXB_NUM, TXB_NUM, TXB_NUM, TXB_NUM, TXB_Z_R}; + +# ifdef VIRTSER_ENABLE +static void send_steno_chord_bolt(void) { + for (uint8_t i = 0; i < BOLT_STROKE_SIZE; ++i) { + // TX Bolt uses variable length packets where each byte corresponds to a bit array of certain keys. + // If a user chorded the keys of the first group with keys of the last group, for example, there + // would be bytes of 0x00 in `chord` for the middle groups which we mustn't send. + if (chord[i]) { virtser_send(chord[i]); -#endif } } + // Sending a null packet is not always necessary, but it is simpler and more reliable + // to unconditionally send it every time instead of keeping track of more states and + // creating more branches in the execution of the program. + virtser_send(0); } +# else +# pragma message "VIRTSER_ENABLE = yes is required for TX Bolt to work properly out of the box!" +# endif // VIRTSER_ENABLE + +/** + * @precondition: `key` is pressed + */ +static bool add_bolt_key_to_chord(uint8_t key) { + uint8_t boltcode = pgm_read_byte(boltmap + key); + chord[TXB_GET_GROUP(boltcode)] |= boltcode; + return false; +} +#endif // STENO_ENABLE_BOLT + +#ifdef STENO_COMBINEDMAP +/* Used to look up when pressing the middle row key to combine two consonant or vowel keys */ +static const uint16_t combinedmap_first[] PROGMEM = {STN_S1, STN_TL, STN_PL, STN_HL, STN_FR, STN_PR, STN_LR, STN_TR, STN_DR, STN_A, STN_E}; +static const uint16_t combinedmap_second[] PROGMEM = {STN_S2, STN_KL, STN_WL, STN_RL, STN_RR, STN_BR, STN_GR, STN_SR, STN_ZR, STN_O, STN_U}; +#endif +#ifdef STENO_ENABLE_ALL void steno_init() { if (!eeconfig_is_enabled()) { eeconfig_init(); @@ -94,19 +135,20 @@ void steno_init() { } void steno_set_mode(steno_mode_t new_mode) { - steno_clear_state(); + steno_clear_chord(); mode = new_mode; eeprom_update_byte(EECONFIG_STENOMODE, mode); } +#endif // STENO_ENABLE_ALL /* override to intercept chords right before they get sent. * return zero to suppress normal sending behavior. */ -__attribute__((weak)) bool send_steno_chord_user(steno_mode_t mode, uint8_t chord[6]) { +__attribute__((weak)) bool send_steno_chord_user(steno_mode_t mode, uint8_t chord[MAX_STROKE_SIZE]) { return true; } -__attribute__((weak)) bool postprocess_steno_user(uint16_t keycode, keyrecord_t *record, steno_mode_t mode, uint8_t chord[6], int8_t pressed) { +__attribute__((weak)) bool postprocess_steno_user(uint16_t keycode, keyrecord_t *record, steno_mode_t mode, uint8_t chord[MAX_STROKE_SIZE], int8_t n_pressed_keys) { return true; } @@ -114,108 +156,94 @@ __attribute__((weak)) bool process_steno_user(uint16_t keycode, keyrecord_t *rec return true; } -static void send_steno_chord(void) { - if (send_steno_chord_user(mode, chord)) { - switch (mode) { - case STENO_MODE_BOLT: - send_steno_state(BOLT_STATE_SIZE, false); -#ifdef VIRTSER_ENABLE - virtser_send(0); // terminating byte -#endif - break; - case STENO_MODE_GEMINI: - chord[0] |= 0x80; // Indicate start of packet - send_steno_state(GEMINI_STATE_SIZE, true); - break; - } +bool process_steno(uint16_t keycode, keyrecord_t *record) { + if (keycode < QK_STENO || keycode > QK_STENO_MAX) { + return true; // Not a steno key, pass it further along the chain + /* + * Clearing or sending the chord state is not necessary as we intentionally ignore whatever + * normal keyboard keys the user may have tapped while chording steno keys. + */ } - steno_clear_state(); -} - -uint8_t *steno_get_state(void) { - return &state[0]; -} - -uint8_t *steno_get_chord(void) { - return &chord[0]; -} - -static bool update_state_bolt(uint8_t key, bool press) { - uint8_t boltcode = pgm_read_byte(boltmap + key); - if (press) { - state[TXB_GET_GROUP(boltcode)] |= boltcode; - chord[TXB_GET_GROUP(boltcode)] |= boltcode; - } else { - state[TXB_GET_GROUP(boltcode)] &= ~boltcode; + if (IS_NOEVENT(record->event)) { + return true; } - return false; -} - -static bool update_state_gemini(uint8_t key, bool press) { - int idx = key / 7; - uint8_t bit = 1 << (6 - (key % 7)); - if (press) { - state[idx] |= bit; - chord[idx] |= bit; - } else { - state[idx] &= ~bit; + if (!process_steno_user(keycode, record)) { + return false; // User fully processed the steno key themselves } - return false; -} - -bool process_steno(uint16_t keycode, keyrecord_t *record) { switch (keycode) { +#ifdef STENO_ENABLE_ALL case QK_STENO_BOLT: - if (!process_steno_user(keycode, record)) { - return false; - } if (IS_PRESSED(record->event)) { steno_set_mode(STENO_MODE_BOLT); } return false; case QK_STENO_GEMINI: - if (!process_steno_user(keycode, record)) { - return false; - } if (IS_PRESSED(record->event)) { steno_set_mode(STENO_MODE_GEMINI); } return false; +#endif // STENO_ENABLE_ALL #ifdef STENO_COMBINEDMAP case QK_STENO_COMB ... QK_STENO_COMB_MAX: { - uint8_t result; - result = process_steno(combinedmap_first[keycode - QK_STENO_COMB], record); - result &= process_steno(combinedmap_second[keycode - QK_STENO_COMB], record); - return result; + bool first_result = process_steno(combinedmap_first[keycode - QK_STENO_COMB], record); + bool second_result = process_steno(combinedmap_second[keycode - QK_STENO_COMB], record); + return first_result && second_result; } -#endif +#endif // STENO_COMBINEDMAP case STN__MIN ... STN__MAX: - if (!process_steno_user(keycode, record)) { - return false; - } - switch (mode) { - case STENO_MODE_BOLT: - update_state_bolt(keycode - QK_STENO, IS_PRESSED(record->event)); - break; - case STENO_MODE_GEMINI: - update_state_gemini(keycode - QK_STENO, IS_PRESSED(record->event)); - break; - } - // allow postprocessing hooks - if (postprocess_steno_user(keycode, record, mode, chord, pressed)) { - if (IS_PRESSED(record->event)) { - ++pressed; - } else { - --pressed; - if (pressed <= 0) { - pressed = 0; - send_steno_chord(); - } + if (IS_PRESSED(record->event)) { + n_pressed_keys++; + switch (mode) { +#ifdef STENO_ENABLE_BOLT + case STENO_MODE_BOLT: + add_bolt_key_to_chord(keycode - QK_STENO); + break; +#endif // STENO_ENABLE_BOLT +#ifdef STENO_ENABLE_GEMINI + case STENO_MODE_GEMINI: + add_gemini_key_to_chord(keycode - QK_STENO); + break; +#endif // STENO_ENABLE_GEMINI + default: + return false; } + if (!postprocess_steno_user(keycode, record, mode, chord, n_pressed_keys)) { + return false; + } + } else { // is released + n_pressed_keys--; + if (!postprocess_steno_user(keycode, record, mode, chord, n_pressed_keys)) { + return false; + } + if (n_pressed_keys > 0) { + // User hasn't released all keys yet, + // so the chord cannot be sent + return false; + } + n_pressed_keys = 0; + if (!send_steno_chord_user(mode, chord)) { + steno_clear_chord(); + return false; + } + switch (mode) { +#if defined(STENO_ENABLE_BOLT) && defined(VIRTSER_ENABLE) + case STENO_MODE_BOLT: + send_steno_chord_bolt(); + break; +#endif // STENO_ENABLE_BOLT && VIRTSER_ENABLE +#if defined(STENO_ENABLE_GEMINI) && defined(VIRTSER_ENABLE) + case STENO_MODE_GEMINI: + send_steno_chord_gemini(); + break; +#endif // STENO_ENABLE_GEMINI && VIRTSER_ENABLE + default: + break; + } + steno_clear_chord(); } - return false; + break; } - return true; + return false; } diff --git a/quantum/process_keycode/process_steno.h b/quantum/process_keycode/process_steno.h index d11fd40af0..68d6097b9b 100644 --- a/quantum/process_keycode/process_steno.h +++ b/quantum/process_keycode/process_steno.h @@ -18,10 +18,22 @@ #include "quantum.h" -typedef enum { STENO_MODE_BOLT, STENO_MODE_GEMINI } steno_mode_t; +#define BOLT_STROKE_SIZE 4 +#define GEMINI_STROKE_SIZE 6 -bool process_steno(uint16_t keycode, keyrecord_t *record); -void steno_init(void); -void steno_set_mode(steno_mode_t mode); -uint8_t *steno_get_state(void); -uint8_t *steno_get_chord(void); +#ifdef STENO_ENABLE_GEMINI +# define MAX_STROKE_SIZE GEMINI_STROKE_SIZE +#else +# define MAX_STROKE_SIZE BOLT_STROKE_SIZE +#endif + +typedef enum { + STENO_MODE_GEMINI, + STENO_MODE_BOLT, +} steno_mode_t; + +bool process_steno(uint16_t keycode, keyrecord_t *record); +#ifdef STENO_ENABLE_ALL +void steno_init(void); +void steno_set_mode(steno_mode_t mode); +#endif // STENO_ENABLE_ALL -- cgit v1.2.3 From 9dc7b9d40cfa199875cdc9e2e05b15d3f463b415 Mon Sep 17 00:00:00 2001 From: "FREEWING.JP" Date: Sat, 2 Jul 2022 20:48:26 +0900 Subject: Added Wait time to sending each Keys for Dynamic Macros function (#16800) Co-authored-by: Joel Challis --- quantum/process_keycode/process_dynamic_macro.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'quantum/process_keycode') diff --git a/quantum/process_keycode/process_dynamic_macro.c b/quantum/process_keycode/process_dynamic_macro.c index a1ada2d5a2..a7555fdd40 100644 --- a/quantum/process_keycode/process_dynamic_macro.c +++ b/quantum/process_keycode/process_dynamic_macro.c @@ -86,6 +86,9 @@ void dynamic_macro_play(keyrecord_t *macro_buffer, keyrecord_t *macro_end, int8_ while (macro_buffer != macro_end) { process_record(macro_buffer); macro_buffer += direction; +#ifdef DYNAMIC_MACRO_DELAY + wait_ms(DYNAMIC_MACRO_DELAY); +#endif } clear_keyboard(); -- cgit v1.2.3 From 8b78fac4511cd006b7be92e5789d29de6118fad3 Mon Sep 17 00:00:00 2001 From: torkel104 Date: Sat, 2 Jul 2022 14:03:40 +0200 Subject: Fix keys being discarded after using the leader key (#17287) --- quantum/process_keycode/process_leader.c | 1 + 1 file changed, 1 insertion(+) (limited to 'quantum/process_keycode') diff --git a/quantum/process_keycode/process_leader.c b/quantum/process_keycode/process_leader.c index c2fd02e5c7..ae00b3227a 100644 --- a/quantum/process_keycode/process_leader.c +++ b/quantum/process_keycode/process_leader.c @@ -64,6 +64,7 @@ bool process_leader(uint16_t keycode, keyrecord_t *record) { } else { leading = false; leader_end(); + return true; } # ifdef LEADER_PER_KEY_TIMING leader_time = timer_read(); -- cgit v1.2.3 From 871eeae4eab476cb64b40e0636ec19deeea79525 Mon Sep 17 00:00:00 2001 From: Osamu Aoki Date: Sat, 2 Jul 2022 21:08:48 +0900 Subject: PoC: Swap Escape and Caps (#16336) --- quantum/process_keycode/process_magic.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'quantum/process_keycode') diff --git a/quantum/process_keycode/process_magic.c b/quantum/process_keycode/process_magic.c index 10161adda3..ae60f29bf5 100644 --- a/quantum/process_keycode/process_magic.c +++ b/quantum/process_keycode/process_magic.c @@ -45,12 +45,16 @@ bool process_magic(uint16_t keycode, keyrecord_t *record) { case MAGIC_SWAP_LCTL_LGUI ... MAGIC_EE_HANDS_RIGHT: case MAGIC_TOGGLE_GUI: case MAGIC_TOGGLE_CONTROL_CAPSLOCK: + case MAGIC_SWAP_ESCAPE_CAPSLOCK ... MAGIC_TOGGLE_ESCAPE_CAPSLOCK: /* keymap config */ keymap_config.raw = eeconfig_read_keymap(); switch (keycode) { case MAGIC_SWAP_CONTROL_CAPSLOCK: keymap_config.swap_control_capslock = true; break; + case MAGIC_SWAP_ESCAPE_CAPSLOCK: + keymap_config.swap_escape_capslock = true; + break; case MAGIC_CAPSLOCK_TO_CONTROL: keymap_config.capslock_to_control = true; break; @@ -94,6 +98,9 @@ bool process_magic(uint16_t keycode, keyrecord_t *record) { case MAGIC_UNSWAP_CONTROL_CAPSLOCK: keymap_config.swap_control_capslock = false; break; + case MAGIC_UNSWAP_ESCAPE_CAPSLOCK: + keymap_config.swap_escape_capslock = false; + break; case MAGIC_UNCAPSLOCK_TO_CONTROL: keymap_config.capslock_to_control = false; break; @@ -172,6 +179,9 @@ bool process_magic(uint16_t keycode, keyrecord_t *record) { case MAGIC_TOGGLE_CONTROL_CAPSLOCK: keymap_config.swap_control_capslock = !keymap_config.swap_control_capslock; break; + case MAGIC_TOGGLE_ESCAPE_CAPSLOCK: + keymap_config.swap_escape_capslock = !keymap_config.swap_escape_capslock; + break; } eeconfig_update_keymap(keymap_config.raw); -- cgit v1.2.3 From 3ecb0a80af9e4ce4194a34032642933641730706 Mon Sep 17 00:00:00 2001 From: Ryan Date: Sat, 2 Jul 2022 22:10:08 +1000 Subject: Feature-ify Send String (#17275) --- quantum/process_keycode/process_auto_shift.c | 2 ++ quantum/process_keycode/process_dynamic_tapping_term.c | 2 ++ 2 files changed, 4 insertions(+) (limited to 'quantum/process_keycode') diff --git a/quantum/process_keycode/process_auto_shift.c b/quantum/process_keycode/process_auto_shift.c index e6a7c01f2a..8cb45bc0ae 100644 --- a/quantum/process_keycode/process_auto_shift.c +++ b/quantum/process_keycode/process_auto_shift.c @@ -325,11 +325,13 @@ void autoshift_disable(void) { # ifndef AUTO_SHIFT_NO_SETUP void autoshift_timer_report(void) { +# ifdef SEND_STRING_ENABLE char display[8]; snprintf(display, 8, "\n%d\n", autoshift_timeout); send_string((const char *)display); +# endif } # endif diff --git a/quantum/process_keycode/process_dynamic_tapping_term.c b/quantum/process_keycode/process_dynamic_tapping_term.c index bdc5529e33..b682f34da6 100644 --- a/quantum/process_keycode/process_dynamic_tapping_term.c +++ b/quantum/process_keycode/process_dynamic_tapping_term.c @@ -22,12 +22,14 @@ #endif static void tapping_term_report(void) { +#ifdef SEND_STRING_ENABLE const char *tapping_term_str = get_u16_str(g_tapping_term, ' '); // Skip padding spaces while (*tapping_term_str == ' ') { tapping_term_str++; } send_string(tapping_term_str); +#endif } bool process_dynamic_tapping_term(uint16_t keycode, keyrecord_t *record) { -- cgit v1.2.3 From 0365f640af0a732366f1c71e86f6534629225161 Mon Sep 17 00:00:00 2001 From: CoffeeIsLife <36961653+CoffeeIsLife87@users.noreply.github.com> Date: Sat, 2 Jul 2022 07:49:49 -0500 Subject: Allow for RGB actions to take place on Keydown instead of Keyup (#16886) * Allow for switch on keydown * add docs --- quantum/process_keycode/process_rgb.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'quantum/process_keycode') diff --git a/quantum/process_keycode/process_rgb.c b/quantum/process_keycode/process_rgb.c index c805bd615d..dae129786e 100644 --- a/quantum/process_keycode/process_rgb.c +++ b/quantum/process_keycode/process_rgb.c @@ -52,7 +52,11 @@ static void __attribute__((noinline, unused)) handleKeycodeRGBMode(const uint8_t */ bool process_rgb(const uint16_t keycode, const keyrecord_t *record) { // need to trigger on key-up for edge-case issue +#ifndef RGB_TRIGGER_ON_KEYDOWN if (!record->event.pressed) { +#else + if (record->event.pressed) { +#endif #if (defined(RGBLIGHT_ENABLE) && !defined(RGBLIGHT_DISABLE_KEYCODES)) || (defined(RGB_MATRIX_ENABLE) && !defined(RGB_MATRIX_DISABLE_KEYCODES)) uint8_t shifted = get_mods() & MOD_MASK_SHIFT; #endif -- cgit v1.2.3 From 95c1cc425e596ddcfff7bebd678635fea7ffbcdc Mon Sep 17 00:00:00 2001 From: precondition <57645186+precondition@users.noreply.github.com> Date: Fri, 29 Jul 2022 06:51:01 +0200 Subject: =?UTF-8?q?Rename=20postprocess=5Fsteno=5Fuser=20=E2=86=92=20post?= =?UTF-8?q?=5Fprocess=5Fsteno=5Fuser=20(#17823)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- quantum/process_keycode/process_steno.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'quantum/process_keycode') diff --git a/quantum/process_keycode/process_steno.c b/quantum/process_keycode/process_steno.c index 20b8b9db4b..30a0d4056f 100644 --- a/quantum/process_keycode/process_steno.c +++ b/quantum/process_keycode/process_steno.c @@ -148,7 +148,7 @@ __attribute__((weak)) bool send_steno_chord_user(steno_mode_t mode, uint8_t chor return true; } -__attribute__((weak)) bool postprocess_steno_user(uint16_t keycode, keyrecord_t *record, steno_mode_t mode, uint8_t chord[MAX_STROKE_SIZE], int8_t n_pressed_keys) { +__attribute__((weak)) bool post_process_steno_user(uint16_t keycode, keyrecord_t *record, steno_mode_t mode, uint8_t chord[MAX_STROKE_SIZE], int8_t n_pressed_keys) { return true; } @@ -209,12 +209,12 @@ bool process_steno(uint16_t keycode, keyrecord_t *record) { default: return false; } - if (!postprocess_steno_user(keycode, record, mode, chord, n_pressed_keys)) { + if (!post_process_steno_user(keycode, record, mode, chord, n_pressed_keys)) { return false; } } else { // is released n_pressed_keys--; - if (!postprocess_steno_user(keycode, record, mode, chord, n_pressed_keys)) { + if (!post_process_steno_user(keycode, record, mode, chord, n_pressed_keys)) { return false; } if (n_pressed_keys > 0) { -- cgit v1.2.3 From d93dfbff3cdec41d9a6a226bcddeec0270b20aca Mon Sep 17 00:00:00 2001 From: Drashna Jaelre Date: Fri, 29 Jul 2022 17:32:06 -0700 Subject: Fix check when compiling HID Joystick with digital driver (#17844) --- quantum/process_keycode/process_joystick.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'quantum/process_keycode') diff --git a/quantum/process_keycode/process_joystick.c b/quantum/process_keycode/process_joystick.c index 8c3e71616f..e867606074 100644 --- a/quantum/process_keycode/process_joystick.c +++ b/quantum/process_keycode/process_joystick.c @@ -106,7 +106,7 @@ bool process_joystick_analogread_quantum() { wait_us(10); -# if defined(__AVR__) || defined(PROTOCOL_CHIBIOS) +# if defined(ANALOG_JOYSTICK_ENABLE) && (defined(__AVR__) || defined(PROTOCOL_CHIBIOS)) int16_t axis_val = analogReadPin(joystick_axes[axis_index].input_pin); # else // default to resting position -- cgit v1.2.3 From 6a0d90f81a9b9938591b32f9321530e68e5cea0b Mon Sep 17 00:00:00 2001 From: Pascal Getreuer <50221757+getreuer@users.noreply.github.com> Date: Sat, 13 Aug 2022 06:48:51 -0700 Subject: Fix Caps Word capitalization when used with Combos + Auto Shift. (#17549) --- quantum/process_keycode/process_auto_shift.c | 7 ++++++- quantum/process_keycode/process_caps_word.c | 4 ++++ quantum/process_keycode/process_combo.c | 9 +++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) (limited to 'quantum/process_keycode') diff --git a/quantum/process_keycode/process_auto_shift.c b/quantum/process_keycode/process_auto_shift.c index 8cb45bc0ae..3ff188ba7e 100644 --- a/quantum/process_keycode/process_auto_shift.c +++ b/quantum/process_keycode/process_auto_shift.c @@ -123,7 +123,12 @@ bool get_autoshift_shift_state(uint16_t keycode) { /** \brief Restores the shift key if it was cancelled by Auto Shift */ static void autoshift_flush_shift(void) { autoshift_flags.holding_shift = false; - del_weak_mods(MOD_BIT(KC_LSFT)); +# ifdef CAPS_WORD_ENABLE + if (!is_caps_word_on()) +# endif // CAPS_WORD_ENABLE + { + del_weak_mods(MOD_BIT(KC_LSFT)); + } if (autoshift_flags.cancelling_lshift) { autoshift_flags.cancelling_lshift = false; add_mods(MOD_BIT(KC_LSFT)); diff --git a/quantum/process_keycode/process_caps_word.c b/quantum/process_keycode/process_caps_word.c index ffd509a914..bc4369846c 100644 --- a/quantum/process_keycode/process_caps_word.c +++ b/quantum/process_keycode/process_caps_word.c @@ -131,7 +131,11 @@ bool process_caps_word(uint16_t keycode, keyrecord_t* record) { #endif // SWAP_HANDS_ENABLE } +#ifdef AUTO_SHIFT_ENABLE + del_weak_mods(get_autoshift_state() ? ~MOD_BIT(KC_LSFT) : 0xff); +#else clear_weak_mods(); +#endif // AUTO_SHIFT_ENABLE if (caps_word_press_user(keycode)) { send_keyboard_report(); return true; diff --git a/quantum/process_keycode/process_combo.c b/quantum/process_keycode/process_combo.c index d5a649adb3..e5135e5a64 100644 --- a/quantum/process_keycode/process_combo.c +++ b/quantum/process_keycode/process_combo.c @@ -227,7 +227,16 @@ static inline void dump_key_buffer(void) { #endif } record->event.time = 0; + +#if defined(CAPS_WORD_ENABLE) && defined(AUTO_SHIFT_ENABLE) + // Edge case: preserve the weak Left Shift mod if both Caps Word and + // Auto Shift are on. Caps Word capitalizes by setting the weak Left + // Shift mod during the press event, but Auto Shift doesn't send the + // key until it receives the release event. + del_weak_mods((is_caps_word_on() && get_autoshift_state()) ? ~MOD_BIT(KC_LSFT) : 0xff); +#else clear_weak_mods(); +#endif // defined(CAPS_WORD_ENABLE) && defined(AUTO_SHIFT_ENABLE) #if TAP_CODE_DELAY > 0 // only delay once and for a non-tapping key -- cgit v1.2.3 From 6fc7c03e9581ddde68b32f2f76f8053628da6465 Mon Sep 17 00:00:00 2001 From: Chewxy Date: Mon, 15 Aug 2022 05:24:52 +1000 Subject: Added emacs as an "operating system" for input mode. (#16949) --- quantum/process_keycode/process_unicode_common.c | 40 ++++++++++++++++++++---- quantum/process_keycode/process_unicode_common.h | 1 + 2 files changed, 35 insertions(+), 6 deletions(-) (limited to 'quantum/process_keycode') diff --git a/quantum/process_keycode/process_unicode_common.c b/quantum/process_keycode/process_unicode_common.c index 6cb2ba8dbb..8de31c055c 100644 --- a/quantum/process_keycode/process_unicode_common.c +++ b/quantum/process_keycode/process_unicode_common.c @@ -117,6 +117,12 @@ __attribute__((weak)) void unicode_input_start(void) { tap_code(UNICODE_KEY_WINC); tap_code(KC_U); break; + case UC_EMACS: + // The usual way to type unicode in emacs is C-x-8 then the unicode number in hex + tap_code16(LCTL(KC_X)); + tap_code16(KC_8); + tap_code16(KC_ENTER); + break; } wait_ms(UNICODE_TYPE_DELAY); @@ -142,6 +148,9 @@ __attribute__((weak)) void unicode_input_finish(void) { case UC_WINC: tap_code(KC_ENTER); break; + case UC_EMACS: + tap_code16(KC_ENTER); + break; } set_mods(unicode_saved_mods); // Reregister previously set mods @@ -167,6 +176,9 @@ __attribute__((weak)) void unicode_input_cancel(void) { tap_code(KC_NUM_LOCK); } break; + case UC_EMACS: + tap_code16(LCTL(KC_G)); // C-g cancels + break; } set_mods(unicode_saved_mods); // Reregister previously set mods @@ -299,14 +311,30 @@ bool process_unicode_common(uint16_t keycode, keyrecord_t *record) { cycle_unicode_input_mode(shifted ? +1 : -1); audio_helper(); break; - - case UNICODE_MODE_MAC ... UNICODE_MODE_WINC: { - // Keycodes and input modes follow the same ordering - uint8_t delta = keycode - UNICODE_MODE_MAC; - set_unicode_input_mode(UC_MAC + delta); + case UNICODE_MODE_MAC: + set_unicode_input_mode(UC_MAC); + audio_helper(); + break; + case UNICODE_MODE_LNX: + set_unicode_input_mode(UC_LNX); + audio_helper(); + break; + case UNICODE_MODE_WIN: + set_unicode_input_mode(UC_WIN); + audio_helper(); + break; + case UNICODE_MODE_BSD: + set_unicode_input_mode(UC_BSD); + audio_helper(); + break; + case UNICODE_MODE_WINC: + set_unicode_input_mode(UC_WINC); + audio_helper(); + break; + case UNICODE_MODE_EMACS: + set_unicode_input_mode(UC_EMACS); audio_helper(); break; - } } } diff --git a/quantum/process_keycode/process_unicode_common.h b/quantum/process_keycode/process_unicode_common.h index 8a4494c939..15e798dbb3 100644 --- a/quantum/process_keycode/process_unicode_common.h +++ b/quantum/process_keycode/process_unicode_common.h @@ -64,6 +64,7 @@ enum unicode_input_modes { UC_WIN, // Windows using EnableHexNumpad UC_BSD, // BSD (not implemented) UC_WINC, // Windows using WinCompose (https://github.com/samhocevar/wincompose) + UC_EMACS, // Emacs is an operating system in search of a good text editor UC__COUNT // Number of available input modes (always leave at the end) }; -- cgit v1.2.3 From 95c43a275984fb57cdf6e5afd02190700e0205bc Mon Sep 17 00:00:00 2001 From: Pascal Getreuer <50221757+getreuer@users.noreply.github.com> Date: Sun, 14 Aug 2022 12:25:32 -0700 Subject: Fix Caps Word to treat mod-taps more consistently. (#17463) * Fix Caps Word to treat mod-taps more consistently. Previously, holding any mod-tap key while Caps Word is active stops Caps Word, and this happens regardless of `caps_word_press_user()`. Yet for regular mod keys, AltGr (KC_RALT) is ignored, Shift keys are passed to `caps_word_press_user()` to determine whether to continue, and similarly, a key `RSFT(KC_RALT)` representing Right Shift + Alt is passed to `caps_word_press_user()` to determine whether to continue. This commit makes held mod-tap keys consistent with regular mod keys: * Holding a `RALT_T` mod-tap is ignored. * When holding a shift mod-tap key, `KC_LSFT` or `KC_RSFT` is passed to `caps_word_press_user()` to determine whether to continue. * When holding a Right Shift + Alt (`RSA_T`) mod-tap, `RSFT(KC_RALT)` is passed to `caps_word_press_user()`. Particularly, with this fix a user may choose to continue Caps Word when a shift mod-tap key is held by adding `KC_LSFT` and `KC_RSFT` cases in `caps_word_press_user()`. For instance as ``` bool caps_word_press_user(uint16_t keycode) { switch (keycode) { // Keycodes that continue Caps Word, with shift applied. case KC_A ... KC_Z: case KC_MINS: add_weak_mods(MOD_BIT(KC_LSFT)); // Apply shift to the next key. return true; // Keycodes that continue Caps Word, without shifting. case KC_1 ... KC_0: case KC_BSPC: case KC_DEL: case KC_UNDS: case KC_LSFT: // <<< Added here. case KC_RSFT: return true; default: return false; // Deactivate Caps Word. } } ``` * Fix Caps Word to treat mod-taps more consistently. Previously, holding any mod-tap key while Caps Word is active stops Caps Word, and this happens regardless of `caps_word_press_user()`. Yet for regular mod keys, AltGr (KC_RALT) is ignored, Shift keys are passed to `caps_word_press_user()` to determine whether to continue, and similarly, a key `RSFT(KC_RALT)` representing Right Shift + Alt is passed to `caps_word_press_user()` to determine whether to continue. This commit makes held mod-tap keys consistent with regular mod keys: * Holding a `RALT_T` mod-tap is ignored. * When holding a shift mod-tap key, `KC_LSFT` or `KC_RSFT` is passed to `caps_word_press_user()` to determine whether to continue. * When holding a Right Shift + Alt (`RSA_T`) mod-tap, `RSFT(KC_RALT)` is passed to `caps_word_press_user()`. Particularly, with this fix a user may choose to continue Caps Word when a shift mod-tap key is held by adding `KC_LSFT` and `KC_RSFT` cases in `caps_word_press_user()`. For instance as ``` bool caps_word_press_user(uint16_t keycode) { switch (keycode) { // Keycodes that continue Caps Word, with shift applied. case KC_A ... KC_Z: case KC_MINS: add_weak_mods(MOD_BIT(KC_LSFT)); // Apply shift to the next key. return true; // Keycodes that continue Caps Word, without shifting. case KC_1 ... KC_0: case KC_BSPC: case KC_DEL: case KC_UNDS: case KC_LSFT: // <<< Added here. case KC_RSFT: return true; default: return false; // Deactivate Caps Word. } } ``` * Update quantum/process_keycode/process_caps_word.c Co-authored-by: Joel Challis --- quantum/process_keycode/process_caps_word.c | 32 +++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) (limited to 'quantum/process_keycode') diff --git a/quantum/process_keycode/process_caps_word.c b/quantum/process_keycode/process_caps_word.c index bc4369846c..1b9583196d 100644 --- a/quantum/process_keycode/process_caps_word.c +++ b/quantum/process_keycode/process_caps_word.c @@ -101,14 +101,34 @@ bool process_caps_word(uint16_t keycode, keyrecord_t* record) { return true; #ifndef NO_ACTION_TAPPING + // Corresponding to mod keys above, a held mod-tap is handled as: + // * For shift mods, pass KC_LSFT or KC_RSFT to + // caps_word_press_user() to determine whether to continue. + // * For Shift + AltGr (MOD_RSFT | MOD_RALT), pass RSFT(KC_RALT). + // * AltGr (MOD_RALT) is ignored. + // * Otherwise stop Caps Word. case QK_MOD_TAP ... QK_MOD_TAP_MAX: - if (record->tap.count == 0) { - // Deactivate if a mod becomes active through holding - // a mod-tap key. - caps_word_off(); - return true; + if (record->tap.count == 0) { // Mod-tap key is held. + const uint8_t mods = (keycode >> 8) & 0x1f; + switch (mods) { + case MOD_LSFT: + keycode = KC_LSFT; + break; + case MOD_RSFT: + keycode = KC_RSFT; + break; + case MOD_RSFT | MOD_RALT: + keycode = RSFT(KC_RALT); + break; + case MOD_RALT: + return true; + default: + caps_word_off(); + return true; + } + } else { + keycode &= 0xff; } - keycode &= 0xff; break; # ifndef NO_ACTION_LAYER -- cgit v1.2.3 From 0306bdf7fdb2f48f833c8887fd13e91b17d7c3c9 Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Tue, 6 Sep 2022 05:25:32 +0100 Subject: Remove use of legacy keycode (#18281) --- quantum/process_keycode/process_auto_shift.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'quantum/process_keycode') diff --git a/quantum/process_keycode/process_auto_shift.h b/quantum/process_keycode/process_auto_shift.h index ac6a143746..86adb04985 100644 --- a/quantum/process_keycode/process_auto_shift.h +++ b/quantum/process_keycode/process_auto_shift.h @@ -32,7 +32,7 @@ #define AUTO_SHIFT_SPECIAL \ KC_TAB: \ case KC_MINUS ... KC_SLASH: \ - case KC_NONUS_BSLASH + case KC_NONUS_BACKSLASH // clang-format on bool process_auto_shift(uint16_t keycode, keyrecord_t *record); -- cgit v1.2.3