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 -- quantum/quantum.c | 3 - quantum/quantum.h | 6 - quantum/quantum_keycodes.h | 6 +- quantum/quantum_keycodes_legacy.h | 3 + 7 files changed, 6 insertions(+), 388 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') 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 diff --git a/quantum/quantum.c b/quantum/quantum.c index ac3e2d90b4..4efcc6bd8f 100644 --- a/quantum/quantum.c +++ b/quantum/quantum.c @@ -304,9 +304,6 @@ bool process_record_quantum(keyrecord_t *record) { #ifdef DYNAMIC_TAPPING_TERM_ENABLE process_dynamic_tapping_term(keycode, record) && #endif -#ifdef TERMINAL_ENABLE - process_terminal(keycode, record) && -#endif #ifdef CAPS_WORD_ENABLE process_caps_word(keycode, record) && #endif diff --git a/quantum/quantum.h b/quantum/quantum.h index 92e1af1c40..8a7a20c706 100644 --- a/quantum/quantum.h +++ b/quantum/quantum.h @@ -141,12 +141,6 @@ extern layer_state_t layer_state; # include "process_key_lock.h" #endif -#ifdef TERMINAL_ENABLE -# include "process_terminal.h" -#else -# include "process_terminal_nop.h" -#endif - #ifdef SPACE_CADET_ENABLE # include "process_space_cadet.h" #endif diff --git a/quantum/quantum_keycodes.h b/quantum/quantum_keycodes.h index 40355d799a..869826ce19 100644 --- a/quantum/quantum_keycodes.h +++ b/quantum/quantum_keycodes.h @@ -473,9 +473,9 @@ enum quantum_keycodes { // Lock Key KC_LOCK, // 5D2B - // Terminal - TERM_ON, // 5D2C - TERM_OFF, // 5D2D + // Unused slots + UNUSED_000, // 5D2C + UNUSED_001, // 5D2D // Sequencer SQ_ON, // 5D2E diff --git a/quantum/quantum_keycodes_legacy.h b/quantum/quantum_keycodes_legacy.h index ed9455ee74..51380d9c50 100644 --- a/quantum/quantum_keycodes_legacy.h +++ b/quantum/quantum_keycodes_legacy.h @@ -11,3 +11,6 @@ #define KC_GESC QK_GRAVE_ESCAPE #define EEP_RST QK_CLEAR_EEPROM + +#define TERM_ON _Static_assert(false, "The Terminal feature has been removed from QMK. Please remove use of TERM_ON/TERM_OFF from your keymap.") +#define TERM_OFF _Static_assert(false, "The Terminal feature has been removed from QMK.. Please remove use of TERM_ON/TERM_OFF from your keymap.") \ No newline at end of file -- cgit v1.2.3 From d44a950c107492b801d567a507e2213376e7f47c Mon Sep 17 00:00:00 2001 From: precondition <57645186+precondition@users.noreply.github.com> Date: Tue, 31 May 2022 07:55:33 +0200 Subject: Use TAP_HOLD_CAPS_DELAY for KC_LOCKING_CAPS_LOCK (#17099) --- quantum/action.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'quantum') diff --git a/quantum/action.c b/quantum/action.c index 4e81a5466f..83f6e2a970 100644 --- a/quantum/action.c +++ b/quantum/action.c @@ -844,7 +844,7 @@ __attribute__((weak)) void register_code(uint8_t code) { # endif add_key(KC_CAPS_LOCK); send_keyboard_report(); - wait_ms(100); + wait_ms(TAP_HOLD_CAPS_DELAY); del_key(KC_CAPS_LOCK); send_keyboard_report(); } -- cgit v1.2.3 From af02baae78a3c4a06a646bc338b35524b5e5e7f5 Mon Sep 17 00:00:00 2001 From: Dasky <32983009+daskygit@users.noreply.github.com> Date: Wed, 1 Jun 2022 00:10:05 +0100 Subject: Allow larger SPLIT_USB_TIMEOUT with default SPLIT_USB_TIMEOUT_POLL (#17272) * Switch SPLIT_USB_DETECT loop to uint16_t * Add assertion --- quantum/split_common/split_util.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'quantum') diff --git a/quantum/split_common/split_util.c b/quantum/split_common/split_util.c index 7d50adf758..8b11e51374 100644 --- a/quantum/split_common/split_util.c +++ b/quantum/split_common/split_util.c @@ -57,8 +57,9 @@ static uint8_t connection_errors = 0; volatile bool isLeftHand = true; #if defined(SPLIT_USB_DETECT) +_Static_assert((SPLIT_USB_TIMEOUT / SPLIT_USB_TIMEOUT_POLL) <= UINT16_MAX, "Please lower SPLIT_USB_TIMEOUT and/or increase SPLIT_USB_TIMEOUT_POLL."); static bool usbIsActive(void) { - for (uint8_t i = 0; i < (SPLIT_USB_TIMEOUT / SPLIT_USB_TIMEOUT_POLL); i++) { + for (uint16_t i = 0; i < (SPLIT_USB_TIMEOUT / SPLIT_USB_TIMEOUT_POLL); i++) { // This will return true if a USB connection has been established if (usb_connected_state()) { return true; -- cgit v1.2.3 From 08c556b78b9dc672a2aa2388bbd3fdf408e3ce98 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Sun, 5 Jun 2022 10:26:02 +1000 Subject: Add keymap wrappers for introspection into the keymap. (#17229) * Introspection handlers for keymaps. * Renaming. --- quantum/keymap.h | 2 ++ quantum/keymap_introspection.c | 25 +++++++++++++++++++++++++ quantum/keymap_introspection.h | 15 +++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 quantum/keymap_introspection.c create mode 100644 quantum/keymap_introspection.h (limited to 'quantum') diff --git a/quantum/keymap.h b/quantum/keymap.h index d64b271efb..081bc54ebe 100644 --- a/quantum/keymap.h +++ b/quantum/keymap.h @@ -55,3 +55,5 @@ extern const uint16_t keymaps[][MATRIX_ROWS][MATRIX_COLS]; // Ensure we have a forward declaration for the encoder map # include "encoder.h" #endif + +#include "keymap_introspection.h" diff --git a/quantum/keymap_introspection.c b/quantum/keymap_introspection.c new file mode 100644 index 0000000000..9628b41eef --- /dev/null +++ b/quantum/keymap_introspection.c @@ -0,0 +1,25 @@ +// Copyright 2022 Nick Brassel (@tzarc) +// SPDX-License-Identifier: GPL-2.0-or-later + +// Pull the actual keymap code so that we can inspect stuff from it +#include KEYMAP_C + +#include "keymap_introspection.h" + +#define NUM_KEYMAP_LAYERS ((uint8_t)(sizeof(keymaps) / ((MATRIX_ROWS) * (MATRIX_COLS) * sizeof(uint16_t)))) + +uint8_t keymap_layer_count(void) { + return NUM_KEYMAP_LAYERS; +} + +#if defined(ENCODER_ENABLE) && defined(ENCODER_MAP_ENABLE) + +# define NUM_ENCODERMAP_LAYERS ((uint8_t)(sizeof(encoder_map) / ((NUM_ENCODERS) * (2) * sizeof(uint16_t)))) + +uint8_t encodermap_layer_count(void) { + return NUM_ENCODERMAP_LAYERS; +} + +_Static_assert(NUM_KEYMAP_LAYERS == NUM_ENCODERMAP_LAYERS, "Number of encoder_map layers doesn't match the number of keymap layers"); + +#endif // defined(ENCODER_ENABLE) && defined(ENCODER_MAP_ENABLE) diff --git a/quantum/keymap_introspection.h b/quantum/keymap_introspection.h new file mode 100644 index 0000000000..23f6f2016f --- /dev/null +++ b/quantum/keymap_introspection.h @@ -0,0 +1,15 @@ +// Copyright 2022 Nick Brassel (@tzarc) +// SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + +#include + +// Get the number of layers defined in the keymap +uint8_t keymap_layer_count(void); + +#if defined(ENCODER_ENABLE) && defined(ENCODER_MAP_ENABLE) + +// Get the number of layers defined in the encoder map +uint8_t encodermap_layer_count(void); + +#endif // defined(ENCODER_ENABLE) && defined(ENCODER_MAP_ENABLE) -- 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 + quantum/quantum.c | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'quantum') 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: diff --git a/quantum/quantum.c b/quantum/quantum.c index ac3e2d90b4..33121f6b95 100644 --- a/quantum/quantum.c +++ b/quantum/quantum.c @@ -289,6 +289,9 @@ bool process_record_quantum(keyrecord_t *record) { #ifdef TAP_DANCE_ENABLE process_tap_dance(keycode, record) && #endif +#ifdef CAPS_WORD_ENABLE + process_caps_word(keycode, record) && +#endif #if defined(UNICODE_COMMON_ENABLE) process_unicode_common(keycode, record) && #endif @@ -307,9 +310,6 @@ bool process_record_quantum(keyrecord_t *record) { #ifdef TERMINAL_ENABLE process_terminal(keycode, record) && #endif -#ifdef CAPS_WORD_ENABLE - process_caps_word(keycode, record) && -#endif #ifdef SPACE_CADET_ENABLE process_space_cadet(keycode, record) && #endif -- cgit v1.2.3 From 85b3b98570262b97851b726a31819e8864ff1bb3 Mon Sep 17 00:00:00 2001 From: Dasky <32983009+daskygit@users.noreply.github.com> Date: Mon, 6 Jun 2022 01:33:32 +0100 Subject: Move SPLIT_HAND_PIN setup to split_pre_init (#17271) * Move SPLIT_HAND_PIN setup to split_pre_init * doppelganger should use old behaviour * Add comment for future Co-authored-by: Joel Challis Co-authored-by: Joel Challis --- quantum/split_common/split_util.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'quantum') diff --git a/quantum/split_common/split_util.c b/quantum/split_common/split_util.c index 8b11e51374..48b9cce6d4 100644 --- a/quantum/split_common/split_util.c +++ b/quantum/split_common/split_util.c @@ -94,7 +94,6 @@ static uint8_t peek_matrix_intersection(pin_t out_pin, pin_t in_pin) { __attribute__((weak)) bool is_keyboard_left(void) { #if defined(SPLIT_HAND_PIN) // Test pin SPLIT_HAND_PIN for High/Low, if low it's right hand - setPinInput(SPLIT_HAND_PIN); # ifdef SPLIT_HAND_PIN_LOW_IS_LEFT return !readPin(SPLIT_HAND_PIN); # else @@ -133,6 +132,10 @@ __attribute__((weak)) bool is_keyboard_master(void) { // this code runs before the keyboard is fully initialized void split_pre_init(void) { +#if defined(SPLIT_HAND_PIN) + setPinInput(SPLIT_HAND_PIN); + wait_us(100); +#endif isLeftHand = is_keyboard_left(); #if defined(RGBLIGHT_ENABLE) && defined(RGBLED_SPLIT) -- cgit v1.2.3 From 0ab51ee29d6e980a50b27f122a10d0f7de4b1aed Mon Sep 17 00:00:00 2001 From: Drashna Jaelre Date: Wed, 8 Jun 2022 18:39:16 -0700 Subject: Add support for large Mouse Reports (#16371) Co-authored-by: Sergey Vlasov Co-authored-by: Ryan --- quantum/pointing_device.c | 31 +++++++++++++++----- quantum/pointing_device.h | 10 +++++++ quantum/pointing_device_drivers.c | 62 +++++++++++++++++++++++---------------- 3 files changed, 71 insertions(+), 32 deletions(-) (limited to 'quantum') diff --git a/quantum/pointing_device.c b/quantum/pointing_device.c index a160647890..3c2e2bc09d 100644 --- a/quantum/pointing_device.c +++ b/quantum/pointing_device.c @@ -177,7 +177,8 @@ __attribute__((weak)) void pointing_device_send(void) { report_mouse_t pointing_device_adjust_by_defines(report_mouse_t mouse_report) { // Support rotation of the sensor data #if defined(POINTING_DEVICE_ROTATION_90) || defined(POINTING_DEVICE_ROTATION_180) || defined(POINTING_DEVICE_ROTATION_270) - int8_t x = mouse_report.x, y = mouse_report.y; + mouse_xy_report_t x = mouse_report.x; + mouse_xy_report_t y = mouse_report.y; # if defined(POINTING_DEVICE_ROTATION_90) mouse_report.x = y; mouse_report.y = -x; @@ -347,7 +348,7 @@ void pointing_device_set_cpi_on_side(bool left, uint16_t cpi) { * @param[in] int16_t value * @return int8_t clamped value */ -static inline int8_t pointing_device_movement_clamp(int16_t value) { +static inline int8_t pointing_device_hv_clamp(int16_t value) { if (value < INT8_MIN) { return INT8_MIN; } else if (value > INT8_MAX) { @@ -357,6 +358,21 @@ static inline int8_t pointing_device_movement_clamp(int16_t value) { } } +/** + * @brief clamps int16_t to int8_t + * + * @param[in] clamp_range_t value + * @return mouse_xy_report_t clamped value + */ +static inline mouse_xy_report_t pointing_device_xy_clamp(clamp_range_t value) { + if (value < XY_REPORT_MIN) { + return XY_REPORT_MIN; + } else if (value > XY_REPORT_MAX) { + return XY_REPORT_MAX; + } else { + return value; + } +} /** * @brief combines 2 mouse reports and returns 2 * @@ -369,10 +385,10 @@ static inline int8_t pointing_device_movement_clamp(int16_t value) { * @return combined report_mouse_t of left_report and right_report */ report_mouse_t pointing_device_combine_reports(report_mouse_t left_report, report_mouse_t right_report) { - left_report.x = pointing_device_movement_clamp((int16_t)left_report.x + right_report.x); - left_report.y = pointing_device_movement_clamp((int16_t)left_report.y + right_report.y); - left_report.h = pointing_device_movement_clamp((int16_t)left_report.h + right_report.h); - left_report.v = pointing_device_movement_clamp((int16_t)left_report.v + right_report.v); + left_report.x = pointing_device_xy_clamp((clamp_range_t)left_report.x + right_report.x); + left_report.y = pointing_device_xy_clamp((clamp_range_t)left_report.y + right_report.y); + left_report.h = pointing_device_hv_clamp((int16_t)left_report.h + right_report.h); + left_report.v = pointing_device_hv_clamp((int16_t)left_report.v + right_report.v); left_report.buttons |= right_report.buttons; return left_report; } @@ -390,7 +406,8 @@ report_mouse_t pointing_device_combine_reports(report_mouse_t left_report, repor report_mouse_t pointing_device_adjust_by_defines_right(report_mouse_t mouse_report) { // Support rotation of the sensor data # if defined(POINTING_DEVICE_ROTATION_90_RIGHT) || defined(POINTING_DEVICE_ROTATION_RIGHT) || defined(POINTING_DEVICE_ROTATION_RIGHT) - int8_t x = mouse_report.x, y = mouse_report.y; + mouse_xy_report_t x = mouse_report.x; + mouse_xy_report_t y = mouse_report.y; # if defined(POINTING_DEVICE_ROTATION_90_RIGHT) mouse_report.x = y; mouse_report.y = -x; diff --git a/quantum/pointing_device.h b/quantum/pointing_device.h index 5c0eaeaf34..1e5ef9590c 100644 --- a/quantum/pointing_device.h +++ b/quantum/pointing_device.h @@ -75,6 +75,16 @@ typedef enum { POINTING_DEVICE_BUTTON8, } pointing_device_buttons_t; +#ifdef MOUSE_EXTENDED_REPORT +# define XY_REPORT_MIN INT16_MIN +# define XY_REPORT_MAX INT16_MAX +typedef int32_t clamp_range_t; +#else +# define XY_REPORT_MIN INT8_MIN +# define XY_REPORT_MAX INT8_MAX +typedef int16_t clamp_range_t; +#endif + void pointing_device_init(void); void pointing_device_task(void); void pointing_device_send(void); diff --git a/quantum/pointing_device_drivers.c b/quantum/pointing_device_drivers.c index 56363c7ac6..41d7018c86 100644 --- a/quantum/pointing_device_drivers.c +++ b/quantum/pointing_device_drivers.c @@ -22,8 +22,8 @@ #include "timer.h" #include -// hid mouse reports cannot exceed -127 to 127, so constrain to that value -#define constrain_hid(amt) ((amt) < -127 ? -127 : ((amt) > 127 ? 127 : (amt))) +#define CONSTRAIN_HID(amt) ((amt) < INT8_MIN ? INT8_MIN : ((amt) > INT8_MAX ? INT8_MAX : (amt))) +#define CONSTRAIN_HID_XY(amt) ((amt) < XY_REPORT_MIN ? XY_REPORT_MIN : ((amt) > XY_REPORT_MAX ? XY_REPORT_MAX : (amt))) // get_report functions should probably be moved to their respective drivers. #if defined(POINTING_DEVICE_DRIVER_adns5050) @@ -35,8 +35,8 @@ report_mouse_t adns5050_get_report(report_mouse_t mouse_report) { if (debug_mouse) dprintf("Raw ] X: %d, Y: %d\n", data.dx, data.dy); # endif - mouse_report.x = data.dx; - mouse_report.y = data.dy; + mouse_report.x = (mouse_xy_report_t)data.dx; + mouse_report.y = (mouse_xy_report_t)data.dy; } return mouse_report; @@ -55,11 +55,8 @@ const pointing_device_driver_t pointing_device_driver = { report_mouse_t adns9800_get_report_driver(report_mouse_t mouse_report) { report_adns9800_t sensor_report = adns9800_get_report(); - int8_t clamped_x = constrain_hid(sensor_report.x); - int8_t clamped_y = constrain_hid(sensor_report.y); - - mouse_report.x = clamped_x; - mouse_report.y = clamped_y; + mouse_report.x = CONSTRAIN_HID_XY(sensor_report.x); + mouse_report.y = CONSTRAIN_HID_XY(sensor_report.y); return mouse_report; } @@ -107,16 +104,16 @@ const pointing_device_driver_t pointing_device_driver = { # endif report_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) { - pinnacle_data_t touchData = cirque_pinnacle_read_data(); - static uint16_t x = 0, y = 0, mouse_timer = 0; - int8_t report_x = 0, report_y = 0; - static bool is_z_down = false; + pinnacle_data_t touchData = cirque_pinnacle_read_data(); + static uint16_t x = 0, y = 0, mouse_timer = 0; + mouse_xy_report_t report_x = 0, report_y = 0; + static bool is_z_down = false; cirque_pinnacle_scale_data(&touchData, cirque_pinnacle_get_scale(), cirque_pinnacle_get_scale()); // Scale coordinates to arbitrary X, Y resolution if (x && y && touchData.xValue && touchData.yValue) { - report_x = (int8_t)(touchData.xValue - x); - report_y = (int8_t)(touchData.yValue - y); + report_x = (mouse_xy_report_t)(touchData.xValue - x); + report_y = (mouse_xy_report_t)(touchData.yValue - y); } x = touchData.xValue; y = touchData.yValue; @@ -157,11 +154,26 @@ const pointing_device_driver_t pointing_device_driver = { // clang-format on #elif defined(POINTING_DEVICE_DRIVER_pimoroni_trackball) + +mouse_xy_report_t pimoroni_trackball_adapt_values(clamp_range_t* offset) { + if (*offset > XY_REPORT_MAX) { + *offset -= XY_REPORT_MAX; + return (mouse_xy_report_t)XY_REPORT_MAX; + } else if (*offset < XY_REPORT_MIN) { + *offset += XY_REPORT_MAX; + return (mouse_xy_report_t)XY_REPORT_MIN; + } else { + mouse_xy_report_t temp_return = *offset; + *offset = 0; + return temp_return; + } +} + report_mouse_t pimoroni_trackball_get_report(report_mouse_t mouse_report) { - static uint16_t debounce = 0; - static uint8_t error_count = 0; - pimoroni_data_t pimoroni_data = {0}; - static int16_t x_offset = 0, y_offset = 0; + static uint16_t debounce = 0; + static uint8_t error_count = 0; + pimoroni_data_t pimoroni_data = {0}; + static clamp_range_t x_offset = 0, y_offset = 0; if (error_count < PIMORONI_TRACKBALL_ERROR_COUNT) { i2c_status_t status = read_pimoroni_trackball(&pimoroni_data); @@ -174,8 +186,8 @@ report_mouse_t pimoroni_trackball_get_report(report_mouse_t mouse_report) { if (!debounce) { x_offset += pimoroni_trackball_get_offsets(pimoroni_data.right, pimoroni_data.left, PIMORONI_TRACKBALL_SCALE); y_offset += pimoroni_trackball_get_offsets(pimoroni_data.down, pimoroni_data.up, PIMORONI_TRACKBALL_SCALE); - pimoroni_trackball_adapt_values(&mouse_report.x, &x_offset); - pimoroni_trackball_adapt_values(&mouse_report.y, &y_offset); + mouse_report.x = pimoroni_trackball_adapt_values(&x_offset); + mouse_report.y = pimoroni_trackball_adapt_values(&y_offset); } else { debounce--; } @@ -221,8 +233,8 @@ report_mouse_t pmw3360_get_report(report_mouse_t mouse_report) { # endif MotionStart = timer_read(); } - mouse_report.x = constrain_hid(data.dx); - mouse_report.y = constrain_hid(data.dy); + mouse_report.x = CONSTRAIN_HID_XY(data.dx); + mouse_report.y = CONSTRAIN_HID_XY(data.dy); } return mouse_report; @@ -259,8 +271,8 @@ report_mouse_t pmw3389_get_report(report_mouse_t mouse_report) { # endif MotionStart = timer_read(); } - mouse_report.x = constrain_hid(data.dx); - mouse_report.y = constrain_hid(data.dy); + mouse_report.x = CONSTRAIN_HID_XY(data.dx); + mouse_report.y = CONSTRAIN_HID_XY(data.dy); } return mouse_report; -- cgit v1.2.3 From 0fd08da806711112147cd6c3b9bdee0250f9f103 Mon Sep 17 00:00:00 2001 From: yiancar Date: Sat, 11 Jun 2022 01:20:01 +0100 Subject: Scale brigthness for VIA (#17352) Co-authored-by: yiancar --- quantum/via.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'quantum') diff --git a/quantum/via.c b/quantum/via.c index 320bd5546d..a37bd38da8 100644 --- a/quantum/via.c +++ b/quantum/via.c @@ -64,6 +64,7 @@ void via_qmk_rgblight_get_value(uint8_t *data); #endif #if defined(VIA_QMK_RGB_MATRIX_ENABLE) +# include void via_qmk_rgb_matrix_set_value(uint8_t *data); void via_qmk_rgb_matrix_get_value(uint8_t *data); void eeconfig_update_rgb_matrix(void); @@ -421,7 +422,7 @@ void via_qmk_backlight_get_value(uint8_t *data) { switch (*value_id) { case id_qmk_backlight_brightness: { // level / BACKLIGHT_LEVELS * 255 - value_data[0] = ((uint16_t)get_backlight_level()) * 255 / BACKLIGHT_LEVELS; + value_data[0] = ((uint16_t)get_backlight_level() * UINT8_MAX) / BACKLIGHT_LEVELS; break; } case id_qmk_backlight_effect: { @@ -441,7 +442,7 @@ void via_qmk_backlight_set_value(uint8_t *data) { switch (*value_id) { case id_qmk_backlight_brightness: { // level / 255 * BACKLIGHT_LEVELS - backlight_level_noeeprom(((uint16_t)value_data[0]) * BACKLIGHT_LEVELS / 255); + backlight_level_noeeprom(((uint16_t)value_data[0] * BACKLIGHT_LEVELS) / UINT8_MAX); break; } case id_qmk_backlight_effect: { @@ -466,7 +467,7 @@ void via_qmk_rgblight_get_value(uint8_t *data) { uint8_t *value_data = &(data[1]); switch (*value_id) { case id_qmk_rgblight_brightness: { - value_data[0] = rgblight_get_val(); + value_data[0] = ((uint16_t)rgblight_get_val() * UINT8_MAX) / RGBLIGHT_LIMIT_VAL; break; } case id_qmk_rgblight_effect: { @@ -490,7 +491,7 @@ void via_qmk_rgblight_set_value(uint8_t *data) { uint8_t *value_data = &(data[1]); switch (*value_id) { case id_qmk_rgblight_brightness: { - rgblight_sethsv_noeeprom(rgblight_get_hue(), rgblight_get_sat(), value_data[0]); + rgblight_sethsv_noeeprom(rgblight_get_hue(), rgblight_get_sat(), ((uint16_t)value_data[0] * RGBLIGHT_LIMIT_VAL) / UINT8_MAX; break; } case id_qmk_rgblight_effect: { @@ -557,7 +558,7 @@ void via_qmk_rgb_matrix_get_value(uint8_t *data) { uint8_t *value_data = &(data[1]); switch (*value_id) { case id_qmk_rgblight_brightness: - value_data[0] = rgb_matrix_get_val(); + value_data[0] = ((uint16_t)rgb_matrix_get_val() * UINT8_MAX) / RGB_MATRIX_MAXIMUM_BRIGHTNESS; break; case id_qmk_rgblight_effect: value_data[0] = rgb_matrix_get_mode(); @@ -577,7 +578,7 @@ void via_qmk_rgb_matrix_set_value(uint8_t *data) { uint8_t *value_data = &(data[1]); switch (*value_id) { case id_qmk_rgblight_brightness: - rgb_matrix_sethsv_noeeprom(rgb_matrix_get_hue(), rgb_matrix_get_sat(), value_data[0]); + rgb_matrix_sethsv_noeeprom(rgb_matrix_get_hue(), rgb_matrix_get_sat(), scale8(value_data[0], RGB_MATRIX_MAXIMUM_BRIGHTNESS)); break; case id_qmk_rgblight_effect: rgb_matrix_mode_noeeprom(value_data[0]); -- cgit v1.2.3 From 568924c76cb29620e2cb7d2f2f5b1beddf8cf623 Mon Sep 17 00:00:00 2001 From: Dasky <32983009+daskygit@users.noreply.github.com> Date: Sat, 11 Jun 2022 03:07:58 +0100 Subject: Add missing bracket for VIA brightness scaling (#17354) --- quantum/via.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'quantum') diff --git a/quantum/via.c b/quantum/via.c index a37bd38da8..1ded1f5fe8 100644 --- a/quantum/via.c +++ b/quantum/via.c @@ -491,7 +491,7 @@ void via_qmk_rgblight_set_value(uint8_t *data) { uint8_t *value_data = &(data[1]); switch (*value_id) { case id_qmk_rgblight_brightness: { - rgblight_sethsv_noeeprom(rgblight_get_hue(), rgblight_get_sat(), ((uint16_t)value_data[0] * RGBLIGHT_LIMIT_VAL) / UINT8_MAX; + rgblight_sethsv_noeeprom(rgblight_get_hue(), rgblight_get_sat(), ((uint16_t)value_data[0] * RGBLIGHT_LIMIT_VAL) / UINT8_MAX); break; } case id_qmk_rgblight_effect: { -- cgit v1.2.3 From 80034a847fdfe91cf0d6ef9399d34d90c15d8a54 Mon Sep 17 00:00:00 2001 From: Drashna Jaelre Date: Fri, 10 Jun 2022 22:01:46 -0700 Subject: Ensure that rgb+via compiles in all cases (#17355) --- quantum/via.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'quantum') diff --git a/quantum/via.c b/quantum/via.c index 1ded1f5fe8..d2ef0862cc 100644 --- a/quantum/via.c +++ b/quantum/via.c @@ -461,6 +461,9 @@ void via_qmk_backlight_set_value(uint8_t *data) { #endif // #if defined(VIA_QMK_BACKLIGHT_ENABLE) #if defined(VIA_QMK_RGBLIGHT_ENABLE) +# ifndef RGBLIGHT_LIMIT_VAL +# define RGBLIGHT_LIMIT_VAL 255 +# endif void via_qmk_rgblight_get_value(uint8_t *data) { uint8_t *value_id = &(data[0]); @@ -518,6 +521,11 @@ void via_qmk_rgblight_set_value(uint8_t *data) { #if defined(VIA_QMK_RGB_MATRIX_ENABLE) +# if !defined(RGB_MATRIX_MAXIMUM_BRIGHTNESS) || RGB_MATRIX_MAXIMUM_BRIGHTNESS > UINT8_MAX +# undef RGB_MATRIX_MAXIMUM_BRIGHTNESS +# define RGB_MATRIX_MAXIMUM_BRIGHTNESS UINT8_MAX +# endif + // VIA supports only 4 discrete values for effect speed; map these to some // useful speed values for RGB Matrix. enum speed_values { -- 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') 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 0da6562c4df570729889690e21061229c5648b73 Mon Sep 17 00:00:00 2001 From: Drashna Jaelre Date: Sat, 18 Jun 2022 14:37:51 -0700 Subject: Make default layer size 16-bit (#15286) Co-authored-by: James Young <18669334+noroadsleft@users.noreply.github.com> --- quantum/action_layer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'quantum') diff --git a/quantum/action_layer.h b/quantum/action_layer.h index b87d096eed..bd1085a70f 100644 --- a/quantum/action_layer.h +++ b/quantum/action_layer.h @@ -41,7 +41,7 @@ along with this program. If not, see . #endif #if !defined(LAYER_STATE_8BIT) && !defined(LAYER_STATE_16BIT) && !defined(LAYER_STATE_32BIT) -# define LAYER_STATE_32BIT +# define LAYER_STATE_16BIT #endif #if defined(LAYER_STATE_8BIT) -- cgit v1.2.3 From c725b6bf89878aebe9c5ffa2f6c05abd9ea98117 Mon Sep 17 00:00:00 2001 From: Albert Y <76888457+filterpaper@users.noreply.github.com> Date: Mon, 20 Jun 2022 02:15:55 +0800 Subject: [Core] Mouse key kinetic mode fix (#17176) Co-authored-by: Drashna Jaelre --- quantum/mousekey.c | 27 ++++++++++++++++----------- quantum/mousekey.h | 4 ++-- 2 files changed, 18 insertions(+), 13 deletions(-) (limited to 'quantum') diff --git a/quantum/mousekey.c b/quantum/mousekey.c index 64d0e66682..81e887d529 100644 --- a/quantum/mousekey.c +++ b/quantum/mousekey.c @@ -66,11 +66,18 @@ uint8_t mk_time_to_max = MOUSEKEY_TIME_TO_MAX; /* milliseconds between the initial key press and first repeated motion event (0-2550) */ uint8_t mk_wheel_delay = MOUSEKEY_WHEEL_DELAY / 10; /* milliseconds between repeated motion events (0-255) */ -uint8_t mk_wheel_interval = MOUSEKEY_WHEEL_INTERVAL; +# ifdef MK_KINETIC_SPEED +float mk_wheel_interval = 1000.0f / MOUSEKEY_WHEEL_INITIAL_MOVEMENTS; +# else +uint8_t mk_wheel_interval = MOUSEKEY_WHEEL_INTERVAL; +# endif uint8_t mk_wheel_max_speed = MOUSEKEY_WHEEL_MAX_SPEED; uint8_t mk_wheel_time_to_max = MOUSEKEY_WHEEL_TIME_TO_MAX; # ifndef MK_COMBINED +# ifndef MK_KINETIC_SPEED + +/* Default accelerated mode */ static uint8_t move_unit(void) { uint16_t unit; @@ -108,8 +115,7 @@ static uint8_t wheel_unit(void) { return (unit > MOUSEKEY_WHEEL_MAX ? MOUSEKEY_WHEEL_MAX : (unit == 0 ? 1 : unit)); } -# else /* #ifndef MK_COMBINED */ -# ifdef MK_KINETIC_SPEED +# else /* #ifndef MK_KINETIC_SPEED */ /* * Kinetic movement acceleration algorithm @@ -147,27 +153,27 @@ static uint8_t move_unit(void) { return speed > MOUSEKEY_MOVE_MAX ? MOUSEKEY_MOVE_MAX : speed; } -float mk_wheel_interval = 1000.0f / MOUSEKEY_WHEEL_INITIAL_MOVEMENTS; - static uint8_t wheel_unit(void) { float speed = MOUSEKEY_WHEEL_INITIAL_MOVEMENTS; if (mousekey_accel & ((1 << 0) | (1 << 2))) { speed = mousekey_accel & (1 << 2) ? MOUSEKEY_WHEEL_ACCELERATED_MOVEMENTS : MOUSEKEY_WHEEL_DECELERATED_MOVEMENTS; - } else if (mousekey_repeat && mouse_timer) { + } else if (mousekey_wheel_repeat && mouse_timer) { if (mk_wheel_interval != MOUSEKEY_WHEEL_BASE_MOVEMENTS) { const float time_elapsed = timer_elapsed(mouse_timer) / 50; speed = MOUSEKEY_WHEEL_INITIAL_MOVEMENTS + 1 * time_elapsed + 1 * 0.5 * time_elapsed * time_elapsed; } speed = speed > MOUSEKEY_WHEEL_BASE_MOVEMENTS ? MOUSEKEY_WHEEL_BASE_MOVEMENTS : speed; } - mk_wheel_interval = 1000.0f / speed; - return 1; + return (uint8_t)speed > MOUSEKEY_WHEEL_INITIAL_MOVEMENTS ? 2 : 1; } -# else /* #ifndef MK_KINETIC_SPEED */ +# endif /* #ifndef MK_KINETIC_SPEED */ +# else /* #ifndef MK_COMBINED */ + +/* Combined mode */ static uint8_t move_unit(void) { uint16_t unit; @@ -205,8 +211,7 @@ static uint8_t wheel_unit(void) { return (unit > MOUSEKEY_WHEEL_MAX ? MOUSEKEY_WHEEL_MAX : (unit == 0 ? 1 : unit)); } -# endif /* #ifndef MK_KINETIC_SPEED */ -# endif /* #ifndef MK_COMBINED */ +# endif /* #ifndef MK_COMBINED */ void mousekey_task(void) { // report cursor and scroll movement independently diff --git a/quantum/mousekey.h b/quantum/mousekey.h index 03da5f282a..d44f47fc1f 100644 --- a/quantum/mousekey.h +++ b/quantum/mousekey.h @@ -39,7 +39,7 @@ along with this program. If not, see . # ifndef MK_KINETIC_SPEED # define MOUSEKEY_MOVE_DELTA 8 # else -# define MOUSEKEY_MOVE_DELTA 5 +# define MOUSEKEY_MOVE_DELTA 16 # endif # endif # ifndef MOUSEKEY_WHEEL_DELTA @@ -82,7 +82,7 @@ along with this program. If not, see . # define MOUSEKEY_INITIAL_SPEED 100 # endif # ifndef MOUSEKEY_BASE_SPEED -# define MOUSEKEY_BASE_SPEED 1000 +# define MOUSEKEY_BASE_SPEED 5000 # endif # ifndef MOUSEKEY_DECELERATED_SPEED # define MOUSEKEY_DECELERATED_SPEED 400 -- cgit v1.2.3 From 62eaec52e0b6aadfea629e7457d1d7d8647e840c Mon Sep 17 00:00:00 2001 From: Dasky <32983009+daskygit@users.noreply.github.com> Date: Mon, 20 Jun 2022 15:31:27 +0100 Subject: Init eeconfig before reading handedness (#17256) --- quantum/split_common/split_util.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'quantum') diff --git a/quantum/split_common/split_util.c b/quantum/split_common/split_util.c index 48b9cce6d4..4892b7f8d8 100644 --- a/quantum/split_common/split_util.c +++ b/quantum/split_common/split_util.c @@ -135,6 +135,10 @@ void split_pre_init(void) { #if defined(SPLIT_HAND_PIN) setPinInput(SPLIT_HAND_PIN); wait_us(100); +#elif defined(EE_HANDS) + if (!eeconfig_is_enabled()) { + eeconfig_init(); + } #endif isLeftHand = is_keyboard_left(); -- cgit v1.2.3 From be42c5fb9885d3bcbf47f99ad6cef343ba2b0b97 Mon Sep 17 00:00:00 2001 From: Dasky <32983009+daskygit@users.noreply.github.com> Date: Tue, 21 Jun 2022 18:53:34 +0100 Subject: Fix RGB heatmap to use XY positions and use correct led limits (#17184) * Fix RGB heatmap to use XY positions * lower effect area limit and make configurable * tidy up macro * Fix triggering in both directions. * add docs * fix bug when decreasing value * performance tweak --- .../rgb_matrix/animations/typing_heatmap_anim.h | 84 +++++++++++----------- quantum/rgb_matrix/rgb_matrix.c | 11 ++- 2 files changed, 51 insertions(+), 44 deletions(-) (limited to 'quantum') diff --git a/quantum/rgb_matrix/animations/typing_heatmap_anim.h b/quantum/rgb_matrix/animations/typing_heatmap_anim.h index 4b17c4c3ed..cfc3fc015b 100644 --- a/quantum/rgb_matrix/animations/typing_heatmap_anim.h +++ b/quantum/rgb_matrix/animations/typing_heatmap_anim.h @@ -6,30 +6,35 @@ RGB_MATRIX_EFFECT(TYPING_HEATMAP) # define RGB_MATRIX_TYPING_HEATMAP_DECREASE_DELAY_MS 25 # endif +# ifndef RGB_MATRIX_TYPING_HEATMAP_SPREAD +# define RGB_MATRIX_TYPING_HEATMAP_SPREAD 40 +# endif + +# ifndef RGB_MATRIX_TYPING_HEATMAP_AREA_LIMIT +# define RGB_MATRIX_TYPING_HEATMAP_AREA_LIMIT 16 +# endif void process_rgb_matrix_typing_heatmap(uint8_t row, uint8_t col) { # ifdef RGB_MATRIX_TYPING_HEATMAP_SLIM // Limit effect to pressed keys g_rgb_frame_buffer[row][col] = qadd8(g_rgb_frame_buffer[row][col], 32); # else - uint8_t m_row = row - 1; - uint8_t p_row = row + 1; - uint8_t m_col = col - 1; - uint8_t p_col = col + 1; - - if (m_col < col) g_rgb_frame_buffer[row][m_col] = qadd8(g_rgb_frame_buffer[row][m_col], 16); - g_rgb_frame_buffer[row][col] = qadd8(g_rgb_frame_buffer[row][col], 32); - if (p_col < MATRIX_COLS) g_rgb_frame_buffer[row][p_col] = qadd8(g_rgb_frame_buffer[row][p_col], 16); - - if (p_row < MATRIX_ROWS) { - if (m_col < col) g_rgb_frame_buffer[p_row][m_col] = qadd8(g_rgb_frame_buffer[p_row][m_col], 13); - g_rgb_frame_buffer[p_row][col] = qadd8(g_rgb_frame_buffer[p_row][col], 16); - if (p_col < MATRIX_COLS) g_rgb_frame_buffer[p_row][p_col] = qadd8(g_rgb_frame_buffer[p_row][p_col], 13); - } - - if (m_row < row) { - if (m_col < col) g_rgb_frame_buffer[m_row][m_col] = qadd8(g_rgb_frame_buffer[m_row][m_col], 13); - g_rgb_frame_buffer[m_row][col] = qadd8(g_rgb_frame_buffer[m_row][col], 16); - if (p_col < MATRIX_COLS) g_rgb_frame_buffer[m_row][p_col] = qadd8(g_rgb_frame_buffer[m_row][p_col], 13); + for (uint8_t i_row = 0; i_row < MATRIX_ROWS; i_row++) { + for (uint8_t i_col = 0; i_col < MATRIX_COLS; i_col++) { + if (i_row == row && i_col == col) { + g_rgb_frame_buffer[row][col] = qadd8(g_rgb_frame_buffer[row][col], 32); + } else { +# define LED_DISTANCE(led_a, led_b) sqrt16(((int8_t)(led_a.x - led_b.x) * (int8_t)(led_a.x - led_b.x)) + ((int8_t)(led_a.y - led_b.y) * (int8_t)(led_a.y - led_b.y))) + uint8_t distance = LED_DISTANCE(g_led_config.point[g_led_config.matrix_co[row][col]], g_led_config.point[g_led_config.matrix_co[i_row][i_col]]); +# undef LED_DISTANCE + if (distance <= RGB_MATRIX_TYPING_HEATMAP_SPREAD) { + uint8_t amount = qsub8(RGB_MATRIX_TYPING_HEATMAP_SPREAD, distance); + if (amount > RGB_MATRIX_TYPING_HEATMAP_AREA_LIMIT) { + amount = RGB_MATRIX_TYPING_HEATMAP_AREA_LIMIT; + } + g_rgb_frame_buffer[i_row][i_col] = qadd8(g_rgb_frame_buffer[i_row][i_col], amount); + } + } + } } # endif } @@ -40,10 +45,7 @@ static uint16_t heatmap_decrease_timer; static bool decrease_heatmap_values; bool TYPING_HEATMAP(effect_params_t* params) { - // Modified version of RGB_MATRIX_USE_LIMITS to work off of matrix row / col size - uint8_t led_min = RGB_MATRIX_LED_PROCESS_LIMIT * params->iter; - uint8_t led_max = led_min + RGB_MATRIX_LED_PROCESS_LIMIT; - if (led_max > sizeof(g_rgb_frame_buffer)) led_max = sizeof(g_rgb_frame_buffer); + RGB_MATRIX_USE_LIMITS(led_min, led_max); if (params->init) { rgb_matrix_set_color_all(0, 0, 0); @@ -63,28 +65,26 @@ bool TYPING_HEATMAP(effect_params_t* params) { } // Render heatmap & decrease - for (int i = led_min; i < led_max; i++) { - uint8_t row = i % MATRIX_ROWS; - uint8_t col = i / MATRIX_ROWS; - uint8_t val = g_rgb_frame_buffer[row][col]; - - // set the pixel colour - uint8_t led[LED_HITS_TO_REMEMBER]; - uint8_t led_count = rgb_matrix_map_row_column_to_led(row, col, led); - for (uint8_t j = 0; j < led_count; ++j) { - if (!HAS_ANY_FLAGS(g_led_config.flags[led[j]], params->flags)) continue; - - HSV hsv = {170 - qsub8(val, 85), rgb_matrix_config.hsv.s, scale8((qadd8(170, val) - 170) * 3, rgb_matrix_config.hsv.v)}; - RGB rgb = rgb_matrix_hsv_to_rgb(hsv); - rgb_matrix_set_color(led[j], rgb.r, rgb.g, rgb.b); - } - - if (decrease_heatmap_values) { - g_rgb_frame_buffer[row][col] = qsub8(val, 1); + uint8_t count = 0; + for (uint8_t row = 0; row < MATRIX_ROWS && count < RGB_MATRIX_LED_PROCESS_LIMIT; row++) { + for (uint8_t col = 0; col < MATRIX_COLS && RGB_MATRIX_LED_PROCESS_LIMIT; col++) { + if (g_led_config.matrix_co[row][col] >= led_min && g_led_config.matrix_co[row][col] < led_max) { + count++; + uint8_t val = g_rgb_frame_buffer[row][col]; + if (!HAS_ANY_FLAGS(g_led_config.flags[g_led_config.matrix_co[row][col]], params->flags)) continue; + + HSV hsv = {170 - qsub8(val, 85), rgb_matrix_config.hsv.s, scale8((qadd8(170, val) - 170) * 3, rgb_matrix_config.hsv.v)}; + RGB rgb = rgb_matrix_hsv_to_rgb(hsv); + rgb_matrix_set_color(g_led_config.matrix_co[row][col], rgb.r, rgb.g, rgb.b); + + if (decrease_heatmap_values) { + g_rgb_frame_buffer[row][col] = qsub8(val, 1); + } + } } } - return led_max < sizeof(g_rgb_frame_buffer); + return rgb_matrix_check_finished_leds(led_max); } # endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS diff --git a/quantum/rgb_matrix/rgb_matrix.c b/quantum/rgb_matrix/rgb_matrix.c index f721dfc7f2..a51e379025 100644 --- a/quantum/rgb_matrix/rgb_matrix.c +++ b/quantum/rgb_matrix/rgb_matrix.c @@ -249,8 +249,15 @@ void process_rgb_matrix(uint8_t row, uint8_t col, bool pressed) { #endif // RGB_MATRIX_KEYREACTIVE_ENABLED #if defined(RGB_MATRIX_FRAMEBUFFER_EFFECTS) && defined(ENABLE_RGB_MATRIX_TYPING_HEATMAP) - if (rgb_matrix_config.mode == RGB_MATRIX_TYPING_HEATMAP) { - process_rgb_matrix_typing_heatmap(row, col); +# if defined(RGB_MATRIX_KEYRELEASES) + if (!pressed) +# else + if (pressed) +# endif // defined(RGB_MATRIX_KEYRELEASES) + { + if (rgb_matrix_config.mode == RGB_MATRIX_TYPING_HEATMAP) { + process_rgb_matrix_typing_heatmap(row, col); + } } #endif // defined(RGB_MATRIX_FRAMEBUFFER_EFFECTS) && defined(ENABLE_RGB_MATRIX_TYPING_HEATMAP) } -- cgit v1.2.3 From 2239527871deef1253ebe885df167726a100f971 Mon Sep 17 00:00:00 2001 From: SmugSam <88806297+SmugSam@users.noreply.github.com> Date: Wed, 22 Jun 2022 21:47:15 -0400 Subject: In honor of king terry (#17387) --- quantum/audio/song_list.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'quantum') diff --git a/quantum/audio/song_list.h b/quantum/audio/song_list.h index 3e425abb47..ff22e6fe95 100644 --- a/quantum/audio/song_list.h +++ b/quantum/audio/song_list.h @@ -144,6 +144,12 @@ */ #define USSR_ANTHEM B__NOTE(_G6), B__NOTE(_C7), W__NOTE(_G6), H__NOTE(_A6), B__NOTE(_B6), W__NOTE(_E6), W__NOTE(_E6), B__NOTE(_A6), W__NOTE(_G6), H__NOTE(_F6), B__NOTE(_G6), W__NOTE(_C6), W__NOTE(_C6), B__NOTE(_D6), W__NOTE(_D6), W__NOTE(_E6), B__NOTE(_D6), W__NOTE(_D6), W__NOTE(_G6), B__NOTE(_F6), W__NOTE(_G6), W__NOTE(_A6), B__NOTE(_B6), +/* Title: Hymn Risen + * Author/Composer: Terrance Andrew Davis + * License: Public Domain + */ +#define TOS_HYMN_RISEN H__NOTE(_D5), H__NOTE(_E5), HD_NOTE(_F5), HD_NOTE(_F5), H__NOTE(_F5), HD_NOTE(_D5), E__NOTE(_E5), E__NOTE(_E5), H__NOTE(_C5), Q__NOTE(_D5), Q__NOTE(_D5), H__NOTE(_E5), H__NOTE(_C5), Q__NOTE(_G5), Q__NOTE(_F5), H__NOTE(_D5), H__NOTE(_E5), HD_NOTE(_F5), HD_NOTE(_F5), H__NOTE(_F5), HD_NOTE(_D5), E__NOTE(_E5), E__NOTE(_E5), H__NOTE(_C5), Q__NOTE(_D5), Q__NOTE(_D5), H__NOTE(_E5), H__NOTE(_C5), Q__NOTE(_G5), Q__NOTE(_F5), H__NOTE(_D5), H__NOTE(_C5), W__NOTE(_D5), W__NOTE(_E5), Q__NOTE(_A4), H__NOTE(_A4), Q__NOTE(_E5), Q__NOTE(_E5), Q__NOTE(_F5), Q__NOTE(_E5), Q__NOTE(_D5), Q__NOTE(_G5), Q__NOTE(_B4), Q__NOTE(_D5), Q__NOTE(_C5), M__NOTE(_F5, 80), H__NOTE(_D5), H__NOTE(_C5), W__NOTE(_D5), W__NOTE(_E5), Q__NOTE(_A4), H__NOTE(_A4), Q__NOTE(_E5), Q__NOTE(_E5), Q__NOTE(_F5), Q__NOTE(_E5), Q__NOTE(_D5), Q__NOTE(_G5), Q__NOTE(_B4), Q__NOTE(_D5), Q__NOTE(_C5), M__NOTE(_F5, 80) + /* Removed sounds + This list is here solely for compatibility, so that removed songs don't just break things * If you think that any of these songs were wrongfully removed, let us know and provide -- 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/keyboard.c | 2 +- quantum/keymap_extras/keymap_steno.h | 28 +++ quantum/process_keycode/process_steno.c | 312 +++++++++++++++++--------------- quantum/process_keycode/process_steno.h | 24 ++- 4 files changed, 217 insertions(+), 149 deletions(-) (limited to 'quantum') diff --git a/quantum/keyboard.c b/quantum/keyboard.c index a65f9d6d18..8273299a9a 100644 --- a/quantum/keyboard.c +++ b/quantum/keyboard.c @@ -381,7 +381,7 @@ void keyboard_init(void) { #ifdef ENCODER_ENABLE encoder_init(); #endif -#ifdef STENO_ENABLE +#ifdef STENO_ENABLE_ALL steno_init(); #endif #ifdef POINTING_DEVICE_ENABLE diff --git a/quantum/keymap_extras/keymap_steno.h b/quantum/keymap_extras/keymap_steno.h index e888ccd643..07d96b7465 100644 --- a/quantum/keymap_extras/keymap_steno.h +++ b/quantum/keymap_extras/keymap_steno.h @@ -89,3 +89,31 @@ enum steno_combined_keycodes { STN_COMB_MAX = STN_EU, }; #endif + +#ifdef STENO_ENABLE_BOLT +// 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 +#endif // STENO_ENABLE_BOLT 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 3b9e186019f74d5046fbdd399b5cfa0baf513a75 Mon Sep 17 00:00:00 2001 From: precondition <57645186+precondition@users.noreply.github.com> Date: Fri, 24 Jun 2022 12:40:09 +0200 Subject: Do not enable PERMISSIVE_HOLD when TAPPING_TERM exceeds 500ms (#15674) --- quantum/action_tapping.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'quantum') diff --git a/quantum/action_tapping.c b/quantum/action_tapping.c index 3c8b5678b7..df3317ac05 100644 --- a/quantum/action_tapping.c +++ b/quantum/action_tapping.c @@ -125,7 +125,7 @@ void action_tapping_process(keyrecord_t record) { /* return true when key event is processed or consumed. */ bool process_tapping(keyrecord_t *keyp) { keyevent_t event = keyp->event; -# if (defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)) || defined(TAPPING_TERM_PER_KEY) || defined(PERMISSIVE_HOLD_PER_KEY) || defined(TAPPING_FORCE_HOLD_PER_KEY) || defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY) +# if (defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)) || defined(PERMISSIVE_HOLD_PER_KEY) || defined(TAPPING_FORCE_HOLD_PER_KEY) || defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY) uint16_t tapping_keycode = get_record_keycode(&tapping_key, false); # endif @@ -164,17 +164,15 @@ bool process_tapping(keyrecord_t *keyp) { * useful for long TAPPING_TERM but may prevent fast typing. */ // clang-format off -# if defined(TAPPING_TERM_PER_KEY) || (TAPPING_TERM >= 500) || defined(PERMISSIVE_HOLD) || defined(PERMISSIVE_HOLD_PER_KEY) || (defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)) +# if defined(PERMISSIVE_HOLD) || defined(PERMISSIVE_HOLD_PER_KEY) || (defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)) else if ( ( - ( - GET_TAPPING_TERM(tapping_keycode, &tapping_key) >= 500 + IS_RELEASED(event) && waiting_buffer_typed(event) # ifdef PERMISSIVE_HOLD_PER_KEY - || get_permissive_hold(tapping_keycode, &tapping_key) + && get_permissive_hold(tapping_keycode, &tapping_key) # elif defined(PERMISSIVE_HOLD) - || true + && true # endif - ) && IS_RELEASED(event) && waiting_buffer_typed(event) ) // Causes nested taps to not wait past TAPPING_TERM/RETRO_SHIFT // unnecessarily and fixes them for Layer Taps. -- cgit v1.2.3 From 01bc974365581da6563e2bbd2425233d97280fdd Mon Sep 17 00:00:00 2001 From: Thomas Kriechbaumer Date: Sat, 25 Jun 2022 22:22:28 +0200 Subject: improvements for Cirque Pinnacle trackpads (#17091) --- quantum/pointing_device_drivers.c | 45 +++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 11 deletions(-) (limited to 'quantum') diff --git a/quantum/pointing_device_drivers.c b/quantum/pointing_device_drivers.c index 41d7018c86..435fcabf7c 100644 --- a/quantum/pointing_device_drivers.c +++ b/quantum/pointing_device_drivers.c @@ -50,6 +50,7 @@ const pointing_device_driver_t pointing_device_driver = { .get_cpi = adns5050_get_cpi, }; // clang-format on + #elif defined(POINTING_DEVICE_DRIVER_adns9800) report_mouse_t adns9800_get_report_driver(report_mouse_t mouse_report) { @@ -69,6 +70,7 @@ const pointing_device_driver_t pointing_device_driver = { .get_cpi = adns9800_get_cpi }; // clang-format on + #elif defined(POINTING_DEVICE_DRIVER_analog_joystick) report_mouse_t analog_joystick_get_report(report_mouse_t mouse_report) { report_analog_joystick_t data = analog_joystick_read(); @@ -93,6 +95,7 @@ const pointing_device_driver_t pointing_device_driver = { .get_cpi = NULL }; // clang-format on + #elif defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_i2c) || defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_spi) # ifndef CIRQUE_PINNACLE_TAPPING_TERM # include "action.h" @@ -104,22 +107,40 @@ const pointing_device_driver_t pointing_device_driver = { # endif report_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) { - pinnacle_data_t touchData = cirque_pinnacle_read_data(); - static uint16_t x = 0, y = 0, mouse_timer = 0; - mouse_xy_report_t report_x = 0, report_y = 0; - static bool is_z_down = false; + pinnacle_data_t touchData = cirque_pinnacle_read_data(); + mouse_xy_report_t report_x = 0, report_y = 0; + static mouse_xy_report_t x = 0, y = 0; + static uint16_t mouse_timer = 0; + static bool is_z_down = false; + +# if !CIRQUE_PINNACLE_POSITION_MODE +# error Cirque Pinnacle with relative mode not implemented yet. +# endif + + if (!touchData.valid) { + return mouse_report; + } + +# if CONSOLE_ENABLE + if (debug_mouse && touchData.touchDown) { + dprintf("cirque_pinnacle touchData x=%4d y=%4d z=%2d\n", touchData.xValue, touchData.yValue, touchData.zValue); + } +# endif - cirque_pinnacle_scale_data(&touchData, cirque_pinnacle_get_scale(), cirque_pinnacle_get_scale()); // Scale coordinates to arbitrary X, Y resolution + // Scale coordinates to arbitrary X, Y resolution + cirque_pinnacle_scale_data(&touchData, cirque_pinnacle_get_scale(), cirque_pinnacle_get_scale()); if (x && y && touchData.xValue && touchData.yValue) { report_x = (mouse_xy_report_t)(touchData.xValue - x); report_y = (mouse_xy_report_t)(touchData.yValue - y); } - x = touchData.xValue; - y = touchData.yValue; + x = touchData.xValue; + y = touchData.yValue; + mouse_report.x = report_x; + mouse_report.y = report_y; - if ((bool)touchData.zValue != is_z_down) { - is_z_down = (bool)touchData.zValue; + if (touchData.touchDown != is_z_down) { + is_z_down = touchData.touchDown; if (!touchData.zValue) { if (timer_elapsed(mouse_timer) < CIRQUE_PINNACLE_TAPPING_TERM && mouse_timer != 0) { mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, true, POINTING_DEVICE_BUTTON1); @@ -138,8 +159,6 @@ report_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) { if (timer_elapsed(mouse_timer) > (CIRQUE_PINNACLE_TOUCH_DEBOUNCE)) { mouse_timer = 0; } - mouse_report.x = report_x; - mouse_report.y = report_y; return mouse_report; } @@ -210,6 +229,7 @@ const pointing_device_driver_t pointing_device_driver = { .get_cpi = pimoroni_trackball_get_cpi }; // clang-format on + #elif defined(POINTING_DEVICE_DRIVER_pmw3360) static void pmw3360_device_init(void) { pmw3360_init(0); @@ -248,6 +268,7 @@ const pointing_device_driver_t pointing_device_driver = { .get_cpi = pmw3360_get_cpi }; // clang-format on + #elif defined(POINTING_DEVICE_DRIVER_pmw3389) static void pmw3389_device_init(void) { pmw3389_init(); @@ -286,6 +307,7 @@ const pointing_device_driver_t pointing_device_driver = { .get_cpi = pmw3389_get_cpi }; // clang-format on + #else __attribute__((weak)) void pointing_device_driver_init(void) {} __attribute__((weak)) report_mouse_t pointing_device_driver_get_report(report_mouse_t mouse_report) { @@ -304,4 +326,5 @@ const pointing_device_driver_t pointing_device_driver = { .set_cpi = pointing_device_driver_set_cpi }; // clang-format on + #endif -- cgit v1.2.3 From 0d013a21e1347fc38e742b2bd876329e86c153a9 Mon Sep 17 00:00:00 2001 From: Thomas Kriechbaumer Date: Sun, 26 Jun 2022 18:15:25 +0200 Subject: [Split] Verify Split Pointing Device config (#17481) --- quantum/pointing_device.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'quantum') diff --git a/quantum/pointing_device.c b/quantum/pointing_device.c index 3c2e2bc09d..3aa4941687 100644 --- a/quantum/pointing_device.c +++ b/quantum/pointing_device.c @@ -25,6 +25,13 @@ #if (defined(POINTING_DEVICE_ROTATION_90) + defined(POINTING_DEVICE_ROTATION_180) + defined(POINTING_DEVICE_ROTATION_270)) > 1 # error More than one rotation selected. This is not supported. #endif + +#if defined(POINTING_DEVICE_LEFT) || defined(POINTING_DEVICE_RIGHT) || defined(POINTING_DEVICE_COMBINED) +# ifndef SPLIT_POINTING_ENABLE +# error "Using POINTING_DEVICE_LEFT or POINTING_DEVICE_RIGHT or POINTING_DEVICE_COMBINED, then SPLIT_POINTING_ENABLE is required but has not been defined" +# endif +#endif + #if defined(SPLIT_POINTING_ENABLE) # include "transactions.h" # include "keyboard.h" -- cgit v1.2.3 From 01ecf332ff9f0c8f61e493cdca116f58d80bb5fb Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Mon, 27 Jun 2022 07:18:21 +1000 Subject: Generic wear-leveling algorithm (#16996) * Initial import of wear-leveling algorithm. * Alignment. * Docs tweaks. * Lock/unlock. * Update quantum/wear_leveling/wear_leveling_internal.h Co-authored-by: Stefan Kerkmann * More tests, fix issue with consolidation when unlocked. * More tests. * Review comments. * Add plumbing for FNV1a. * Another test checking that checksum mismatch clears the cache. * Check that the write log still gets played back. Co-authored-by: Stefan Kerkmann --- quantum/wear_leveling/tests/backing_mocks.cpp | 154 ++++ quantum/wear_leveling/tests/backing_mocks.hpp | 210 ++++++ quantum/wear_leveling/tests/rules.mk | 66 ++ quantum/wear_leveling/tests/testlist.mk | 6 + .../wear_leveling/tests/wear_leveling_2byte.cpp | 228 ++++++ .../tests/wear_leveling_2byte_optimized_writes.cpp | 295 ++++++++ .../wear_leveling/tests/wear_leveling_4byte.cpp | 193 +++++ .../wear_leveling/tests/wear_leveling_8byte.cpp | 178 +++++ .../wear_leveling/tests/wear_leveling_general.cpp | 204 ++++++ quantum/wear_leveling/wear_leveling.c | 779 +++++++++++++++++++++ quantum/wear_leveling/wear_leveling.h | 54 ++ quantum/wear_leveling/wear_leveling_internal.h | 145 ++++ 12 files changed, 2512 insertions(+) create mode 100644 quantum/wear_leveling/tests/backing_mocks.cpp create mode 100644 quantum/wear_leveling/tests/backing_mocks.hpp create mode 100644 quantum/wear_leveling/tests/rules.mk create mode 100644 quantum/wear_leveling/tests/testlist.mk create mode 100644 quantum/wear_leveling/tests/wear_leveling_2byte.cpp create mode 100644 quantum/wear_leveling/tests/wear_leveling_2byte_optimized_writes.cpp create mode 100644 quantum/wear_leveling/tests/wear_leveling_4byte.cpp create mode 100644 quantum/wear_leveling/tests/wear_leveling_8byte.cpp create mode 100644 quantum/wear_leveling/tests/wear_leveling_general.cpp create mode 100644 quantum/wear_leveling/wear_leveling.c create mode 100644 quantum/wear_leveling/wear_leveling.h create mode 100644 quantum/wear_leveling/wear_leveling_internal.h (limited to 'quantum') diff --git a/quantum/wear_leveling/tests/backing_mocks.cpp b/quantum/wear_leveling/tests/backing_mocks.cpp new file mode 100644 index 0000000000..1dbb26f8e7 --- /dev/null +++ b/quantum/wear_leveling/tests/backing_mocks.cpp @@ -0,0 +1,154 @@ +// Copyright 2022 Nick Brassel (@tzarc) +// SPDX-License-Identifier: GPL-2.0-or-later +#include "gtest/gtest.h" +#include "gmock/gmock.h" +#include "backing_mocks.hpp" + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Backing Store Mock implementation +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void MockBackingStore::reset_instance() { + for (auto&& e : backing_storage) + e.reset(); + + locked = true; + + backing_erasure_count = 0; + backing_max_write_count = 0; + backing_total_write_count = 0; + + backing_init_invoke_count = 0; + backing_unlock_invoke_count = 0; + backing_erase_invoke_count = 0; + backing_write_invoke_count = 0; + backing_lock_invoke_count = 0; + + init_success_callback = [](std::uint64_t) { return true; }; + erase_success_callback = [](std::uint64_t) { return true; }; + unlock_success_callback = [](std::uint64_t) { return true; }; + write_success_callback = [](std::uint64_t, std::uint32_t) { return true; }; + lock_success_callback = [](std::uint64_t) { return true; }; + + write_log.clear(); +} + +bool MockBackingStore::init(void) { + ++backing_init_invoke_count; + + if (init_success_callback) { + return init_success_callback(backing_init_invoke_count); + } + return true; +} + +bool MockBackingStore::unlock(void) { + ++backing_unlock_invoke_count; + + EXPECT_TRUE(is_locked()) << "Attempted to unlock but was not locked"; + locked = false; + + if (unlock_success_callback) { + return unlock_success_callback(backing_unlock_invoke_count); + } + return true; +} + +bool MockBackingStore::erase(void) { + ++backing_erase_invoke_count; + + // Erase each slot + for (std::size_t i = 0; i < backing_storage.size(); ++i) { + // Drop out of erase early with failure if we need to + if (erase_success_callback && !erase_success_callback(backing_erase_invoke_count)) { + append_log(true); + return false; + } + + backing_storage[i].erase(); + } + + // Keep track of the erase in the write log so that we can verify during tests + append_log(true); + + ++backing_erasure_count; + return true; +} + +bool MockBackingStore::write(uint32_t address, backing_store_int_t value) { + ++backing_write_invoke_count; + + // precondition: value's buffer size already matches BACKING_STORE_WRITE_SIZE + EXPECT_TRUE(address % BACKING_STORE_WRITE_SIZE == 0) << "Supplied address was not aligned with the backing store integral size"; + EXPECT_TRUE(address + BACKING_STORE_WRITE_SIZE <= WEAR_LEVELING_BACKING_SIZE) << "Address would result of out-of-bounds access"; + EXPECT_FALSE(is_locked()) << "Write was attempted without being unlocked first"; + + // Drop out of write early with failure if we need to + if (write_success_callback && !write_success_callback(backing_write_invoke_count, address)) { + return false; + } + + // Write the complement as we're simulating flash memory -- 0xFF means 0x00 + std::size_t index = address / BACKING_STORE_WRITE_SIZE; + backing_storage[index].set(~value); + + // Keep track of the write log so that we can verify during tests + append_log(address, value); + + // Keep track of the total number of writes into the backing store + ++backing_total_write_count; + + return true; +} + +bool MockBackingStore::lock(void) { + ++backing_lock_invoke_count; + + EXPECT_FALSE(is_locked()) << "Attempted to lock but was not unlocked"; + locked = true; + + if (lock_success_callback) { + return lock_success_callback(backing_lock_invoke_count); + } + return true; +} + +bool MockBackingStore::read(uint32_t address, backing_store_int_t& value) const { + // precondition: value's buffer size already matches BACKING_STORE_WRITE_SIZE + EXPECT_TRUE(address % BACKING_STORE_WRITE_SIZE == 0) << "Supplied address was not aligned with the backing store integral size"; + EXPECT_TRUE(address + BACKING_STORE_WRITE_SIZE <= WEAR_LEVELING_BACKING_SIZE) << "Address would result of out-of-bounds access"; + + // Read and take the complement as we're simulating flash memory -- 0xFF means 0x00 + std::size_t index = address / BACKING_STORE_WRITE_SIZE; + value = ~backing_storage[index].get(); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Backing Implementation +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +extern "C" bool backing_store_init(void) { + return MockBackingStore::Instance().init(); +} + +extern "C" bool backing_store_unlock(void) { + return MockBackingStore::Instance().unlock(); +} + +extern "C" bool backing_store_erase(void) { + return MockBackingStore::Instance().erase(); +} + +extern "C" bool backing_store_write(uint32_t address, backing_store_int_t value) { + return MockBackingStore::Instance().write(address, value); +} + +extern "C" bool backing_store_lock(void) { + return MockBackingStore::Instance().lock(); +} + +extern "C" bool backing_store_read(uint32_t address, backing_store_int_t* value) { + return MockBackingStore::Instance().read(address, *value); +} diff --git a/quantum/wear_leveling/tests/backing_mocks.hpp b/quantum/wear_leveling/tests/backing_mocks.hpp new file mode 100644 index 0000000000..e7af7895f3 --- /dev/null +++ b/quantum/wear_leveling/tests/backing_mocks.hpp @@ -0,0 +1,210 @@ +// Copyright 2022 Nick Brassel (@tzarc) +// SPDX-License-Identifier: GPL-2.0-or-later +#pragma once +#include +#include +#include +#include +#include +#include +#include + +extern "C" { +#include "fnv.h" +#include "wear_leveling.h" +#include "wear_leveling_internal.h" +}; + +// Maximum number of mock write log entries to keep +using MOCK_WRITE_LOG_MAX_ENTRIES = std::integral_constant; +// Complement to the backing store integral, for emulating flash erases of all bytes=0xFF +using BACKING_STORE_INTEGRAL_COMPLEMENT = std::integral_constant; +// Total number of elements stored in the backing arrays +using BACKING_STORE_ELEMENT_COUNT = std::integral_constant; + +class MockBackingStoreElement { + private: + backing_store_int_t value; + std::size_t writes; + std::size_t erases; + + public: + MockBackingStoreElement() : value(BACKING_STORE_INTEGRAL_COMPLEMENT::value), writes(0), erases(0) {} + void reset() { + erase(); + writes = 0; + erases = 0; + } + void erase() { + if (!is_erased()) { + ++erases; + } + value = BACKING_STORE_INTEGRAL_COMPLEMENT::value; + } + backing_store_int_t get() const { + return value; + } + void set(const backing_store_int_t& v) { + EXPECT_TRUE(is_erased()) << "Attempted write at index which isn't empty."; + value = v; + ++writes; + } + std::size_t num_writes() const { + return writes; + } + std::size_t num_erases() const { + return erases; + } + bool is_erased() const { + return value == BACKING_STORE_INTEGRAL_COMPLEMENT::value; + } +}; + +struct MockBackingStoreLogEntry { + MockBackingStoreLogEntry(uint32_t address, backing_store_int_t value) : address(address), value(value), erased(false) {} + MockBackingStoreLogEntry(bool erased) : address(0), value(0), erased(erased) {} + uint32_t address = 0; // The address of the operation + backing_store_int_t value = 0; // The value of the operation + bool erased = false; // Whether the entire backing store was erased +}; + +class MockBackingStore { + private: + MockBackingStore() { + reset_instance(); + } + + // Type containing each of the entries and the write counts + using storage_t = std::array; + + // Whether the backing store is locked + bool locked; + // The actual data stored in the emulated flash + storage_t backing_storage; + // The number of erase cycles that have occurred + std::uint64_t backing_erasure_count; + // The max number of writes to an element of the backing store + std::uint64_t backing_max_write_count; + // The total number of writes to all elements of the backing store + std::uint64_t backing_total_write_count; + // The write log for the backing store + std::vector write_log; + + // The number of times each API was invoked + std::uint64_t backing_init_invoke_count; + std::uint64_t backing_unlock_invoke_count; + std::uint64_t backing_erase_invoke_count; + std::uint64_t backing_write_invoke_count; + std::uint64_t backing_lock_invoke_count; + + // Whether init should succeed + std::function init_success_callback; + // Whether erase should succeed + std::function erase_success_callback; + // Whether unlocks should succeed + std::function unlock_success_callback; + // Whether writes should succeed + std::function write_success_callback; + // Whether locks should succeed + std::function lock_success_callback; + + template + void append_log(Args&&... args) { + if (write_log.size() < MOCK_WRITE_LOG_MAX_ENTRIES::value) { + write_log.emplace_back(std::forward(args)...); + } + } + + public: + static MockBackingStore& Instance() { + static MockBackingStore instance; + return instance; + } + + std::uint64_t erasure_count() const { + return backing_erasure_count; + } + std::uint64_t max_write_count() const { + return backing_max_write_count; + } + std::uint64_t total_write_count() const { + return backing_total_write_count; + } + + // The number of times each API was invoked + std::uint64_t init_invoke_count() const { + return backing_init_invoke_count; + } + std::uint64_t unlock_invoke_count() const { + return backing_unlock_invoke_count; + } + std::uint64_t erase_invoke_count() const { + return backing_erase_invoke_count; + } + std::uint64_t write_invoke_count() const { + return backing_write_invoke_count; + } + std::uint64_t lock_invoke_count() const { + return backing_lock_invoke_count; + } + + // Clear out the internal data for the next run + void reset_instance(); + + bool is_locked() const { + return locked; + } + + // APIs for the backing store + bool init(); + bool unlock(); + bool erase(); + bool write(std::uint32_t address, backing_store_int_t value); + bool lock(); + bool read(std::uint32_t address, backing_store_int_t& value) const; + + // Control over when init/writes/erases should succeed + void set_init_callback(std::function callback) { + init_success_callback = callback; + } + void set_erase_callback(std::function callback) { + erase_success_callback = callback; + } + void set_unlock_callback(std::function callback) { + unlock_success_callback = callback; + } + void set_write_callback(std::function callback) { + write_success_callback = callback; + } + void set_lock_callback(std::function callback) { + lock_success_callback = callback; + } + + auto storage_begin() const -> decltype(backing_storage.begin()) { + return backing_storage.begin(); + } + auto storage_end() const -> decltype(backing_storage.end()) { + return backing_storage.end(); + } + + auto storage_begin() -> decltype(backing_storage.begin()) { + return backing_storage.begin(); + } + auto storage_end() -> decltype(backing_storage.end()) { + return backing_storage.end(); + } + + auto log_begin() -> decltype(write_log.begin()) { + return write_log.begin(); + } + auto log_end() -> decltype(write_log.end()) { + return write_log.end(); + } + + auto log_begin() const -> decltype(write_log.begin()) { + return write_log.begin(); + } + auto log_end() const -> decltype(write_log.end()) { + return write_log.end(); + } +}; diff --git a/quantum/wear_leveling/tests/rules.mk b/quantum/wear_leveling/tests/rules.mk new file mode 100644 index 0000000000..4d7a964049 --- /dev/null +++ b/quantum/wear_leveling/tests/rules.mk @@ -0,0 +1,66 @@ +wear_leveling_common_DEFS := \ + -DWEAR_LEVELING_TESTS +wear_leveling_common_SRC := \ + $(LIB_PATH)/fnv/qmk_fnv_type_validation.c \ + $(LIB_PATH)/fnv/hash_32a.c \ + $(LIB_PATH)/fnv/hash_64a.c \ + $(QUANTUM_PATH)/wear_leveling/wear_leveling.c \ + $(QUANTUM_PATH)/wear_leveling/tests/backing_mocks.cpp +wear_leveling_common_INC := \ + $(LIB_PATH)/fnv \ + $(QUANTUM_PATH)/wear_leveling + +wear_leveling_general_DEFS := \ + $(wear_leveling_common_DEFS) \ + -DBACKING_STORE_WRITE_SIZE=2 \ + -DWEAR_LEVELING_BACKING_SIZE=48 \ + -DWEAR_LEVELING_LOGICAL_SIZE=16 +wear_leveling_general_SRC := \ + $(wear_leveling_common_SRC) \ + $(QUANTUM_PATH)/wear_leveling/tests/wear_leveling_general.cpp +wear_leveling_general_INC := \ + $(wear_leveling_common_INC) + +wear_leveling_2byte_optimized_writes_DEFS := \ + $(wear_leveling_common_DEFS) \ + -DBACKING_STORE_WRITE_SIZE=2 \ + -DWEAR_LEVELING_BACKING_SIZE=65536 \ + -DWEAR_LEVELING_LOGICAL_SIZE=32768 +wear_leveling_2byte_optimized_writes_SRC := \ + $(wear_leveling_common_SRC) \ + $(QUANTUM_PATH)/wear_leveling/tests/wear_leveling_2byte_optimized_writes.cpp +wear_leveling_2byte_optimized_writes_INC := \ + $(wear_leveling_common_INC) + +wear_leveling_2byte_DEFS := \ + $(wear_leveling_common_DEFS) \ + -DBACKING_STORE_WRITE_SIZE=2 \ + -DWEAR_LEVELING_BACKING_SIZE=48 \ + -DWEAR_LEVELING_LOGICAL_SIZE=16 +wear_leveling_2byte_SRC := \ + $(wear_leveling_common_SRC) \ + $(QUANTUM_PATH)/wear_leveling/tests/wear_leveling_2byte.cpp +wear_leveling_2byte_INC := \ + $(wear_leveling_common_INC) + +wear_leveling_4byte_DEFS := \ + $(wear_leveling_common_DEFS) \ + -DBACKING_STORE_WRITE_SIZE=4 \ + -DWEAR_LEVELING_BACKING_SIZE=48 \ + -DWEAR_LEVELING_LOGICAL_SIZE=16 +wear_leveling_4byte_SRC := \ + $(wear_leveling_common_SRC) \ + $(QUANTUM_PATH)/wear_leveling/tests/wear_leveling_4byte.cpp +wear_leveling_4byte_INC := \ + $(wear_leveling_common_INC) + +wear_leveling_8byte_DEFS := \ + $(wear_leveling_common_DEFS) \ + -DBACKING_STORE_WRITE_SIZE=8 \ + -DWEAR_LEVELING_BACKING_SIZE=48 \ + -DWEAR_LEVELING_LOGICAL_SIZE=16 +wear_leveling_8byte_SRC := \ + $(wear_leveling_common_SRC) \ + $(QUANTUM_PATH)/wear_leveling/tests/wear_leveling_8byte.cpp +wear_leveling_8byte_INC := \ + $(wear_leveling_common_INC) \ No newline at end of file diff --git a/quantum/wear_leveling/tests/testlist.mk b/quantum/wear_leveling/tests/testlist.mk new file mode 100644 index 0000000000..32cfc178b4 --- /dev/null +++ b/quantum/wear_leveling/tests/testlist.mk @@ -0,0 +1,6 @@ +TEST_LIST += \ + wear_leveling_general \ + wear_leveling_2byte_optimized_writes \ + wear_leveling_2byte \ + wear_leveling_4byte \ + wear_leveling_8byte diff --git a/quantum/wear_leveling/tests/wear_leveling_2byte.cpp b/quantum/wear_leveling/tests/wear_leveling_2byte.cpp new file mode 100644 index 0000000000..b749c32b04 --- /dev/null +++ b/quantum/wear_leveling/tests/wear_leveling_2byte.cpp @@ -0,0 +1,228 @@ +// Copyright 2022 Nick Brassel (@tzarc) +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include "gtest/gtest.h" +#include "gmock/gmock.h" +#include "backing_mocks.hpp" + +class WearLeveling2Byte : public ::testing::Test { + protected: + void SetUp() override { + MockBackingStore::Instance().reset_instance(); + wear_leveling_init(); + } +}; + +static std::array verify_data; + +static wear_leveling_status_t test_write(const uint32_t address, const void* value, size_t length) { + memcpy(&verify_data[address], value, length); + return wear_leveling_write(address, value, length); +} + +/** + * This test verifies that the first write after initialisation occurs after the FNV1a_64 hash location. + */ +TEST_F(WearLeveling2Byte, FirstWriteOccursAfterHash) { + auto& inst = MockBackingStore::Instance(); + uint8_t test_value = 0x15; + test_write(0x02, &test_value, sizeof(test_value)); + EXPECT_EQ(inst.log_begin()->address, WEAR_LEVELING_LOGICAL_SIZE + 8) << "Invalid first write address."; +} + +/** + * This test verifies that the first write after initialisation occurs after the FNV1a_64 hash location, after an erase has occurred. + */ +TEST_F(WearLeveling2Byte, FirstWriteOccursAfterHash_AfterErase) { + auto& inst = MockBackingStore::Instance(); + uint8_t test_value = 0x15; + wear_leveling_erase(); + test_write(0x02, &test_value, sizeof(test_value)); + EXPECT_EQ((inst.log_begin() + 1)->address, WEAR_LEVELING_LOGICAL_SIZE + 8) << "Invalid first write address."; +} + +/** + * This test forces consolidation by writing enough to the write log that it overflows, consolidating the data into the + * base logical area. + */ +TEST_F(WearLeveling2Byte, ConsolidationOverflow) { + auto& inst = MockBackingStore::Instance(); + + // Generate a test block of data which forces OPTIMIZED_64 writes + std::array testvalue; + + // Write the data + std::iota(testvalue.begin(), testvalue.end(), 0x20); + EXPECT_EQ(test_write(0, testvalue.data(), testvalue.size()), WEAR_LEVELING_CONSOLIDATED) << "Write returned incorrect status"; + uint8_t dummy = 0x40; + EXPECT_EQ(test_write(0x04, &dummy, sizeof(dummy)), WEAR_LEVELING_SUCCESS) << "Write returned incorrect status"; + + // All writes are at address<64, so each logical byte written will generate 1 write log entry, thus 1 backing store write. + // Expected log: + // [0..11]: optimised64, backing address 0x18, logical address 0x00 + // [12]: erase + // [13..20]: consolidated data, backing address 0x00, logical address 0x00 + // [21..24]: FNV1a_64 result, backing address 0x10 + // [25]: optimised64, backing address 0x18, logical address 0x04 + EXPECT_EQ(std::distance(inst.log_begin(), inst.log_end()), 26); + + // Verify the backing store writes for the write log + std::size_t index; + write_log_entry_t e; + for (index = 0; index < 12; ++index) { + auto write_iter = inst.log_begin() + index; + EXPECT_EQ(write_iter->address, WEAR_LEVELING_LOGICAL_SIZE + 8 + (index * BACKING_STORE_WRITE_SIZE)) << "Invalid write log address"; + e.raw16[0] = write_iter->value; + EXPECT_EQ(LOG_ENTRY_GET_TYPE(e), LOG_ENTRY_TYPE_OPTIMIZED_64) << "Invalid write log entry type"; + } + + // Verify the backing store erase + { + index = 12; + auto write_iter = inst.log_begin() + index; + e.raw16[0] = write_iter->value; + EXPECT_TRUE(write_iter->erased) << "Backing store erase did not occur as required"; + } + + // Verify the backing store writes for consolidation + for (index = 13; index < 21; ++index) { + auto write_iter = inst.log_begin() + index; + EXPECT_EQ(write_iter->address, (index - 13) * BACKING_STORE_WRITE_SIZE) << "Invalid write log entry address"; + } + + // Verify the FNV1a_64 write + { + EXPECT_EQ((inst.log_begin() + 21)->address, WEAR_LEVELING_LOGICAL_SIZE) << "Invalid write log address"; + e.raw16[0] = (inst.log_begin() + 21)->value; + e.raw16[1] = (inst.log_begin() + 22)->value; + e.raw16[2] = (inst.log_begin() + 23)->value; + e.raw16[3] = (inst.log_begin() + 24)->value; + EXPECT_EQ(e.raw64, fnv_64a_buf(testvalue.data(), testvalue.size(), FNV1A_64_INIT)) << "Invalid checksum"; // Note that checksum is based on testvalue, as we overwrote one byte and need to consult the consolidated data, not the current + } + + // Verify the final write + EXPECT_EQ((inst.log_begin() + 25)->address, WEAR_LEVELING_LOGICAL_SIZE + 8) << "Invalid write log address"; + + // Verify the data is what we expected + std::array readback; + EXPECT_EQ(wear_leveling_read(0, readback.data(), WEAR_LEVELING_LOGICAL_SIZE), WEAR_LEVELING_SUCCESS) << "Failed to read back the saved data"; + EXPECT_TRUE(memcmp(readback.data(), verify_data.data(), WEAR_LEVELING_LOGICAL_SIZE) == 0) << "Readback did not match"; + + // Re-init and re-read, verifying the reload capability + EXPECT_NE(wear_leveling_init(), WEAR_LEVELING_FAILED) << "Re-initialisation failed"; + EXPECT_EQ(wear_leveling_read(0, readback.data(), WEAR_LEVELING_LOGICAL_SIZE), WEAR_LEVELING_SUCCESS) << "Failed to read back the saved data"; + EXPECT_TRUE(memcmp(readback.data(), verify_data.data(), WEAR_LEVELING_LOGICAL_SIZE) == 0) << "Readback did not match"; +} + +/** + * This test verifies multibyte readback gets canceled with an out-of-bounds address. + */ +TEST_F(WearLeveling2Byte, PlaybackReadbackMultibyte_OOB) { + auto& inst = MockBackingStore::Instance(); + auto logstart = inst.storage_begin() + (WEAR_LEVELING_LOGICAL_SIZE / sizeof(backing_store_int_t)); + + // Invalid FNV1a_64 hash + (logstart + 0)->set(0); + (logstart + 1)->set(0); + (logstart + 2)->set(0); + (logstart + 3)->set(0); + + // Set up a 2-byte logical write of [0x11,0x12] at logical offset 0x01 + auto entry0 = LOG_ENTRY_MAKE_MULTIBYTE(0x01, 2); + entry0.raw8[3] = 0x11; + entry0.raw8[4] = 0x12; + (logstart + 4)->set(~entry0.raw16[0]); + (logstart + 5)->set(~entry0.raw16[1]); + (logstart + 6)->set(~entry0.raw16[2]); + + // Set up a 2-byte logical write of [0x13,0x14] at logical offset 0x1000 (out of bounds) + auto entry1 = LOG_ENTRY_MAKE_MULTIBYTE(0x1000, 2); + entry1.raw8[3] = 0x13; + entry1.raw8[4] = 0x14; + (logstart + 7)->set(~entry1.raw16[0]); + (logstart + 8)->set(~entry1.raw16[1]); + (logstart + 9)->set(~entry1.raw16[2]); + + // Set up a 2-byte logical write of [0x15,0x16] at logical offset 0x01 + auto entry2 = LOG_ENTRY_MAKE_MULTIBYTE(0x01, 2); + entry2.raw8[3] = 0x15; + entry2.raw8[4] = 0x16; + (logstart + 10)->set(~entry2.raw16[0]); + (logstart + 11)->set(~entry2.raw16[1]); + (logstart + 12)->set(~entry2.raw16[2]); + + EXPECT_EQ(inst.erasure_count(), 0) << "Invalid initial erase count"; + EXPECT_EQ(wear_leveling_init(), WEAR_LEVELING_CONSOLIDATED) << "Readback should have failed and triggered consolidation"; + EXPECT_EQ(inst.erasure_count(), 1) << "Invalid final erase count"; + + uint8_t buf[2]; + wear_leveling_read(0x01, buf, sizeof(buf)); + EXPECT_EQ(buf[0], 0x11) << "Readback should have maintained the previous pre-failure value from the write log"; + EXPECT_EQ(buf[1], 0x12) << "Readback should have maintained the previous pre-failure value from the write log"; +} + +/** + * This test verifies optimized 64 readback gets canceled with an out-of-bounds address. + */ +TEST_F(WearLeveling2Byte, PlaybackReadbackOptimized64_OOB) { + auto& inst = MockBackingStore::Instance(); + auto logstart = inst.storage_begin() + (WEAR_LEVELING_LOGICAL_SIZE / sizeof(backing_store_int_t)); + + // Invalid FNV1a_64 hash + (logstart + 0)->set(0); + (logstart + 1)->set(0); + (logstart + 2)->set(0); + (logstart + 3)->set(0); + + // Set up a 1-byte logical write of 0x11 at logical offset 0x01 + auto entry0 = LOG_ENTRY_MAKE_OPTIMIZED_64(0x01, 0x11); + (logstart + 4)->set(~entry0.raw16[0]); + + // Set up a 1-byte logical write of 0x11 at logical offset 0x30 (out of bounds) + auto entry1 = LOG_ENTRY_MAKE_OPTIMIZED_64(0x30, 0x11); + (logstart + 5)->set(~entry1.raw16[0]); + + // Set up a 1-byte logical write of 0x12 at logical offset 0x01 + auto entry2 = LOG_ENTRY_MAKE_OPTIMIZED_64(0x01, 0x12); + (logstart + 6)->set(~entry2.raw16[0]); + + EXPECT_EQ(inst.erasure_count(), 0) << "Invalid initial erase count"; + EXPECT_EQ(wear_leveling_init(), WEAR_LEVELING_CONSOLIDATED) << "Readback should have failed and triggered consolidation"; + EXPECT_EQ(inst.erasure_count(), 1) << "Invalid final erase count"; + uint8_t tmp; + wear_leveling_read(0x01, &tmp, sizeof(tmp)); + EXPECT_EQ(tmp, 0x11) << "Readback should have maintained the previous pre-failure value from the write log"; +} + +/** + * This test verifies word 0/1 readback gets canceled with an out-of-bounds address. + */ +TEST_F(WearLeveling2Byte, PlaybackReadbackWord01_OOB) { + auto& inst = MockBackingStore::Instance(); + auto logstart = inst.storage_begin() + (WEAR_LEVELING_LOGICAL_SIZE / sizeof(backing_store_int_t)); + + // Invalid FNV1a_64 hash + (logstart + 0)->set(0); + (logstart + 1)->set(0); + (logstart + 2)->set(0); + (logstart + 3)->set(0); + + // Set up a 1-byte logical write of 1 at logical offset 0x02 + auto entry0 = LOG_ENTRY_MAKE_WORD_01(0x02, 1); + (logstart + 4)->set(~entry0.raw16[0]); + + // Set up a 1-byte logical write of 1 at logical offset 0x1000 (out of bounds) + auto entry1 = LOG_ENTRY_MAKE_WORD_01(0x1000, 1); + (logstart + 5)->set(~entry1.raw16[0]); + + // Set up a 1-byte logical write of 0 at logical offset 0x02 + auto entry2 = LOG_ENTRY_MAKE_WORD_01(0x02, 0); + (logstart + 6)->set(~entry2.raw16[0]); + + EXPECT_EQ(inst.erasure_count(), 0) << "Invalid initial erase count"; + EXPECT_EQ(wear_leveling_init(), WEAR_LEVELING_CONSOLIDATED) << "Readback should have failed and triggered consolidation"; + EXPECT_EQ(inst.erasure_count(), 1) << "Invalid final erase count"; + uint8_t tmp; + wear_leveling_read(0x02, &tmp, sizeof(tmp)); + EXPECT_EQ(tmp, 1) << "Readback should have maintained the previous pre-failure value from the write log"; +} diff --git a/quantum/wear_leveling/tests/wear_leveling_2byte_optimized_writes.cpp b/quantum/wear_leveling/tests/wear_leveling_2byte_optimized_writes.cpp new file mode 100644 index 0000000000..0b03113c89 --- /dev/null +++ b/quantum/wear_leveling/tests/wear_leveling_2byte_optimized_writes.cpp @@ -0,0 +1,295 @@ +// Copyright 2022 Nick Brassel (@tzarc) +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include "gtest/gtest.h" +#include "gmock/gmock.h" +#include "backing_mocks.hpp" + +class WearLeveling2ByteOptimizedWrites : public ::testing::Test { + protected: + void SetUp() override { + MockBackingStore::Instance().reset_instance(); + wear_leveling_init(); + } +}; + +static std::array verify_data; + +static wear_leveling_status_t test_write(const uint32_t address, const void* value, size_t length) { + memcpy(&verify_data[address], value, length); + return wear_leveling_write(address, value, length); +} + +/** + * This test ensures the correct number of backing store writes occurs with a multibyte write, given the input buffer size. + */ +TEST_F(WearLeveling2ByteOptimizedWrites, MultibyteBackingStoreWriteCounts) { + auto& inst = MockBackingStore::Instance(); + + for (std::size_t length = 1; length <= 5; ++length) { + // Clear things out + std::fill(verify_data.begin(), verify_data.end(), 0); + inst.reset_instance(); + wear_leveling_init(); + + // Generate a test block of data + std::vector testvalue(length); + std::iota(testvalue.begin(), testvalue.end(), 0x20); + + // Write the data + EXPECT_EQ(test_write(2000, testvalue.data(), testvalue.size()), WEAR_LEVELING_SUCCESS) << "Write failed with incorrect status"; + + std::size_t expected; + if (length > 3) { + expected = 4; + } else if (length > 1) { + expected = 3; + } else { + expected = 2; + } + + // Check that we got the expected number of write log entries + EXPECT_EQ(std::distance(inst.log_begin(), inst.log_end()), expected); + } +} + +/** + * This test runs through writing U16 values of `0` or `1` over the entire logical address range, to even addresses only. + * - Addresses <16384 will result in a single optimised backing write + * - Higher addresses will result in a multibyte write of 3 backing writes + */ +TEST_F(WearLeveling2ByteOptimizedWrites, WriteOneThenZeroToEvenAddresses) { + auto& inst = MockBackingStore::Instance(); + + // Only attempt writes for each address up to a limit that would NOT force a consolidated data write. + std::size_t writes_per_loop = (MOCK_WRITE_LOG_MAX_ENTRIES::value / 6) - 1; // Worst case is 6 writes for each pair of writes of 0/1 + std::size_t final_address; + for (uint32_t address = 0; address < WEAR_LEVELING_LOGICAL_SIZE; address += (writes_per_loop * 2)) { + // Clear things out + std::fill(verify_data.begin(), verify_data.end(), 0); + inst.reset_instance(); + wear_leveling_init(); + + // Loop through all the addresses in this range + std::size_t expected = 0; + for (uint32_t offset = 0; offset < (writes_per_loop * 2); offset += 2) { + // If we're about to exceed the limit of the logical store, skip the writes + if (address + offset + 2 > WEAR_LEVELING_LOGICAL_SIZE) { + break; + } + + // The default erased value of the wear-leveling cache is zero, so we write a one first, then a zero, to ensure a backing store write occurs. + uint16_t val = 1; + EXPECT_EQ(test_write(address + offset, &val, sizeof(val)), WEAR_LEVELING_SUCCESS) << "Write failed with incorrect status"; + val = 0; + EXPECT_EQ(test_write(address + offset, &val, sizeof(val)), WEAR_LEVELING_SUCCESS) << "Write failed with incorrect status"; + + std::size_t backing_store_writes_expected = 0; + if (address + offset < 16384) { + // A U16 value of 0/1 at an even address <16384 will result in 1 backing write each, so we need 2 backing writes for 2 logical writes + backing_store_writes_expected = 2; + } else { + // All other addresses result in a multibyte write (3 backing store writes) to write two local bytes of data + backing_store_writes_expected = 6; + } + + // Keep track of the total number of expected writes to the backing store + expected += backing_store_writes_expected; + + // Verify we're at the correct number of writes + EXPECT_EQ(std::distance(inst.log_begin(), inst.log_end()), expected) << "Write log doesn't match required number of backing store writes for address " << (address + offset); + + // Verify that the write log entries we expect are actually present + std::size_t write_index = expected - backing_store_writes_expected; + auto write_iter = inst.log_begin() + write_index; + write_log_entry_t e; + if (address + offset < 16384) { + // A U16 value of 0/1 at an even address <16384 will result in 1 backing write each, so we need 2 backing writes for 2 logical writes + for (std::size_t i = 0; i < 2; ++i) { + e.raw16[0] = write_iter->value; + EXPECT_EQ(LOG_ENTRY_GET_TYPE(e), LOG_ENTRY_TYPE_WORD_01) << "Invalid write log entry type at " << (address + offset); + ++write_iter; + } + } else { + // Multibyte write + e.raw16[0] = write_iter->value; + EXPECT_EQ(LOG_ENTRY_GET_TYPE(e), LOG_ENTRY_TYPE_MULTIBYTE) << "Invalid write log entry type at " << (address + offset); + EXPECT_EQ(LOG_ENTRY_MULTIBYTE_GET_LENGTH(e), 2) << "Invalid write log entry length at " << (address + offset); + ++write_iter; + } + + // Keep track of the final address written, so we can verify the entire logical range was handled + final_address = address + offset; + } + + // Verify the number of writes that occurred to the backing store + size_t backing_write_count = std::distance(inst.log_begin(), inst.log_end()); + EXPECT_EQ(backing_write_count, expected) << "Invalid write count at address " << address; + + // Verify the data is what we expected + std::array readback; + EXPECT_EQ(wear_leveling_read(0, readback.data(), WEAR_LEVELING_LOGICAL_SIZE), WEAR_LEVELING_SUCCESS) << "Failed to read back the saved data"; + EXPECT_TRUE(memcmp(readback.data(), verify_data.data(), WEAR_LEVELING_LOGICAL_SIZE) == 0) << "Readback for address " << address << " did not match"; + + // Re-init and re-read, testing the reload capability + EXPECT_NE(wear_leveling_init(), WEAR_LEVELING_FAILED) << "Re-initialisation failed"; + EXPECT_EQ(wear_leveling_read(0, readback.data(), WEAR_LEVELING_LOGICAL_SIZE), WEAR_LEVELING_SUCCESS) << "Failed to read back the saved data"; + EXPECT_TRUE(memcmp(readback.data(), verify_data.data(), WEAR_LEVELING_LOGICAL_SIZE) == 0) << "Readback for address " << address << " did not match"; + } + + // Verify the full range of the logical area got written + EXPECT_EQ(final_address, WEAR_LEVELING_LOGICAL_SIZE - 2) << "Invalid final write address"; +} + +/** + * This test runs through writing U16 values of `0` or `1` over the entire logical address range, to odd addresses only. + * - Addresses <63 will result in 2 optimised backing writes + * - Address 63 results in a single optimised backing write for the first logical byte, and a multibyte write of 2 backing writes for the second logical byte + * - Higher addresses will result in a multibyte write of 3 backing writes + */ +TEST_F(WearLeveling2ByteOptimizedWrites, WriteOneThenZeroToOddAddresses) { + auto& inst = MockBackingStore::Instance(); + + // Only attempt writes for each address up to a limit that would NOT force a consolidated data write. + std::size_t writes_per_loop = (MOCK_WRITE_LOG_MAX_ENTRIES::value / 6) - 1; // Worst case is 6 writes for each pair of writes of 0/1 + std::size_t final_address; + for (uint32_t address = 1; address < WEAR_LEVELING_LOGICAL_SIZE; address += (writes_per_loop * 2)) { + // Clear things out + std::fill(verify_data.begin(), verify_data.end(), 0); + inst.reset_instance(); + wear_leveling_init(); + + // Loop through all the addresses in this range + std::size_t expected = 0; + for (uint32_t offset = 0; offset < (writes_per_loop * 2); offset += 2) { + // If we're about to exceed the limit of the logical store, skip the writes + if (address + offset + 2 > WEAR_LEVELING_LOGICAL_SIZE) { + break; + } + + // The default erased value of the wear-leveling cache is zero, so we write a one first, then a zero, to ensure a backing store write occurs. + uint16_t val = 1; + EXPECT_EQ(test_write(address + offset, &val, sizeof(val)), WEAR_LEVELING_SUCCESS) << "Write failed with incorrect status"; + val = 0; + EXPECT_EQ(test_write(address + offset, &val, sizeof(val)), WEAR_LEVELING_SUCCESS) << "Write failed with incorrect status"; + + std::size_t backing_store_writes_expected = 0; + if (address + offset < 63) { + // A U16 value of 0/1 at an odd address <64 will result in 2 backing writes each, so we need 4 backing writes for 2 logical writes + backing_store_writes_expected = 4; + } else if (address + offset == 63) { + // If we're straddling the boundary for optimised bytes (addr==64), then the first logical byte is written using the optimised write (1 backing + // store write), and the second logical byte uses a multibyte write (2 backing store writes) + backing_store_writes_expected = 2 // First logical bytes written using optimised log entries + + 4; // Second logical bytes written using multibyte log entries + } else { + // All other addresses result in a multibyte write (3 backing store writes) to write two local bytes of data + backing_store_writes_expected = 6; + } + + // Keep track of the total number of expected writes to the backing store + expected += backing_store_writes_expected; + + // Verify we're at the correct number of writes + EXPECT_EQ(std::distance(inst.log_begin(), inst.log_end()), expected) << "Write log doesn't match required number of backing store writes for address " << (address + offset); + + // Verify that the write log entries we expect are actually present + std::size_t write_index = expected - backing_store_writes_expected; + auto write_iter = inst.log_begin() + write_index; + write_log_entry_t e; + if (address + offset < 63) { + // A U16 value of 0/1 at an odd address <64 will result in 2 backing writes each, so we need 4 backing writes for 2 logical writes + for (std::size_t i = 0; i < 4; ++i) { + e.raw16[0] = write_iter->value; + EXPECT_EQ(LOG_ENTRY_GET_TYPE(e), LOG_ENTRY_TYPE_OPTIMIZED_64) << "Invalid write log entry type"; + ++write_iter; + } + } else if (address + offset == 63) { + // First log entry is the 64-addr optimised one + e.raw16[0] = write_iter->value; + EXPECT_EQ(LOG_ENTRY_GET_TYPE(e), LOG_ENTRY_TYPE_OPTIMIZED_64) << "Invalid write log entry type"; + ++write_iter; + + // Second log entry is the multibyte entry for the second logical byte + e.raw16[0] = write_iter->value; + EXPECT_EQ(LOG_ENTRY_GET_TYPE(e), LOG_ENTRY_TYPE_MULTIBYTE) << "Invalid write log entry type"; + EXPECT_EQ(LOG_ENTRY_MULTIBYTE_GET_LENGTH(e), 1) << "Invalid write log entry length"; + ++write_iter; + } else { + // Multibyte write + e.raw16[0] = write_iter->value; + EXPECT_EQ(LOG_ENTRY_GET_TYPE(e), LOG_ENTRY_TYPE_MULTIBYTE) << "Invalid write log entry type"; + EXPECT_EQ(LOG_ENTRY_MULTIBYTE_GET_LENGTH(e), 2) << "Invalid write log entry length"; + ++write_iter; + } + + // Keep track of the final address written, so we can verify the entire logical range was handled + final_address = address + offset; + } + + // Verify the number of writes that occurred to the backing store + size_t backing_write_count = std::distance(inst.log_begin(), inst.log_end()); + EXPECT_EQ(backing_write_count, expected) << "Invalid write count at address " << address; + + // Verify the data is what we expected + std::array readback; + EXPECT_EQ(wear_leveling_read(0, readback.data(), WEAR_LEVELING_LOGICAL_SIZE), WEAR_LEVELING_SUCCESS) << "Failed to read back the saved data"; + EXPECT_TRUE(memcmp(readback.data(), verify_data.data(), WEAR_LEVELING_LOGICAL_SIZE) == 0) << "Readback for address " << address << " did not match"; + + // Re-init and re-read, testing the reload capability + EXPECT_NE(wear_leveling_init(), WEAR_LEVELING_FAILED) << "Re-initialisation failed"; + EXPECT_EQ(wear_leveling_read(0, readback.data(), WEAR_LEVELING_LOGICAL_SIZE), WEAR_LEVELING_SUCCESS) << "Failed to read back the saved data"; + EXPECT_TRUE(memcmp(readback.data(), verify_data.data(), WEAR_LEVELING_LOGICAL_SIZE) == 0) << "Readback for address " << address << " did not match"; + } + + // Verify the full range of the logical area got written + EXPECT_EQ(final_address, WEAR_LEVELING_LOGICAL_SIZE - 3) << "Invalid final write address"; +} + +/** + * This test verifies readback after playback of the write log, simulating power loss and reboot. + */ +TEST_F(WearLeveling2ByteOptimizedWrites, PlaybackReadbackOptimized64_Success) { + auto& inst = MockBackingStore::Instance(); + auto logstart = inst.storage_begin() + (WEAR_LEVELING_LOGICAL_SIZE / sizeof(backing_store_int_t)); + + // Invalid FNV1a_64 hash + (logstart + 0)->set(0); + (logstart + 1)->set(0); + (logstart + 2)->set(0); + (logstart + 3)->set(0); + + // Set up a 1-byte logical write of 0x11 at logical offset 0x01 + auto entry0 = LOG_ENTRY_MAKE_OPTIMIZED_64(0x01, 0x11); + (logstart + 4)->set(~entry0.raw16[0]); // start at offset 4 to skip FNV1a_64 result + + wear_leveling_init(); + uint8_t tmp; + + wear_leveling_read(0x01, &tmp, sizeof(tmp)); + EXPECT_EQ(tmp, 0x11) << "Failed to read back the seeded data"; +} + +/** + * This test verifies readback after playback of the write log, simulating power loss and reboot. + */ +TEST_F(WearLeveling2ByteOptimizedWrites, PlaybackReadbackWord01_Success) { + auto& inst = MockBackingStore::Instance(); + auto logstart = inst.storage_begin() + (WEAR_LEVELING_LOGICAL_SIZE / sizeof(backing_store_int_t)); + + // Invalid FNV1a_64 hash + (logstart + 0)->set(0); + (logstart + 1)->set(0); + (logstart + 2)->set(0); + (logstart + 3)->set(0); + + // Set up a 1-byte logical write of 1 at logical offset 0x02 + auto entry0 = LOG_ENTRY_MAKE_WORD_01(0x02, 1); + (logstart + 4)->set(~entry0.raw16[0]); // start at offset 4 to skip FNV1a_64 result + + wear_leveling_init(); + uint8_t tmp; + + wear_leveling_read(0x02, &tmp, sizeof(tmp)); + EXPECT_EQ(tmp, 1) << "Failed to read back the seeded data"; +} diff --git a/quantum/wear_leveling/tests/wear_leveling_4byte.cpp b/quantum/wear_leveling/tests/wear_leveling_4byte.cpp new file mode 100644 index 0000000000..54482c5fe7 --- /dev/null +++ b/quantum/wear_leveling/tests/wear_leveling_4byte.cpp @@ -0,0 +1,193 @@ +// Copyright 2022 Nick Brassel (@tzarc) +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include "gtest/gtest.h" +#include "gmock/gmock.h" +#include "backing_mocks.hpp" + +class WearLeveling4Byte : public ::testing::Test { + protected: + void SetUp() override { + MockBackingStore::Instance().reset_instance(); + wear_leveling_init(); + } +}; + +static std::array verify_data; + +static wear_leveling_status_t test_write(const uint32_t address, const void* value, size_t length) { + memcpy(&verify_data[address], value, length); + return wear_leveling_write(address, value, length); +} + +/** + * This test verifies that the first write after initialisation occurs after the FNV1a_64 hash location. + */ +TEST_F(WearLeveling4Byte, FirstWriteOccursAfterHash) { + auto& inst = MockBackingStore::Instance(); + uint8_t test_value = 0x15; + test_write(0x02, &test_value, sizeof(test_value)); + EXPECT_EQ(inst.log_begin()->address, WEAR_LEVELING_LOGICAL_SIZE + 8) << "Invalid first write address."; +} + +/** + * This test verifies that the first write after initialisation occurs after the FNV1a_64 hash location, after an erase has occurred. + */ +TEST_F(WearLeveling4Byte, FirstWriteOccursAfterHash_AfterErase) { + auto& inst = MockBackingStore::Instance(); + uint8_t test_value = 0x15; + wear_leveling_erase(); + test_write(0x02, &test_value, sizeof(test_value)); + EXPECT_EQ((inst.log_begin() + 1)->address, WEAR_LEVELING_LOGICAL_SIZE + 8) << "Invalid first write address."; +} + +/** + * This test ensures the correct number of backing store writes occurs with a multibyte write, given the input buffer size. + */ +TEST_F(WearLeveling4Byte, MultibyteBackingStoreWriteCounts) { + auto& inst = MockBackingStore::Instance(); + + for (std::size_t length = 1; length <= 5; ++length) { + // Clear things out + std::fill(verify_data.begin(), verify_data.end(), 0); + inst.reset_instance(); + wear_leveling_init(); + + // Generate a test block of data + std::vector testvalue(length); + std::iota(testvalue.begin(), testvalue.end(), 0x20); + + // Write the data + EXPECT_EQ(test_write(0, testvalue.data(), testvalue.size()), WEAR_LEVELING_SUCCESS) << "Write failed with incorrect status"; + + std::size_t expected; + if (length > 1) { + expected = 2; + } else { + expected = 1; + } + + // Check that we got the expected number of write log entries + EXPECT_EQ(std::distance(inst.log_begin(), inst.log_end()), expected); + } +} + +/** + * This test forces consolidation by writing enough to the write log that it overflows, consolidating the data into the + * base logical area. + */ +TEST_F(WearLeveling4Byte, ConsolidationOverflow) { + auto& inst = MockBackingStore::Instance(); + + // Generate a test block of data + std::array testvalue; + + // Write the data + std::iota(testvalue.begin(), testvalue.end(), 0x20); + EXPECT_EQ(test_write(0, testvalue.data(), testvalue.size()), WEAR_LEVELING_CONSOLIDATED) << "Write returned incorrect status"; + uint8_t dummy = 0x40; + EXPECT_EQ(test_write(0x04, &dummy, sizeof(dummy)), WEAR_LEVELING_SUCCESS) << "Write returned incorrect status"; + + // Expected log: + // [0,1]: multibyte, 5 bytes, backing address 0x18, logical address 0x00 + // [2,3]: multibyte, 5 bytes, backing address 0x20, logical address 0x05 + // [4,5]: multibyte, 5 bytes, backing address 0x28, logical address 0x0A, triggers consolidation + // [6]: erase + // [7,8]: consolidated data, backing address 0x00, logical address 0x00 + // [9,10]: consolidated data, backing address 0x08, logical address 0x08 + // [11,12]: FNV1a_64 result, backing address 0x10 + // [13]: multibyte, 1 byte, backing address 0x18, logical address 0x04 + EXPECT_EQ(std::distance(inst.log_begin(), inst.log_end()), 14); + + // Verify the backing store writes for the write log + std::size_t index; + write_log_entry_t e; + for (index = 0; index < 6; ++index) { + auto write_iter = inst.log_begin() + index; + EXPECT_EQ(write_iter->address, WEAR_LEVELING_LOGICAL_SIZE + 8 + (index * BACKING_STORE_WRITE_SIZE)) << "Invalid write log address"; + + // If this is the backing store write that contains the metadata, verify it + if (index % 2 == 0) { + write_log_entry_t e; + e.raw64 = write_iter->value; + EXPECT_EQ(LOG_ENTRY_GET_TYPE(e), LOG_ENTRY_TYPE_MULTIBYTE) << "Invalid write log entry type"; + } + } + + // Verify the backing store erase + { + index = 6; + auto write_iter = inst.log_begin() + index; + e.raw64 = write_iter->value; + EXPECT_TRUE(write_iter->erased) << "Backing store erase did not occur as required"; + } + + // Verify the backing store writes for consolidation + for (index = 7; index < 11; ++index) { + auto write_iter = inst.log_begin() + index; + EXPECT_EQ(write_iter->address, (index - 7) * BACKING_STORE_WRITE_SIZE) << "Invalid write log entry address"; + } + + // Verify the FNV1a_64 write + { + EXPECT_EQ((inst.log_begin() + 11)->address, WEAR_LEVELING_LOGICAL_SIZE) << "Invalid write log address"; + e.raw32[0] = (inst.log_begin() + 11)->value; + e.raw32[1] = (inst.log_begin() + 12)->value; + EXPECT_EQ(e.raw64, fnv_64a_buf(testvalue.data(), testvalue.size(), FNV1A_64_INIT)) << "Invalid checksum"; // Note that checksum is based on testvalue, as we overwrote one byte and need to consult the consolidated data, not the current + } + + // Verify the final write + EXPECT_EQ((inst.log_begin() + 13)->address, WEAR_LEVELING_LOGICAL_SIZE + 8) << "Invalid write log address"; + + // Verify the data is what we expected + std::array readback; + EXPECT_EQ(wear_leveling_read(0, readback.data(), WEAR_LEVELING_LOGICAL_SIZE), WEAR_LEVELING_SUCCESS) << "Failed to read back the saved data"; + EXPECT_TRUE(memcmp(readback.data(), verify_data.data(), WEAR_LEVELING_LOGICAL_SIZE) == 0) << "Readback did not match"; + + // Re-init and re-read, verifying the reload capability + EXPECT_NE(wear_leveling_init(), WEAR_LEVELING_FAILED) << "Re-initialisation failed"; + EXPECT_EQ(wear_leveling_read(0, readback.data(), WEAR_LEVELING_LOGICAL_SIZE), WEAR_LEVELING_SUCCESS) << "Failed to read back the saved data"; + EXPECT_TRUE(memcmp(readback.data(), verify_data.data(), WEAR_LEVELING_LOGICAL_SIZE) == 0) << "Readback did not match"; +} + +/** + * This test verifies multibyte readback gets canceled with an out-of-bounds address. + */ +TEST_F(WearLeveling4Byte, PlaybackReadbackMultibyte_OOB) { + auto& inst = MockBackingStore::Instance(); + auto logstart = inst.storage_begin() + (WEAR_LEVELING_LOGICAL_SIZE / sizeof(backing_store_int_t)); + + // Invalid FNV1a_64 hash + (logstart + 0)->set(0); + (logstart + 1)->set(0); + + // Set up a 2-byte logical write of [0x11,0x12] at logical offset 0x01 + auto entry0 = LOG_ENTRY_MAKE_MULTIBYTE(0x01, 2); + entry0.raw8[3] = 0x11; + entry0.raw8[4] = 0x12; + (logstart + 2)->set(~entry0.raw32[0]); + (logstart + 3)->set(~entry0.raw32[1]); + + // Set up a 2-byte logical write of [0x13,0x14] at logical offset 0x1000 (out of bounds) + auto entry1 = LOG_ENTRY_MAKE_MULTIBYTE(0x1000, 2); + entry1.raw8[3] = 0x13; + entry1.raw8[4] = 0x14; + (logstart + 4)->set(~entry1.raw32[0]); + (logstart + 5)->set(~entry1.raw32[1]); + + // Set up a 2-byte logical write of [0x15,0x16] at logical offset 0x10 + auto entry2 = LOG_ENTRY_MAKE_MULTIBYTE(0x01, 2); + entry2.raw8[3] = 0x15; + entry2.raw8[4] = 0x16; + (logstart + 6)->set(~entry2.raw32[0]); + (logstart + 7)->set(~entry2.raw32[1]); + + EXPECT_EQ(inst.erasure_count(), 0) << "Invalid initial erase count"; + EXPECT_EQ(wear_leveling_init(), WEAR_LEVELING_CONSOLIDATED) << "Readback should have failed and triggered consolidation"; + EXPECT_EQ(inst.erasure_count(), 1) << "Invalid final erase count"; + + uint8_t buf[2]; + wear_leveling_read(0x01, buf, sizeof(buf)); + EXPECT_EQ(buf[0], 0x11) << "Readback should have maintained the previous pre-failure value from the write log"; + EXPECT_EQ(buf[1], 0x12) << "Readback should have maintained the previous pre-failure value from the write log"; +} diff --git a/quantum/wear_leveling/tests/wear_leveling_8byte.cpp b/quantum/wear_leveling/tests/wear_leveling_8byte.cpp new file mode 100644 index 0000000000..c27c21d034 --- /dev/null +++ b/quantum/wear_leveling/tests/wear_leveling_8byte.cpp @@ -0,0 +1,178 @@ +// Copyright 2022 Nick Brassel (@tzarc) +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include "gtest/gtest.h" +#include "gmock/gmock.h" +#include "backing_mocks.hpp" + +class WearLeveling8Byte : public ::testing::Test { + protected: + void SetUp() override { + MockBackingStore::Instance().reset_instance(); + wear_leveling_init(); + } +}; + +static std::array verify_data; + +static wear_leveling_status_t test_write(const uint32_t address, const void* value, size_t length) { + memcpy(&verify_data[address], value, length); + return wear_leveling_write(address, value, length); +} + +/** + * This test verifies that the first write after initialisation occurs after the FNV1a_64 hash location. + */ +TEST_F(WearLeveling8Byte, FirstWriteOccursAfterHash) { + auto& inst = MockBackingStore::Instance(); + uint8_t test_value = 0x15; + test_write(0x02, &test_value, sizeof(test_value)); + EXPECT_EQ(inst.log_begin()->address, WEAR_LEVELING_LOGICAL_SIZE + 8) << "Invalid first write address."; +} + +/** + * This test verifies that the first write after initialisation occurs after the FNV1a_64 hash location, after an erase has occurred. + */ +TEST_F(WearLeveling8Byte, FirstWriteOccursAfterHash_AfterErase) { + auto& inst = MockBackingStore::Instance(); + uint8_t test_value = 0x15; + wear_leveling_erase(); + test_write(0x02, &test_value, sizeof(test_value)); + EXPECT_EQ((inst.log_begin() + 1)->address, WEAR_LEVELING_LOGICAL_SIZE + 8) << "Invalid first write address."; +} + +/** + * This test ensures the correct number of backing store writes occurs with a multibyte write, given the input buffer size. + */ +TEST_F(WearLeveling8Byte, MultibyteBackingStoreWriteCounts) { + auto& inst = MockBackingStore::Instance(); + + for (std::size_t length = 1; length <= 5; ++length) { + // Clear things out + std::fill(verify_data.begin(), verify_data.end(), 0); + inst.reset_instance(); + wear_leveling_init(); + + // Generate a test block of data + std::vector testvalue(length); + std::iota(testvalue.begin(), testvalue.end(), 0x20); + + // Write the data + EXPECT_EQ(test_write(0, testvalue.data(), testvalue.size()), WEAR_LEVELING_SUCCESS) << "Write failed with incorrect status"; + + // Check that we got the expected number of write log entries + EXPECT_EQ(std::distance(inst.log_begin(), inst.log_end()), 1); + } +} + +/** + * This test forces consolidation by writing enough to the write log that it overflows, consolidating the data into the + * base logical area. + */ +TEST_F(WearLeveling8Byte, ConsolidationOverflow) { + auto& inst = MockBackingStore::Instance(); + + // Generate a test block of data + std::array testvalue; + + // Write the data + std::iota(testvalue.begin(), testvalue.end(), 0x20); + EXPECT_EQ(test_write(0, testvalue.data(), testvalue.size()), WEAR_LEVELING_CONSOLIDATED) << "Write returned incorrect status"; + uint8_t dummy = 0x40; + EXPECT_EQ(test_write(0x04, &dummy, sizeof(dummy)), WEAR_LEVELING_SUCCESS) << "Write returned incorrect status"; + + // Expected log: + // [0]: multibyte, 5 bytes, backing address 0x18, logical address 0x00 + // [1]: multibyte, 5 bytes, backing address 0x20, logical address 0x05 + // [2]: multibyte, 5 bytes, backing address 0x28, logical address 0x0A, triggers consolidation + // [3]: erase + // [4]: consolidated data, backing address 0x00, logical address 0x00 + // [5]: consolidated data, backing address 0x08, logical address 0x08 + // [6]: FNV1a_64 result, backing address 0x10 + // [7]: multibyte, 1 byte, backing address 0x18, logical address 0x04 + EXPECT_EQ(std::distance(inst.log_begin(), inst.log_end()), 8); + + // Verify the backing store writes for the write log + std::size_t index; + write_log_entry_t e; + for (index = 0; index < 3; ++index) { + auto write_iter = inst.log_begin() + index; + EXPECT_EQ(write_iter->address, WEAR_LEVELING_LOGICAL_SIZE + 8 + (index * BACKING_STORE_WRITE_SIZE)) << "Invalid write log address"; + + write_log_entry_t e; + e.raw64 = write_iter->value; + EXPECT_EQ(LOG_ENTRY_GET_TYPE(e), LOG_ENTRY_TYPE_MULTIBYTE) << "Invalid write log entry type"; + } + + // Verify the backing store erase + { + index = 3; + auto write_iter = inst.log_begin() + index; + e.raw64 = write_iter->value; + EXPECT_TRUE(write_iter->erased) << "Backing store erase did not occur as required"; + } + + // Verify the backing store writes for consolidation + for (index = 4; index < 6; ++index) { + auto write_iter = inst.log_begin() + index; + EXPECT_EQ(write_iter->address, (index - 4) * BACKING_STORE_WRITE_SIZE) << "Invalid write log entry address"; + } + + // Verify the FNV1a_64 write + { + EXPECT_EQ((inst.log_begin() + 6)->address, WEAR_LEVELING_LOGICAL_SIZE) << "Invalid write log address"; + e.raw64 = (inst.log_begin() + 6)->value; + EXPECT_EQ(e.raw64, fnv_64a_buf(testvalue.data(), testvalue.size(), FNV1A_64_INIT)) << "Invalid checksum"; // Note that checksum is based on testvalue, as we overwrote one byte and need to consult the consolidated data, not the current + } + + // Verify the final write + EXPECT_EQ((inst.log_begin() + 7)->address, WEAR_LEVELING_LOGICAL_SIZE + 8) << "Invalid write log address"; + + // Verify the data is what we expected + std::array readback; + EXPECT_EQ(wear_leveling_read(0, readback.data(), WEAR_LEVELING_LOGICAL_SIZE), WEAR_LEVELING_SUCCESS) << "Failed to read back the saved data"; + EXPECT_TRUE(memcmp(readback.data(), verify_data.data(), WEAR_LEVELING_LOGICAL_SIZE) == 0) << "Readback did not match"; + + // Re-init and re-read, verifying the reload capability + EXPECT_NE(wear_leveling_init(), WEAR_LEVELING_FAILED) << "Re-initialisation failed"; + EXPECT_EQ(wear_leveling_read(0, readback.data(), WEAR_LEVELING_LOGICAL_SIZE), WEAR_LEVELING_SUCCESS) << "Failed to read back the saved data"; + EXPECT_TRUE(memcmp(readback.data(), verify_data.data(), WEAR_LEVELING_LOGICAL_SIZE) == 0) << "Readback did not match"; +} + +/** + * This test verifies multibyte readback gets canceled with an out-of-bounds address. + */ +TEST_F(WearLeveling8Byte, PlaybackReadbackMultibyte_OOB) { + auto& inst = MockBackingStore::Instance(); + auto logstart = inst.storage_begin() + (WEAR_LEVELING_LOGICAL_SIZE / sizeof(backing_store_int_t)); + + // Invalid FNV1a_64 hash + (logstart + 0)->set(0); + + // Set up a 2-byte logical write of [0x11,0x12] at logical offset 0x01 + auto entry0 = LOG_ENTRY_MAKE_MULTIBYTE(0x01, 2); + entry0.raw8[3] = 0x11; + entry0.raw8[4] = 0x12; + (logstart + 1)->set(~entry0.raw64); + + // Set up a 2-byte logical write of [0x13,0x14] at logical offset 0x1000 (out of bounds) + auto entry1 = LOG_ENTRY_MAKE_MULTIBYTE(0x1000, 2); + entry1.raw8[3] = 0x13; + entry1.raw8[4] = 0x14; + (logstart + 2)->set(~entry1.raw64); + + // Set up a 2-byte logical write of [0x15,0x16] at logical offset 0x10 + auto entry2 = LOG_ENTRY_MAKE_MULTIBYTE(0x01, 2); + entry2.raw8[3] = 0x15; + entry2.raw8[4] = 0x16; + (logstart + 3)->set(~entry2.raw64); + + EXPECT_EQ(inst.erasure_count(), 0) << "Invalid initial erase count"; + EXPECT_EQ(wear_leveling_init(), WEAR_LEVELING_CONSOLIDATED) << "Readback should have failed and triggered consolidation"; + EXPECT_EQ(inst.erasure_count(), 1) << "Invalid final erase count"; + + uint8_t buf[2]; + wear_leveling_read(0x01, buf, sizeof(buf)); + EXPECT_EQ(buf[0], 0x11) << "Readback should have maintained the previous pre-failure value from the write log"; + EXPECT_EQ(buf[1], 0x12) << "Readback should have maintained the previous pre-failure value from the write log"; +} diff --git a/quantum/wear_leveling/tests/wear_leveling_general.cpp b/quantum/wear_leveling/tests/wear_leveling_general.cpp new file mode 100644 index 0000000000..76a4bf7bf3 --- /dev/null +++ b/quantum/wear_leveling/tests/wear_leveling_general.cpp @@ -0,0 +1,204 @@ +// Copyright 2022 Nick Brassel (@tzarc) +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include "gtest/gtest.h" +#include "gmock/gmock.h" +#include "backing_mocks.hpp" + +class WearLevelingGeneral : public ::testing::Test { + protected: + void SetUp() override { + MockBackingStore::Instance().reset_instance(); + wear_leveling_init(); + } +}; + +/** + * This test verifies that even if there is consolidated data present, if the checksum doesn't match then the cache is zero'd after reading the consolidated area, but before write log is played back. + */ +TEST_F(WearLevelingGeneral, InvalidChecksum_ConsolidatedDataIgnored) { + auto& inst = MockBackingStore::Instance(); + auto logstart = inst.storage_begin() + (WEAR_LEVELING_LOGICAL_SIZE / sizeof(backing_store_int_t)); + + // Generate a test block of data + std::array testvalue; + std::iota(testvalue.begin(), testvalue.end(), 0x20); + + // Write the data + EXPECT_EQ(wear_leveling_write(0, testvalue.data(), testvalue.size()), WEAR_LEVELING_CONSOLIDATED) << "Write returned incorrect status"; + + // Invalidate the checksum + (logstart + 0)->erase(); + (logstart + 1)->erase(); + (logstart + 2)->erase(); + (logstart + 3)->erase(); + + // Set up a 1-byte logical write of [0x11] at logical offset 0x01 + auto entry0 = LOG_ENTRY_MAKE_OPTIMIZED_64(0x01, 0x11); + (logstart + 4)->set(~entry0.raw16[0]); + + // Re-init + EXPECT_EQ(wear_leveling_init(), WEAR_LEVELING_SUCCESS) << "Init returned incorrect status"; + EXPECT_EQ(wear_leveling_read(0, testvalue.data(), testvalue.size()), WEAR_LEVELING_SUCCESS) << "Failed to read"; + for (int i = 0; i < WEAR_LEVELING_LOGICAL_SIZE; ++i) { + EXPECT_EQ(testvalue[i], i == 0x01 ? 0x11 : 0x00) << "Invalid readback"; + } +} + +/** + * This test verifies that writing the same data multiple times does not result in subsequent writes to the backing store. + */ +TEST_F(WearLevelingGeneral, SameValue_SingleBackingWrite) { + auto& inst = MockBackingStore::Instance(); + + uint8_t test_val = 0x14; + EXPECT_EQ(wear_leveling_write(0x02, &test_val, sizeof(test_val)), WEAR_LEVELING_SUCCESS) << "First overall write operation should have succeeded"; + + uint64_t invoke_count = inst.unlock_invoke_count(); + uint64_t erase_count = inst.erase_invoke_count(); + uint64_t write_count = inst.write_invoke_count(); + uint64_t lock_count = inst.lock_invoke_count(); + + for (int i = 0; i < 10; ++i) { + EXPECT_EQ(wear_leveling_write(0x02, &test_val, sizeof(test_val)), WEAR_LEVELING_SUCCESS) << "Subsequent overall write operation should have succeeded"; + + EXPECT_EQ(inst.unlock_invoke_count(), invoke_count) << "Unlock count should match"; + EXPECT_EQ(inst.erase_invoke_count(), erase_count) << "Erase count should match"; + EXPECT_EQ(inst.write_invoke_count(), write_count) << "Write count should match"; + EXPECT_EQ(inst.lock_invoke_count(), lock_count) << "Lock count should match"; + } +} + +/** + * This test verifies that no other invocations occur if `backing_store_init()` fails. + */ +TEST_F(WearLevelingGeneral, InitFailure) { + auto& inst = MockBackingStore::Instance(); + inst.reset_instance(); // make sure the counters are all zero + inst.set_init_callback([](std::uint64_t count) { return false; }); + + EXPECT_EQ(inst.erasure_count(), 0) << "Invalid initial erase count"; + EXPECT_EQ(wear_leveling_init(), WEAR_LEVELING_FAILED) << "Init should have failed"; + EXPECT_EQ(inst.erasure_count(), 0) << "Invalid final erase count"; + + EXPECT_EQ(inst.init_invoke_count(), 1) << "Init should have been invoked once"; + EXPECT_EQ(inst.unlock_invoke_count(), 0) << "Unlock should not have been invoked"; + EXPECT_EQ(inst.erase_invoke_count(), 0) << "Erase should not have been invoked"; + EXPECT_EQ(inst.write_invoke_count(), 0) << "Write should not have been invoked"; + EXPECT_EQ(inst.lock_invoke_count(), 0) << "Lock should not have been invoked"; +} + +/** + * This test verifies that no invocations occur if the supplied address is out of range while writing. + */ +TEST_F(WearLevelingGeneral, WriteFailure_OOB) { + auto& inst = MockBackingStore::Instance(); + + uint8_t test_val = 0x14; + EXPECT_EQ(wear_leveling_write(0x21349830, &test_val, sizeof(test_val)), WEAR_LEVELING_FAILED) << "Overall write operation should have failed"; + + EXPECT_EQ(inst.unlock_invoke_count(), 0) << "Unlock should not have been invoked"; + EXPECT_EQ(inst.erase_invoke_count(), 0) << "Erase should not have been invoked"; + EXPECT_EQ(inst.write_invoke_count(), 0) << "Write should not have been invoked"; + EXPECT_EQ(inst.lock_invoke_count(), 0) << "Lock should not have been invoked"; +} + +/** + * This test verifies that a single write occurs if the supplied address and data length hits the edge of the logical area. + */ +TEST_F(WearLevelingGeneral, WriteSuccess_BoundaryOK) { + auto& inst = MockBackingStore::Instance(); + + uint16_t test_val = 0x14; + EXPECT_EQ(wear_leveling_write(WEAR_LEVELING_LOGICAL_SIZE - sizeof(test_val), &test_val, sizeof(test_val)), WEAR_LEVELING_SUCCESS) << "Overall write operation should have succeeded"; + + EXPECT_EQ(inst.unlock_invoke_count(), 1) << "Unlock should have been invoked once"; + EXPECT_EQ(inst.erase_invoke_count(), 0) << "Erase should not have been invoked"; + EXPECT_EQ(inst.write_invoke_count(), 2) << "Write should have been invoked twice"; + EXPECT_EQ(inst.lock_invoke_count(), 1) << "Lock should have been invoked once"; +} + +/** + * This test verifies that no invocations occur if the supplied address and length would generate writes outside the logical range. + */ +TEST_F(WearLevelingGeneral, WriteFailure_BoundaryOverflow) { + auto& inst = MockBackingStore::Instance(); + + uint16_t test_val = 0x14; + EXPECT_EQ(wear_leveling_write(WEAR_LEVELING_LOGICAL_SIZE - sizeof(test_val) + 1, &test_val, sizeof(test_val)), WEAR_LEVELING_FAILED) << "Overall write operation should have failed"; + + EXPECT_EQ(inst.unlock_invoke_count(), 0) << "Unlock should not have been invoked"; + EXPECT_EQ(inst.erase_invoke_count(), 0) << "Erase should not have been invoked"; + EXPECT_EQ(inst.write_invoke_count(), 0) << "Write should not have been invoked"; + EXPECT_EQ(inst.lock_invoke_count(), 0) << "Lock should not have been invoked"; +} + +/** + * This test verifies that no invocations occur if the supplied address is out of range while reading. + */ +TEST_F(WearLevelingGeneral, ReadFailure_OOB) { + auto& inst = MockBackingStore::Instance(); + + uint8_t test_val = 0; + EXPECT_EQ(wear_leveling_read(0x21349830, &test_val, sizeof(test_val)), WEAR_LEVELING_FAILED) << "Overall read operation should have failed"; + + EXPECT_EQ(inst.unlock_invoke_count(), 0) << "Unlock should not have been invoked"; + EXPECT_EQ(inst.erase_invoke_count(), 0) << "Erase should not have been invoked"; + EXPECT_EQ(inst.write_invoke_count(), 0) << "Write should not have been invoked"; + EXPECT_EQ(inst.lock_invoke_count(), 0) << "Lock should not have been invoked"; +} + +/** + * This test verifies that no write invocations occur if `backing_store_unlock()` fails. + */ +TEST_F(WearLevelingGeneral, UnlockFailure_NoWrite) { + auto& inst = MockBackingStore::Instance(); + inst.set_unlock_callback([](std::uint64_t count) { return false; }); + + uint8_t test_val = 0x14; + EXPECT_EQ(wear_leveling_write(0x04, &test_val, sizeof(test_val)), WEAR_LEVELING_FAILED) << "Overall write operation should have failed"; + + EXPECT_EQ(inst.unlock_invoke_count(), 1) << "Unlock should have been invoked once"; + EXPECT_EQ(inst.erase_invoke_count(), 0) << "Erase should not have been invoked"; + EXPECT_EQ(inst.write_invoke_count(), 0) << "Write should not have been invoked"; + EXPECT_EQ(inst.lock_invoke_count(), 0) << "Lock should not have been invoked"; + + test_val = 0; + wear_leveling_read(0x04, &test_val, sizeof(test_val)); + EXPECT_EQ(test_val, 0x14) << "Readback should come from cache regardless of unlock failure"; +} + +/** + * This test verifies that no erase invocations occur if `backing_store_unlock()` fails. + */ +TEST_F(WearLevelingGeneral, UnlockFailure_NoErase) { + auto& inst = MockBackingStore::Instance(); + inst.set_unlock_callback([](std::uint64_t count) { return false; }); + + EXPECT_EQ(wear_leveling_erase(), WEAR_LEVELING_FAILED) << "Overall erase operation should have failed"; + + EXPECT_EQ(inst.unlock_invoke_count(), 1) << "Unlock should have been invoked once"; + EXPECT_EQ(inst.erase_invoke_count(), 0) << "Erase should not have been invoked"; + EXPECT_EQ(inst.write_invoke_count(), 0) << "Write should not have been invoked"; + EXPECT_EQ(inst.lock_invoke_count(), 0) << "Lock should not have been invoked"; +} + +/** + * This test verifies that only one write invocation occurs if `backing_store_write()` fails. + */ +TEST_F(WearLevelingGeneral, WriteFailure_NoSubsequentWrites) { + auto& inst = MockBackingStore::Instance(); + inst.set_write_callback([](std::uint64_t count, std::uint32_t address) { return false; }); + + uint8_t test_val = 0x14; + EXPECT_EQ(wear_leveling_write(0x04, &test_val, sizeof(test_val)), WEAR_LEVELING_FAILED) << "Overall write operation should have failed"; + + EXPECT_EQ(inst.unlock_invoke_count(), 1) << "Unlock should have been invoked once"; + EXPECT_EQ(inst.erase_invoke_count(), 0) << "Erase should not have been invoked"; + EXPECT_EQ(inst.write_invoke_count(), 1) << "Write should have been invoked once"; + EXPECT_EQ(inst.lock_invoke_count(), 1) << "Lock should have been invoked once"; + + test_val = 0; + wear_leveling_read(0x04, &test_val, sizeof(test_val)); + EXPECT_EQ(test_val, 0x14) << "Readback should come from cache regardless of unlock failure"; +} diff --git a/quantum/wear_leveling/wear_leveling.c b/quantum/wear_leveling/wear_leveling.c new file mode 100644 index 0000000000..8418ae77bf --- /dev/null +++ b/quantum/wear_leveling/wear_leveling.c @@ -0,0 +1,779 @@ +// Copyright 2022 Nick Brassel (@tzarc) +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include "fnv.h" +#include "wear_leveling.h" +#include "wear_leveling_internal.h" + +/* + This wear leveling algorithm is adapted from algorithms from previous + implementations in QMK, namely: + - Artur F. (http://engsta.com/stm32-flash-memory-eeprom-emulator/) + - Yiancar -- QMK's base implementation for STM32F303 + - Ilya Zhuravlev -- initial wear leveling algorithm + - Don Kjer -- increased flash density algorithm + - Nick Brassel (@tzarc) -- decoupled for use on other peripherals + + At this layer, it is assumed that any reads/writes from the backing store + have a "reset state" after erasure of zero. + It is up to the backing store to perform translation of values, such as + taking the complement in order to deal with flash memory's reset value. + + Terminology: + + - Backing store: this is the storage area used by the wear leveling + algorithm. + + - Backing size: this is the amount of storage provided by the backing + store for use by the wear leveling algorithm. + + - Backing write size: this is the minimum number of bytes the backing + store can write in a single operation. + + - Logical data: this is the externally-visible "emulated EEPROM" that + external subsystems "see" when performing reads/writes. + + - Logical size: this is the amount of storage available for use + externally. Effectively, the "size of the EEPROM". + + - Write log: this is a section of the backing store used to keep track + of modifications without overwriting existing data. This log is + "played back" on startup such that any subsequent reads are capable + of returning the latest data. + + - Consolidated data: this is a section of the backing store reserved for + use for the latest copy of logical data. This is only ever written + when the write log is full -- the latest values for the logical data + are written here and the write log is cleared. + + Configurables: + + - BACKING_STORE_WRITE_SIZE: The number of bytes requires for a write + operation. This is defined by the capabilities of the backing store. + + - WEAR_LEVELING_BACKING_SIZE: The number of bytes provided by the + backing store for use by the wear leveling algorithm. This is + defined by the capabilities of the backing store. This value must + also be at least twice the size of the logical size, as well as a + multiple of the logical size. + + - WEAR_LEVELING_LOGICAL_SIZE: The number of bytes externally visible + to other subsystems performing reads/writes. This must be a multiple + of the write size. + + General algorithm: + + During initialization: + * The contents of the consolidated data section are read into cache. + * The contents of the write log are "played back" and update the + cache accordingly. + + During reads: + * Logical data is served from the cache. + + During writes: + * The cache is updated with the new data. + * A new write log entry is appended to the log. + * If the log's full, data is consolidated and the write log cleared. + + Write log structure: + + The first 8 bytes of the write log are a FNV1a_64 hash of the contents + of the consolidated data area, in an attempt to detect and guard against + any data corruption. + + The write log follows the hash: + + Given that the algorithm needs to cater for 2-, 4-, and 8-byte writes, + a variable-length write log entry is used such that the minimal amount + of storage is used based off the backing store write size. + + Firstly, an empty log entry is expected to be all zeros. If the backing + store uses 0xFF for cleared bytes, it should return the complement, such + that this wear-leveling algorithm "receives" zeros. + + For multi-byte writes, up to 8 bytes will be used for each log entry, + depending on the size of backing store writes: + + ╔ Multi-byte Log Entry (2, 4-byte) ═╗ + ║00XXXYYY║YYYYYYYY║YYYYYYYY║AAAAAAAA║ + ║ └┬┘└┬┘║└──┬───┘║└──┬───┘║└──┬───┘║ + ║ LenAdd║ Address║ Address║Value[0]║ + ╚════════╩════════╩════════╩════════╝ + ╔ Multi-byte Log Entry (2-byte) ══════════════════════╗ + ║00XXXYYY║YYYYYYYY║YYYYYYYY║AAAAAAAA║BBBBBBBB║CCCCCCCC║ + ║ └┬┘└┬┘║└──┬───┘║└──┬───┘║└──┬───┘║└──┬───┘║└──┬───┘║ + ║ LenAdd║ Address║ Address║Value[0]║Value[1]║Value[2]║ + ╚════════╩════════╩════════╩════════╩════════╩════════╝ + ╔ Multi-byte Log Entry (2, 4, 8-byte) ══════════════════════════════════╗ + ║00XXXYYY║YYYYYYYY║YYYYYYYY║AAAAAAAA║BBBBBBBB║CCCCCCCC║DDDDDDDD║EEEEEEEE║ + ║ └┬┘└┬┘║└──┬───┘║└──┬───┘║└──┬───┘║└──┬───┘║└──┬───┘║└──┬───┘║└──┬───┘║ + ║ LenAdd║ Address║ Address║Value[0]║Value[1]║Value[2]║Value[3]║Value[4]║ + ╚════════╩════════╩════════╩════════╩════════╩════════╩════════╩════════╝ + + 19 bits are used for the address, which allows for a max logical size of + 512kB. Up to 5 bytes can be included in a single log entry. + + For 2-byte backing store writes, the last two bytes are optional + depending on the length of data to be written. Accordingly, either 3 + or 4 backing store write operations will occur. + For 4-byte backing store writes, either one or two write operations + occur, depending on the length. + For 8-byte backing store writes, one write operation occur. + + 2-byte backing store optimizations: + + For single byte writes, addresses between 0...63 are encoded in a single + backing store write operation. 4- and 8-byte backing stores do not have + this optimization as it does not minimize the number of bytes written. + + ╔ Byte-Entry ════╗ + ║01XXXXXXYYYYYYYY║ + ║ └─┬──┘└──┬───┘║ + ║ Address Value ║ + ╚════════════════╝ + 0 <= Address < 0x40 (64) + + A second optimization takes into account uint16_t writes of 0 or 1, + specifically catering for KC_NO and KC_TRANSPARENT in the dynamic keymap + subsystem. This is valid only for the first 16kB of logical data -- + addresses outside this range will use the multi-byte encoding above. + + ╔ U16-Encoded 0 ═╗ + ║100XXXXXXXXXXXXX║ + ║ │└─────┬─────┘║ + ║ │Address >> 1 ║ + ║ └── Value: 0 ║ + ╚════════════════╝ + 0 <= Address <= 0x3FFE (16382) + + ╔ U16-Encoded 1 ═╗ + ║101XXXXXXXXXXXXX║ + ║ │└─────┬─────┘║ + ║ │Address >> 1 ║ + ║ └── Value: 1 ║ + ╚════════════════╝ + 0 <= Address <= 0x3FFE (16382) */ + +/** + * Storage area for the wear-leveling cache. + */ +static struct __attribute__((__aligned__(BACKING_STORE_WRITE_SIZE))) { + __attribute__((__aligned__(BACKING_STORE_WRITE_SIZE))) uint8_t cache[(WEAR_LEVELING_LOGICAL_SIZE)]; + uint32_t write_address; + bool unlocked; +} wear_leveling; + +/** + * Locking helper: status + */ +typedef enum backing_store_lock_status_t { STATUS_FAILURE = 0, STATUS_SUCCESS, STATUS_UNCHANGED } backing_store_lock_status_t; + +/** + * Locking helper: unlock + */ +static inline backing_store_lock_status_t wear_leveling_unlock(void) { + if (wear_leveling.unlocked) { + return STATUS_UNCHANGED; + } + if (!backing_store_unlock()) { + return STATUS_FAILURE; + } + wear_leveling.unlocked = true; + return STATUS_SUCCESS; +} + +/** + * Locking helper: lock + */ +static inline backing_store_lock_status_t wear_leveling_lock(void) { + if (!wear_leveling.unlocked) { + return STATUS_UNCHANGED; + } + if (!backing_store_lock()) { + return STATUS_FAILURE; + } + wear_leveling.unlocked = false; + return STATUS_SUCCESS; +} + +/** + * Resets the cache, ensuring the write address is correctly initialised. + */ +static void wear_leveling_clear_cache(void) { + memset(wear_leveling.cache, 0, (WEAR_LEVELING_LOGICAL_SIZE)); + wear_leveling.write_address = (WEAR_LEVELING_LOGICAL_SIZE) + 8; // +8 is due to the FNV1a_64 of the consolidated buffer +} + +/** + * Reads the consolidated data from the backing store into the cache. + * Does not consider the write log. + */ +static wear_leveling_status_t wear_leveling_read_consolidated(void) { + wl_dprintf("Reading consolidated data\n"); + + wear_leveling_status_t status = WEAR_LEVELING_SUCCESS; + for (int address = 0; address < (WEAR_LEVELING_LOGICAL_SIZE); address += (BACKING_STORE_WRITE_SIZE)) { + backing_store_int_t *const loc = (backing_store_int_t *)&wear_leveling.cache[address]; + backing_store_int_t temp; + bool ok = backing_store_read(address, &temp); + if (!ok) { + wl_dprintf("Failed to read from backing store\n"); + status = WEAR_LEVELING_FAILED; + break; + } + *loc = temp; + } + + // Verify the FNV1a_64 result + if (status != WEAR_LEVELING_FAILED) { + uint64_t expected = fnv_64a_buf(wear_leveling.cache, (WEAR_LEVELING_LOGICAL_SIZE), FNV1A_64_INIT); + write_log_entry_t entry; +#if BACKING_STORE_WRITE_SIZE == 2 + backing_store_read((WEAR_LEVELING_LOGICAL_SIZE) + 0, &entry.raw16[0]); + backing_store_read((WEAR_LEVELING_LOGICAL_SIZE) + 2, &entry.raw16[1]); + backing_store_read((WEAR_LEVELING_LOGICAL_SIZE) + 4, &entry.raw16[2]); + backing_store_read((WEAR_LEVELING_LOGICAL_SIZE) + 6, &entry.raw16[3]); +#elif BACKING_STORE_WRITE_SIZE == 4 + backing_store_read((WEAR_LEVELING_LOGICAL_SIZE) + 0, &entry.raw32[0]); + backing_store_read((WEAR_LEVELING_LOGICAL_SIZE) + 4, &entry.raw32[1]); +#elif BACKING_STORE_WRITE_SIZE == 8 + backing_store_read((WEAR_LEVELING_LOGICAL_SIZE) + 0, &entry.raw64); +#endif + // If we have a mismatch, clear the cache but do not flag a failure, + // which will cater for the completely clean MCU case. + if (entry.raw64 != expected) { + wear_leveling_clear_cache(); + } + } + + // If we failed for any reason, then clear the cache + if (status == WEAR_LEVELING_FAILED) { + wear_leveling_clear_cache(); + } + + return status; +} + +/** + * Writes the current cache to consolidated data at the beginning of the backing store. + * Does not clear the write log. + */ +static wear_leveling_status_t wear_leveling_write_consolidated(void) { + wl_dprintf("Writing consolidated data\n"); + + wear_leveling_status_t status = WEAR_LEVELING_CONSOLIDATED; + backing_store_lock_status_t lock_status = wear_leveling_unlock(); + for (int address = 0; address < (WEAR_LEVELING_LOGICAL_SIZE); address += (BACKING_STORE_WRITE_SIZE)) { + const backing_store_int_t value = *(backing_store_int_t *)&wear_leveling.cache[address]; + backing_store_int_t temp; + bool ok = backing_store_read(address, &temp); + if (!ok) { + wl_dprintf("Failed to read from backing store\n"); + status = WEAR_LEVELING_FAILED; + break; + } + if (temp != value) { + ok = backing_store_write(address, value); + if (!ok) { + wl_dprintf("Failed to write to backing store\n"); + status = WEAR_LEVELING_FAILED; + break; + } + } + } + + if (status != WEAR_LEVELING_FAILED) { + // Write out the FNV1a_64 result of the consolidated data + write_log_entry_t entry; + entry.raw64 = fnv_64a_buf(wear_leveling.cache, (WEAR_LEVELING_LOGICAL_SIZE), FNV1A_64_INIT); + do { +#if BACKING_STORE_WRITE_SIZE == 2 + if (!backing_store_write((WEAR_LEVELING_LOGICAL_SIZE) + 0, entry.raw16[0])) { + status = WEAR_LEVELING_FAILED; + break; + } + if (!backing_store_write((WEAR_LEVELING_LOGICAL_SIZE) + 2, entry.raw16[1])) { + status = WEAR_LEVELING_FAILED; + break; + } + if (!backing_store_write((WEAR_LEVELING_LOGICAL_SIZE) + 4, entry.raw16[2])) { + status = WEAR_LEVELING_FAILED; + break; + } + if (!backing_store_write((WEAR_LEVELING_LOGICAL_SIZE) + 6, entry.raw16[3])) { + status = WEAR_LEVELING_FAILED; + break; + } +#elif BACKING_STORE_WRITE_SIZE == 4 + if (!backing_store_write((WEAR_LEVELING_LOGICAL_SIZE) + 0, entry.raw32[0])) { + status = WEAR_LEVELING_FAILED; + break; + } + if (!backing_store_write((WEAR_LEVELING_LOGICAL_SIZE) + 4, entry.raw32[1])) { + status = WEAR_LEVELING_FAILED; + break; + } +#elif BACKING_STORE_WRITE_SIZE == 8 + if (!backing_store_write((WEAR_LEVELING_LOGICAL_SIZE) + 0, entry.raw64)) { + status = WEAR_LEVELING_FAILED; + break; + } +#endif + } while (0); + } + + if (lock_status == STATUS_SUCCESS) { + wear_leveling_lock(); + } + return status; +} + +/** + * Forces a write of the current cache. + * Erases the backing store, including the write log. + * During this operation, there is the potential for data loss if a power loss occurs. + */ +static wear_leveling_status_t wear_leveling_consolidate_force(void) { + wl_dprintf("Erasing backing store\n"); + + // Erase the backing store. Expectation is that any un-written values that are read back after this call come back as zero. + bool ok = backing_store_erase(); + if (!ok) { + wl_dprintf("Failed to erase backing store\n"); + return WEAR_LEVELING_FAILED; + } + + // Write the cache to the first section of the backing store. + wear_leveling_status_t status = wear_leveling_write_consolidated(); + if (status == WEAR_LEVELING_FAILED) { + wl_dprintf("Failed to write consolidated data\n"); + } + + // Next write of the log occurs after the consolidated values at the start of the backing store. + wear_leveling.write_address = (WEAR_LEVELING_LOGICAL_SIZE) + 8; // +8 due to the FNV1a_64 of the consolidated area + + return status; +} + +/** + * Potential write of the current cache to the backing store. + * Skipped if the current write log position is not at the end of the backing store. + * During this operation, there is the potential for data loss if a power loss occurs. + * + * @return true if consolidation occurred + */ +static wear_leveling_status_t wear_leveling_consolidate_if_needed(void) { + if (wear_leveling.write_address >= (WEAR_LEVELING_BACKING_SIZE)) { + return wear_leveling_consolidate_force(); + } + + return WEAR_LEVELING_SUCCESS; +} + +/** + * Appends the supplied fixed-width entry to the write log, optionally consolidating if the log is full. + * + * @return true if consolidation occurred + */ +static wear_leveling_status_t wear_leveling_append_raw(backing_store_int_t value) { + bool ok = backing_store_write(wear_leveling.write_address, value); + if (!ok) { + wl_dprintf("Failed to write to backing store\n"); + return WEAR_LEVELING_FAILED; + } + wear_leveling.write_address += (BACKING_STORE_WRITE_SIZE); + return wear_leveling_consolidate_if_needed(); +} + +/** + * Handles writing multi_byte-encoded data to the backing store. + * + * @return true if consolidation occurred + */ +static wear_leveling_status_t wear_leveling_write_raw_multibyte(uint32_t address, const void *value, size_t length) { + const uint8_t * p = value; + write_log_entry_t log = LOG_ENTRY_MAKE_MULTIBYTE(address, length); + for (size_t i = 0; i < length; ++i) { + log.raw8[3 + i] = p[i]; + } + + // Write to the backing store. See the multi-byte log format in the documentation header at the top of the file. + wear_leveling_status_t status; +#if BACKING_STORE_WRITE_SIZE == 2 + status = wear_leveling_append_raw(log.raw16[0]); + if (status != WEAR_LEVELING_SUCCESS) { + return status; + } + + status = wear_leveling_append_raw(log.raw16[1]); + if (status != WEAR_LEVELING_SUCCESS) { + return status; + } + + if (length > 1) { + status = wear_leveling_append_raw(log.raw16[2]); + if (status != WEAR_LEVELING_SUCCESS) { + return status; + } + } + + if (length > 3) { + status = wear_leveling_append_raw(log.raw16[3]); + if (status != WEAR_LEVELING_SUCCESS) { + return status; + } + } +#elif BACKING_STORE_WRITE_SIZE == 4 + status = wear_leveling_append_raw(log.raw32[0]); + if (status != WEAR_LEVELING_SUCCESS) { + return status; + } + + if (length > 1) { + status = wear_leveling_append_raw(log.raw32[1]); + if (status != WEAR_LEVELING_SUCCESS) { + return status; + } + } +#elif BACKING_STORE_WRITE_SIZE == 8 + status = wear_leveling_append_raw(log.raw64); + if (status != WEAR_LEVELING_SUCCESS) { + return status; + } +#endif + return status; +} + +/** + * Handles the actual writing of logical data into the write log section of the backing store. + */ +static wear_leveling_status_t wear_leveling_write_raw(uint32_t address, const void *value, size_t length) { + const uint8_t * p = value; + size_t remaining = length; + wear_leveling_status_t status = WEAR_LEVELING_SUCCESS; + while (remaining > 0) { +#if BACKING_STORE_WRITE_SIZE == 2 + // Small-write optimizations - uint16_t, 0 or 1, address is even, address <16384: + if (remaining >= 2 && address % 2 == 0 && address < 16384) { + const uint16_t v = *(const uint16_t *)p; + if (v == 0 || v == 1) { + const write_log_entry_t log = LOG_ENTRY_MAKE_WORD_01(address, v); + status = wear_leveling_append_raw(log.raw16[0]); + if (status != WEAR_LEVELING_SUCCESS) { + // If consolidation occurred, then the cache has already been written to the consolidated area. No need to continue. + // If a failure occurred, pass it on. + return status; + } + + remaining -= 2; + address += 2; + p += 2; + continue; + } + } + + // Small-write optimizations - address<64: + if (address < 64) { + const write_log_entry_t log = LOG_ENTRY_MAKE_OPTIMIZED_64(address, *p); + status = wear_leveling_append_raw(log.raw16[0]); + if (status != WEAR_LEVELING_SUCCESS) { + // If consolidation occurred, then the cache has already been written to the consolidated area. No need to continue. + // If a failure occurred, pass it on. + return status; + } + + remaining--; + address++; + p++; + continue; + } +#endif // BACKING_STORE_WRITE_SIZE == 2 + const size_t this_length = remaining >= LOG_ENTRY_MULTIBYTE_MAX_BYTES ? LOG_ENTRY_MULTIBYTE_MAX_BYTES : remaining; + status = wear_leveling_write_raw_multibyte(address, p, this_length); + if (status != WEAR_LEVELING_SUCCESS) { + // If consolidation occurred, then the cache has already been written to the consolidated area. No need to continue. + // If a failure occurred, pass it on. + return status; + } + remaining -= this_length; + address += (uint32_t)this_length; + p += this_length; + } + + return status; +} + +/** + * "Replays" the write log from the backing store, updating the local cache with updated values. + */ +static wear_leveling_status_t wear_leveling_playback_log(void) { + wl_dprintf("Playback write log\n"); + + wear_leveling_status_t status = WEAR_LEVELING_SUCCESS; + bool cancel_playback = false; + uint32_t address = (WEAR_LEVELING_LOGICAL_SIZE) + 8; // +8 due to the FNV1a_64 of the consolidated area + while (!cancel_playback && address < (WEAR_LEVELING_BACKING_SIZE)) { + backing_store_int_t value; + bool ok = backing_store_read(address, &value); + if (!ok) { + wl_dprintf("Failed to load from backing store, skipping playback of write log\n"); + cancel_playback = true; + status = WEAR_LEVELING_FAILED; + break; + } + if (value == 0) { + wl_dprintf("Found empty slot, no more log entries\n"); + cancel_playback = true; + break; + } + + // If we got a nonzero value, then we need to increment the address to ensure next write occurs at next location + address += (BACKING_STORE_WRITE_SIZE); + + // Read from the write log + write_log_entry_t log; +#if BACKING_STORE_WRITE_SIZE == 2 + log.raw16[0] = value; +#elif BACKING_STORE_WRITE_SIZE == 4 + log.raw32[0] = value; +#elif BACKING_STORE_WRITE_SIZE == 8 + log.raw64 = value; +#endif + + switch (LOG_ENTRY_GET_TYPE(log)) { + case LOG_ENTRY_TYPE_MULTIBYTE: { +#if BACKING_STORE_WRITE_SIZE == 2 + ok = backing_store_read(address, &log.raw16[1]); + if (!ok) { + wl_dprintf("Failed to load from backing store, skipping playback of write log\n"); + cancel_playback = true; + status = WEAR_LEVELING_FAILED; + break; + } + address += (BACKING_STORE_WRITE_SIZE); +#endif // BACKING_STORE_WRITE_SIZE == 2 + const uint32_t a = LOG_ENTRY_MULTIBYTE_GET_ADDRESS(log); + const uint8_t l = LOG_ENTRY_MULTIBYTE_GET_LENGTH(log); + + if (a + l > (WEAR_LEVELING_LOGICAL_SIZE)) { + cancel_playback = true; + status = WEAR_LEVELING_FAILED; + break; + } + +#if BACKING_STORE_WRITE_SIZE == 2 + if (l > 1) { + ok = backing_store_read(address, &log.raw16[2]); + if (!ok) { + wl_dprintf("Failed to load from backing store, skipping playback of write log\n"); + cancel_playback = true; + status = WEAR_LEVELING_FAILED; + break; + } + address += (BACKING_STORE_WRITE_SIZE); + } + if (l > 3) { + ok = backing_store_read(address, &log.raw16[3]); + if (!ok) { + wl_dprintf("Failed to load from backing store, skipping playback of write log\n"); + cancel_playback = true; + status = WEAR_LEVELING_FAILED; + break; + } + address += (BACKING_STORE_WRITE_SIZE); + } +#elif BACKING_STORE_WRITE_SIZE == 4 + if (l > 1) { + ok = backing_store_read(address, &log.raw32[1]); + if (!ok) { + wl_dprintf("Failed to load from backing store, skipping playback of write log\n"); + cancel_playback = true; + status = WEAR_LEVELING_FAILED; + break; + } + address += (BACKING_STORE_WRITE_SIZE); + } +#endif + + memcpy(&wear_leveling.cache[a], &log.raw8[3], l); + } break; +#if BACKING_STORE_WRITE_SIZE == 2 + case LOG_ENTRY_TYPE_OPTIMIZED_64: { + const uint32_t a = LOG_ENTRY_OPTIMIZED_64_GET_ADDRESS(log); + const uint8_t v = LOG_ENTRY_OPTIMIZED_64_GET_VALUE(log); + + if (a >= (WEAR_LEVELING_LOGICAL_SIZE)) { + cancel_playback = true; + status = WEAR_LEVELING_FAILED; + break; + } + + wear_leveling.cache[a] = v; + } break; + case LOG_ENTRY_TYPE_WORD_01: { + const uint32_t a = LOG_ENTRY_WORD_01_GET_ADDRESS(log); + const uint8_t v = LOG_ENTRY_WORD_01_GET_VALUE(log); + + if (a + 1 >= (WEAR_LEVELING_LOGICAL_SIZE)) { + cancel_playback = true; + status = WEAR_LEVELING_FAILED; + break; + } + + wear_leveling.cache[a + 0] = v; + wear_leveling.cache[a + 1] = 0; + } break; +#endif // BACKING_STORE_WRITE_SIZE == 2 + default: { + cancel_playback = true; + status = WEAR_LEVELING_FAILED; + } break; + } + } + + // We've reached the end of the log, so we're at the new write location + wear_leveling.write_address = address; + + if (status == WEAR_LEVELING_FAILED) { + // If we had a failure during readback, assume we're corrupted -- force a consolidation with the data we already have + status = wear_leveling_consolidate_force(); + } else { + // Consolidate the cache + write log if required + status = wear_leveling_consolidate_if_needed(); + } + + return status; +} + +/** + * Wear-leveling initialization + */ +wear_leveling_status_t wear_leveling_init(void) { + wl_dprintf("Init\n"); + + // Reset the cache + wear_leveling_clear_cache(); + + // Initialise the backing store + if (!backing_store_init()) { + // If it failed, clear the cache and return with failure + wear_leveling_clear_cache(); + return WEAR_LEVELING_FAILED; + } + + // Read the previous consolidated values, then replay the existing write log so that the cache has the "live" values + wear_leveling_status_t status = wear_leveling_read_consolidated(); + if (status == WEAR_LEVELING_FAILED) { + // If it failed, clear the cache and return with failure + wear_leveling_clear_cache(); + return status; + } + + status = wear_leveling_playback_log(); + if (status == WEAR_LEVELING_FAILED) { + // If it failed, clear the cache and return with failure + wear_leveling_clear_cache(); + return status; + } + + return status; +} + +/** + * Wear-leveling erase. + * Post-condition: any reads from the backing store directly after an erase operation must come back as zero. + */ +wear_leveling_status_t wear_leveling_erase(void) { + wl_dprintf("Erase\n"); + + // Unlock the backing store + backing_store_lock_status_t lock_status = wear_leveling_unlock(); + if (lock_status == STATUS_FAILURE) { + wear_leveling_lock(); + return WEAR_LEVELING_FAILED; + } + + // Perform the erase + bool ret = backing_store_erase(); + wear_leveling_clear_cache(); + + // Lock the backing store if we acquired the lock successfully + if (lock_status == STATUS_SUCCESS) { + ret &= (wear_leveling_lock() != STATUS_FAILURE); + } + + return ret ? WEAR_LEVELING_SUCCESS : WEAR_LEVELING_FAILED; +} + +/** + * Writes logical data into the backing store. Skips writes if there are no changes to values. + */ +wear_leveling_status_t wear_leveling_write(const uint32_t address, const void *value, size_t length) { + wl_assert(address + length <= (WEAR_LEVELING_LOGICAL_SIZE)); + if (address + length > (WEAR_LEVELING_LOGICAL_SIZE)) { + return WEAR_LEVELING_FAILED; + } + + wl_dprintf("Write "); + wl_dump(address, value, length); + + // Skip write if there's no change compared to the current cached value + if (memcmp(value, &wear_leveling.cache[address], length) == 0) { + return true; + } + + // Update the cache before writing to the backing store -- if we hit the end of the backing store during writes to the log then we'll force a consolidation in-line + memcpy(&wear_leveling.cache[address], value, length); + + // Unlock the backing store + backing_store_lock_status_t lock_status = wear_leveling_unlock(); + if (lock_status == STATUS_FAILURE) { + wear_leveling_lock(); + return WEAR_LEVELING_FAILED; + } + + // Perform the actual write + wear_leveling_status_t status = wear_leveling_write_raw(address, value, length); + switch (status) { + case WEAR_LEVELING_CONSOLIDATED: + case WEAR_LEVELING_FAILED: + // If the write triggered consolidation, or the write failed, then nothing else needs to occur. + break; + + case WEAR_LEVELING_SUCCESS: + // Consolidate the cache + write log if required + status = wear_leveling_consolidate_if_needed(); + break; + + default: + // Unsure how we'd get here... + status = WEAR_LEVELING_FAILED; + break; + } + + if (lock_status == STATUS_SUCCESS) { + if (wear_leveling_lock() == STATUS_FAILURE) { + status = WEAR_LEVELING_FAILED; + } + } + + return status; +} + +/** + * Reads logical data from the cache. + */ +wear_leveling_status_t wear_leveling_read(const uint32_t address, void *value, size_t length) { + wl_assert(address + length <= (WEAR_LEVELING_LOGICAL_SIZE)); + if (address + length > (WEAR_LEVELING_LOGICAL_SIZE)) { + return WEAR_LEVELING_FAILED; + } + + // Only need to copy from the cache + memcpy(value, &wear_leveling.cache[address], length); + + wl_dprintf("Read "); + wl_dump(address, value, length); + return WEAR_LEVELING_SUCCESS; +} diff --git a/quantum/wear_leveling/wear_leveling.h b/quantum/wear_leveling/wear_leveling.h new file mode 100644 index 0000000000..6641bc49b3 --- /dev/null +++ b/quantum/wear_leveling/wear_leveling.h @@ -0,0 +1,54 @@ +// Copyright 2022 Nick Brassel (@tzarc) +// SPDX-License-Identifier: GPL-2.0-or-later +#pragma once +#include +#include + +/** + * @typedef Status returned from any wear-leveling API. + */ +typedef enum wear_leveling_status_t { + WEAR_LEVELING_FAILED, //< Invocation failed + WEAR_LEVELING_SUCCESS, //< Invocation succeeded + WEAR_LEVELING_CONSOLIDATED //< Invocation succeeded, consolidation occurred +} wear_leveling_status_t; + +/** + * Wear-leveling initialization + * + * @return Status of the request + */ +wear_leveling_status_t wear_leveling_init(void); + +/** + * Wear-leveling erasure. + * + * Clears the wear-leveling area, with the definition that the "reset state" of all data is zero. + * + * @return Status of the request + */ +wear_leveling_status_t wear_leveling_erase(void); + +/** + * Writes logical data into the backing store. + * + * Skips writes if there are no changes to written values. The entire written block is considered when attempting to + * determine if an overwrite should occur -- if there is any data mismatch the entire block will be written to the log, + * not just the changed bytes. + * + * @param address[in] the logical address to write data + * @param value[in] pointer to the source buffer + * @param length[in] length of the data + * @return Status of the request + */ +wear_leveling_status_t wear_leveling_write(uint32_t address, const void* value, size_t length); + +/** + * Reads logical data from the cache. + * + * @param address[in] the logical address to read data + * @param value[out] pointer to the destination buffer + * @param length[in] length of the data + * @return Status of the request + */ +wear_leveling_status_t wear_leveling_read(uint32_t address, void* value, size_t length); diff --git a/quantum/wear_leveling/wear_leveling_internal.h b/quantum/wear_leveling/wear_leveling_internal.h new file mode 100644 index 0000000000..74b43932df --- /dev/null +++ b/quantum/wear_leveling/wear_leveling_internal.h @@ -0,0 +1,145 @@ +// Copyright 2022 Nick Brassel (@tzarc) +// SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + +#ifdef __cplusplus +# define _Static_assert static_assert +#endif + +#include +#include + +#if BACKING_STORE_WRITE_SIZE == 2 +typedef uint16_t backing_store_int_t; +#elif BACKING_STORE_WRITE_SIZE == 4 +typedef uint32_t backing_store_int_t; +#elif BACKING_STORE_WRITE_SIZE == 8 +typedef uint64_t backing_store_int_t; +#else +# error Invalid BACKING_STORE_WRITE_SIZE, needs to be 2/4/8. +#endif + +#ifndef WEAR_LEVELING_BACKING_SIZE +# error WEAR_LEVELING_BACKING_SIZE was not set. +#endif + +#ifndef WEAR_LEVELING_LOGICAL_SIZE +# error WEAR_LEVELING_LOGICAL_SIZE was not set. +#endif + +#ifdef WEAR_LEVELING_DEBUG_OUTPUT +# include +# define wl_dprintf(...) printf("Wear leveling: " __VA_ARGS__) +# define wl_dump(address, value, length) \ + do { \ + printf("[0x%04X]: ", (int)(address)); \ + const uint8_t* p = (const uint8_t*)(value); \ + for (int i = 0; i < (length); ++i) { \ + printf(" %02X", (int)p[i]); \ + } \ + printf("\n"); \ + } while (0) +#else +# define wl_dprintf(...) \ + do { \ + } while (0) +# define wl_dump(...) \ + do { \ + } while (0) +#endif // WEAR_LEVELING_DEBUG_OUTPUT + +#ifdef WEAR_LEVELING_ASSERTS +# include +# define wl_assert(...) assert(__VA_ARGS__) +#else +# define wl_assert(...) \ + do { \ + } while (0) +#endif // WEAR_LEVELING_ASSERTS + +// Compile-time validation of configurable options +_Static_assert(WEAR_LEVELING_BACKING_SIZE >= (WEAR_LEVELING_LOGICAL_SIZE * 2), "Total backing size must be at least twice the size of the logical size"); +_Static_assert(WEAR_LEVELING_LOGICAL_SIZE % BACKING_STORE_WRITE_SIZE == 0, "Logical size must be a multiple of write size"); +_Static_assert(WEAR_LEVELING_BACKING_SIZE % WEAR_LEVELING_LOGICAL_SIZE == 0, "Backing size must be a multiple of logical size"); + +// Backing Store API, to be implemented elsewhere by flash driver etc. +bool backing_store_init(void); +bool backing_store_unlock(void); +bool backing_store_erase(void); +bool backing_store_write(uint32_t address, backing_store_int_t value); +bool backing_store_lock(void); +bool backing_store_read(uint32_t address, backing_store_int_t* value); + +/** + * Helper type used to contain a write log entry. + */ +typedef union write_log_entry_t { + uint64_t raw64; + uint32_t raw32[2]; + uint16_t raw16[4]; + uint8_t raw8[8]; +} write_log_entry_t; + +_Static_assert(sizeof(write_log_entry_t) == 8, "Wear leveling write log entry size was not 8"); + +/** + * Log entry type discriminator. + */ +enum { + // 0x00 -- Multi-byte storage type + LOG_ENTRY_TYPE_MULTIBYTE, + + // 0x01 -- 2-byte backing store write optimization: address < 64 + LOG_ENTRY_TYPE_OPTIMIZED_64, + + // 0x02 -- 2-byte backing store write optimization: word-encoded 0/1 values + LOG_ENTRY_TYPE_WORD_01, + + LOG_ENTRY_TYPES +}; + +_Static_assert(LOG_ENTRY_TYPES <= (1 << 2), "Too many log entry types to fit into 2 bits of storage"); + +#define BITMASK_FOR_BITCOUNT(n) ((1 << (n)) - 1) + +#define LOG_ENTRY_GET_TYPE(entry) (((entry).raw8[0] >> 6) & BITMASK_FOR_BITCOUNT(2)) + +#define LOG_ENTRY_MULTIBYTE_MAX_BYTES 5 +#define LOG_ENTRY_MULTIBYTE_GET_ADDRESS(entry) (((((uint32_t)((entry).raw8[0])) & BITMASK_FOR_BITCOUNT(3)) << 16) | (((uint32_t)((entry).raw8[1])) << 8) | (entry).raw8[2]) +#define LOG_ENTRY_MULTIBYTE_GET_LENGTH(entry) ((uint8_t)(((entry).raw8[0] >> 3) & BITMASK_FOR_BITCOUNT(3))) +#define LOG_ENTRY_MAKE_MULTIBYTE(address, length) \ + (write_log_entry_t) { \ + .raw8 = { \ + [0] = (((((uint8_t)LOG_ENTRY_TYPE_MULTIBYTE) & BITMASK_FOR_BITCOUNT(2)) << 6) /* type */ \ + | ((((uint8_t)(length)) & BITMASK_FOR_BITCOUNT(3)) << 3) /* length */ \ + | ((((uint8_t)((address) >> 16))) & BITMASK_FOR_BITCOUNT(3)) /* address */ \ + ), \ + [1] = (((uint8_t)((address) >> 8)) & BITMASK_FOR_BITCOUNT(8)), /* address */ \ + [2] = (((uint8_t)(address)) & BITMASK_FOR_BITCOUNT(8)), /* address */ \ + } \ + } + +#define LOG_ENTRY_OPTIMIZED_64_GET_ADDRESS(entry) ((uint32_t)((entry).raw8[0] & BITMASK_FOR_BITCOUNT(6))) +#define LOG_ENTRY_OPTIMIZED_64_GET_VALUE(entry) ((entry).raw8[1]) +#define LOG_ENTRY_MAKE_OPTIMIZED_64(address, value) \ + (write_log_entry_t) { \ + .raw8 = { \ + [0] = (((((uint8_t)LOG_ENTRY_TYPE_OPTIMIZED_64) & BITMASK_FOR_BITCOUNT(2)) << 6) /* type */ \ + | ((((uint8_t)(address))) & BITMASK_FOR_BITCOUNT(6)) /* address */ \ + ), \ + [1] = ((uint8_t)(value)), /* value */ \ + } \ + } + +#define LOG_ENTRY_WORD_01_GET_ADDRESS(entry) ((((uint32_t)(((entry).raw8[0]) & BITMASK_FOR_BITCOUNT(5))) << 9) | (((uint32_t)((entry).raw8[1])) << 1)) +#define LOG_ENTRY_WORD_01_GET_VALUE(entry) ((uint8_t)((entry).raw8[0] >> 5) & BITMASK_FOR_BITCOUNT(1)) +#define LOG_ENTRY_MAKE_WORD_01(address, value) \ + (write_log_entry_t) { \ + .raw8 = { \ + [0] = (((((uint8_t)LOG_ENTRY_TYPE_WORD_01) & BITMASK_FOR_BITCOUNT(2)) << 6) /* type */ \ + | (((((uint8_t)((value) ? 1 : 0))) & BITMASK_FOR_BITCOUNT(1)) << 5) /* value */ \ + | ((((uint8_t)((address) >> 9))) & BITMASK_FOR_BITCOUNT(5)) /* address */ \ + ), \ + [1] = (uint8_t)((address) >> 1), /* address */ \ + } \ + } -- cgit v1.2.3 From 43f8d365ba01b1e61b0c6cbbf6ef28eea9dee2b9 Mon Sep 17 00:00:00 2001 From: Drashna Jaelre Date: Tue, 28 Jun 2022 15:28:44 -0700 Subject: [Bug] Fix issue with mousekey movement getting stuck (#17493) * [Bug] Fix issue with mousekey movement getting stuck * Lint --- quantum/mousekey.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'quantum') diff --git a/quantum/mousekey.c b/quantum/mousekey.c index 64d0e66682..33d71af083 100644 --- a/quantum/mousekey.c +++ b/quantum/mousekey.c @@ -70,6 +70,10 @@ uint8_t mk_wheel_interval = MOUSEKEY_WHEEL_INTERVAL; uint8_t mk_wheel_max_speed = MOUSEKEY_WHEEL_MAX_SPEED; uint8_t mk_wheel_time_to_max = MOUSEKEY_WHEEL_TIME_TO_MAX; +bool should_mousekey_report_send(report_mouse_t *mouse_report) { + return mouse_report->x || mouse_report->y || mouse_report->v || mouse_report->h; +} + # ifndef MK_COMBINED static uint8_t move_unit(void) { @@ -252,7 +256,7 @@ void mousekey_task(void) { } } - if (has_mouse_report_changed(&mouse_report, &tmpmr)) { + if (has_mouse_report_changed(&mouse_report, &tmpmr) || should_mousekey_report_send(&mouse_report)) { mousekey_send(); } memcpy(&mouse_report, &tmpmr, sizeof(tmpmr)); @@ -358,7 +362,7 @@ void mousekey_task(void) { mouse_report.h = tmpmr.h; } - if (has_mouse_report_changed(&mouse_report, &tmpmr)) { + if (has_mouse_report_changed(&mouse_report, &tmpmr) || should_mousekey_report_send(&mouse_report)) { mousekey_send(); } memcpy(&mouse_report, &tmpmr, sizeof(tmpmr)); -- cgit v1.2.3 From 74bec84740a2cc06e4409d28886fafb6afad1ef6 Mon Sep 17 00:00:00 2001 From: Drashna Jaelre Date: Wed, 29 Jun 2022 01:16:31 -0700 Subject: [Bug] Fix 3 speed mousekey mode (#17500) --- quantum/mousekey.c | 8 ++++---- quantum/mousekey.h | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'quantum') diff --git a/quantum/mousekey.c b/quantum/mousekey.c index 33d71af083..c5e3f8bcda 100644 --- a/quantum/mousekey.c +++ b/quantum/mousekey.c @@ -70,10 +70,6 @@ uint8_t mk_wheel_interval = MOUSEKEY_WHEEL_INTERVAL; uint8_t mk_wheel_max_speed = MOUSEKEY_WHEEL_MAX_SPEED; uint8_t mk_wheel_time_to_max = MOUSEKEY_WHEEL_TIME_TO_MAX; -bool should_mousekey_report_send(report_mouse_t *mouse_report) { - return mouse_report->x || mouse_report->y || mouse_report->v || mouse_report->h; -} - # ifndef MK_COMBINED static uint8_t move_unit(void) { @@ -499,3 +495,7 @@ static void mousekey_debug(void) { report_mouse_t mousekey_get_report(void) { return mouse_report; } + +bool should_mousekey_report_send(report_mouse_t *mouse_report) { + return mouse_report->x || mouse_report->y || mouse_report->v || mouse_report->h; +} diff --git a/quantum/mousekey.h b/quantum/mousekey.h index 03da5f282a..1714e52ff6 100644 --- a/quantum/mousekey.h +++ b/quantum/mousekey.h @@ -174,6 +174,7 @@ void mousekey_off(uint8_t code); void mousekey_clear(void); void mousekey_send(void); report_mouse_t mousekey_get_report(void); +bool should_mousekey_report_send(report_mouse_t *mouse_report); #ifdef __cplusplus } -- cgit v1.2.3 From dcdc7290e5e800689876c4d3ba393e926f966fbe Mon Sep 17 00:00:00 2001 From: Dasky <32983009+daskygit@users.noreply.github.com> Date: Wed, 29 Jun 2022 09:25:03 +0100 Subject: RGB Matrix Heatmap - Skip positions with NO_LED (#17488) --- quantum/rgb_matrix/animations/typing_heatmap_anim.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'quantum') diff --git a/quantum/rgb_matrix/animations/typing_heatmap_anim.h b/quantum/rgb_matrix/animations/typing_heatmap_anim.h index cfc3fc015b..a05c07760e 100644 --- a/quantum/rgb_matrix/animations/typing_heatmap_anim.h +++ b/quantum/rgb_matrix/animations/typing_heatmap_anim.h @@ -18,8 +18,14 @@ void process_rgb_matrix_typing_heatmap(uint8_t row, uint8_t col) { // Limit effect to pressed keys g_rgb_frame_buffer[row][col] = qadd8(g_rgb_frame_buffer[row][col], 32); # else + if (g_led_config.matrix_co[row][col] == NO_LED) { // skip as pressed key doesn't have an led position + return; + } for (uint8_t i_row = 0; i_row < MATRIX_ROWS; i_row++) { for (uint8_t i_col = 0; i_col < MATRIX_COLS; i_col++) { + if (g_led_config.matrix_co[i_row][i_col] == NO_LED) { // skip as target key doesn't have an led position + continue; + } if (i_row == row && i_col == col) { g_rgb_frame_buffer[row][col] = qadd8(g_rgb_frame_buffer[row][col], 32); } else { -- cgit v1.2.3 From 34e244cecf62afb30ee5a4362867f24b03675691 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Thu, 30 Jun 2022 07:42:23 +1000 Subject: Wear-leveling EEPROM drivers: `embedded_flash`, `spi_flash`, `legacy` (#17376) --- quantum/wear_leveling/wear_leveling.c | 97 ++++++++++++-------------- quantum/wear_leveling/wear_leveling_internal.h | 16 +++-- 2 files changed, 54 insertions(+), 59 deletions(-) (limited to 'quantum') diff --git a/quantum/wear_leveling/wear_leveling.c b/quantum/wear_leveling/wear_leveling.c index 8418ae77bf..0a519639ea 100644 --- a/quantum/wear_leveling/wear_leveling.c +++ b/quantum/wear_leveling/wear_leveling.c @@ -213,36 +213,29 @@ static wear_leveling_status_t wear_leveling_read_consolidated(void) { wl_dprintf("Reading consolidated data\n"); wear_leveling_status_t status = WEAR_LEVELING_SUCCESS; - for (int address = 0; address < (WEAR_LEVELING_LOGICAL_SIZE); address += (BACKING_STORE_WRITE_SIZE)) { - backing_store_int_t *const loc = (backing_store_int_t *)&wear_leveling.cache[address]; - backing_store_int_t temp; - bool ok = backing_store_read(address, &temp); - if (!ok) { - wl_dprintf("Failed to read from backing store\n"); - status = WEAR_LEVELING_FAILED; - break; - } - *loc = temp; + if (!backing_store_read_bulk(0, (backing_store_int_t *)wear_leveling.cache, sizeof(wear_leveling.cache) / sizeof(backing_store_int_t))) { + wl_dprintf("Failed to read from backing store\n"); + status = WEAR_LEVELING_FAILED; } // Verify the FNV1a_64 result if (status != WEAR_LEVELING_FAILED) { uint64_t expected = fnv_64a_buf(wear_leveling.cache, (WEAR_LEVELING_LOGICAL_SIZE), FNV1A_64_INIT); write_log_entry_t entry; + wl_dprintf("Reading checksum\n"); #if BACKING_STORE_WRITE_SIZE == 2 - backing_store_read((WEAR_LEVELING_LOGICAL_SIZE) + 0, &entry.raw16[0]); - backing_store_read((WEAR_LEVELING_LOGICAL_SIZE) + 2, &entry.raw16[1]); - backing_store_read((WEAR_LEVELING_LOGICAL_SIZE) + 4, &entry.raw16[2]); - backing_store_read((WEAR_LEVELING_LOGICAL_SIZE) + 6, &entry.raw16[3]); + backing_store_read_bulk((WEAR_LEVELING_LOGICAL_SIZE), entry.raw16, 4); #elif BACKING_STORE_WRITE_SIZE == 4 - backing_store_read((WEAR_LEVELING_LOGICAL_SIZE) + 0, &entry.raw32[0]); - backing_store_read((WEAR_LEVELING_LOGICAL_SIZE) + 4, &entry.raw32[1]); + backing_store_read_bulk((WEAR_LEVELING_LOGICAL_SIZE), entry.raw32, 2); #elif BACKING_STORE_WRITE_SIZE == 8 backing_store_read((WEAR_LEVELING_LOGICAL_SIZE) + 0, &entry.raw64); #endif // If we have a mismatch, clear the cache but do not flag a failure, // which will cater for the completely clean MCU case. - if (entry.raw64 != expected) { + if (entry.raw64 == expected) { + wl_dprintf("Checksum matches, consolidated data is correct\n"); + } else { + wl_dprintf("Checksum mismatch, clearing cache\n"); wear_leveling_clear_cache(); } } @@ -258,64 +251,36 @@ static wear_leveling_status_t wear_leveling_read_consolidated(void) { /** * Writes the current cache to consolidated data at the beginning of the backing store. * Does not clear the write log. + * Pre-condition: this is just after an erase, so we can write directly without reading. */ static wear_leveling_status_t wear_leveling_write_consolidated(void) { wl_dprintf("Writing consolidated data\n"); - wear_leveling_status_t status = WEAR_LEVELING_CONSOLIDATED; backing_store_lock_status_t lock_status = wear_leveling_unlock(); - for (int address = 0; address < (WEAR_LEVELING_LOGICAL_SIZE); address += (BACKING_STORE_WRITE_SIZE)) { - const backing_store_int_t value = *(backing_store_int_t *)&wear_leveling.cache[address]; - backing_store_int_t temp; - bool ok = backing_store_read(address, &temp); - if (!ok) { - wl_dprintf("Failed to read from backing store\n"); - status = WEAR_LEVELING_FAILED; - break; - } - if (temp != value) { - ok = backing_store_write(address, value); - if (!ok) { - wl_dprintf("Failed to write to backing store\n"); - status = WEAR_LEVELING_FAILED; - break; - } - } + wear_leveling_status_t status = WEAR_LEVELING_CONSOLIDATED; + if (!backing_store_write_bulk(0, (backing_store_int_t *)wear_leveling.cache, sizeof(wear_leveling.cache) / sizeof(backing_store_int_t))) { + wl_dprintf("Failed to write to backing store\n"); + status = WEAR_LEVELING_FAILED; } if (status != WEAR_LEVELING_FAILED) { // Write out the FNV1a_64 result of the consolidated data write_log_entry_t entry; entry.raw64 = fnv_64a_buf(wear_leveling.cache, (WEAR_LEVELING_LOGICAL_SIZE), FNV1A_64_INIT); + wl_dprintf("Writing checksum\n"); do { #if BACKING_STORE_WRITE_SIZE == 2 - if (!backing_store_write((WEAR_LEVELING_LOGICAL_SIZE) + 0, entry.raw16[0])) { - status = WEAR_LEVELING_FAILED; - break; - } - if (!backing_store_write((WEAR_LEVELING_LOGICAL_SIZE) + 2, entry.raw16[1])) { - status = WEAR_LEVELING_FAILED; - break; - } - if (!backing_store_write((WEAR_LEVELING_LOGICAL_SIZE) + 4, entry.raw16[2])) { - status = WEAR_LEVELING_FAILED; - break; - } - if (!backing_store_write((WEAR_LEVELING_LOGICAL_SIZE) + 6, entry.raw16[3])) { + if (!backing_store_write_bulk((WEAR_LEVELING_LOGICAL_SIZE), entry.raw16, 4)) { status = WEAR_LEVELING_FAILED; break; } #elif BACKING_STORE_WRITE_SIZE == 4 - if (!backing_store_write((WEAR_LEVELING_LOGICAL_SIZE) + 0, entry.raw32[0])) { - status = WEAR_LEVELING_FAILED; - break; - } - if (!backing_store_write((WEAR_LEVELING_LOGICAL_SIZE) + 4, entry.raw32[1])) { + if (!backing_store_write_bulk((WEAR_LEVELING_LOGICAL_SIZE), entry.raw32, 2)) { status = WEAR_LEVELING_FAILED; break; } #elif BACKING_STORE_WRITE_SIZE == 8 - if (!backing_store_write((WEAR_LEVELING_LOGICAL_SIZE) + 0, entry.raw64)) { + if (!backing_store_write((WEAR_LEVELING_LOGICAL_SIZE), entry.raw64)) { status = WEAR_LEVELING_FAILED; break; } @@ -777,3 +742,27 @@ wear_leveling_status_t wear_leveling_read(const uint32_t address, void *value, s wl_dump(address, value, length); return WEAR_LEVELING_SUCCESS; } + +/** + * Weak implementation of bulk read, drivers can implement more optimised implementations. + */ +__attribute__((weak)) bool backing_store_read_bulk(uint32_t address, backing_store_int_t *values, size_t item_count) { + for (size_t i = 0; i < item_count; ++i) { + if (!backing_store_read(address + (i * BACKING_STORE_WRITE_SIZE), &values[i])) { + return false; + } + } + return true; +} + +/** + * Weak implementation of bulk write, drivers can implement more optimised implementations. + */ +__attribute__((weak)) bool backing_store_write_bulk(uint32_t address, backing_store_int_t *values, size_t item_count) { + for (size_t i = 0; i < item_count; ++i) { + if (!backing_store_write(address + (i * BACKING_STORE_WRITE_SIZE), values[i])) { + return false; + } + } + return true; +} \ No newline at end of file diff --git a/quantum/wear_leveling/wear_leveling_internal.h b/quantum/wear_leveling/wear_leveling_internal.h index 74b43932df..e83f9b22ea 100644 --- a/quantum/wear_leveling/wear_leveling_internal.h +++ b/quantum/wear_leveling/wear_leveling_internal.h @@ -28,21 +28,25 @@ typedef uint64_t backing_store_int_t; #endif #ifdef WEAR_LEVELING_DEBUG_OUTPUT -# include -# define wl_dprintf(...) printf("Wear leveling: " __VA_ARGS__) +# include +# define bs_dprintf(...) dprintf("Backing store: " __VA_ARGS__) +# define wl_dprintf(...) dprintf("Wear leveling: " __VA_ARGS__) # define wl_dump(address, value, length) \ do { \ - printf("[0x%04X]: ", (int)(address)); \ + dprintf("[0x%04X]: ", (int)(address)); \ const uint8_t* p = (const uint8_t*)(value); \ for (int i = 0; i < (length); ++i) { \ - printf(" %02X", (int)p[i]); \ + dprintf(" %02X", (int)p[i]); \ } \ - printf("\n"); \ + dprintf("\n"); \ } while (0) #else # define wl_dprintf(...) \ do { \ } while (0) +# define bs_dprintf(...) \ + do { \ + } while (0) # define wl_dump(...) \ do { \ } while (0) @@ -67,8 +71,10 @@ bool backing_store_init(void); bool backing_store_unlock(void); bool backing_store_erase(void); bool backing_store_write(uint32_t address, backing_store_int_t value); +bool backing_store_write_bulk(uint32_t address, backing_store_int_t* values, size_t item_count); // weak implementation already provided, optimized implementation can be implemented by driver bool backing_store_lock(void); bool backing_store_read(uint32_t address, backing_store_int_t* value); +bool backing_store_read_bulk(uint32_t address, backing_store_int_t* values, size_t item_count); // weak implementation already provided, optimized implementation can be implemented by driver /** * Helper type used to contain a write log entry. -- cgit v1.2.3 From d7173967087e022d20d1f9c812b1b668e9d3f71b Mon Sep 17 00:00:00 2001 From: Stefan Kerkmann Date: Thu, 30 Jun 2022 13:19:27 +0200 Subject: [Core] Add Raspberry Pi RP2040 support (#14877) * Disable RESET keycode because of naming conflicts * Add Pico SDK as submodule * Add RP2040 build support to QMK * Adjust USB endpoint structs for RP2040 * Add RP2040 bootloader and double-tap reset routine * Add generic and pro micro RP2040 boards * Add RP2040 onekey keyboard * Add WS2812 PIO DMA enabled driver and documentation Supports regular and open-drain output configuration. RP2040 GPIOs are sadly not 5V tolerant, so this is a bit use-less or needs extra hardware or you take the risk to fry your hardware. * Adjust SIO Driver for RP2040 * Adjust I2C Driver for RP2040 * Adjust SPI Driver for RP2040 * Add PIO serial driver and documentation * Add general RP2040 documentation * Apply suggestions from code review Co-authored-by: Nick Brassel Co-authored-by: Nick Brassel --- quantum/keymap.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'quantum') diff --git a/quantum/keymap.h b/quantum/keymap.h index 081bc54ebe..edff484129 100644 --- a/quantum/keymap.h +++ b/quantum/keymap.h @@ -46,6 +46,11 @@ along with this program. If not, see . #include "quantum_keycodes.h" +// Gross hack, remove me and change RESET keycode to QK_BOOT +#if defined(MCU_RP) +# undef RESET +#endif + // translates key to keycode uint16_t keymap_key_to_keycode(uint8_t layer, keypos_t key); -- cgit v1.2.3 From b67ae67687b00ffb11ef9c4652e7f7f77b0c7b6d Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Sat, 2 Jul 2022 13:20:45 +1000 Subject: [QP] Add ILI9488 support. (#17438) --- quantum/painter/qp.h | 4 ++++ quantum/painter/rules.mk | 13 ++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) (limited to 'quantum') diff --git a/quantum/painter/qp.h b/quantum/painter/qp.h index e1c14d156c..47f077d0cf 100644 --- a/quantum/painter/qp.h +++ b/quantum/painter/qp.h @@ -440,6 +440,10 @@ int16_t qp_drawtext_recolor(painter_device_t device, uint16_t x, uint16_t y, pai # include "qp_ili9341.h" #endif // QUANTUM_PAINTER_ILI9341_ENABLE +#ifdef QUANTUM_PAINTER_ILI9488_ENABLE +# include "qp_ili9488.h" +#endif // QUANTUM_PAINTER_ILI9488_ENABLE + #ifdef QUANTUM_PAINTER_ST7789_ENABLE # include "qp_st7789.h" #endif // QUANTUM_PAINTER_ST7789_ENABLE diff --git a/quantum/painter/rules.mk b/quantum/painter/rules.mk index 9115d3d406..675a1a5460 100644 --- a/quantum/painter/rules.mk +++ b/quantum/painter/rules.mk @@ -3,7 +3,7 @@ QUANTUM_PAINTER_DRIVERS ?= QUANTUM_PAINTER_ANIMATIONS_ENABLE ?= yes # The list of permissible drivers that can be listed in QUANTUM_PAINTER_DRIVERS -VALID_QUANTUM_PAINTER_DRIVERS := ili9163_spi ili9341_spi st7789_spi gc9a01_spi ssd1351_spi +VALID_QUANTUM_PAINTER_DRIVERS := ili9163_spi ili9341_spi ili9488_spi st7789_spi gc9a01_spi ssd1351_spi #------------------------------------------------------------------------------- @@ -61,6 +61,17 @@ define handle_quantum_painter_driver $(DRIVER_PATH)/painter/tft_panel/qp_tft_panel.c \ $(DRIVER_PATH)/painter/ili9xxx/qp_ili9341.c \ + else ifeq ($$(strip $$(CURRENT_PAINTER_DRIVER)),ili9488_spi) + QUANTUM_PAINTER_NEEDS_COMMS_SPI := yes + QUANTUM_PAINTER_NEEDS_COMMS_SPI_DC_RESET := yes + OPT_DEFS += -DQUANTUM_PAINTER_ILI9488_ENABLE -DQUANTUM_PAINTER_ILI9488_SPI_ENABLE + COMMON_VPATH += \ + $(DRIVER_PATH)/painter/tft_panel \ + $(DRIVER_PATH)/painter/ili9xxx + SRC += \ + $(DRIVER_PATH)/painter/tft_panel/qp_tft_panel.c \ + $(DRIVER_PATH)/painter/ili9xxx/qp_ili9488.c \ + else ifeq ($$(strip $$(CURRENT_PAINTER_DRIVER)),st7789_spi) QUANTUM_PAINTER_NEEDS_COMMS_SPI := yes QUANTUM_PAINTER_NEEDS_COMMS_SPI_DC_RESET := yes -- cgit v1.2.3 From c8fbfca1b614cbe74b1c30aeb1c2c02d5ebe0458 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Sat, 2 Jul 2022 13:22:46 +1000 Subject: [QP] Explicit buffer alignment. (#17532) --- quantum/painter/qp_draw_core.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'quantum') diff --git a/quantum/painter/qp_draw_core.c b/quantum/painter/qp_draw_core.c index c31c734132..309ef93dd0 100644 --- a/quantum/painter/qp_draw_core.c +++ b/quantum/painter/qp_draw_core.c @@ -20,17 +20,17 @@ _Static_assert((QUANTUM_PAINTER_PIXDATA_BUFFER_SIZE > 0) && (QUANTUM_PAINTER_PIX // // Buffer used for transmitting native pixel data to the downstream device. -uint8_t qp_internal_global_pixdata_buffer[QUANTUM_PAINTER_PIXDATA_BUFFER_SIZE]; +__attribute__((__aligned__(4))) uint8_t qp_internal_global_pixdata_buffer[QUANTUM_PAINTER_PIXDATA_BUFFER_SIZE]; // Static buffer to contain a generated color palette -static bool generated_palette = false; -static int16_t generated_steps = -1; -static qp_pixel_t interpolated_fg_hsv888; -static qp_pixel_t interpolated_bg_hsv888; +static bool generated_palette = false; +static int16_t generated_steps = -1; +__attribute__((__aligned__(4))) static qp_pixel_t interpolated_fg_hsv888; +__attribute__((__aligned__(4))) static qp_pixel_t interpolated_bg_hsv888; #if QUANTUM_PAINTER_SUPPORTS_256_PALETTE -qp_pixel_t qp_internal_global_pixel_lookup_table[256]; +__attribute__((__aligned__(4))) qp_pixel_t qp_internal_global_pixel_lookup_table[256]; #else -qp_pixel_t qp_internal_global_pixel_lookup_table[16]; +__attribute__((__aligned__(4))) qp_pixel_t qp_internal_global_pixel_lookup_table[16]; #endif //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -- cgit v1.2.3 From 5846b40f7444af96b0d8ddf3af9b558193c2475d Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Sat, 2 Jul 2022 15:18:50 +1000 Subject: RP2040 emulated EEPROM. (#17519) --- quantum/wear_leveling/wear_leveling.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'quantum') diff --git a/quantum/wear_leveling/wear_leveling.c b/quantum/wear_leveling/wear_leveling.c index 0a519639ea..429df45df5 100644 --- a/quantum/wear_leveling/wear_leveling.c +++ b/quantum/wear_leveling/wear_leveling.c @@ -421,7 +421,7 @@ static wear_leveling_status_t wear_leveling_write_raw(uint32_t address, const vo #if BACKING_STORE_WRITE_SIZE == 2 // Small-write optimizations - uint16_t, 0 or 1, address is even, address <16384: if (remaining >= 2 && address % 2 == 0 && address < 16384) { - const uint16_t v = *(const uint16_t *)p; + const uint16_t v = ((uint16_t)p[1]) << 8 | p[0]; // don't just dereference a uint16_t here -- if unaligned it generates faults on some MCUs if (v == 0 || v == 1) { const write_log_entry_t log = LOG_ENTRY_MAKE_WORD_01(address, v); status = wear_leveling_append_raw(log.raw16[0]); @@ -765,4 +765,4 @@ __attribute__((weak)) bool backing_store_write_bulk(uint32_t address, backing_st } } return true; -} \ No newline at end of file +} -- 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') 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 0112938140b4a59904f4b0b8c3893588410013cb Mon Sep 17 00:00:00 2001 From: Jouke Witteveen Date: Sat, 2 Jul 2022 13:57:05 +0200 Subject: Expose the time of the last change to the LED state (#17222) --- quantum/led.c | 12 +++++++++++- quantum/led.h | 3 +++ 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'quantum') diff --git a/quantum/led.c b/quantum/led.c index c5ddbc22c5..444d38f751 100644 --- a/quantum/led.c +++ b/quantum/led.c @@ -15,6 +15,7 @@ */ #include "led.h" #include "host.h" +#include "timer.h" #include "debug.h" #include "gpio.h" @@ -54,6 +55,14 @@ static void handle_backlight_caps_lock(led_t led_state) { } #endif +static uint32_t last_led_modification_time = 0; +uint32_t last_led_activity_time(void) { + return last_led_modification_time; +} +uint32_t last_led_activity_elapsed(void) { + return timer_elapsed32(last_led_modification_time); +} + /** \brief Lock LED set callback - keymap/user level * * \deprecated Use led_update_user() instead. @@ -174,7 +183,8 @@ void led_task(void) { // update LED uint8_t led_status = host_keyboard_leds(); if (last_led_status != led_status) { - last_led_status = led_status; + last_led_status = led_status; + last_led_modification_time = timer_read32(); if (debug_keyboard) { debug("led_task: "); diff --git a/quantum/led.h b/quantum/led.h index 934d25312c..b8262cbd8e 100644 --- a/quantum/led.h +++ b/quantum/led.h @@ -61,6 +61,9 @@ void led_set_kb(uint8_t usb_led); bool led_update_user(led_t led_state); bool led_update_kb(led_t led_state); +uint32_t last_led_activity_time(void); // Timestamp of the LED activity +uint32_t last_led_activity_elapsed(void); // Number of milliseconds since the last LED activity + #ifdef __cplusplus } #endif -- 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') 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/command.c | 2 ++ quantum/keycode_config.c | 4 ++++ quantum/keycode_config.h | 1 + quantum/process_keycode/process_magic.c | 10 ++++++++++ quantum/quantum_keycodes.h | 8 ++++++++ 5 files changed, 25 insertions(+) (limited to 'quantum') diff --git a/quantum/command.c b/quantum/command.c index f90d73207c..2e94cb44b8 100644 --- a/quantum/command.c +++ b/quantum/command.c @@ -282,6 +282,7 @@ static void print_eeconfig(void) { ".swap_grave_esc: %u\n" ".swap_backslash_backspace: %u\n" ".nkro: %u\n" + ".swap_escape_capslock: %u\n" , kc.raw , kc.swap_control_capslock @@ -294,6 +295,7 @@ static void print_eeconfig(void) { , kc.swap_grave_esc , kc.swap_backslash_backspace , kc.nkro + , kc.swap_escape_capslock ); /* clang-format on */ # ifdef BACKLIGHT_ENABLE diff --git a/quantum/keycode_config.c b/quantum/keycode_config.c index dd2a17e242..5b5cc5d28e 100644 --- a/quantum/keycode_config.c +++ b/quantum/keycode_config.c @@ -29,6 +29,8 @@ uint16_t keycode_config(uint16_t keycode) { case KC_LOCKING_CAPS_LOCK: if (keymap_config.swap_control_capslock || keymap_config.capslock_to_control) { return KC_LEFT_CTRL; + } else if (keymap_config.swap_escape_capslock) { + return KC_ESCAPE; } return keycode; case KC_LEFT_CTRL: @@ -96,6 +98,8 @@ uint16_t keycode_config(uint16_t keycode) { case KC_ESCAPE: if (keymap_config.swap_grave_esc) { return KC_GRAVE; + } else if (keymap_config.swap_escape_capslock) { + return KC_CAPS_LOCK; } return KC_ESCAPE; case KC_BACKSLASH: diff --git a/quantum/keycode_config.h b/quantum/keycode_config.h index a2cb025ed2..81a8e61471 100644 --- a/quantum/keycode_config.h +++ b/quantum/keycode_config.h @@ -38,6 +38,7 @@ typedef union { bool swap_lctl_lgui : 1; bool swap_rctl_rgui : 1; bool oneshot_enable : 1; + bool swap_escape_capslock : 1; }; } keymap_config_t; 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); diff --git a/quantum/quantum_keycodes.h b/quantum/quantum_keycodes.h index 869826ce19..456fad6f1b 100644 --- a/quantum/quantum_keycodes.h +++ b/quantum/quantum_keycodes.h @@ -605,6 +605,10 @@ enum quantum_keycodes { CAPS_WORD, + MAGIC_SWAP_ESCAPE_CAPSLOCK, + MAGIC_UNSWAP_ESCAPE_CAPSLOCK, + MAGIC_TOGGLE_ESCAPE_CAPSLOCK, + // Start of custom keycode range for keyboards and keymaps - always leave at the end SAFE_RANGE }; @@ -756,6 +760,10 @@ enum quantum_keycodes { #define CL_CAPS MAGIC_UNCAPSLOCK_TO_CONTROL #define CL_TOGG MAGIC_TOGGLE_CONTROL_CAPSLOCK +#define EC_SWAP MAGIC_SWAP_ESCAPE_CAPSLOCK +#define EC_NORM MAGIC_UNSWAP_ESCAPE_CAPSLOCK +#define EC_TOGG MAGIC_TOGGLE_ESCAPE_CAPSLOCK + #define LCG_SWP MAGIC_SWAP_LCTL_LGUI #define LCG_NRM MAGIC_UNSWAP_LCTL_LGUI #define RCG_SWP MAGIC_SWAP_RCTL_RGUI -- 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 + .../process_keycode/process_dynamic_tapping_term.c | 2 + quantum/quantum.h | 5 +- quantum/send_string.c | 322 ------------- quantum/send_string.h | 54 --- quantum/send_string/send_string.c | 324 +++++++++++++ quantum/send_string/send_string.h | 152 +++++++ quantum/send_string/send_string_keycodes.h | 434 ++++++++++++++++++ quantum/send_string_keycodes.h | 505 --------------------- 9 files changed, 918 insertions(+), 882 deletions(-) delete mode 100644 quantum/send_string.c delete mode 100644 quantum/send_string.h create mode 100644 quantum/send_string/send_string.c create mode 100644 quantum/send_string/send_string.h create mode 100644 quantum/send_string/send_string_keycodes.h delete mode 100644 quantum/send_string_keycodes.h (limited to 'quantum') 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) { diff --git a/quantum/quantum.h b/quantum/quantum.h index 8a7a20c706..f3a8a323c7 100644 --- a/quantum/quantum.h +++ b/quantum/quantum.h @@ -49,7 +49,6 @@ #include "action_util.h" #include "action_tapping.h" #include "print.h" -#include "send_string.h" #include "suspend.h" #include #include @@ -169,6 +168,10 @@ extern layer_state_t layer_state; # include "hd44780.h" #endif +#ifdef SEND_STRING_ENABLE +# include "send_string.h" +#endif + #ifdef HAPTIC_ENABLE # include "haptic.h" # include "process_haptic.h" diff --git a/quantum/send_string.c b/quantum/send_string.c deleted file mode 100644 index 0de12ba12d..0000000000 --- a/quantum/send_string.c +++ /dev/null @@ -1,322 +0,0 @@ -/* Copyright 2021 - * - * 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 - -#include "quantum.h" - -#include "send_string.h" - -#if defined(AUDIO_ENABLE) && defined(SENDSTRING_BELL) -# include "audio.h" -# ifndef BELL_SOUND -# define BELL_SOUND TERMINAL_SOUND -# endif -float bell_song[][2] = SONG(BELL_SOUND); -#endif - -// clang-format off - -/* Bit-Packed look-up table to convert an ASCII character to whether - * [Shift] needs to be sent with the keycode. - */ -__attribute__((weak)) const uint8_t ascii_to_shift_lut[16] PROGMEM = { - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), - - KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 0), - KCLUT_ENTRY(1, 1, 1, 1, 0, 0, 0, 0), - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), - KCLUT_ENTRY(0, 0, 1, 0, 1, 0, 1, 1), - KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1), - KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1), - KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1), - KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 1, 1), - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), - KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0) -}; - -/* Bit-Packed look-up table to convert an ASCII character to whether - * [AltGr] needs to be sent with the keycode. - */ -__attribute__((weak)) const uint8_t ascii_to_altgr_lut[16] PROGMEM = { - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), - - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0) -}; - -/* Bit-Packed look-up table to convert an ASCII character to whether - * [Space] needs to be sent after the keycode - */ -__attribute__((weak)) const uint8_t ascii_to_dead_lut[16] PROGMEM = { - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), - - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), - KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0) -}; - -/* Look-up table to convert an ASCII character to a keycode. - */ -__attribute__((weak)) const uint8_t ascii_to_keycode_lut[128] PROGMEM = { - // NUL SOH STX ETX EOT ENQ ACK BEL - XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, - // BS TAB LF VT FF CR SO SI - KC_BSPC, KC_TAB, KC_ENT, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, - // DLE DC1 DC2 DC3 DC4 NAK SYN ETB - XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, - // CAN EM SUB ESC FS GS RS US - XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, - - // ! " # $ % & ' - KC_SPC, KC_1, KC_QUOT, KC_3, KC_4, KC_5, KC_7, KC_QUOT, - // ( ) * + , - . / - KC_9, KC_0, KC_8, KC_EQL, KC_COMM, KC_MINS, KC_DOT, KC_SLSH, - // 0 1 2 3 4 5 6 7 - KC_0, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, - // 8 9 : ; < = > ? - KC_8, KC_9, KC_SCLN, KC_SCLN, KC_COMM, KC_EQL, KC_DOT, KC_SLSH, - // @ A B C D E F G - KC_2, KC_A, KC_B, KC_C, KC_D, KC_E, KC_F, KC_G, - // H I J K L M N O - KC_H, KC_I, KC_J, KC_K, KC_L, KC_M, KC_N, KC_O, - // P Q R S T U V W - KC_P, KC_Q, KC_R, KC_S, KC_T, KC_U, KC_V, KC_W, - // X Y Z [ \ ] ^ _ - KC_X, KC_Y, KC_Z, KC_LBRC, KC_BSLS, KC_RBRC, KC_6, KC_MINS, - // ` a b c d e f g - KC_GRV, KC_A, KC_B, KC_C, KC_D, KC_E, KC_F, KC_G, - // h i j k l m n o - KC_H, KC_I, KC_J, KC_K, KC_L, KC_M, KC_N, KC_O, - // p q r s t u v w - KC_P, KC_Q, KC_R, KC_S, KC_T, KC_U, KC_V, KC_W, - // x y z { | } ~ DEL - KC_X, KC_Y, KC_Z, KC_LBRC, KC_BSLS, KC_RBRC, KC_GRV, KC_DEL -}; - -// clang-format on - -// Note: we bit-pack in "reverse" order to optimize loading -#define PGM_LOADBIT(mem, pos) ((pgm_read_byte(&((mem)[(pos) / 8])) >> ((pos) % 8)) & 0x01) - -void send_string(const char *str) { - send_string_with_delay(str, 0); -} - -void send_string_P(const char *str) { - send_string_with_delay_P(str, 0); -} - -void send_string_with_delay(const char *str, uint8_t interval) { - while (1) { - char ascii_code = *str; - if (!ascii_code) break; - if (ascii_code == SS_QMK_PREFIX) { - ascii_code = *(++str); - if (ascii_code == SS_TAP_CODE) { - // tap - uint8_t keycode = *(++str); - tap_code(keycode); - } else if (ascii_code == SS_DOWN_CODE) { - // down - uint8_t keycode = *(++str); - register_code(keycode); - } else if (ascii_code == SS_UP_CODE) { - // up - uint8_t keycode = *(++str); - unregister_code(keycode); - } else if (ascii_code == SS_DELAY_CODE) { - // delay - int ms = 0; - uint8_t keycode = *(++str); - while (isdigit(keycode)) { - ms *= 10; - ms += keycode - '0'; - keycode = *(++str); - } - while (ms--) - wait_ms(1); - } - } else { - send_char(ascii_code); - } - ++str; - // interval - { - uint8_t ms = interval; - while (ms--) - wait_ms(1); - } - } -} - -void send_string_with_delay_P(const char *str, uint8_t interval) { - while (1) { - char ascii_code = pgm_read_byte(str); - if (!ascii_code) break; - if (ascii_code == SS_QMK_PREFIX) { - ascii_code = pgm_read_byte(++str); - if (ascii_code == SS_TAP_CODE) { - // tap - uint8_t keycode = pgm_read_byte(++str); - tap_code(keycode); - } else if (ascii_code == SS_DOWN_CODE) { - // down - uint8_t keycode = pgm_read_byte(++str); - register_code(keycode); - } else if (ascii_code == SS_UP_CODE) { - // up - uint8_t keycode = pgm_read_byte(++str); - unregister_code(keycode); - } else if (ascii_code == SS_DELAY_CODE) { - // delay - int ms = 0; - uint8_t keycode = pgm_read_byte(++str); - while (isdigit(keycode)) { - ms *= 10; - ms += keycode - '0'; - keycode = pgm_read_byte(++str); - } - while (ms--) - wait_ms(1); - } - } else { - send_char(ascii_code); - } - ++str; - // interval - { - uint8_t ms = interval; - while (ms--) - wait_ms(1); - } - } -} - -void send_char(char ascii_code) { -#if defined(AUDIO_ENABLE) && defined(SENDSTRING_BELL) - if (ascii_code == '\a') { // BEL - PLAY_SONG(bell_song); - return; - } -#endif - - uint8_t keycode = pgm_read_byte(&ascii_to_keycode_lut[(uint8_t)ascii_code]); - bool is_shifted = PGM_LOADBIT(ascii_to_shift_lut, (uint8_t)ascii_code); - bool is_altgred = PGM_LOADBIT(ascii_to_altgr_lut, (uint8_t)ascii_code); - bool is_dead = PGM_LOADBIT(ascii_to_dead_lut, (uint8_t)ascii_code); - - if (is_shifted) { - register_code(KC_LSFT); - } - if (is_altgred) { - register_code(KC_RALT); - } - tap_code(keycode); - if (is_altgred) { - unregister_code(KC_RALT); - } - if (is_shifted) { - unregister_code(KC_LSFT); - } - if (is_dead) { - tap_code(KC_SPACE); - } -} - -void send_dword(uint32_t number) { - send_word(number >> 16); - send_word(number & 0xFFFFUL); -} - -void send_word(uint16_t number) { - send_byte(number >> 8); - send_byte(number & 0xFF); -} - -void send_byte(uint8_t number) { - send_nibble(number >> 4); - send_nibble(number & 0xF); -} - -void send_nibble(uint8_t number) { - switch (number & 0xF) { - case 0 ... 9: - send_char(number + '0'); - break; - case 10 ... 15: - send_char(number - 10 + 'a'); - break; - } -} - -void tap_random_base64(void) { -#if defined(__AVR_ATmega32U4__) - uint8_t key = (TCNT0 + TCNT1 + TCNT3 + TCNT4) % 64; -#else - uint8_t key = rand() % 64; -#endif - switch (key) { - case 0 ... 25: - send_char(key + 'A'); - break; - case 26 ... 51: - send_char(key - 26 + 'a'); - break; - case 52: - send_char('0'); - break; - case 53 ... 61: - send_char(key - 53 + '1'); - break; - case 62: - send_char('+'); - break; - case 63: - send_char('/'); - break; - } -} diff --git a/quantum/send_string.h b/quantum/send_string.h deleted file mode 100644 index b90e6f6890..0000000000 --- a/quantum/send_string.h +++ /dev/null @@ -1,54 +0,0 @@ -/* Copyright 2021 - * - * 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 - -#include "progmem.h" -#include "send_string_keycodes.h" - -#define SEND_STRING(string) send_string_P(PSTR(string)) -#define SEND_STRING_DELAY(string, interval) send_string_with_delay_P(PSTR(string), interval) - -// Look-Up Tables (LUTs) to convert ASCII character to keycode sequence. -extern const uint8_t ascii_to_shift_lut[16]; -extern const uint8_t ascii_to_altgr_lut[16]; -extern const uint8_t ascii_to_dead_lut[16]; -extern const uint8_t ascii_to_keycode_lut[128]; - -// clang-format off -#define KCLUT_ENTRY(a, b, c, d, e, f, g, h) \ - ( ((a) ? 1 : 0) << 0 \ - | ((b) ? 1 : 0) << 1 \ - | ((c) ? 1 : 0) << 2 \ - | ((d) ? 1 : 0) << 3 \ - | ((e) ? 1 : 0) << 4 \ - | ((f) ? 1 : 0) << 5 \ - | ((g) ? 1 : 0) << 6 \ - | ((h) ? 1 : 0) << 7 ) -// clang-format on - -void send_string(const char *str); -void send_string_with_delay(const char *str, uint8_t interval); -void send_string_P(const char *str); -void send_string_with_delay_P(const char *str, uint8_t interval); -void send_char(char ascii_code); - -void send_dword(uint32_t number); -void send_word(uint16_t number); -void send_byte(uint8_t number); -void send_nibble(uint8_t number); - -void tap_random_base64(void); diff --git a/quantum/send_string/send_string.c b/quantum/send_string/send_string.c new file mode 100644 index 0000000000..818a52f6dc --- /dev/null +++ b/quantum/send_string/send_string.c @@ -0,0 +1,324 @@ +/* Copyright 2021 + * + * 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 + +#include "quantum.h" + +#include "send_string.h" + +#if defined(AUDIO_ENABLE) && defined(SENDSTRING_BELL) +# include "audio.h" +# ifndef BELL_SOUND +# define BELL_SOUND TERMINAL_SOUND +# endif +float bell_song[][2] = SONG(BELL_SOUND); +#endif + +// clang-format off + +/* Bit-Packed look-up table to convert an ASCII character to whether + * [Shift] needs to be sent with the keycode. + */ +__attribute__((weak)) const uint8_t ascii_to_shift_lut[16] PROGMEM = { + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), + + KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 0), + KCLUT_ENTRY(1, 1, 1, 1, 0, 0, 0, 0), + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), + KCLUT_ENTRY(0, 0, 1, 0, 1, 0, 1, 1), + KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1), + KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1), + KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1), + KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 1, 1), + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), + KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0) +}; + +/* Bit-Packed look-up table to convert an ASCII character to whether + * [AltGr] needs to be sent with the keycode. + */ +__attribute__((weak)) const uint8_t ascii_to_altgr_lut[16] PROGMEM = { + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), + + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0) +}; + +/* Bit-Packed look-up table to convert an ASCII character to whether + * [Space] needs to be sent after the keycode + */ +__attribute__((weak)) const uint8_t ascii_to_dead_lut[16] PROGMEM = { + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), + + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), + KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0) +}; + +/* Look-up table to convert an ASCII character to a keycode. + */ +__attribute__((weak)) const uint8_t ascii_to_keycode_lut[128] PROGMEM = { + // NUL SOH STX ETX EOT ENQ ACK BEL + XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, + // BS TAB LF VT FF CR SO SI + KC_BSPC, KC_TAB, KC_ENT, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, + // DLE DC1 DC2 DC3 DC4 NAK SYN ETB + XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, + // CAN EM SUB ESC FS GS RS US + XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, + + // ! " # $ % & ' + KC_SPC, KC_1, KC_QUOT, KC_3, KC_4, KC_5, KC_7, KC_QUOT, + // ( ) * + , - . / + KC_9, KC_0, KC_8, KC_EQL, KC_COMM, KC_MINS, KC_DOT, KC_SLSH, + // 0 1 2 3 4 5 6 7 + KC_0, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, + // 8 9 : ; < = > ? + KC_8, KC_9, KC_SCLN, KC_SCLN, KC_COMM, KC_EQL, KC_DOT, KC_SLSH, + // @ A B C D E F G + KC_2, KC_A, KC_B, KC_C, KC_D, KC_E, KC_F, KC_G, + // H I J K L M N O + KC_H, KC_I, KC_J, KC_K, KC_L, KC_M, KC_N, KC_O, + // P Q R S T U V W + KC_P, KC_Q, KC_R, KC_S, KC_T, KC_U, KC_V, KC_W, + // X Y Z [ \ ] ^ _ + KC_X, KC_Y, KC_Z, KC_LBRC, KC_BSLS, KC_RBRC, KC_6, KC_MINS, + // ` a b c d e f g + KC_GRV, KC_A, KC_B, KC_C, KC_D, KC_E, KC_F, KC_G, + // h i j k l m n o + KC_H, KC_I, KC_J, KC_K, KC_L, KC_M, KC_N, KC_O, + // p q r s t u v w + KC_P, KC_Q, KC_R, KC_S, KC_T, KC_U, KC_V, KC_W, + // x y z { | } ~ DEL + KC_X, KC_Y, KC_Z, KC_LBRC, KC_BSLS, KC_RBRC, KC_GRV, KC_DEL +}; + +// clang-format on + +// Note: we bit-pack in "reverse" order to optimize loading +#define PGM_LOADBIT(mem, pos) ((pgm_read_byte(&((mem)[(pos) / 8])) >> ((pos) % 8)) & 0x01) + +void send_string(const char *string) { + send_string_with_delay(string, 0); +} + +void send_string_with_delay(const char *string, uint8_t interval) { + while (1) { + char ascii_code = *string; + if (!ascii_code) break; + if (ascii_code == SS_QMK_PREFIX) { + ascii_code = *(++string); + if (ascii_code == SS_TAP_CODE) { + // tap + uint8_t keycode = *(++string); + tap_code(keycode); + } else if (ascii_code == SS_DOWN_CODE) { + // down + uint8_t keycode = *(++string); + register_code(keycode); + } else if (ascii_code == SS_UP_CODE) { + // up + uint8_t keycode = *(++string); + unregister_code(keycode); + } else if (ascii_code == SS_DELAY_CODE) { + // delay + int ms = 0; + uint8_t keycode = *(++string); + while (isdigit(keycode)) { + ms *= 10; + ms += keycode - '0'; + keycode = *(++string); + } + while (ms--) + wait_ms(1); + } + } else { + send_char(ascii_code); + } + ++string; + // interval + { + uint8_t ms = interval; + while (ms--) + wait_ms(1); + } + } +} + +void send_char(char ascii_code) { +#if defined(AUDIO_ENABLE) && defined(SENDSTRING_BELL) + if (ascii_code == '\a') { // BEL + PLAY_SONG(bell_song); + return; + } +#endif + + uint8_t keycode = pgm_read_byte(&ascii_to_keycode_lut[(uint8_t)ascii_code]); + bool is_shifted = PGM_LOADBIT(ascii_to_shift_lut, (uint8_t)ascii_code); + bool is_altgred = PGM_LOADBIT(ascii_to_altgr_lut, (uint8_t)ascii_code); + bool is_dead = PGM_LOADBIT(ascii_to_dead_lut, (uint8_t)ascii_code); + + if (is_shifted) { + register_code(KC_LEFT_SHIFT); + } + if (is_altgred) { + register_code(KC_RIGHT_ALT); + } + tap_code(keycode); + if (is_altgred) { + unregister_code(KC_RIGHT_ALT); + } + if (is_shifted) { + unregister_code(KC_LEFT_SHIFT); + } + if (is_dead) { + tap_code(KC_SPACE); + } +} + +void send_dword(uint32_t number) { + send_word(number >> 16); + send_word(number & 0xFFFFUL); +} + +void send_word(uint16_t number) { + send_byte(number >> 8); + send_byte(number & 0xFF); +} + +void send_byte(uint8_t number) { + send_nibble(number >> 4); + send_nibble(number & 0xF); +} + +void send_nibble(uint8_t number) { + switch (number & 0xF) { + case 0 ... 9: + send_char(number + '0'); + break; + case 10 ... 15: + send_char(number - 10 + 'a'); + break; + } +} + +void tap_random_base64(void) { +#if defined(__AVR_ATmega32U4__) + uint8_t key = (TCNT0 + TCNT1 + TCNT3 + TCNT4) % 64; +#else + uint8_t key = rand() % 64; +#endif + switch (key) { + case 0 ... 25: + send_char(key + 'A'); + break; + case 26 ... 51: + send_char(key - 26 + 'a'); + break; + case 52: + send_char('0'); + break; + case 53 ... 61: + send_char(key - 53 + '1'); + break; + case 62: + send_char('+'); + break; + case 63: + send_char('/'); + break; + } +} + +#if defined(__AVR__) +void send_string_P(const char *string) { + send_string_with_delay_P(string, 0); +} + +void send_string_with_delay_P(const char *string, uint8_t interval) { + while (1) { + char ascii_code = pgm_read_byte(string); + if (!ascii_code) break; + if (ascii_code == SS_QMK_PREFIX) { + ascii_code = pgm_read_byte(++string); + if (ascii_code == SS_TAP_CODE) { + // tap + uint8_t keycode = pgm_read_byte(++string); + tap_code(keycode); + } else if (ascii_code == SS_DOWN_CODE) { + // down + uint8_t keycode = pgm_read_byte(++string); + register_code(keycode); + } else if (ascii_code == SS_UP_CODE) { + // up + uint8_t keycode = pgm_read_byte(++string); + unregister_code(keycode); + } else if (ascii_code == SS_DELAY_CODE) { + // delay + int ms = 0; + uint8_t keycode = pgm_read_byte(++string); + while (isdigit(keycode)) { + ms *= 10; + ms += keycode - '0'; + keycode = pgm_read_byte(++string); + } + while (ms--) + wait_ms(1); + } + } else { + send_char(ascii_code); + } + ++string; + // interval + { + uint8_t ms = interval; + while (ms--) + wait_ms(1); + } + } +} +#endif diff --git a/quantum/send_string/send_string.h b/quantum/send_string/send_string.h new file mode 100644 index 0000000000..4eb55b88dc --- /dev/null +++ b/quantum/send_string/send_string.h @@ -0,0 +1,152 @@ +/* Copyright 2021 + * + * 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 . + */ + +/** + * \defgroup send_string + * + * Send String API. These functions allow you to create macros by typing out sequences of keystrokes. + * \{ + */ + +#include + +#include "progmem.h" +#include "send_string_keycodes.h" + +// Look-Up Tables (LUTs) to convert ASCII character to keycode sequence. +extern const uint8_t ascii_to_shift_lut[16]; +extern const uint8_t ascii_to_altgr_lut[16]; +extern const uint8_t ascii_to_dead_lut[16]; +extern const uint8_t ascii_to_keycode_lut[128]; + +// clang-format off +#define KCLUT_ENTRY(a, b, c, d, e, f, g, h) \ + ( ((a) ? 1 : 0) << 0 \ + | ((b) ? 1 : 0) << 1 \ + | ((c) ? 1 : 0) << 2 \ + | ((d) ? 1 : 0) << 3 \ + | ((e) ? 1 : 0) << 4 \ + | ((f) ? 1 : 0) << 5 \ + | ((g) ? 1 : 0) << 6 \ + | ((h) ? 1 : 0) << 7 ) +// clang-format on + +/** + * \brief Type out a string of ASCII characters. + * + * This function simply calls `send_string_with_delay(string, 0)`. + * + * Most keycodes from the basic keycode range are also supported by way of a special sequence - see `send_string_keycodes.h`. + * + * \param string The string to type out. + */ +void send_string(const char *string); + +/** + * \brief Type out a string of ASCII characters, with a delay between each character. + * + * \param string The string to type out. + * \param interval The amount of time, in milliseconds, to wait before typing the next character. + */ +void send_string_with_delay(const char *string, uint8_t interval); + +/** + * \brief Type out an ASCII character. + * + * \param ascii_code The character to type. + */ +void send_char(char ascii_code); + +/** + * \brief Type out an eight digit (unsigned 32-bit) hexadecimal value. + * + * The format is `[0-9a-f]{8}`, eg. `00000000` through `ffffffff`. + * + * \param number The value to type, from 0 to 4,294,967,295. + */ +void send_dword(uint32_t number); + +/** + * \brief Type out a four digit (unsigned 16-bit) hexadecimal value. + * + * The format is `[0-9a-f]{4}`, eg. `0000` through `ffff`. + * + * \param number The value to type, from 0 to 65,535. + */ +void send_word(uint16_t number); + +/** + * \brief Type out a two digit (8-bit) hexadecimal value. + * + * The format is `[0-9a-f]{2}`, eg. `00` through `ff`. + * + * \param number The value to type, from 0 to 255. + */ +void send_byte(uint8_t number); + +/** + * \brief Type out a single hexadecimal digit. + * + * The format is `[0-9a-f]{1}`, eg. `0` through `f`. + * + * \param number The value to type, from 0 to 15. + */ +void send_nibble(uint8_t number); + +/** + * \brief Type a pseudorandom character from the set `A-Z`, `a-z`, `0-9`, `+` and `/`. + */ +void tap_random_base64(void); + +#if defined(__AVR__) || defined(__DOXYGEN__) +/** + * \brief Type out a PROGMEM string of ASCII characters. + * + * On ARM devices, this function is simply an alias for send_string_with_delay(string, 0). + * + * \param string The string to type out. + */ +void send_string_P(const char *string); + +/** + * \brief Type out a PROGMEM string of ASCII characters, with a delay between each character. + * + * On ARM devices, this function is simply an alias for send_string_with_delay(string, interval). + * + * \param string The string to type out. + * \param interval The amount of time, in milliseconds, to wait before typing the next character. + */ +void send_string_with_delay_P(const char *string, uint8_t interval); +#else +# define send_string_P(string) send_string_with_delay(string, 0) +# define send_string_with_delay_P(string, interval) send_string_with_delay(string, interval) +#endif + +/** + * \brief Shortcut macro for send_string_with_delay_P(PSTR(string), 0). + * + * On ARM devices, this define evaluates to send_string_with_delay(string, 0). + */ +#define SEND_STRING(string) send_string_with_delay_P(PSTR(string), 0) + +/** + * \brief Shortcut macro for send_string_with_delay_P(PSTR(string), interval). + * + * On ARM devices, this define evaluates to send_string_with_delay(string, interval). + */ +#define SEND_STRING_DELAY(string, interval) send_string_with_delay_P(PSTR(string), interval) + +/** \} */ diff --git a/quantum/send_string/send_string_keycodes.h b/quantum/send_string/send_string_keycodes.h new file mode 100644 index 0000000000..7017e03d5a --- /dev/null +++ b/quantum/send_string/send_string_keycodes.h @@ -0,0 +1,434 @@ +/* Copyright 2019 + * + * 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 + +// clang-format off + +/* Punctuation */ +#define X_ENT X_ENTER +#define X_ESC X_ESCAPE +#define X_BSPC X_BSPACE +#define X_SPC X_SPACE +#define X_MINS X_MINUS +#define X_EQL X_EQUAL +#define X_LBRC X_LBRACKET +#define X_RBRC X_RBRACKET +#define X_BSLS X_BSLASH +#define X_NUHS X_NONUS_HASH +#define X_SCLN X_SCOLON +#define X_QUOT X_QUOTE +#define X_GRV X_GRAVE +#define X_COMM X_COMMA +#define X_SLSH X_SLASH +#define X_NUBS X_NONUS_BSLASH + +/* Lock Keys */ +#define X_CLCK X_CAPSLOCK +#define X_CAPS X_CAPSLOCK +#define X_SLCK X_SCROLLLOCK +#define X_NLCK X_NUMLOCK +#define X_LCAP X_LOCKING_CAPS +#define X_LNUM X_LOCKING_NUM +#define X_LSCR X_LOCKING_SCROLL + +/* Commands */ +#define X_PSCR X_PSCREEN +#define X_PAUS X_PAUSE +#define X_BRK X_PAUSE +#define X_INS X_INSERT +#define X_DEL X_DELETE +#define X_PGDN X_PGDOWN +#define X_RGHT X_RIGHT +#define X_APP X_APPLICATION +#define X_EXEC X_EXECUTE +#define X_SLCT X_SELECT +#define X_AGIN X_AGAIN +#define X_PSTE X_PASTE +#define X_ERAS X_ALT_ERASE +#define X_CLR X_CLEAR + +/* Keypad */ +#define X_PSLS X_KP_SLASH +#define X_PAST X_KP_ASTERISK +#define X_PMNS X_KP_MINUS +#define X_PPLS X_KP_PLUS +#define X_PENT X_KP_ENTER +#define X_P1 X_KP_1 +#define X_P2 X_KP_2 +#define X_P3 X_KP_3 +#define X_P4 X_KP_4 +#define X_P5 X_KP_5 +#define X_P6 X_KP_6 +#define X_P7 X_KP_7 +#define X_P8 X_KP_8 +#define X_P9 X_KP_9 +#define X_P0 X_KP_0 +#define X_PDOT X_KP_DOT +#define X_PEQL X_KP_EQUAL +#define X_PCMM X_KP_COMMA + +/* Japanese specific */ +#define X_ZKHK X_GRAVE +#define X_RO X_INT1 +#define X_KANA X_INT2 +#define X_JYEN X_INT3 +#define X_HENK X_INT4 +#define X_MHEN X_INT5 + +/* Korean specific */ +#define X_HAEN X_LANG1 +#define X_HANJ X_LANG2 + +/* Modifiers */ +#define X_LCTL X_LCTRL +#define X_LSFT X_LSHIFT +#define X_LOPT X_LALT +#define X_LCMD X_LGUI +#define X_LWIN X_LGUI +#define X_RCTL X_RCTRL +#define X_RSFT X_RSHIFT +#define X_ALGR X_RALT +#define X_ROPT X_RALT +#define X_RCMD X_RGUI +#define X_RWIN X_RGUI + +/* Generic Desktop Page (0x01) */ +#define X_PWR X_SYSTEM_POWER +#define X_SLEP X_SYSTEM_SLEEP +#define X_WAKE X_SYSTEM_WAKE + +/* Consumer Page (0x0C) */ +#define X_MUTE X_AUDIO_MUTE +#define X_VOLU X_AUDIO_VOL_UP +#define X_VOLD X_AUDIO_VOL_DOWN +#define X_MNXT X_MEDIA_NEXT_TRACK +#define X_MPRV X_MEDIA_PREV_TRACK +#define X_MSTP X_MEDIA_STOP +#define X_MPLY X_MEDIA_PLAY_PAUSE +#define X_MSEL X_MEDIA_SELECT +#define X_EJCT X_MEDIA_EJECT +#define X_CALC X_CALCULATOR +#define X_MYCM X_MY_COMPUTER +#define X_WSCH X_WWW_SEARCH +#define X_WHOM X_WWW_HOME +#define X_WBAK X_WWW_BACK +#define X_WFWD X_WWW_FORWARD +#define X_WSTP X_WWW_STOP +#define X_WREF X_WWW_REFRESH +#define X_WFAV X_WWW_FAVORITES +#define X_MFFD X_MEDIA_FAST_FORWARD +#define X_MRWD X_MEDIA_REWIND +#define X_BRIU X_BRIGHTNESS_UP +#define X_BRID X_BRIGHTNESS_DOWN + +/* System Specific */ +#define X_BRMU X_PAUSE +#define X_BRMD X_SCROLLLOCK + +/* Mouse Keys */ +#define X_MS_U X_MS_UP +#define X_MS_D X_MS_DOWN +#define X_MS_L X_MS_LEFT +#define X_MS_R X_MS_RIGHT +#define X_BTN1 X_MS_BTN1 +#define X_BTN2 X_MS_BTN2 +#define X_BTN3 X_MS_BTN3 +#define X_BTN4 X_MS_BTN4 +#define X_BTN5 X_MS_BTN5 +#define X_WH_U X_MS_WH_UP +#define X_WH_D X_MS_WH_DOWN +#define X_WH_L X_MS_WH_LEFT +#define X_WH_R X_MS_WH_RIGHT +#define X_ACL0 X_MS_ACCEL0 +#define X_ACL1 X_MS_ACCEL1 +#define X_ACL2 X_MS_ACCEL2 + +/* Keyboard/Keypad Page (0x07) */ +#define X_A 04 +#define X_B 05 +#define X_C 06 +#define X_D 07 +#define X_E 08 +#define X_F 09 +#define X_G 0a +#define X_H 0b +#define X_I 0c +#define X_J 0d +#define X_K 0e +#define X_L 0f +#define X_M 10 +#define X_N 11 +#define X_O 12 +#define X_P 13 +#define X_Q 14 +#define X_R 15 +#define X_S 16 +#define X_T 17 +#define X_U 18 +#define X_V 19 +#define X_W 1a +#define X_X 1b +#define X_Y 1c +#define X_Z 1d +#define X_1 1e +#define X_2 1f +#define X_3 20 +#define X_4 21 +#define X_5 22 +#define X_6 23 +#define X_7 24 +#define X_8 25 +#define X_9 26 +#define X_0 27 +#define X_ENTER 28 +#define X_ESCAPE 29 +#define X_BSPACE 2a +#define X_TAB 2b +#define X_SPACE 2c +#define X_MINUS 2d +#define X_EQUAL 2e +#define X_LBRACKET 2f +#define X_RBRACKET 30 +#define X_BSLASH 31 +#define X_NONUS_HASH 32 +#define X_SCOLON 33 +#define X_QUOTE 34 +#define X_GRAVE 35 +#define X_COMMA 36 +#define X_DOT 37 +#define X_SLASH 38 +#define X_CAPSLOCK 39 +#define X_F1 3a +#define X_F2 3b +#define X_F3 3c +#define X_F4 3d +#define X_F5 3e +#define X_F6 3f +#define X_F7 40 +#define X_F8 41 +#define X_F9 42 +#define X_F10 43 +#define X_F11 44 +#define X_F12 45 +#define X_PSCREEN 46 +#define X_SCROLLLOCK 47 +#define X_PAUSE 48 +#define X_INSERT 49 +#define X_HOME 4a +#define X_PGUP 4b +#define X_DELETE 4c +#define X_END 4d +#define X_PGDOWN 4e +#define X_RIGHT 4f +#define X_LEFT 50 +#define X_DOWN 51 +#define X_UP 52 +#define X_NUMLOCK 53 +#define X_KP_SLASH 54 +#define X_KP_ASTERISK 55 +#define X_KP_MINUS 56 +#define X_KP_PLUS 57 +#define X_KP_ENTER 58 +#define X_KP_1 59 +#define X_KP_2 5a +#define X_KP_3 5b +#define X_KP_4 5c +#define X_KP_5 5d +#define X_KP_6 5e +#define X_KP_7 5f +#define X_KP_8 60 +#define X_KP_9 61 +#define X_KP_0 62 +#define X_KP_DOT 63 +#define X_NONUS_BSLASH 64 +#define X_APPLICATION 65 +#define X_POWER 66 +#define X_KP_EQUAL 67 +#define X_F13 68 +#define X_F14 69 +#define X_F15 6a +#define X_F16 6b +#define X_F17 6c +#define X_F18 6d +#define X_F19 6e +#define X_F20 6f +#define X_F21 70 +#define X_F22 71 +#define X_F23 72 +#define X_F24 73 +#define X_EXECUTE 74 +#define X_HELP 75 +#define X_MENU 76 +#define X_SELECT 77 +#define X_STOP 78 +#define X_AGAIN 79 +#define X_UNDO 7a +#define X_CUT 7b +#define X_COPY 7c +#define X_PASTE 7d +#define X_FIND 7e +#define X__MUTE 7f +#define X__VOLUP 80 +#define X__VOLDOWN 81 +#define X_LOCKING_CAPS 82 +#define X_LOCKING_NUM 83 +#define X_LOCKING_SCROLL 84 +#define X_KP_COMMA 85 +#define X_KP_EQUAL_AS400 86 +#define X_INT1 87 +#define X_INT2 88 +#define X_INT3 89 +#define X_INT4 8a +#define X_INT5 8b +#define X_INT6 8c +#define X_INT7 8d +#define X_INT8 8e +#define X_INT9 8f +#define X_LANG1 90 +#define X_LANG2 91 +#define X_LANG3 92 +#define X_LANG4 93 +#define X_LANG5 94 +#define X_LANG6 95 +#define X_LANG7 96 +#define X_LANG8 97 +#define X_LANG9 98 +#define X_ALT_ERASE 99 +#define X_SYSREQ 9a +#define X_CANCEL 9b +#define X_CLEAR 9c +#define X_PRIOR 9d +#define X_RETURN 9e +#define X_SEPARATOR 9f +#define X_OUT a0 +#define X_OPER a1 +#define X_CLEAR_AGAIN a2 +#define X_CRSEL a3 +#define X_EXSEL a4 + +/* Modifiers */ +#define X_LCTRL e0 +#define X_LSHIFT e1 +#define X_LALT e2 +#define X_LGUI e3 +#define X_RCTRL e4 +#define X_RSHIFT e5 +#define X_RALT e6 +#define X_RGUI e7 + +/* Media and Function keys */ +/* Generic Desktop Page (0x01) */ +#define X_SYSTEM_POWER a5 +#define X_SYSTEM_SLEEP a6 +#define X_SYSTEM_WAKE a7 + +/* Consumer Page (0x0C) */ +#define X_AUDIO_MUTE a8 +#define X_AUDIO_VOL_UP a9 +#define X_AUDIO_VOL_DOWN aa +#define X_MEDIA_NEXT_TRACK ab +#define X_MEDIA_PREV_TRACK ac +#define X_MEDIA_STOP ad +#define X_MEDIA_PLAY_PAUSE ae +#define X_MEDIA_SELECT af +#define X_MEDIA_EJECT b0 +#define X_MAIL b1 +#define X_CALCULATOR b2 +#define X_MY_COMPUTER b3 +#define X_WWW_SEARCH b4 +#define X_WWW_HOME b5 +#define X_WWW_BACK b6 +#define X_WWW_FORWARD b7 +#define X_WWW_STOP b8 +#define X_WWW_REFRESH b9 +#define X_WWW_FAVORITES ba +#define X_MEDIA_FAST_FORWARD bb +#define X_MEDIA_REWIND bc +#define X_BRIGHTNESS_UP bd +#define X_BRIGHTNESS_DOWN be + +/* Mouse Buttons (unallocated range in HID spec) */ +#ifdef VIA_ENABLE +#define X_MS_UP f0 +#define X_MS_DOWN f1 +#define X_MS_LEFT f2 +#define X_MS_RIGHT f3 +#define X_MS_BTN1 f4 +#define X_MS_BTN2 f5 +#define X_MS_BTN3 f6 +#define X_MS_BTN4 f7 +#define X_MS_BTN5 f8 +#define X_MS_BTN6 f8 +#define X_MS_BTN7 f8 +#define X_MS_BTN8 f8 +#else +#define X_MS_UP ed +#define X_MS_DOWN ee +#define X_MS_LEFT ef +#define X_MS_RIGHT f0 +#define X_MS_BTN1 f1 +#define X_MS_BTN2 f2 +#define X_MS_BTN3 f3 +#define X_MS_BTN4 f4 +#define X_MS_BTN5 f5 +#define X_MS_BTN6 f6 +#define X_MS_BTN7 f7 +#define X_MS_BTN8 f8 +#endif +#define X_MS_WH_UP f9 +#define X_MS_WH_DOWN fa +#define X_MS_WH_LEFT fb +#define X_MS_WH_RIGHT fc +#define X_MS_ACCEL0 fd +#define X_MS_ACCEL1 fe +#define X_MS_ACCEL2 ff + +// Send string macros +#define STRINGIZE(z) #z +#define ADD_SLASH_X(y) STRINGIZE(\x##y) +#define SYMBOL_STR(x) ADD_SLASH_X(x) + +#define SS_QMK_PREFIX 1 + +#define SS_TAP_CODE 1 +#define SS_DOWN_CODE 2 +#define SS_UP_CODE 3 +#define SS_DELAY_CODE 4 + +#define SS_TAP(keycode) "\1\1" SYMBOL_STR(keycode) +#define SS_DOWN(keycode) "\1\2" SYMBOL_STR(keycode) +#define SS_UP(keycode) "\1\3" SYMBOL_STR(keycode) +#define SS_DELAY(msecs) "\1\4" STRINGIZE(msecs) "|" + +// `string` arguments must not be parenthesized +#define SS_LCTL(string) SS_DOWN(X_LCTL) string SS_UP(X_LCTL) +#define SS_LSFT(string) SS_DOWN(X_LSFT) string SS_UP(X_LSFT) +#define SS_LALT(string) SS_DOWN(X_LALT) string SS_UP(X_LALT) +#define SS_LGUI(string) SS_DOWN(X_LGUI) string SS_UP(X_LGUI) +#define SS_LCMD(string) SS_LGUI(string) +#define SS_LWIN(string) SS_LGUI(string) + +#define SS_RCTL(string) SS_DOWN(X_RCTL) string SS_UP(X_RCTL) +#define SS_RSFT(string) SS_DOWN(X_RSFT) string SS_UP(X_RSFT) +#define SS_RALT(string) SS_DOWN(X_RALT) string SS_UP(X_RALT) +#define SS_RGUI(string) SS_DOWN(X_RGUI) string SS_UP(X_RGUI) +#define SS_ALGR(string) SS_RALT(string) +#define SS_RCMD(string) SS_RGUI(string) +#define SS_RWIN(string) SS_RGUI(string) + +// DEPRECATED +#define SS_LCTRL(string) SS_LCTL(string) diff --git a/quantum/send_string_keycodes.h b/quantum/send_string_keycodes.h deleted file mode 100644 index b35bf66b7b..0000000000 --- a/quantum/send_string_keycodes.h +++ /dev/null @@ -1,505 +0,0 @@ -/* Copyright 2019 - * - * 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 - -// clang-format off - -/* Punctuation */ -#define X_ENT X_ENTER -#define X_ESC X_ESCAPE -#define X_BSPC X_BACKSPACE -#define X_SPC X_SPACE -#define X_MINS X_MINUS -#define X_EQL X_EQUAL -#define X_LBRC X_LEFT_BRACKET -#define X_RBRC X_RIGHT_BRACKET -#define X_BSLS X_BACKSLASH -#define X_NUHS X_NONUS_HASH -#define X_SCLN X_SEMICOLON -#define X_QUOT X_QUOTE -#define X_GRV X_GRAVE -#define X_COMM X_COMMA -#define X_SLSH X_SLASH -#define X_NUBS X_NONUS_BACKSLASH - -/* Lock Keys */ -#define X_CAPS X_CAPS_LOCK -#define X_SCRL X_SCROLL_LOCK -#define X_NUM X_NUM_LOCK -#define X_LCAP X_LOCKING_CAPS_LOCK -#define X_LNUM X_LOCKING_NUM_LOCK -#define X_LSCR X_LOCKING_SCROLL_LOCK - -/* Commands */ -#define X_PSCR X_PRINT_SCREEN -#define X_PAUS X_PAUSE -#define X_BRK X_PAUSE -#define X_INS X_INSERT -#define X_PGUP X_PAGE_UP -#define X_DEL X_DELETE -#define X_PGDN X_PAGE_DOWN -#define X_RGHT X_RIGHT -#define X_APP X_APPLICATION -#define X_EXEC X_EXECUTE -#define X_SLCT X_SELECT -#define X_AGIN X_AGAIN -#define X_PSTE X_PASTE -#define X_ERAS X_ALTERNATE_ERASE -#define X_SYRQ X_SYSTEM_REQUEST -#define X_CNCL X_CANCEL -#define X_CLR X_CLEAR -#define X_PRIR X_PRIOR -#define X_RETN X_RETURN -#define X_SEPR X_SEPARATOR -#define X_CLAG X_CLEAR_AGAIN -#define X_CRSL X_CRSEL -#define X_EXSL X_EXSEL - -/* Keypad */ -#define X_PSLS X_KP_SLASH -#define X_PAST X_KP_ASTERISK -#define X_PMNS X_KP_MINUS -#define X_PPLS X_KP_PLUS -#define X_PENT X_KP_ENTER -#define X_P1 X_KP_1 -#define X_P2 X_KP_2 -#define X_P3 X_KP_3 -#define X_P4 X_KP_4 -#define X_P5 X_KP_5 -#define X_P6 X_KP_6 -#define X_P7 X_KP_7 -#define X_P8 X_KP_8 -#define X_P9 X_KP_9 -#define X_P0 X_KP_0 -#define X_PDOT X_KP_DOT -#define X_PEQL X_KP_EQUAL -#define X_PCMM X_KP_COMMA - -/* Language Specific */ -#define X_INT1 X_INTERNATIONAL_1 -#define X_INT2 X_INTERNATIONAL_2 -#define X_INT3 X_INTERNATIONAL_3 -#define X_INT4 X_INTERNATIONAL_4 -#define X_INT5 X_INTERNATIONAL_5 -#define X_INT6 X_INTERNATIONAL_6 -#define X_INT7 X_INTERNATIONAL_7 -#define X_INT8 X_INTERNATIONAL_8 -#define X_INT9 X_INTERNATIONAL_9 -#define X_LNG1 X_LANGUAGE_1 -#define X_LNG2 X_LANGUAGE_2 -#define X_LNG3 X_LANGUAGE_3 -#define X_LNG4 X_LANGUAGE_4 -#define X_LNG5 X_LANGUAGE_5 -#define X_LNG6 X_LANGUAGE_6 -#define X_LNG7 X_LANGUAGE_7 -#define X_LNG8 X_LANGUAGE_8 -#define X_LNG9 X_LANGUAGE_9 - -/* Modifiers */ -#define X_LCTL X_LEFT_CTRL -#define X_LSFT X_LEFT_SHIFT -#define X_LALT X_LEFT_ALT -#define X_LOPT X_LEFT_ALT -#define X_LGUI X_LEFT_GUI -#define X_LCMD X_LEFT_GUI -#define X_LWIN X_LEFT_GUI -#define X_RCTL X_RIGHT_CTRL -#define X_RSFT X_RIGHT_SHIFT -#define X_RALT X_RIGHT_ALT -#define X_ALGR X_RIGHT_ALT -#define X_ROPT X_RIGHT_ALT -#define X_RGUI X_RIGHT_GUI -#define X_RCMD X_RIGHT_GUI -#define X_RWIN X_RIGHT_GUI - -/* Generic Desktop Page (0x01) */ -#define X_PWR X_SYSTEM_POWER -#define X_SLEP X_SYSTEM_SLEEP -#define X_WAKE X_SYSTEM_WAKE - -/* Consumer Page (0x0C) */ -#define X_MUTE X_AUDIO_MUTE -#define X_VOLU X_AUDIO_VOL_UP -#define X_VOLD X_AUDIO_VOL_DOWN -#define X_MNXT X_MEDIA_NEXT_TRACK -#define X_MPRV X_MEDIA_PREV_TRACK -#define X_MSTP X_MEDIA_STOP -#define X_MPLY X_MEDIA_PLAY_PAUSE -#define X_MSEL X_MEDIA_SELECT -#define X_EJCT X_MEDIA_EJECT -#define X_CALC X_CALCULATOR -#define X_MYCM X_MY_COMPUTER -#define X_WSCH X_WWW_SEARCH -#define X_WHOM X_WWW_HOME -#define X_WBAK X_WWW_BACK -#define X_WFWD X_WWW_FORWARD -#define X_WSTP X_WWW_STOP -#define X_WREF X_WWW_REFRESH -#define X_WFAV X_WWW_FAVORITES -#define X_MFFD X_MEDIA_FAST_FORWARD -#define X_MRWD X_MEDIA_REWIND -#define X_BRIU X_BRIGHTNESS_UP -#define X_BRID X_BRIGHTNESS_DOWN - -/* System Specific */ -#define X_BRMU X_PAUSE -#define X_BRMD X_SCROLL_LOCK - -/* Mouse Keys */ -#define X_MS_U X_MS_UP -#define X_MS_D X_MS_DOWN -#define X_MS_L X_MS_LEFT -#define X_MS_R X_MS_RIGHT -#define X_BTN1 X_MS_BTN1 -#define X_BTN2 X_MS_BTN2 -#define X_BTN3 X_MS_BTN3 -#define X_BTN4 X_MS_BTN4 -#define X_BTN5 X_MS_BTN5 -#define X_BTN6 X_MS_BTN6 -#define X_BTN7 X_MS_BTN7 -#define X_BTN8 X_MS_BTN8 -#define X_WH_U X_MS_WH_UP -#define X_WH_D X_MS_WH_DOWN -#define X_WH_L X_MS_WH_LEFT -#define X_WH_R X_MS_WH_RIGHT -#define X_ACL0 X_MS_ACCEL0 -#define X_ACL1 X_MS_ACCEL1 -#define X_ACL2 X_MS_ACCEL2 - -/* Keyboard/Keypad Page (0x07) */ -#define X_A 04 -#define X_B 05 -#define X_C 06 -#define X_D 07 -#define X_E 08 -#define X_F 09 -#define X_G 0a -#define X_H 0b -#define X_I 0c -#define X_J 0d -#define X_K 0e -#define X_L 0f -#define X_M 10 -#define X_N 11 -#define X_O 12 -#define X_P 13 -#define X_Q 14 -#define X_R 15 -#define X_S 16 -#define X_T 17 -#define X_U 18 -#define X_V 19 -#define X_W 1a -#define X_X 1b -#define X_Y 1c -#define X_Z 1d -#define X_1 1e -#define X_2 1f -#define X_3 20 -#define X_4 21 -#define X_5 22 -#define X_6 23 -#define X_7 24 -#define X_8 25 -#define X_9 26 -#define X_0 27 -#define X_ENTER 28 -#define X_ESCAPE 29 -#define X_BACKSPACE 2a -#define X_TAB 2b -#define X_SPACE 2c -#define X_MINUS 2d -#define X_EQUAL 2e -#define X_LEFT_BRACKET 2f -#define X_RIGHT_BRACKET 30 -#define X_BACKSLASH 31 -#define X_NONUS_HASH 32 -#define X_SEMICOLON 33 -#define X_QUOTE 34 -#define X_GRAVE 35 -#define X_COMMA 36 -#define X_DOT 37 -#define X_SLASH 38 -#define X_CAPS_LOCK 39 -#define X_F1 3a -#define X_F2 3b -#define X_F3 3c -#define X_F4 3d -#define X_F5 3e -#define X_F6 3f -#define X_F7 40 -#define X_F8 41 -#define X_F9 42 -#define X_F10 43 -#define X_F11 44 -#define X_F12 45 -#define X_PRINT_SCREEN 46 -#define X_SCROLL_LOCK 47 -#define X_PAUSE 48 -#define X_INSERT 49 -#define X_HOME 4a -#define X_PAGE_UP 4b -#define X_DELETE 4c -#define X_END 4d -#define X_PAGE_DOWN 4e -#define X_RIGHT 4f -#define X_LEFT 50 -#define X_DOWN 51 -#define X_UP 52 -#define X_NUM_LOCK 53 -#define X_KP_SLASH 54 -#define X_KP_ASTERISK 55 -#define X_KP_MINUS 56 -#define X_KP_PLUS 57 -#define X_KP_ENTER 58 -#define X_KP_1 59 -#define X_KP_2 5a -#define X_KP_3 5b -#define X_KP_4 5c -#define X_KP_5 5d -#define X_KP_6 5e -#define X_KP_7 5f -#define X_KP_8 60 -#define X_KP_9 61 -#define X_KP_0 62 -#define X_KP_DOT 63 -#define X_NONUS_BACKSLASH 64 -#define X_APPLICATION 65 -#define X_KB_POWER 66 -#define X_KP_EQUAL 67 -#define X_F13 68 -#define X_F14 69 -#define X_F15 6a -#define X_F16 6b -#define X_F17 6c -#define X_F18 6d -#define X_F19 6e -#define X_F20 6f -#define X_F21 70 -#define X_F22 71 -#define X_F23 72 -#define X_F24 73 -#define X_EXECUTE 74 -#define X_HELP 75 -#define X_MENU 76 -#define X_SELECT 77 -#define X_STOP 78 -#define X_AGAIN 79 -#define X_UNDO 7a -#define X_CUT 7b -#define X_COPY 7c -#define X_PASTE 7d -#define X_FIND 7e -#define X_KB_MUTE 7f -#define X_KB_VOLUME_UP 80 -#define X_KB_VOLUME_DOWN 81 -#define X_LOCKING_CAPS_LOCK 82 -#define X_LOCKING_NUM_LOCK 83 -#define X_LOCKING_SCROLL_LOCK 84 -#define X_KP_COMMA 85 -#define X_KP_EQUAL_AS400 86 -#define X_INTERNATIONAL_1 87 -#define X_INTERNATIONAL_2 88 -#define X_INTERNATIONAL_3 89 -#define X_INTERNATIONAL_4 8a -#define X_INTERNATIONAL_5 8b -#define X_INTERNATIONAL_6 8c -#define X_INTERNATIONAL_7 8d -#define X_INTERNATIONAL_8 8e -#define X_INTERNATIONAL_9 8f -#define X_LANGUAGE_1 90 -#define X_LANGUAGE_2 91 -#define X_LANGUAGE_3 92 -#define X_LANGUAGE_4 93 -#define X_LANGUAGE_5 94 -#define X_LANGUAGE_6 95 -#define X_LANGUAGE_7 96 -#define X_LANGUAGE_8 97 -#define X_LANGUAGE_9 98 -#define X_ALTERNATE_ERASE 99 -#define X_SYSTEM_REQUEST 9a -#define X_CANCEL 9b -#define X_CLEAR 9c -#define X_PRIOR 9d -#define X_RETURN 9e -#define X_SEPARATOR 9f -#define X_OUT a0 -#define X_OPER a1 -#define X_CLEAR_AGAIN a2 -#define X_CRSEL a3 -#define X_EXSEL a4 - -/* Modifiers */ -#define X_LEFT_CTRL e0 -#define X_LEFT_SHIFT e1 -#define X_LEFT_ALT e2 -#define X_LEFT_GUI e3 -#define X_RIGHT_CTRL e4 -#define X_RIGHT_SHIFT e5 -#define X_RIGHT_ALT e6 -#define X_RIGHT_GUI e7 - -/* Media and Function keys */ -/* Generic Desktop Page (0x01) */ -#define X_SYSTEM_POWER a5 -#define X_SYSTEM_SLEEP a6 -#define X_SYSTEM_WAKE a7 - -/* Consumer Page (0x0C) */ -#define X_AUDIO_MUTE a8 -#define X_AUDIO_VOL_UP a9 -#define X_AUDIO_VOL_DOWN aa -#define X_MEDIA_NEXT_TRACK ab -#define X_MEDIA_PREV_TRACK ac -#define X_MEDIA_STOP ad -#define X_MEDIA_PLAY_PAUSE ae -#define X_MEDIA_SELECT af -#define X_MEDIA_EJECT b0 -#define X_MAIL b1 -#define X_CALCULATOR b2 -#define X_MY_COMPUTER b3 -#define X_WWW_SEARCH b4 -#define X_WWW_HOME b5 -#define X_WWW_BACK b6 -#define X_WWW_FORWARD b7 -#define X_WWW_STOP b8 -#define X_WWW_REFRESH b9 -#define X_WWW_FAVORITES ba -#define X_MEDIA_FAST_FORWARD bb -#define X_MEDIA_REWIND bc -#define X_BRIGHTNESS_UP bd -#define X_BRIGHTNESS_DOWN be - -/* Mouse Buttons (unallocated range in HID spec) */ -#ifdef VIA_ENABLE -#define X_MS_UP f0 -#define X_MS_DOWN f1 -#define X_MS_LEFT f2 -#define X_MS_RIGHT f3 -#define X_MS_BTN1 f4 -#define X_MS_BTN2 f5 -#define X_MS_BTN3 f6 -#define X_MS_BTN4 f7 -#define X_MS_BTN5 f8 -#define X_MS_BTN6 f8 -#define X_MS_BTN7 f8 -#define X_MS_BTN8 f8 -#else -#define X_MS_UP ed -#define X_MS_DOWN ee -#define X_MS_LEFT ef -#define X_MS_RIGHT f0 -#define X_MS_BTN1 f1 -#define X_MS_BTN2 f2 -#define X_MS_BTN3 f3 -#define X_MS_BTN4 f4 -#define X_MS_BTN5 f5 -#define X_MS_BTN6 f6 -#define X_MS_BTN7 f7 -#define X_MS_BTN8 f8 -#endif -#define X_MS_WH_UP f9 -#define X_MS_WH_DOWN fa -#define X_MS_WH_LEFT fb -#define X_MS_WH_RIGHT fc -#define X_MS_ACCEL0 fd -#define X_MS_ACCEL1 fe -#define X_MS_ACCEL2 ff - -// Send string macros -#define STRINGIZE(z) #z -#define ADD_SLASH_X(y) STRINGIZE(\x##y) -#define SYMBOL_STR(x) ADD_SLASH_X(x) - -#define SS_QMK_PREFIX 1 - -#define SS_TAP_CODE 1 -#define SS_DOWN_CODE 2 -#define SS_UP_CODE 3 -#define SS_DELAY_CODE 4 - -#define SS_TAP(keycode) "\1\1" SYMBOL_STR(keycode) -#define SS_DOWN(keycode) "\1\2" SYMBOL_STR(keycode) -#define SS_UP(keycode) "\1\3" SYMBOL_STR(keycode) -#define SS_DELAY(msecs) "\1\4" STRINGIZE(msecs) "|" - -// `string` arguments must not be parenthesized -#define SS_LCTL(string) SS_DOWN(X_LCTL) string SS_UP(X_LCTL) -#define SS_LSFT(string) SS_DOWN(X_LSFT) string SS_UP(X_LSFT) -#define SS_LALT(string) SS_DOWN(X_LALT) string SS_UP(X_LALT) -#define SS_LGUI(string) SS_DOWN(X_LGUI) string SS_UP(X_LGUI) -#define SS_LCMD(string) SS_LGUI(string) -#define SS_LWIN(string) SS_LGUI(string) - -#define SS_RCTL(string) SS_DOWN(X_RCTL) string SS_UP(X_RCTL) -#define SS_RSFT(string) SS_DOWN(X_RSFT) string SS_UP(X_RSFT) -#define SS_RALT(string) SS_DOWN(X_RALT) string SS_UP(X_RALT) -#define SS_RGUI(string) SS_DOWN(X_RGUI) string SS_UP(X_RGUI) -#define SS_ALGR(string) SS_RALT(string) -#define SS_RCMD(string) SS_RGUI(string) -#define SS_RWIN(string) SS_RGUI(string) - -// DEPRECATED -#define X_BSPACE X_BACKSPACE -#define X_LBRACKET X_LEFT_BRACKET -#define X_RBRACKET X_RIGHT_BRACKET -#define X_BSLASH X_BACKSLASH -#define X_SCOLON X_SEMICOLON -#define X_CAPSLOCK X_CAPS_LOCK -#define X_PSCREEN X_PRINT_SCREEN -#define X_SCROLLLOCK X_SCROLL_LOCK -#define X_PGDOWN X_PAGE_DOWN -#define X_NUMLOCK X_NUM_LOCK -#define X_NONUS_BSLASH X_NONUS_BACKSLASH -#define X_POWER X_KB_POWER -#define X__MUTE X_KB_MUTE -#define X__VOLUP X_KB_VOLUME_UP -#define X__VOLDOWN X_KB_VOLUME_DOWN -#define X_LOCKING_CAPS X_LOCKING_CAPS_LOCK -#define X_LOCKING_NUM X_LOCKING_NUM_LOCK -#define X_LOCKING_SCROLL X_LOCKING_SCROLL_LOCK -#define X_LANG1 X_LANGUAGE_1 -#define X_LANG2 X_LANGUAGE_2 -#define X_LANG3 X_LANGUAGE_3 -#define X_LANG4 X_LANGUAGE_4 -#define X_LANG5 X_LANGUAGE_5 -#define X_LANG6 X_LANGUAGE_6 -#define X_LANG7 X_LANGUAGE_7 -#define X_LANG8 X_LANGUAGE_8 -#define X_LANG9 X_LANGUAGE_9 -#define X_ALT_ERASE X_ALTERNATE_ERASE -#define X_SYSREQ X_SYSTEM_REQUEST - -#define X_LCTRL X_LEFT_CTRL -#define X_LSHIFT X_LEFT_SHIFT -#define X_RCTRL X_RIGHT_CTRL -#define X_RSHIFT X_RIGHT_SHIFT - -#define X_ZKHK X_GRAVE -#define X_RO X_INTERNATIONAL_1 -#define X_KANA X_INTERNATIONAL_2 -#define X_JYEN X_INTERNATIONAL_3 -#define X_HENK X_INTERNATIONAL_4 -#define X_MHEN X_INTERNATIONAL_5 -#define X_HAEN X_LANGUAGE_1 -#define X_HANJ X_LANGUAGE_2 - -#define X_CLCK X_CAPS_LOCK -#define X_SLCK X_SCROLL_LOCK -#define X_NLCK X_NUM_LOCK - -#define SS_LCTRL(string) SS_LCTL(string) -- cgit v1.2.3 From b8e8a20ca64327d0c5276f87a9dddc8e83cee0de Mon Sep 17 00:00:00 2001 From: Marek Wyborski <9861955+mwyborski@users.noreply.github.com> Date: Sat, 2 Jul 2022 14:12:41 +0200 Subject: Improve ENCODER_DEFAULT_POS to recognize lost ticks (#16932) --- quantum/encoder.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) (limited to 'quantum') diff --git a/quantum/encoder.c b/quantum/encoder.c index 105bed0147..5f8a7ce080 100644 --- a/quantum/encoder.c +++ b/quantum/encoder.c @@ -163,27 +163,38 @@ static bool encoder_update(uint8_t index, uint8_t state) { index += thisHand; #endif encoder_pulses[i] += encoder_LUT[state & 0xF]; + +#ifdef ENCODER_DEFAULT_POS + if ((encoder_pulses[i] >= resolution) || (encoder_pulses[i] <= -resolution) || ((state & 0x3) == ENCODER_DEFAULT_POS)) { + if (encoder_pulses[i] >= 1) { +#else if (encoder_pulses[i] >= resolution) { - encoder_value[index]++; - changed = true; +#endif + + encoder_value[index]++; + changed = true; #ifdef ENCODER_MAP_ENABLE - encoder_exec_mapping(index, ENCODER_COUNTER_CLOCKWISE); + encoder_exec_mapping(index, ENCODER_COUNTER_CLOCKWISE); #else // ENCODER_MAP_ENABLE encoder_update_kb(index, ENCODER_COUNTER_CLOCKWISE); #endif // ENCODER_MAP_ENABLE - } + } + +#ifdef ENCODER_DEFAULT_POS + if (encoder_pulses[i] <= -1) { +#else if (encoder_pulses[i] <= -resolution) { // direction is arbitrary here, but this clockwise - encoder_value[index]--; - changed = true; +#endif + encoder_value[index]--; + changed = true; #ifdef ENCODER_MAP_ENABLE - encoder_exec_mapping(index, ENCODER_CLOCKWISE); + encoder_exec_mapping(index, ENCODER_CLOCKWISE); #else // ENCODER_MAP_ENABLE encoder_update_kb(index, ENCODER_CLOCKWISE); #endif // ENCODER_MAP_ENABLE - } - encoder_pulses[i] %= resolution; + } + encoder_pulses[i] %= resolution; #ifdef ENCODER_DEFAULT_POS - if ((state & 0x3) == ENCODER_DEFAULT_POS) { encoder_pulses[i] = 0; } #endif -- 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') 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 0e5d67145a649480fd49a72712997feb6303a471 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Tue, 5 Jul 2022 08:58:35 +1000 Subject: Allow for `keymaps` array to be implemented in a file other than `$(KEYMAP_C)` (#17559) --- quantum/keymap_introspection.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'quantum') diff --git a/quantum/keymap_introspection.c b/quantum/keymap_introspection.c index 9628b41eef..7a96f802ef 100644 --- a/quantum/keymap_introspection.c +++ b/quantum/keymap_introspection.c @@ -4,6 +4,11 @@ // Pull the actual keymap code so that we can inspect stuff from it #include KEYMAP_C +// Allow for keymap or userspace rules.mk to specify an alternate location for the keymap array +#ifdef INTROSPECTION_KEYMAP_C +# include INTROSPECTION_KEYMAP_C +#endif // INTROSPECTION_KEYMAP_C + #include "keymap_introspection.h" #define NUM_KEYMAP_LAYERS ((uint8_t)(sizeof(keymaps) / ((MATRIX_ROWS) * (MATRIX_COLS) * sizeof(uint16_t)))) -- cgit v1.2.3 From d9bb189e25f14578ca74d6a3aa9f9a467f6f6595 Mon Sep 17 00:00:00 2001 From: Stefan Kerkmann Date: Thu, 7 Jul 2022 09:27:50 +0200 Subject: [Core] Update mpaland/printf to eyalroz/printf fork (#16163) mpaland printf implementation was abandoned in ~2019 and the fork by eyalroz is now regarded to be the goto replacement of it. So this commit incoporates the changes needed to use this fork in QMK. Note that pointer ptrdiff_t is always supported since commit 51c90f93a97fdaef895783ecbe24569be0db7cb8 --- quantum/logging/print.c | 2 +- quantum/logging/print.h | 68 ++++++++++++++++++++++++++++++------------------ quantum/logging/print.mk | 17 +++++++----- 3 files changed, 53 insertions(+), 34 deletions(-) (limited to 'quantum') diff --git a/quantum/logging/print.c b/quantum/logging/print.c index 50a6b826ee..17e6737ac4 100644 --- a/quantum/logging/print.c +++ b/quantum/logging/print.c @@ -28,6 +28,6 @@ void print_set_sendchar(sendchar_func_t send) { func = send; } -void _putchar(char character) { +void putchar_(char character) { func(character); } diff --git a/quantum/logging/print.h b/quantum/logging/print.h index aa72fc7074..5fbf189505 100644 --- a/quantum/logging/print.h +++ b/quantum/logging/print.h @@ -32,6 +32,22 @@ void print_set_sendchar(sendchar_func_t func); +/** + * @brief This macro suppress format warnings for the function that is passed + * in. The main use-case is that `b` format specifier for printing binary + * numbers is not in the official C standard. Inclusion is planned for the + * upcoming C2X C standard, but until then GCC will always output a warning for + * a unknown format specifier. + */ +#define IGNORE_FORMAT_WARNING(func) \ + do { \ + _Pragma("GCC diagnostic push"); \ + _Pragma("GCC diagnostic ignored \"-Wformat\""); \ + _Pragma("GCC diagnostic ignored \"-Wformat-extra-args\""); \ + func; \ + _Pragma("GCC diagnostic pop"); \ + } while (0) + #ifndef NO_PRINT # if __has_include_next("_print.h") # include_next "_print.h" /* Include the platforms print.h */ @@ -78,25 +94,25 @@ void print_set_sendchar(sendchar_func_t func); #define print_hex16(i) xprintf("%04X", i) #define print_hex32(i) xprintf("%08lX", i) /* binary */ -#define print_bin4(i) xprintf("%04b", i) -#define print_bin8(i) xprintf("%08b", i) -#define print_bin16(i) xprintf("%016b", i) -#define print_bin32(i) xprintf("%032lb", i) -#define print_bin_reverse8(i) xprintf("%08b", bitrev(i)) -#define print_bin_reverse16(i) xprintf("%016b", bitrev16(i)) -#define print_bin_reverse32(i) xprintf("%032lb", bitrev32(i)) +#define print_bin4(i) IGNORE_FORMAT_WARNING(xprintf("%04b", i)) +#define print_bin8(i) IGNORE_FORMAT_WARNING(xprintf("%08b", i)) +#define print_bin16(i) IGNORE_FORMAT_WARNING(xprintf("%016b", i)) +#define print_bin32(i) IGNORE_FORMAT_WARNING(xprintf("%032lb", i)) +#define print_bin_reverse8(i) IGNORE_FORMAT_WARNING(xprintf("%08b", bitrev(i))) +#define print_bin_reverse16(i) IGNORE_FORMAT_WARNING(xprintf("%016b", bitrev16(i))) +#define print_bin_reverse32(i) IGNORE_FORMAT_WARNINGxprintf("%032lb", bitrev32(i))) /* print value utility */ #define print_val_dec(v) xprintf(#v ": %u\n", v) #define print_val_decs(v) xprintf(#v ": %d\n", v) #define print_val_hex8(v) xprintf(#v ": %X\n", v) #define print_val_hex16(v) xprintf(#v ": %02X\n", v) #define print_val_hex32(v) xprintf(#v ": %04lX\n", v) -#define print_val_bin8(v) xprintf(#v ": %08b\n", v) -#define print_val_bin16(v) xprintf(#v ": %016b\n", v) -#define print_val_bin32(v) xprintf(#v ": %032lb\n", v) -#define print_val_bin_reverse8(v) xprintf(#v ": %08b\n", bitrev(v)) -#define print_val_bin_reverse16(v) xprintf(#v ": %016b\n", bitrev16(v)) -#define print_val_bin_reverse32(v) xprintf(#v ": %032lb\n", bitrev32(v)) +#define print_val_bin8(v) IGNORE_FORMAT_WARNING(xprintf(#v ": %08b\n", v)) +#define print_val_bin16(v) IGNORE_FORMAT_WARNING(xprintf(#v ": %016b\n", v)) +#define print_val_bin32(v) IGNORE_FORMAT_WARNING(xprintf(#v ": %032lb\n", v)) +#define print_val_bin_reverse8(v) IGNORE_FORMAT_WARNING(xprintf(#v ": %08b\n", bitrev(v))) +#define print_val_bin_reverse16(v) IGNORE_FORMAT_WARNING(xprintf(#v ": %016b\n", bitrev16(v))) +#define print_val_bin_reverse32(v) IGNORE_FORMAT_WARNING(xprintf(#v ": %032lb\n", bitrev32(v))) // User print disables the normal print messages in the body of QMK/TMK code and // is meant as a lightweight alternative to NOPRINT. Use it when you only want to do @@ -114,22 +130,22 @@ void print_set_sendchar(sendchar_func_t func); #define uprint_hex16(i) uprintf("%04X", i) #define uprint_hex32(i) uprintf("%08lX", i) /* binary */ -#define uprint_bin4(i) uprintf("%04b", i) -#define uprint_bin8(i) uprintf("%08b", i) -#define uprint_bin16(i) uprintf("%016b", i) -#define uprint_bin32(i) uprintf("%032lb", i) -#define uprint_bin_reverse8(i) uprintf("%08b", bitrev(i)) -#define uprint_bin_reverse16(i) uprintf("%016b", bitrev16(i)) -#define uprint_bin_reverse32(i) uprintf("%032lb", bitrev32(i)) +#define uprint_bin4(i) IGNORE_FORMAT_WARNING(uprintf("%04b", i)) +#define uprint_bin8(i) IGNORE_FORMAT_WARNING(uprintf("%08b", i)) +#define uprint_bin16(i) IGNORE_FORMAT_WARNING(uprintf("%016b", i)) +#define uprint_bin32(i) IGNORE_FORMAT_WARNING(uprintf("%032lb", i)) +#define uprint_bin_reverse8(i) IGNORE_FORMAT_WARNING(uprintf("%08b", bitrev(i))) +#define uprint_bin_reverse16(i) IGNORE_FORMAT_WARNING(uprintf("%016b", bitrev16(i))) +#define uprint_bin_reverse32(i) IGNORE_FORMAT_WARNING(uprintf("%032lb", bitrev32(i))) /* print value utility */ #define uprint_val_dec(v) uprintf(#v ": %u\n", v) #define uprint_val_decs(v) uprintf(#v ": %d\n", v) #define uprint_val_hex8(v) uprintf(#v ": %X\n", v) #define uprint_val_hex16(v) uprintf(#v ": %02X\n", v) #define uprint_val_hex32(v) uprintf(#v ": %04lX\n", v) -#define uprint_val_bin8(v) uprintf(#v ": %08b\n", v) -#define uprint_val_bin16(v) uprintf(#v ": %016b\n", v) -#define uprint_val_bin32(v) uprintf(#v ": %032lb\n", v) -#define uprint_val_bin_reverse8(v) uprintf(#v ": %08b\n", bitrev(v)) -#define uprint_val_bin_reverse16(v) uprintf(#v ": %016b\n", bitrev16(v)) -#define uprint_val_bin_reverse32(v) uprintf(#v ": %032lb\n", bitrev32(v)) +#define uprint_val_bin8(v) IGNORE_FORMAT_WARNING(uprintf(#v ": %08b\n", v)) +#define uprint_val_bin16(v) IGNORE_FORMAT_WARNING(uprintf(#v ": %016b\n", v)) +#define uprint_val_bin32(v) IGNORE_FORMAT_WARNING(uprintf(#v ": %032lb\n", v)) +#define uprint_val_bin_reverse8(v) IGNORE_FORMAT_WARNING(uprintf(#v ": %08b\n", bitrev(v))) +#define uprint_val_bin_reverse16(v) IGNORE_FORMAT_WARNING(uprintf(#v ": %016b\n", bitrev16(v))) +#define uprint_val_bin_reverse32(v) IGNORE_FORMAT_WARNING(uprintf(#v ": %032lb\n", bitrev32(v))) diff --git a/quantum/logging/print.mk b/quantum/logging/print.mk index 67c004192d..658c533dad 100644 --- a/quantum/logging/print.mk +++ b/quantum/logging/print.mk @@ -1,9 +1,12 @@ -PRINTF_PATH = $(LIB_PATH)/printf +PRINTF_PATH = $(LIB_PATH)/printf/src -VPATH += $(PRINTF_PATH) -SRC += $(PRINTF_PATH)/printf.c +VPATH += $(PRINTF_PATH) $(PRINTF_PATH)/printf +SRC += printf.c QUANTUM_SRC +=$(QUANTUM_DIR)/logging/print.c -OPT_DEFS += -DPRINTF_DISABLE_SUPPORT_FLOAT -OPT_DEFS += -DPRINTF_DISABLE_SUPPORT_EXPONENTIAL -OPT_DEFS += -DPRINTF_DISABLE_SUPPORT_LONG_LONG -OPT_DEFS += -DPRINTF_DISABLE_SUPPORT_PTRDIFF_T + +OPT_DEFS += -DPRINTF_SUPPORT_DECIMAL_SPECIFIERS=0 +OPT_DEFS += -DPRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS=0 +OPT_DEFS += -DPRINTF_SUPPORT_LONG_LONG=0 +OPT_DEFS += -DPRINTF_SUPPORT_WRITEBACK_SPECIFIER=0 +OPT_DEFS += -DSUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS=0 +OPT_DEFS += -DPRINTF_ALIAS_STANDARD_FUNCTION_NAMES=1 -- cgit v1.2.3 From 8224f62806b66f0825b68fd8c00436ee57a28e9a Mon Sep 17 00:00:00 2001 From: Stefan Kerkmann Date: Thu, 7 Jul 2022 10:00:40 +0200 Subject: Make debounce() signal changes in the cooked matrix as return value (#17554) --- quantum/debounce.h | 16 +++++++++++----- quantum/debounce/asym_eager_defer_pk.c | 11 +++++++++-- quantum/debounce/none.c | 11 +++++++---- quantum/debounce/sym_defer_g.c | 12 +++++++++--- quantum/debounce/sym_defer_pk.c | 12 +++++++++--- quantum/debounce/sym_defer_pr.c | 14 +++++++++----- quantum/debounce/sym_eager_pk.c | 7 ++++++- quantum/debounce/sym_eager_pr.c | 11 ++++++++--- quantum/debounce/tests/debounce_test_common.cpp | 6 +++++- quantum/matrix.c | 5 ++--- quantum/matrix_common.c | 5 ++--- 11 files changed, 77 insertions(+), 33 deletions(-) (limited to 'quantum') diff --git a/quantum/debounce.h b/quantum/debounce.h index 3532d9cd7b..a8629654c2 100644 --- a/quantum/debounce.h +++ b/quantum/debounce.h @@ -1,10 +1,16 @@ #pragma once -// raw is the current key state -// on entry cooked is the previous debounced state -// on exit cooked is the current debounced state -// changed is true if raw has changed since the last call -void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed); +/** + * @brief Debounce raw matrix events according to the choosen debounce algorithm. + * + * @param raw The current key state + * @param cooked The debounced key state + * @param num_rows Number of rows to debounce + * @param changed True if raw has changed since the last call + * @return true Cooked has new keychanges after debouncing + * @return false Cooked is the same as before + */ +bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed); void debounce_init(uint8_t num_rows); diff --git a/quantum/debounce/asym_eager_defer_pk.c b/quantum/debounce/asym_eager_defer_pk.c index b1eb4a2b7b..4745c6f465 100644 --- a/quantum/debounce/asym_eager_defer_pk.c +++ b/quantum/debounce/asym_eager_defer_pk.c @@ -55,6 +55,7 @@ static debounce_counter_t *debounce_counters; static fast_timer_t last_time; static bool counters_need_update; static bool matrix_need_update; +static bool cooked_changed; # define DEBOUNCE_ELAPSED 0 @@ -77,8 +78,9 @@ void debounce_free(void) { debounce_counters = NULL; } -void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { +bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { bool updated_last = false; + cooked_changed = false; if (counters_need_update) { fast_timer_t now = timer_read_fast(); @@ -102,6 +104,8 @@ void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool transfer_matrix_values(raw, cooked, num_rows); } + + return cooked_changed; } static void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t elapsed_time) { @@ -123,7 +127,9 @@ static void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_need_update = true; } else { // key-up: defer - cooked[row] = (cooked[row] & ~col_mask) | (raw[row] & col_mask); + matrix_row_t cooked_next = (cooked[row] & ~col_mask) | (raw[row] & col_mask); + cooked_changed |= cooked_next ^ cooked[row]; + cooked[row] = cooked_next; } } else { debounce_pointer->time -= elapsed_time; @@ -152,6 +158,7 @@ static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], ui if (debounce_pointer->pressed) { // key-down: eager cooked[row] ^= col_mask; + cooked_changed = true; } } } else if (debounce_pointer->time != DEBOUNCE_ELAPSED) { diff --git a/quantum/debounce/none.c b/quantum/debounce/none.c index 8a85cc04a8..4cff5e05e2 100644 --- a/quantum/debounce/none.c +++ b/quantum/debounce/none.c @@ -17,13 +17,16 @@ #include "matrix.h" #include "quantum.h" #include +#include void debounce_init(uint8_t num_rows) {} -void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { - for (int i = 0; i < num_rows; i++) { - cooked[i] = raw[i]; - } +bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { + bool cooked_changed = memcmp(raw, cooked, sizeof(matrix_row_t) * num_rows) != 0; + + memcpy(cooked, raw, sizeof(matrix_row_t) * num_rows); + + return cooked_changed; } void debounce_free(void) {} diff --git a/quantum/debounce/sym_defer_g.c b/quantum/debounce/sym_defer_g.c index 47450992a4..d04310a761 100644 --- a/quantum/debounce/sym_defer_g.c +++ b/quantum/debounce/sym_defer_g.c @@ -20,6 +20,7 @@ When no state changes have occured for DEBOUNCE milliseconds, we push the state. #include "matrix.h" #include "timer.h" #include "quantum.h" +#include #ifndef DEBOUNCE # define DEBOUNCE 5 #endif @@ -30,18 +31,23 @@ static fast_timer_t debouncing_time; void debounce_init(uint8_t num_rows) {} -void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { +bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { + bool cooked_changed = false; + if (changed) { debouncing = true; debouncing_time = timer_read_fast(); } if (debouncing && timer_elapsed_fast(debouncing_time) >= DEBOUNCE) { - for (int i = 0; i < num_rows; i++) { - cooked[i] = raw[i]; + if (memcmp(cooked, raw, sizeof(matrix_row_t) * num_rows) != 0) { + memcpy(cooked, raw, sizeof(matrix_row_t) * num_rows); + cooked_changed = true; } debouncing = false; } + + return cooked_changed; } void debounce_free(void) {} diff --git a/quantum/debounce/sym_defer_pk.c b/quantum/debounce/sym_defer_pk.c index 9dee29e28e..7b59b5e100 100644 --- a/quantum/debounce/sym_defer_pk.c +++ b/quantum/debounce/sym_defer_pk.c @@ -48,6 +48,7 @@ typedef uint8_t debounce_counter_t; static debounce_counter_t *debounce_counters; static fast_timer_t last_time; static bool counters_need_update; +static bool cooked_changed; # define DEBOUNCE_ELAPSED 0 @@ -70,8 +71,9 @@ void debounce_free(void) { debounce_counters = NULL; } -void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { +bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { bool updated_last = false; + cooked_changed = false; if (counters_need_update) { fast_timer_t now = timer_read_fast(); @@ -95,6 +97,8 @@ void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool start_debounce_counters(raw, cooked, num_rows); } + + return cooked_changed; } static void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t elapsed_time) { @@ -104,8 +108,10 @@ static void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], for (uint8_t col = 0; col < MATRIX_COLS; col++) { if (*debounce_pointer != DEBOUNCE_ELAPSED) { if (*debounce_pointer <= elapsed_time) { - *debounce_pointer = DEBOUNCE_ELAPSED; - cooked[row] = (cooked[row] & ~(ROW_SHIFTER << col)) | (raw[row] & (ROW_SHIFTER << col)); + *debounce_pointer = DEBOUNCE_ELAPSED; + matrix_row_t cooked_next = (cooked[row] & ~(ROW_SHIFTER << col)) | (raw[row] & (ROW_SHIFTER << col)); + cooked_changed |= cooked[row] ^ cooked_next; + cooked[row] = cooked_next; } else { *debounce_pointer -= elapsed_time; counters_need_update = true; diff --git a/quantum/debounce/sym_defer_pr.c b/quantum/debounce/sym_defer_pr.c index ce24f0922f..452c4599d0 100644 --- a/quantum/debounce/sym_defer_pr.c +++ b/quantum/debounce/sym_defer_pr.c @@ -46,11 +46,12 @@ void debounce_free(void) { last_raw = NULL; } -void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { - uint16_t now = timer_read(); - uint16_t elapsed16 = TIMER_DIFF_16(now, last_time); - last_time = now; - uint8_t elapsed = (elapsed16 > 255) ? 255 : elapsed16; +bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { + uint16_t now = timer_read(); + uint16_t elapsed16 = TIMER_DIFF_16(now, last_time); + last_time = now; + uint8_t elapsed = (elapsed16 > 255) ? 255 : elapsed16; + bool cooked_changed = false; uint8_t* countdown = countdowns; @@ -63,10 +64,13 @@ void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool } else if (*countdown > elapsed) { *countdown -= elapsed; } else if (*countdown) { + cooked_changed |= cooked[row] ^ raw_row; cooked[row] = raw_row; *countdown = 0; } } + + return cooked_changed; } bool debounce_active(void) { diff --git a/quantum/debounce/sym_eager_pk.c b/quantum/debounce/sym_eager_pk.c index fe3e88bb06..f736d1645c 100644 --- a/quantum/debounce/sym_eager_pk.c +++ b/quantum/debounce/sym_eager_pk.c @@ -49,6 +49,7 @@ static debounce_counter_t *debounce_counters; static fast_timer_t last_time; static bool counters_need_update; static bool matrix_need_update; +static bool cooked_changed; # define DEBOUNCE_ELAPSED 0 @@ -71,8 +72,9 @@ void debounce_free(void) { debounce_counters = NULL; } -void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { +bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { bool updated_last = false; + cooked_changed = false; if (counters_need_update) { fast_timer_t now = timer_read_fast(); @@ -96,6 +98,8 @@ void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool transfer_matrix_values(raw, cooked, num_rows); } + + return cooked_changed; } // If the current time is > debounce counter, set the counter to enable input. @@ -132,6 +136,7 @@ static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], ui *debounce_pointer = DEBOUNCE; counters_need_update = true; existing_row ^= col_mask; // flip the bit. + cooked_changed = true; } } debounce_pointer++; diff --git a/quantum/debounce/sym_eager_pr.c b/quantum/debounce/sym_eager_pr.c index 29b0cabefb..aad5ca351b 100644 --- a/quantum/debounce/sym_eager_pr.c +++ b/quantum/debounce/sym_eager_pr.c @@ -48,6 +48,7 @@ static bool matrix_need_update; static debounce_counter_t *debounce_counters; static fast_timer_t last_time; static bool counters_need_update; +static bool cooked_changed; # define DEBOUNCE_ELAPSED 0 @@ -67,8 +68,9 @@ void debounce_free(void) { debounce_counters = NULL; } -void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { +bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { bool updated_last = false; + cooked_changed = false; if (counters_need_update) { fast_timer_t now = timer_read_fast(); @@ -92,6 +94,8 @@ void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool transfer_matrix_values(raw, cooked, num_rows); } + + return cooked_changed; } // If the current time is > debounce counter, set the counter to enable input. @@ -123,8 +127,9 @@ static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], ui // determine new value basd on debounce pointer + raw value if (existing_row != raw_row) { if (*debounce_pointer == DEBOUNCE_ELAPSED) { - *debounce_pointer = DEBOUNCE; - cooked[row] = raw_row; + *debounce_pointer = DEBOUNCE; + cooked[row] = raw_row; + cooked_changed |= cooked[row] ^ raw[row]; counters_need_update = true; } } diff --git a/quantum/debounce/tests/debounce_test_common.cpp b/quantum/debounce/tests/debounce_test_common.cpp index 0d5a7bb766..bd98e32955 100644 --- a/quantum/debounce/tests/debounce_test_common.cpp +++ b/quantum/debounce/tests/debounce_test_common.cpp @@ -125,11 +125,15 @@ void DebounceTest::runDebounce(bool changed) { std::copy(std::begin(input_matrix_), std::end(input_matrix_), std::begin(raw_matrix_)); std::copy(std::begin(output_matrix_), std::end(output_matrix_), std::begin(cooked_matrix_)); - debounce(raw_matrix_, cooked_matrix_, MATRIX_ROWS, changed); + bool cooked_changed = debounce(raw_matrix_, cooked_matrix_, MATRIX_ROWS, changed); if (!std::equal(std::begin(input_matrix_), std::end(input_matrix_), std::begin(raw_matrix_))) { FAIL() << "Fatal error: debounce() modified raw matrix at " << strTime() << "\ninput_matrix: changed=" << changed << "\n" << strMatrix(input_matrix_) << "\nraw_matrix:\n" << strMatrix(raw_matrix_); } + + if (std::equal(std::begin(output_matrix_), std::end(output_matrix_), std::begin(cooked_matrix_)) && cooked_changed) { + FAIL() << "Fatal error: debounce() did detect a wrong cooked matrix change at " << strTime() << "\noutput_matrix: cooked_changed=" << cooked_changed << "\n" << strMatrix(output_matrix_) << "\ncooked_matrix:\n" << strMatrix(cooked_matrix_); + } } void DebounceTest::checkCookedMatrix(bool changed, const std::string &error_message) { diff --git a/quantum/matrix.c b/quantum/matrix.c index db59b73754..db683104ed 100644 --- a/quantum/matrix.c +++ b/quantum/matrix.c @@ -337,10 +337,9 @@ uint8_t matrix_scan(void) { if (changed) memcpy(raw_matrix, curr_matrix, sizeof(curr_matrix)); #ifdef SPLIT_KEYBOARD - debounce(raw_matrix, matrix + thisHand, ROWS_PER_HAND, changed); - changed = (changed || matrix_post_scan()); + changed = debounce(raw_matrix, matrix + thisHand, ROWS_PER_HAND, changed) | matrix_post_scan(); #else - debounce(raw_matrix, matrix, ROWS_PER_HAND, changed); + changed = debounce(raw_matrix, matrix, ROWS_PER_HAND, changed); matrix_scan_quantum(); #endif return (uint8_t)changed; diff --git a/quantum/matrix_common.c b/quantum/matrix_common.c index 2cf8484347..68f0e38297 100644 --- a/quantum/matrix_common.c +++ b/quantum/matrix_common.c @@ -169,10 +169,9 @@ __attribute__((weak)) uint8_t matrix_scan(void) { bool changed = matrix_scan_custom(raw_matrix); #ifdef SPLIT_KEYBOARD - debounce(raw_matrix, matrix + thisHand, ROWS_PER_HAND, changed); - changed = (changed || matrix_post_scan()); + changed = debounce(raw_matrix, matrix + thisHand, ROWS_PER_HAND, changed) | matrix_post_scan(); #else - debounce(raw_matrix, matrix, ROWS_PER_HAND, changed); + changed = debounce(raw_matrix, matrix, ROWS_PER_HAND, changed); matrix_scan_quantum(); #endif -- cgit v1.2.3 From 643f6367a19649a0f5af6d43f2ed7f0e93b21f70 Mon Sep 17 00:00:00 2001 From: Stefan Kerkmann Date: Thu, 7 Jul 2022 14:14:09 +0200 Subject: [Fix] Patches after printf library update (#17584) * Add missing '(' to print_bin_reverse32 declaration * Fix insufficient character buffers on satisfaction75 * Remove \0 character in format string and use corrected offset math instead on rocketboard 16 * Replace snprintf_ with snprintf for djinn * Explicitly ignore format checks for tracktyl manuform that uses %b specifier * Print properly escaped version string in command.c, as PRODUCT or other defines can contain constructs like 'Vendor keyboard 66%' which will be interpreted as a format specifier --- quantum/command.c | 2 +- quantum/logging/print.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'quantum') diff --git a/quantum/command.c b/quantum/command.c index 2e94cb44b8..84757b9b01 100644 --- a/quantum/command.c +++ b/quantum/command.c @@ -161,7 +161,7 @@ static void command_common_help(void) { } static void print_version(void) { - print(/* clang-format off */ + xprintf("%s", /* clang-format off */ "\n\t- Version -\n" "VID: " STR(VENDOR_ID) "(" STR(MANUFACTURER) ") " "PID: " STR(PRODUCT_ID) "(" STR(PRODUCT) ") " diff --git a/quantum/logging/print.h b/quantum/logging/print.h index 5fbf189505..4c4195de50 100644 --- a/quantum/logging/print.h +++ b/quantum/logging/print.h @@ -100,7 +100,7 @@ void print_set_sendchar(sendchar_func_t func); #define print_bin32(i) IGNORE_FORMAT_WARNING(xprintf("%032lb", i)) #define print_bin_reverse8(i) IGNORE_FORMAT_WARNING(xprintf("%08b", bitrev(i))) #define print_bin_reverse16(i) IGNORE_FORMAT_WARNING(xprintf("%016b", bitrev16(i))) -#define print_bin_reverse32(i) IGNORE_FORMAT_WARNINGxprintf("%032lb", bitrev32(i))) +#define print_bin_reverse32(i) IGNORE_FORMAT_WARNING(xprintf("%032lb", bitrev32(i))) /* print value utility */ #define print_val_dec(v) xprintf(#v ": %u\n", v) #define print_val_decs(v) xprintf(#v ": %d\n", v) -- cgit v1.2.3 From 61df87ae2ccaafc8387c4349187f0ab7e9e7bfbc Mon Sep 17 00:00:00 2001 From: "FREEWING.JP" Date: Tue, 12 Jul 2022 04:59:16 +0900 Subject: Added Delay time dynamic keymap's macro feature (#16810) Co-authored-by: Joel Challis Co-authored-by: Ryan --- quantum/dynamic_keymap.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'quantum') diff --git a/quantum/dynamic_keymap.c b/quantum/dynamic_keymap.c index fc1c55784d..cbe9f13940 100644 --- a/quantum/dynamic_keymap.c +++ b/quantum/dynamic_keymap.c @@ -93,6 +93,10 @@ _Static_assert((DYNAMIC_KEYMAP_EEPROM_MAX_ADDR) - (DYNAMIC_KEYMAP_MACRO_EEPROM_A # define DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE (DYNAMIC_KEYMAP_EEPROM_MAX_ADDR - DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR + 1) #endif +#ifndef DYNAMIC_KEYMAP_MACRO_DELAY +# define DYNAMIC_KEYMAP_MACRO_DELAY TAP_CODE_DELAY +#endif + uint8_t dynamic_keymap_get_layer_count(void) { return DYNAMIC_KEYMAP_LAYER_COUNT; } @@ -300,6 +304,6 @@ void dynamic_keymap_macro_send(uint8_t id) { break; } } - send_string(data); + send_string_with_delay(data, DYNAMIC_KEYMAP_MACRO_DELAY); } } -- cgit v1.2.3 From 5db705d054e54a901f8968e3380e13c791991ab3 Mon Sep 17 00:00:00 2001 From: Daniel Kao Date: Tue, 12 Jul 2022 21:17:40 -0700 Subject: Cirque trackpad features: circular scroll, inertial cursor (#17482) --- quantum/pointing_device.h | 2 + quantum/pointing_device_drivers.c | 101 +++++++++++++++++----------- quantum/pointing_device_gestures.c | 133 +++++++++++++++++++++++++++++++++++++ quantum/pointing_device_gestures.h | 58 ++++++++++++++++ 4 files changed, 257 insertions(+), 37 deletions(-) create mode 100644 quantum/pointing_device_gestures.c create mode 100644 quantum/pointing_device_gestures.h (limited to 'quantum') diff --git a/quantum/pointing_device.h b/quantum/pointing_device.h index 1e5ef9590c..8225e55aa2 100644 --- a/quantum/pointing_device.h +++ b/quantum/pointing_device.h @@ -31,6 +31,8 @@ along with this program. If not, see . # include "drivers/sensors/analog_joystick.h" #elif defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_i2c) || defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_spi) # include "drivers/sensors/cirque_pinnacle.h" +# include "drivers/sensors/cirque_pinnacle_gestures.h" +# include "pointing_device_gestures.h" #elif defined(POINTING_DEVICE_DRIVER_pimoroni_trackball) # include "i2c_master.h" # include "drivers/sensors/pimoroni_trackball.h" diff --git a/quantum/pointing_device_drivers.c b/quantum/pointing_device_drivers.c index 435fcabf7c..8f510b3a98 100644 --- a/quantum/pointing_device_drivers.c +++ b/quantum/pointing_device_drivers.c @@ -97,27 +97,48 @@ const pointing_device_driver_t pointing_device_driver = { // clang-format on #elif defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_i2c) || defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_spi) -# ifndef CIRQUE_PINNACLE_TAPPING_TERM -# include "action.h" -# include "action_tapping.h" -# define CIRQUE_PINNACLE_TAPPING_TERM GET_TAPPING_TERM(KC_BTN1, &(keyrecord_t){}) -# endif -# ifndef CIRQUE_PINNACLE_TOUCH_DEBOUNCE -# define CIRQUE_PINNACLE_TOUCH_DEBOUNCE (CIRQUE_PINNACLE_TAPPING_TERM * 8) +# ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE +static bool cursor_glide_enable = true; + +static cursor_glide_context_t glide = {.config = { + .coef = 102, /* Good default friction coef */ + .interval = 10, /* 100sps */ + .trigger_px = 10, /* Default threshold in case of hover, set to 0 if you'd like */ + }}; + +void cirque_pinnacle_enable_cursor_glide(bool enable) { + cursor_glide_enable = enable; +} + +void cirque_pinnacle_configure_cursor_glide(float trigger_px) { + glide.config.trigger_px = trigger_px; +} # endif report_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) { pinnacle_data_t touchData = cirque_pinnacle_read_data(); mouse_xy_report_t report_x = 0, report_y = 0; static mouse_xy_report_t x = 0, y = 0; - static uint16_t mouse_timer = 0; - static bool is_z_down = false; +# ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE + cursor_glide_t glide_report = {0}; + + if (cursor_glide_enable) { + glide_report = cursor_glide_check(&glide); + } +# endif # if !CIRQUE_PINNACLE_POSITION_MODE # error Cirque Pinnacle with relative mode not implemented yet. # endif if (!touchData.valid) { +# ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE + if (cursor_glide_enable && glide_report.valid) { + report_x = glide_report.dx; + report_y = glide_report.dy; + goto mouse_report_update; + } +# endif return mouse_report; } @@ -130,45 +151,51 @@ report_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) { // Scale coordinates to arbitrary X, Y resolution cirque_pinnacle_scale_data(&touchData, cirque_pinnacle_get_scale(), cirque_pinnacle_get_scale()); - if (x && y && touchData.xValue && touchData.yValue) { - report_x = (mouse_xy_report_t)(touchData.xValue - x); - report_y = (mouse_xy_report_t)(touchData.yValue - y); - } - x = touchData.xValue; - y = touchData.yValue; - mouse_report.x = report_x; - mouse_report.y = report_y; - - if (touchData.touchDown != is_z_down) { - is_z_down = touchData.touchDown; - if (!touchData.zValue) { - if (timer_elapsed(mouse_timer) < CIRQUE_PINNACLE_TAPPING_TERM && mouse_timer != 0) { - mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, true, POINTING_DEVICE_BUTTON1); - pointing_device_set_report(mouse_report); - pointing_device_send(); -# if TAP_CODE_DELAY > 0 - wait_ms(TAP_CODE_DELAY); -# endif - mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, false, POINTING_DEVICE_BUTTON1); - pointing_device_set_report(mouse_report); - pointing_device_send(); + if (!cirque_pinnacle_gestures(&mouse_report, touchData)) { + if (x && y && touchData.xValue && touchData.yValue) { + report_x = (mouse_xy_report_t)(touchData.xValue - x); + report_y = (mouse_xy_report_t)(touchData.yValue - y); + } + x = touchData.xValue; + y = touchData.yValue; + +# ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE + if (cursor_glide_enable) { + if (touchData.touchDown) { + cursor_glide_update(&glide, report_x, report_y, touchData.zValue); + } else if (!glide_report.valid) { + glide_report = cursor_glide_start(&glide); + if (glide_report.valid) { + report_x = glide_report.dx; + report_y = glide_report.dy; + } } } - mouse_timer = timer_read(); - } - if (timer_elapsed(mouse_timer) > (CIRQUE_PINNACLE_TOUCH_DEBOUNCE)) { - mouse_timer = 0; +# endif } +# ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE +mouse_report_update: +# endif + mouse_report.x = report_x; + mouse_report.y = report_y; + return mouse_report; } +uint16_t cirque_pinnacle_get_cpi(void) { + return CIRQUE_PINNACLE_PX_TO_INCH(cirque_pinnacle_get_scale()); +} +void cirque_pinnacle_set_cpi(uint16_t cpi) { + cirque_pinnacle_set_scale(CIRQUE_PINNACLE_INCH_TO_PX(cpi)); +} + // clang-format off const pointing_device_driver_t pointing_device_driver = { .init = cirque_pinnacle_init, .get_report = cirque_pinnacle_get_report, - .set_cpi = cirque_pinnacle_set_scale, - .get_cpi = cirque_pinnacle_get_scale + .set_cpi = cirque_pinnacle_set_cpi, + .get_cpi = cirque_pinnacle_get_cpi }; // clang-format on diff --git a/quantum/pointing_device_gestures.c b/quantum/pointing_device_gestures.c new file mode 100644 index 0000000000..02b11ebe3f --- /dev/null +++ b/quantum/pointing_device_gestures.c @@ -0,0 +1,133 @@ +/* Copyright 2022 Daniel Kao + * + * 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 +#include "pointing_device_gestures.h" +#include "timer.h" + +#ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE +# ifdef POINTING_DEVICE_MOTION_PIN +# error POINTING_DEVICE_MOTION_PIN not supported when using inertial cursor. Need repeated calls to get_report() to generate glide events. +# endif + +static void cursor_glide_stop(cursor_glide_context_t* glide) { + memset(&glide->status, 0, sizeof(glide->status)); +} + +static cursor_glide_t cursor_glide(cursor_glide_context_t* glide) { + cursor_glide_status_t* status = &glide->status; + cursor_glide_t report; + int32_t p; + int32_t x, y; + + if (status->v0 == 0) { + report.dx = 0; + report.dy = 0; + report.valid = false; + cursor_glide_stop(glide); + goto exit; + } + + status->counter++; + /* Calculate current 1D position */ + p = status->v0 * status->counter - (int32_t)glide->config.coef * status->counter * status->counter / 2; + /* + * Translate to x & y axes + * Done this way instead of applying friction to each axis separately, so we don't end up with the shorter axis stuck at 0 towards the end of diagonal movements. + */ + x = (int32_t)(p * status->dx0 / status->v0); + y = (int32_t)(p * status->dy0 / status->v0); + report.dx = (mouse_xy_report_t)(x - status->x); + report.dy = (mouse_xy_report_t)(y - status->y); + report.valid = true; + if (report.dx <= 1 && report.dx >= -1 && report.dy <= 1 && report.dy >= -1) { + /* Stop gliding once speed is low enough */ + cursor_glide_stop(glide); + goto exit; + } + status->x = x; + status->y = y; + status->timer = timer_read(); + +exit: + return report; +} + +cursor_glide_t cursor_glide_check(cursor_glide_context_t* glide) { + cursor_glide_t invalid_report = {0, 0, false}; + cursor_glide_status_t* status = &glide->status; + + if (status->z || (status->dx0 == 0 && status->dy0 == 0) || timer_elapsed(status->timer) < glide->config.interval) { + return invalid_report; + } else { + return cursor_glide(glide); + } +} + +static inline uint16_t sqrt32(uint32_t x) { + uint32_t l, m, h; + + if (x == 0) { + return 0; + } else if (x > (UINT16_MAX >> 2)) { + /* Safe upper bound to avoid integer overflow with m * m */ + h = UINT16_MAX; + } else { + /* Upper bound based on closest log2 */ + h = (1 << (((__builtin_clzl(1) - __builtin_clzl(x) + 1) + 1) >> 1)); + } + /* Lower bound based on closest log2 */ + l = (1 << ((__builtin_clzl(1) - __builtin_clzl(x)) >> 1)); + + /* Binary search to find integer square root */ + while (l != h - 1) { + m = (l + h) / 2; + if (m * m <= x) { + l = m; + } else { + h = m; + } + } + return l; +} + +cursor_glide_t cursor_glide_start(cursor_glide_context_t* glide) { + cursor_glide_t invalid_report = {0, 0, false}; + cursor_glide_status_t* status = &glide->status; + + status->timer = timer_read(); + status->counter = 0; + status->v0 = (status->dx0 == 0 && status->dy0 == 0) ? 0.0 : sqrt32(((int32_t)status->dx0 * 256 * status->dx0 * 256) + ((int32_t)status->dy0 * 256 * status->dy0 * 256)); // skip trigonometry if not needed, calculate distance in Q8 + status->x = 0; + status->y = 0; + status->z = 0; + + if (status->v0 < ((uint32_t)glide->config.trigger_px * 256)) { /* Q8 comparison */ + /* Not enough velocity to be worth gliding, abort */ + cursor_glide_stop(glide); + return invalid_report; + } + + return cursor_glide(glide); +} + +void cursor_glide_update(cursor_glide_context_t* glide, mouse_xy_report_t dx, mouse_xy_report_t dy, uint16_t z) { + cursor_glide_status_t* status = &glide->status; + + status->dx0 = dx; + status->dy0 = dy; + status->z = z; +} +#endif diff --git a/quantum/pointing_device_gestures.h b/quantum/pointing_device_gestures.h new file mode 100644 index 0000000000..d2ea44971b --- /dev/null +++ b/quantum/pointing_device_gestures.h @@ -0,0 +1,58 @@ +/* Copyright 2022 Daniel Kao + * + * 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 +#include "report.h" + +#ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE +typedef struct { + mouse_xy_report_t dx; + mouse_xy_report_t dy; + bool valid; +} cursor_glide_t; + +typedef struct { + uint16_t trigger_px; /* Pixels of movement needed to trigger cursor glide */ + uint16_t coef; /* Coefficient of friction */ + uint16_t interval; /* Glide report interval, in milliseconds */ +} cursor_glide_config_t; + +typedef struct { + int32_t v0; + int32_t x; + int32_t y; + uint16_t z; + uint16_t timer; + uint16_t counter; + mouse_xy_report_t dx0; + mouse_xy_report_t dy0; +} cursor_glide_status_t; + +typedef struct { + cursor_glide_config_t config; + cursor_glide_status_t status; +} cursor_glide_context_t; + +/* Check glide report conditions, calculates glide coordinates */ +cursor_glide_t cursor_glide_check(cursor_glide_context_t* glide); + +/* Start glide reporting, gives first set of glide coordinates */ +cursor_glide_t cursor_glide_start(cursor_glide_context_t* glide); + +/* Update glide engine on the latest cursor movement, cursor glide is based on the final movement */ +void cursor_glide_update(cursor_glide_context_t* glide, mouse_xy_report_t dx, mouse_xy_report_t dy, uint16_t z); +#endif -- cgit v1.2.3 From 1dfe2bb49ab9cae1450697bf95d7a4c7bfc18bab Mon Sep 17 00:00:00 2001 From: Drashna Jaelre Date: Tue, 12 Jul 2022 22:18:02 -0700 Subject: Update LED/RGB Matrix flag function behavior (#17651) --- quantum/led_matrix/led_matrix.c | 14 ++++++++++++-- quantum/led_matrix/led_matrix.h | 1 + quantum/rgb_matrix/rgb_matrix.c | 12 +++++++++++- quantum/rgb_matrix/rgb_matrix.h | 1 + 4 files changed, 25 insertions(+), 3 deletions(-) (limited to 'quantum') diff --git a/quantum/led_matrix/led_matrix.c b/quantum/led_matrix/led_matrix.c index 38ed79bed0..75a0aba5a0 100644 --- a/quantum/led_matrix/led_matrix.c +++ b/quantum/led_matrix/led_matrix.c @@ -618,10 +618,20 @@ void led_matrix_decrease_speed(void) { led_matrix_decrease_speed_helper(true); } +void led_matrix_set_flags_eeprom_helper(led_flags_t flags, bool write_to_eeprom) { + led_matrix_config.flags = flags; + eeconfig_flag_led_matrix(write_to_eeprom); + dprintf("led matrix set speed [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_config.flags); +} + led_flags_t led_matrix_get_flags(void) { - return led_matrix_eeconfig.flags; + return led_matrix_config.flags; } void led_matrix_set_flags(led_flags_t flags) { - led_matrix_eeconfig.flags = flags; + led_matrix_set_flags_eeprom_helper(flags, true); +} + +void led_matrix_set_flags_noeeprom(led_flags_t flags) { + led_matrix_set_flags_eeprom_helper(flags, false); } diff --git a/quantum/led_matrix/led_matrix.h b/quantum/led_matrix/led_matrix.h index d21f36e295..b2abec7eb1 100644 --- a/quantum/led_matrix/led_matrix.h +++ b/quantum/led_matrix/led_matrix.h @@ -158,6 +158,7 @@ void led_matrix_decrease_speed(void); void led_matrix_decrease_speed_noeeprom(void); led_flags_t led_matrix_get_flags(void); void led_matrix_set_flags(led_flags_t flags); +void led_matrix_set_flags_noeeprom(led_flags_t flags); typedef struct { /* Perform any initialisation required for the other driver functions to work. */ diff --git a/quantum/rgb_matrix/rgb_matrix.c b/quantum/rgb_matrix/rgb_matrix.c index a51e379025..2730686839 100644 --- a/quantum/rgb_matrix/rgb_matrix.c +++ b/quantum/rgb_matrix/rgb_matrix.c @@ -731,10 +731,20 @@ void rgb_matrix_decrease_speed(void) { rgb_matrix_decrease_speed_helper(true); } +void rgb_matrix_set_flags_eeprom_helper(led_flags_t flags, bool write_to_eeprom) { + rgb_matrix_config.flags = flags; + eeconfig_flag_rgb_matrix(write_to_eeprom); + dprintf("rgb matrix set speed [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", rgb_matrix_config.flags); +} + led_flags_t rgb_matrix_get_flags(void) { return rgb_matrix_config.flags; } void rgb_matrix_set_flags(led_flags_t flags) { - rgb_matrix_config.flags = flags; + rgb_matrix_set_flags_eeprom_helper(flags, true); +} + +void rgb_matrix_set_flags_noeeprom(led_flags_t flags) { + rgb_matrix_set_flags_eeprom_helper(flags, false); } diff --git a/quantum/rgb_matrix/rgb_matrix.h b/quantum/rgb_matrix/rgb_matrix.h index 359d507a4d..fc9fc3e020 100644 --- a/quantum/rgb_matrix/rgb_matrix.h +++ b/quantum/rgb_matrix/rgb_matrix.h @@ -182,6 +182,7 @@ void rgb_matrix_increase_speed_noeeprom(void); void rgb_matrix_decrease_speed(void); void rgb_matrix_decrease_speed_noeeprom(void); led_flags_t rgb_matrix_get_flags(void); +led_flags_t rgb_matrix_get_flags_noeeprom(void); void rgb_matrix_set_flags(led_flags_t flags); #ifndef RGBLIGHT_ENABLE -- cgit v1.2.3 From 09e4001bba6510afd0b34df154b75c17c32e2076 Mon Sep 17 00:00:00 2001 From: Drashna Jaelre Date: Wed, 13 Jul 2022 01:10:51 -0700 Subject: [Bug] Fix compile issue with LED Matrix (#17658) --- quantum/led_matrix/led_matrix.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'quantum') diff --git a/quantum/led_matrix/led_matrix.c b/quantum/led_matrix/led_matrix.c index 75a0aba5a0..14dd0dd48a 100644 --- a/quantum/led_matrix/led_matrix.c +++ b/quantum/led_matrix/led_matrix.c @@ -619,13 +619,13 @@ void led_matrix_decrease_speed(void) { } void led_matrix_set_flags_eeprom_helper(led_flags_t flags, bool write_to_eeprom) { - led_matrix_config.flags = flags; + led_matrix_eeconfig.flags = flags; eeconfig_flag_led_matrix(write_to_eeprom); - dprintf("led matrix set speed [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_config.flags); + dprintf("led matrix set speed [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_eeconfig.flags); } led_flags_t led_matrix_get_flags(void) { - return led_matrix_config.flags; + return led_matrix_eeconfig.flags; } void led_matrix_set_flags(led_flags_t flags) { -- cgit v1.2.3 From 3c58f989295e17d03b66db9a154e02cde7336ece Mon Sep 17 00:00:00 2001 From: Stefan Kerkmann Date: Thu, 14 Jul 2022 11:50:00 +0200 Subject: [Core] PMW33XX drivers overhaul (#17613) * PMW33XX drivers overhaul This combines the PMW3389 and PM3360 drivers as they only differ in the firmware blobs and CPI get and set functions. The following changes have been made: * PMW3389 now gets the same multi-sensor feature that is already available on the PMW3360. * Introduced a shared pmw33xx_report_t struct is now directly readable via SPI transactions instead of individual byte-sized reads, saving multiple copies and bitshift operations. * pmw33(89/60)_get_report functions had unreachable branches in their motion detection logic these have been simplied as much as possible. * The fast firmware upload option has been removed as this becomes obsolete by the newly introduced polled waiting functions for ChibiOS polled waiting * PMW33(60/89)_SPI_LSBFIRST and PMW33(60/89)_SPI_MODE config options have been removed as they don't need to be configurable. * All PMW3389 and PMW3360 defines have been unified to a PMW33XX prefix to reduce code duplication and make the defines interchangeable * Adjust keyboards to PMW33XX naming scheme --- quantum/pointing_device.h | 7 +--- quantum/pointing_device_drivers.c | 87 ++++++++++++--------------------------- 2 files changed, 29 insertions(+), 65 deletions(-) (limited to 'quantum') diff --git a/quantum/pointing_device.h b/quantum/pointing_device.h index 8225e55aa2..a8e8e75e87 100644 --- a/quantum/pointing_device.h +++ b/quantum/pointing_device.h @@ -46,12 +46,9 @@ along with this program. If not, see . # ifdef PIMORONI_TRACKBALL_ROTATE # define POINTING_DEVICE_ROTATION_90 # endif -#elif defined(POINTING_DEVICE_DRIVER_pmw3360) +#elif defined(POINTING_DEVICE_DRIVER_pmw3360) || defined(POINTING_DEVICE_DRIVER_pmw3389) # include "spi_master.h" -# include "drivers/sensors/pmw3360.h" -#elif defined(POINTING_DEVICE_DRIVER_pmw3389) -# include "spi_master.h" -# include "drivers/sensors/pmw3389.h" +# include "drivers/sensors/pmw33xx_common.h" #else void pointing_device_driver_init(void); report_mouse_t pointing_device_driver_get_report(report_mouse_t mouse_report); diff --git a/quantum/pointing_device_drivers.c b/quantum/pointing_device_drivers.c index 8f510b3a98..b7e98e897e 100644 --- a/quantum/pointing_device_drivers.c +++ b/quantum/pointing_device_drivers.c @@ -257,81 +257,48 @@ const pointing_device_driver_t pointing_device_driver = { }; // clang-format on -#elif defined(POINTING_DEVICE_DRIVER_pmw3360) -static void pmw3360_device_init(void) { - pmw3360_init(0); +#elif defined(POINTING_DEVICE_DRIVER_pmw3360) || defined(POINTING_DEVICE_DRIVER_pmw3389) +static void pmw33xx_init_wrapper(void) { + pmw33xx_init(0); } -report_mouse_t pmw3360_get_report(report_mouse_t mouse_report) { - report_pmw3360_t data = pmw3360_read_burst(0); - static uint16_t MotionStart = 0; // Timer for accel, 0 is resting state - - if (data.isOnSurface && data.isMotion) { - // Reset timer if stopped moving - if (!data.isMotion) { - if (MotionStart != 0) MotionStart = 0; - return mouse_report; - } - - // Set timer if new motion - if ((MotionStart == 0) && data.isMotion) { -# ifdef CONSOLE_ENABLE - if (debug_mouse) dprintf("Starting motion.\n"); -# endif - MotionStart = timer_read(); - } - mouse_report.x = CONSTRAIN_HID_XY(data.dx); - mouse_report.y = CONSTRAIN_HID_XY(data.dy); - } - - return mouse_report; +static void pmw33xx_set_cpi_wrapper(uint16_t cpi) { + pmw33xx_set_cpi(0, cpi); } -// clang-format off -const pointing_device_driver_t pointing_device_driver = { - .init = pmw3360_device_init, - .get_report = pmw3360_get_report, - .set_cpi = pmw3360_set_cpi, - .get_cpi = pmw3360_get_cpi -}; -// clang-format on - -#elif defined(POINTING_DEVICE_DRIVER_pmw3389) -static void pmw3389_device_init(void) { - pmw3389_init(); +static uint16_t pmw33xx_get_cpi_wrapper(void) { + return pmw33xx_get_cpi(0); } -report_mouse_t pmw3389_get_report(report_mouse_t mouse_report) { - report_pmw3389_t data = pmw3389_read_burst(); - static uint16_t MotionStart = 0; // Timer for accel, 0 is resting state +report_mouse_t pmw33xx_get_report(report_mouse_t mouse_report) { + pmw33xx_report_t report = pmw33xx_read_burst(0); + static bool in_motion = false; - if (data.isOnSurface && data.isMotion) { - // Reset timer if stopped moving - if (!data.isMotion) { - if (MotionStart != 0) MotionStart = 0; - return mouse_report; - } + if (report.motion.b.is_lifted) { + return mouse_report; + } - // Set timer if new motion - if ((MotionStart == 0) && data.isMotion) { -# ifdef CONSOLE_ENABLE - if (debug_mouse) dprintf("Starting motion.\n"); -# endif - MotionStart = timer_read(); - } - mouse_report.x = CONSTRAIN_HID_XY(data.dx); - mouse_report.y = CONSTRAIN_HID_XY(data.dy); + if (!report.motion.b.is_motion) { + in_motion = false; + return mouse_report; + } + + if (!in_motion) { + in_motion = true; + dprintf("PWM3360 (0): starting motion\n"); } + mouse_report.x = CONSTRAIN_HID_XY(report.delta_x); + mouse_report.y = CONSTRAIN_HID_XY(report.delta_y); return mouse_report; } // clang-format off const pointing_device_driver_t pointing_device_driver = { - .init = pmw3389_device_init, - .get_report = pmw3389_get_report, - .set_cpi = pmw3389_set_cpi, - .get_cpi = pmw3389_get_cpi + .init = pmw33xx_init_wrapper, + .get_report = pmw33xx_get_report, + .set_cpi = pmw33xx_set_cpi_wrapper, + .get_cpi = pmw33xx_get_cpi_wrapper }; // clang-format on -- cgit v1.2.3 From 300dab7962ba50dcb4c550c91097573fa09b8dea Mon Sep 17 00:00:00 2001 From: Albert Y <76888457+filterpaper@users.noreply.github.com> Date: Thu, 14 Jul 2022 19:22:53 +0800 Subject: [Code] Add solid reactive gradient mode (#17228) --- quantum/rgb_matrix/animations/solid_reactive_anim.h | 3 +++ quantum/rgb_matrix/animations/solid_reactive_cross.h | 3 +++ quantum/rgb_matrix/animations/solid_reactive_nexus.h | 3 +++ quantum/rgb_matrix/animations/solid_reactive_simple_anim.h | 3 +++ quantum/rgb_matrix/animations/solid_reactive_wide.h | 3 +++ 5 files changed, 15 insertions(+) (limited to 'quantum') diff --git a/quantum/rgb_matrix/animations/solid_reactive_anim.h b/quantum/rgb_matrix/animations/solid_reactive_anim.h index d3a7ebbdf7..052bfb22fd 100644 --- a/quantum/rgb_matrix/animations/solid_reactive_anim.h +++ b/quantum/rgb_matrix/animations/solid_reactive_anim.h @@ -4,6 +4,9 @@ RGB_MATRIX_EFFECT(SOLID_REACTIVE) # ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS static HSV SOLID_REACTIVE_math(HSV hsv, uint16_t offset) { +# ifdef RGB_MATRIX_SOLID_REACTIVE_GRADIENT_MODE + hsv.h = scale16by8(g_rgb_timer, add8(rgb_matrix_config.speed, 1) >> 6); +# endif hsv.h += qsub8(130, offset); return hsv; } diff --git a/quantum/rgb_matrix/animations/solid_reactive_cross.h b/quantum/rgb_matrix/animations/solid_reactive_cross.h index 043a369b73..9e5703a0ea 100644 --- a/quantum/rgb_matrix/animations/solid_reactive_cross.h +++ b/quantum/rgb_matrix/animations/solid_reactive_cross.h @@ -19,6 +19,9 @@ static HSV SOLID_REACTIVE_CROSS_math(HSV hsv, int16_t dx, int16_t dy, uint8_t di dy = dy * 16 > 255 ? 255 : dy * 16; effect += dx > dy ? dy : dx; if (effect > 255) effect = 255; +# ifdef RGB_MATRIX_SOLID_REACTIVE_GRADIENT_MODE + hsv.h = scale16by8(g_rgb_timer, add8(rgb_matrix_config.speed, 1) >> 6); +# endif hsv.v = qadd8(hsv.v, 255 - effect); return hsv; } diff --git a/quantum/rgb_matrix/animations/solid_reactive_nexus.h b/quantum/rgb_matrix/animations/solid_reactive_nexus.h index 8d62a49fea..64f5064cf9 100644 --- a/quantum/rgb_matrix/animations/solid_reactive_nexus.h +++ b/quantum/rgb_matrix/animations/solid_reactive_nexus.h @@ -16,6 +16,9 @@ static HSV SOLID_REACTIVE_NEXUS_math(HSV hsv, int16_t dx, int16_t dy, uint8_t di if (effect > 255) effect = 255; if (dist > 72) effect = 255; if ((dx > 8 || dx < -8) && (dy > 8 || dy < -8)) effect = 255; +# ifdef RGB_MATRIX_SOLID_REACTIVE_GRADIENT_MODE + hsv.h = scale16by8(g_rgb_timer, add8(rgb_matrix_config.speed, 1) >> 6); +# endif hsv.v = qadd8(hsv.v, 255 - effect); hsv.h = rgb_matrix_config.hsv.h + dy / 4; return hsv; diff --git a/quantum/rgb_matrix/animations/solid_reactive_simple_anim.h b/quantum/rgb_matrix/animations/solid_reactive_simple_anim.h index 0d0a424cf3..bd3ce0817d 100644 --- a/quantum/rgb_matrix/animations/solid_reactive_simple_anim.h +++ b/quantum/rgb_matrix/animations/solid_reactive_simple_anim.h @@ -4,6 +4,9 @@ RGB_MATRIX_EFFECT(SOLID_REACTIVE_SIMPLE) # ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS static HSV SOLID_REACTIVE_SIMPLE_math(HSV hsv, uint16_t offset) { +# ifdef RGB_MATRIX_SOLID_REACTIVE_GRADIENT_MODE + hsv.h = scale16by8(g_rgb_timer, add8(rgb_matrix_config.speed, 1) >> 6); +# endif hsv.v = scale8(255 - offset, hsv.v); return hsv; } diff --git a/quantum/rgb_matrix/animations/solid_reactive_wide.h b/quantum/rgb_matrix/animations/solid_reactive_wide.h index 7598796316..24fedc2020 100644 --- a/quantum/rgb_matrix/animations/solid_reactive_wide.h +++ b/quantum/rgb_matrix/animations/solid_reactive_wide.h @@ -14,6 +14,9 @@ RGB_MATRIX_EFFECT(SOLID_REACTIVE_MULTIWIDE) static HSV SOLID_REACTIVE_WIDE_math(HSV hsv, int16_t dx, int16_t dy, uint8_t dist, uint16_t tick) { uint16_t effect = tick + dist * 5; if (effect > 255) effect = 255; +# ifdef RGB_MATRIX_SOLID_REACTIVE_GRADIENT_MODE + hsv.h = scale16by8(g_rgb_timer, add8(rgb_matrix_config.speed, 1) >> 6); +# endif hsv.v = qadd8(hsv.v, 255 - effect); return hsv; } -- cgit v1.2.3 From 1862ac54540573843055b3b609b9f40d9f7dc21f Mon Sep 17 00:00:00 2001 From: Albert Y <76888457+filterpaper@users.noreply.github.com> Date: Thu, 14 Jul 2022 19:36:51 +0800 Subject: Fix the use of LED limits (#17678) --- quantum/rgb_matrix/animations/raindrops_anim.h | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'quantum') diff --git a/quantum/rgb_matrix/animations/raindrops_anim.h b/quantum/rgb_matrix/animations/raindrops_anim.h index 2d4b53b7b0..a508e51183 100644 --- a/quantum/rgb_matrix/animations/raindrops_anim.h +++ b/quantum/rgb_matrix/animations/raindrops_anim.h @@ -14,23 +14,22 @@ static void raindrops_set_color(int i, effect_params_t* params) { deltaH += 256; } - hsv.h = rgb_matrix_config.hsv.h + (deltaH * (rand() & 0x03)); + hsv.h = rgb_matrix_config.hsv.h + (deltaH * (random8() & 0x03)); RGB rgb = rgb_matrix_hsv_to_rgb(hsv); rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); } bool RAINDROPS(effect_params_t* params) { + RGB_MATRIX_USE_LIMITS(led_min, led_max); if (!params->init) { // Change one LED every tick, make sure speed is not 0 if (scale16by8(g_rgb_timer, qadd8(rgb_matrix_config.speed, 16)) % 10 == 0) { - raindrops_set_color(rand() % DRIVER_LED_TOTAL, params); + raindrops_set_color(random8() % DRIVER_LED_TOTAL, params); + } + } else { + for (int i = led_min; i < led_max; i++) { + raindrops_set_color(i, params); } - return false; - } - - RGB_MATRIX_USE_LIMITS(led_min, led_max); - for (int i = led_min; i < led_max; i++) { - raindrops_set_color(i, params); } return rgb_matrix_check_finished_leds(led_max); } -- cgit v1.2.3 From 2f73e6583766bad3996f15955214800b1ad3b833 Mon Sep 17 00:00:00 2001 From: Albert Y <76888457+filterpaper@users.noreply.github.com> Date: Thu, 14 Jul 2022 19:37:47 +0800 Subject: Add LED limit call (#17679) --- quantum/rgb_matrix/animations/pixel_rain_anim.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'quantum') diff --git a/quantum/rgb_matrix/animations/pixel_rain_anim.h b/quantum/rgb_matrix/animations/pixel_rain_anim.h index 03488b43df..ce2528a26d 100644 --- a/quantum/rgb_matrix/animations/pixel_rain_anim.h +++ b/quantum/rgb_matrix/animations/pixel_rain_anim.h @@ -20,17 +20,14 @@ RGB_MATRIX_EFFECT(PIXEL_RAIN) static bool PIXEL_RAIN(effect_params_t* params) { static uint32_t wait_timer = 0; - if (wait_timer > g_rgb_timer) { - return false; - } inline uint32_t interval(void) { return 500 / scale16by8(qadd8(rgb_matrix_config.speed, 16), 16); } - bool rain_pixel(uint8_t i, effect_params_t * params, bool off) { + void rain_pixel(uint8_t i, effect_params_t * params, bool off) { if (!HAS_ANY_FLAGS(g_led_config.flags[i], params->flags)) { - return true; + return; } if (off) { rgb_matrix_set_color(i, 0, 0, 0); @@ -40,10 +37,13 @@ static bool PIXEL_RAIN(effect_params_t* params) { rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); } wait_timer = g_rgb_timer + interval(); - return false; } - return rain_pixel(mod8(random8(), DRIVER_LED_TOTAL), params, random8() & 2); + RGB_MATRIX_USE_LIMITS(led_min, led_max); + if (g_rgb_timer > wait_timer) { + rain_pixel(mod8(random8(), DRIVER_LED_TOTAL), params, random8() & 2); + } + return rgb_matrix_check_finished_leds(led_max); } # endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS -- cgit v1.2.3 From 42eff8c3726517304ad479f3e760dc34428b4f9f Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Mon, 18 Jul 2022 11:55:27 +0100 Subject: Allow dynamic keymap to compile without via.h (#17703) --- quantum/dynamic_keymap.c | 15 ++++++++------- quantum/via.h | 2 ++ 2 files changed, 10 insertions(+), 7 deletions(-) (limited to 'quantum') diff --git a/quantum/dynamic_keymap.c b/quantum/dynamic_keymap.c index cbe9f13940..e80dd6d534 100644 --- a/quantum/dynamic_keymap.c +++ b/quantum/dynamic_keymap.c @@ -19,7 +19,13 @@ #include "progmem.h" // to read default from flash #include "quantum.h" // for send_string() #include "dynamic_keymap.h" -#include "via.h" // for default VIA_EEPROM_ADDR_END + +#ifdef VIA_ENABLE +# include "via.h" // for VIA_EEPROM_CONFIG_END +# define DYNAMIC_KEYMAP_EEPROM_START (VIA_EEPROM_CONFIG_END) +#else +# define DYNAMIC_KEYMAP_EEPROM_START (EECONFIG_SIZE) +#endif #ifdef ENCODER_ENABLE # include "encoder.h" @@ -55,13 +61,8 @@ #endif // If DYNAMIC_KEYMAP_EEPROM_ADDR not explicitly defined in config.h, -// default it start after VIA_EEPROM_CUSTOM_ADDR+VIA_EEPROM_CUSTOM_SIZE #ifndef DYNAMIC_KEYMAP_EEPROM_ADDR -# ifdef VIA_EEPROM_CUSTOM_CONFIG_ADDR -# define DYNAMIC_KEYMAP_EEPROM_ADDR (VIA_EEPROM_CUSTOM_CONFIG_ADDR + VIA_EEPROM_CUSTOM_CONFIG_SIZE) -# else -# error DYNAMIC_KEYMAP_EEPROM_ADDR not defined -# endif +# define DYNAMIC_KEYMAP_EEPROM_ADDR DYNAMIC_KEYMAP_EEPROM_START #endif // Dynamic encoders starts after dynamic keymaps diff --git a/quantum/via.h b/quantum/via.h index ac29a58902..31d9c21af4 100644 --- a/quantum/via.h +++ b/quantum/via.h @@ -54,6 +54,8 @@ # define VIA_EEPROM_CUSTOM_CONFIG_SIZE 0 #endif +#define VIA_EEPROM_CONFIG_END (VIA_EEPROM_CUSTOM_CONFIG_ADDR + VIA_EEPROM_CUSTOM_CONFIG_SIZE) + // This is changed only when the command IDs change, // so VIA Configurator can detect compatible firmware. #define VIA_PROTOCOL_VERSION 0x0009 -- cgit v1.2.3 From 12eb6444c67456d522c815120de8eed431ead2f8 Mon Sep 17 00:00:00 2001 From: Drashna Jaelre Date: Tue, 19 Jul 2022 17:46:22 -0700 Subject: Add support for PAW3204 Optical Sensor (#17669) Co-authored-by: gompa Co-authored-by: Stefan Kerkmann --- quantum/pointing_device.h | 2 ++ quantum/pointing_device_drivers.c | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+) (limited to 'quantum') diff --git a/quantum/pointing_device.h b/quantum/pointing_device.h index a8e8e75e87..77db5471ea 100644 --- a/quantum/pointing_device.h +++ b/quantum/pointing_device.h @@ -33,6 +33,8 @@ along with this program. If not, see . # include "drivers/sensors/cirque_pinnacle.h" # include "drivers/sensors/cirque_pinnacle_gestures.h" # include "pointing_device_gestures.h" +#elif defined(POINTING_DEVICE_DRIVER_paw3204) +# include "drivers/sensors/paw3204.h" #elif defined(POINTING_DEVICE_DRIVER_pimoroni_trackball) # include "i2c_master.h" # include "drivers/sensors/pimoroni_trackball.h" diff --git a/quantum/pointing_device_drivers.c b/quantum/pointing_device_drivers.c index b7e98e897e..d0b545d22d 100644 --- a/quantum/pointing_device_drivers.c +++ b/quantum/pointing_device_drivers.c @@ -26,6 +26,7 @@ #define CONSTRAIN_HID_XY(amt) ((amt) < XY_REPORT_MIN ? XY_REPORT_MIN : ((amt) > XY_REPORT_MAX ? XY_REPORT_MAX : (amt))) // get_report functions should probably be moved to their respective drivers. + #if defined(POINTING_DEVICE_DRIVER_adns5050) report_mouse_t adns5050_get_report(report_mouse_t mouse_report) { report_adns5050_t data = adns5050_read_burst(); @@ -198,7 +199,27 @@ const pointing_device_driver_t pointing_device_driver = { .get_cpi = cirque_pinnacle_get_cpi }; // clang-format on +#elif defined(POINTING_DEVICE_DRIVER_paw3204) + +report_mouse_t paw3204_get_report(report_mouse_t mouse_report) { + report_paw3204_t data = paw3204_read(); + if (data.isMotion) { +# ifdef CONSOLE_ENABLE + dprintf("Raw ] X: %d, Y: %d\n", data.x, data.y); +# endif + + mouse_report.x = data.x; + mouse_report.y = data.y; + } + return mouse_report; +} +const pointing_device_driver_t pointing_device_driver = { + .init = paw3204_init, + .get_report = paw3204_get_report, + .set_cpi = paw3204_set_cpi, + .get_cpi = paw3204_get_cpi, +}; #elif defined(POINTING_DEVICE_DRIVER_pimoroni_trackball) mouse_xy_report_t pimoroni_trackball_adapt_values(clamp_range_t* offset) { -- cgit v1.2.3 From ee17ffadea41241fc2b397e0bbea8903591dd21c Mon Sep 17 00:00:00 2001 From: Drashna Jaelre Date: Wed, 20 Jul 2022 09:32:00 -0700 Subject: Move Pointing Device code to a subdirectory (#17684) --- quantum/pointing_device.c | 470 --------------------- quantum/pointing_device.h | 117 ----- quantum/pointing_device/pointing_device.c | 470 +++++++++++++++++++++ quantum/pointing_device/pointing_device.h | 117 +++++ quantum/pointing_device/pointing_device_drivers.c | 345 +++++++++++++++ quantum/pointing_device/pointing_device_gestures.c | 133 ++++++ quantum/pointing_device/pointing_device_gestures.h | 58 +++ quantum/pointing_device_drivers.c | 345 --------------- quantum/pointing_device_gestures.c | 133 ------ quantum/pointing_device_gestures.h | 58 --- 10 files changed, 1123 insertions(+), 1123 deletions(-) delete mode 100644 quantum/pointing_device.c delete mode 100644 quantum/pointing_device.h create mode 100644 quantum/pointing_device/pointing_device.c create mode 100644 quantum/pointing_device/pointing_device.h create mode 100644 quantum/pointing_device/pointing_device_drivers.c create mode 100644 quantum/pointing_device/pointing_device_gestures.c create mode 100644 quantum/pointing_device/pointing_device_gestures.h delete mode 100644 quantum/pointing_device_drivers.c delete mode 100644 quantum/pointing_device_gestures.c delete mode 100644 quantum/pointing_device_gestures.h (limited to 'quantum') diff --git a/quantum/pointing_device.c b/quantum/pointing_device.c deleted file mode 100644 index 3aa4941687..0000000000 --- a/quantum/pointing_device.c +++ /dev/null @@ -1,470 +0,0 @@ -/* Copyright 2017 Joshua Broekhuijsen - * Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) - * Copyright 2021 Dasky (@daskygit) - * - * 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 "pointing_device.h" -#include -#include "timer.h" -#ifdef MOUSEKEY_ENABLE -# include "mousekey.h" -#endif -#if (defined(POINTING_DEVICE_ROTATION_90) + defined(POINTING_DEVICE_ROTATION_180) + defined(POINTING_DEVICE_ROTATION_270)) > 1 -# error More than one rotation selected. This is not supported. -#endif - -#if defined(POINTING_DEVICE_LEFT) || defined(POINTING_DEVICE_RIGHT) || defined(POINTING_DEVICE_COMBINED) -# ifndef SPLIT_POINTING_ENABLE -# error "Using POINTING_DEVICE_LEFT or POINTING_DEVICE_RIGHT or POINTING_DEVICE_COMBINED, then SPLIT_POINTING_ENABLE is required but has not been defined" -# endif -#endif - -#if defined(SPLIT_POINTING_ENABLE) -# include "transactions.h" -# include "keyboard.h" - -report_mouse_t shared_mouse_report = {}; -uint16_t shared_cpi = 0; - -/** - * @brief Sets the shared mouse report used be pointing device task - * - * NOTE : Only available when using SPLIT_POINTING_ENABLE - * - * @param[in] new_mouse_report report_mouse_t - */ -void pointing_device_set_shared_report(report_mouse_t new_mouse_report) { - shared_mouse_report = new_mouse_report; -} - -/** - * @brief Gets current pointing device CPI if supported - * - * Gets current cpi of the shared report and returns it as uint16_t - * - * NOTE : Only available when using SPLIT_POINTING_ENABLE - * - * @return cpi value as uint16_t - */ -uint16_t pointing_device_get_shared_cpi(void) { - return shared_cpi; -} - -# if defined(POINTING_DEVICE_LEFT) -# define POINTING_DEVICE_THIS_SIDE is_keyboard_left() -# elif defined(POINTING_DEVICE_RIGHT) -# define POINTING_DEVICE_THIS_SIDE !is_keyboard_left() -# elif defined(POINTING_DEVICE_COMBINED) -# define POINTING_DEVICE_THIS_SIDE true -# endif - -#endif // defined(SPLIT_POINTING_ENABLE) - -static report_mouse_t local_mouse_report = {}; - -extern const pointing_device_driver_t pointing_device_driver; - -/** - * @brief Keyboard level code pointing device initialisation - * - */ -__attribute__((weak)) void pointing_device_init_kb(void) {} - -/** - * @brief User level code pointing device initialisation - * - */ -__attribute__((weak)) void pointing_device_init_user(void) {} - -/** - * @brief Weak function allowing for keyboard level mouse report modification - * - * Takes report_mouse_t struct allowing modification at keyboard level then returns report_mouse_t. - * - * @param[in] mouse_report report_mouse_t - * @return report_mouse_t - */ -__attribute__((weak)) report_mouse_t pointing_device_task_kb(report_mouse_t mouse_report) { - return pointing_device_task_user(mouse_report); -} - -/** - * @brief Weak function allowing for user level mouse report modification - * - * Takes report_mouse_t struct allowing modification at user level then returns report_mouse_t. - * - * @param[in] mouse_report report_mouse_t - * @return report_mouse_t - */ -__attribute__((weak)) report_mouse_t pointing_device_task_user(report_mouse_t mouse_report) { - return mouse_report; -} - -/** - * @brief Handles pointing device buttons - * - * Returns modified button bitmask using bool pressed and selected pointing_device_buttons_t button in uint8_t buttons bitmask. - * - * @param buttons[in] uint8_t bitmask - * @param pressed[in] bool - * @param button[in] pointing_device_buttons_t value - * @return Modified uint8_t bitmask buttons - */ -__attribute__((weak)) uint8_t pointing_device_handle_buttons(uint8_t buttons, bool pressed, pointing_device_buttons_t button) { - if (pressed) { - buttons |= 1 << (button); - } else { - buttons &= ~(1 << (button)); - } - return buttons; -} - -/** - * @brief Initialises pointing device - * - * Initialises pointing device, perform driver init and optional keyboard/user level code. - */ -__attribute__((weak)) void pointing_device_init(void) { -#if defined(SPLIT_POINTING_ENABLE) - if (!(POINTING_DEVICE_THIS_SIDE)) { - return; - } -#endif - pointing_device_driver.init(); -#ifdef POINTING_DEVICE_MOTION_PIN - setPinInputHigh(POINTING_DEVICE_MOTION_PIN); -#endif - pointing_device_init_kb(); - pointing_device_init_user(); -} - -/** - * @brief Sends processed mouse report to host - * - * This sends the mouse report generated by pointing_device_task if changed since the last report. Once send zeros mouse report except buttons. - * - */ -__attribute__((weak)) void pointing_device_send(void) { - static report_mouse_t old_report = {}; - - // If you need to do other things, like debugging, this is the place to do it. - if (has_mouse_report_changed(&local_mouse_report, &old_report)) { - host_mouse_send(&local_mouse_report); - } - // send it and 0 it out except for buttons, so those stay until they are explicity over-ridden using update_pointing_device - local_mouse_report.x = 0; - local_mouse_report.y = 0; - local_mouse_report.v = 0; - local_mouse_report.h = 0; - - memcpy(&old_report, &local_mouse_report, sizeof(local_mouse_report)); -} - -/** - * @brief Adjust mouse report by any optional common pointing configuration defines - * - * This applies rotation or inversion to the mouse report as selected by the pointing device common configuration defines. - * - * @param mouse_report[in] takes a report_mouse_t to be adjusted - * @return report_mouse_t with adjusted values - */ -report_mouse_t pointing_device_adjust_by_defines(report_mouse_t mouse_report) { - // Support rotation of the sensor data -#if defined(POINTING_DEVICE_ROTATION_90) || defined(POINTING_DEVICE_ROTATION_180) || defined(POINTING_DEVICE_ROTATION_270) - mouse_xy_report_t x = mouse_report.x; - mouse_xy_report_t y = mouse_report.y; -# if defined(POINTING_DEVICE_ROTATION_90) - mouse_report.x = y; - mouse_report.y = -x; -# elif defined(POINTING_DEVICE_ROTATION_180) - mouse_report.x = -x; - mouse_report.y = -y; -# elif defined(POINTING_DEVICE_ROTATION_270) - mouse_report.x = -y; - mouse_report.y = x; -# else -# error "How the heck did you get here?!" -# endif -#endif - // Support Inverting the X and Y Axises -#if defined(POINTING_DEVICE_INVERT_X) - mouse_report.x = -mouse_report.x; -#endif -#if defined(POINTING_DEVICE_INVERT_Y) - mouse_report.y = -mouse_report.y; -#endif - return mouse_report; -} - -/** - * @brief Retrieves and processes pointing device data. - * - * This function is part of the keyboard loop and retrieves the mouse report from the pointing device driver. - * It applies any optional configuration e.g. rotation or axis inversion and then initiates a send. - * - */ -__attribute__((weak)) void pointing_device_task(void) { -#if defined(SPLIT_POINTING_ENABLE) - // Don't poll the target side pointing device. - if (!is_keyboard_master()) { - return; - }; -#endif - -#if (POINTING_DEVICE_TASK_THROTTLE_MS > 0) - static uint32_t last_exec = 0; - if (timer_elapsed32(last_exec) < POINTING_DEVICE_TASK_THROTTLE_MS) { - return; - } - last_exec = timer_read32(); -#endif - - // Gather report info -#ifdef POINTING_DEVICE_MOTION_PIN -# if defined(SPLIT_POINTING_ENABLE) -# error POINTING_DEVICE_MOTION_PIN not supported when sharing the pointing device report between sides. -# endif - if (!readPin(POINTING_DEVICE_MOTION_PIN)) -#endif - -#if defined(SPLIT_POINTING_ENABLE) -# if defined(POINTING_DEVICE_COMBINED) - static uint8_t old_buttons = 0; - local_mouse_report.buttons = old_buttons; - local_mouse_report = pointing_device_driver.get_report(local_mouse_report); - old_buttons = local_mouse_report.buttons; -# elif defined(POINTING_DEVICE_LEFT) || defined(POINTING_DEVICE_RIGHT) - local_mouse_report = POINTING_DEVICE_THIS_SIDE ? pointing_device_driver.get_report(local_mouse_report) : shared_mouse_report; -# else -# error "You need to define the side(s) the pointing device is on. POINTING_DEVICE_COMBINED / POINTING_DEVICE_LEFT / POINTING_DEVICE_RIGHT" -# endif -#else - local_mouse_report = pointing_device_driver.get_report(local_mouse_report); -#endif // defined(SPLIT_POINTING_ENABLE) - - // allow kb to intercept and modify report -#if defined(SPLIT_POINTING_ENABLE) && defined(POINTING_DEVICE_COMBINED) - if (is_keyboard_left()) { - local_mouse_report = pointing_device_adjust_by_defines(local_mouse_report); - shared_mouse_report = pointing_device_adjust_by_defines_right(shared_mouse_report); - } else { - local_mouse_report = pointing_device_adjust_by_defines_right(local_mouse_report); - shared_mouse_report = pointing_device_adjust_by_defines(shared_mouse_report); - } - local_mouse_report = is_keyboard_left() ? pointing_device_task_combined_kb(local_mouse_report, shared_mouse_report) : pointing_device_task_combined_kb(shared_mouse_report, local_mouse_report); -#else - local_mouse_report = pointing_device_adjust_by_defines(local_mouse_report); - local_mouse_report = pointing_device_task_kb(local_mouse_report); -#endif - // combine with mouse report to ensure that the combined is sent correctly -#ifdef MOUSEKEY_ENABLE - report_mouse_t mousekey_report = mousekey_get_report(); - local_mouse_report.buttons = local_mouse_report.buttons | mousekey_report.buttons; -#endif - pointing_device_send(); -} - -/** - * @brief Gets current mouse report used by pointing device task - * - * @return report_mouse_t - */ -report_mouse_t pointing_device_get_report(void) { - return local_mouse_report; -} - -/** - * @brief Sets mouse report used be pointing device task - * - * @param[in] mouse_report - */ -void pointing_device_set_report(report_mouse_t mouse_report) { - local_mouse_report = mouse_report; -} - -/** - * @brief Gets current pointing device CPI if supported - * - * Gets current cpi from pointing device driver if supported and returns it as uint16_t - * - * @return cpi value as uint16_t - */ -uint16_t pointing_device_get_cpi(void) { -#if defined(SPLIT_POINTING_ENABLE) - return POINTING_DEVICE_THIS_SIDE ? pointing_device_driver.get_cpi() : shared_cpi; -#else - return pointing_device_driver.get_cpi(); -#endif -} - -/** - * @brief Set pointing device CPI if supported - * - * Takes a uint16_t value to set pointing device cpi if supported by driver. - * - * @param[in] cpi uint16_t value. - */ -void pointing_device_set_cpi(uint16_t cpi) { -#if defined(SPLIT_POINTING_ENABLE) - if (POINTING_DEVICE_THIS_SIDE) { - pointing_device_driver.set_cpi(cpi); - } else { - shared_cpi = cpi; - } -#else - pointing_device_driver.set_cpi(cpi); -#endif -} - -#if defined(SPLIT_POINTING_ENABLE) && defined(POINTING_DEVICE_COMBINED) -/** - * @brief Set pointing device CPI if supported - * - * Takes a bool and uint16_t and allows setting cpi for a single side when using 2 pointing devices with a split keyboard. - * - * NOTE: Only available when using SPLIT_POINTING_ENABLE and POINTING_DEVICE_COMBINED - * - * @param[in] left true = left, false = right. - * @param[in] cpi uint16_t value. - */ -void pointing_device_set_cpi_on_side(bool left, uint16_t cpi) { - bool local = (is_keyboard_left() & left) ? true : false; - if (local) { - pointing_device_driver.set_cpi(cpi); - } else { - shared_cpi = cpi; - } -} - -/** - * @brief clamps int16_t to int8_t - * - * @param[in] int16_t value - * @return int8_t clamped value - */ -static inline int8_t pointing_device_hv_clamp(int16_t value) { - if (value < INT8_MIN) { - return INT8_MIN; - } else if (value > INT8_MAX) { - return INT8_MAX; - } else { - return value; - } -} - -/** - * @brief clamps int16_t to int8_t - * - * @param[in] clamp_range_t value - * @return mouse_xy_report_t clamped value - */ -static inline mouse_xy_report_t pointing_device_xy_clamp(clamp_range_t value) { - if (value < XY_REPORT_MIN) { - return XY_REPORT_MIN; - } else if (value > XY_REPORT_MAX) { - return XY_REPORT_MAX; - } else { - return value; - } -} -/** - * @brief combines 2 mouse reports and returns 2 - * - * Combines 2 report_mouse_t structs, clamping movement values to int8_t and ignores report_id then returns the resulting report_mouse_t struct. - * - * NOTE: Only available when using SPLIT_POINTING_ENABLE and POINTING_DEVICE_COMBINED - * - * @param[in] left_report left report_mouse_t - * @param[in] right_report right report_mouse_t - * @return combined report_mouse_t of left_report and right_report - */ -report_mouse_t pointing_device_combine_reports(report_mouse_t left_report, report_mouse_t right_report) { - left_report.x = pointing_device_xy_clamp((clamp_range_t)left_report.x + right_report.x); - left_report.y = pointing_device_xy_clamp((clamp_range_t)left_report.y + right_report.y); - left_report.h = pointing_device_hv_clamp((int16_t)left_report.h + right_report.h); - left_report.v = pointing_device_hv_clamp((int16_t)left_report.v + right_report.v); - left_report.buttons |= right_report.buttons; - return left_report; -} - -/** - * @brief Adjust mouse report by any optional right pointing configuration defines - * - * This applies rotation or inversion to the mouse report as selected by the pointing device common configuration defines. - * - * NOTE: Only available when using SPLIT_POINTING_ENABLE and POINTING_DEVICE_COMBINED - * - * @param[in] mouse_report report_mouse_t to be adjusted - * @return report_mouse_t with adjusted values - */ -report_mouse_t pointing_device_adjust_by_defines_right(report_mouse_t mouse_report) { - // Support rotation of the sensor data -# if defined(POINTING_DEVICE_ROTATION_90_RIGHT) || defined(POINTING_DEVICE_ROTATION_RIGHT) || defined(POINTING_DEVICE_ROTATION_RIGHT) - mouse_xy_report_t x = mouse_report.x; - mouse_xy_report_t y = mouse_report.y; -# if defined(POINTING_DEVICE_ROTATION_90_RIGHT) - mouse_report.x = y; - mouse_report.y = -x; -# elif defined(POINTING_DEVICE_ROTATION_180_RIGHT) - mouse_report.x = -x; - mouse_report.y = -y; -# elif defined(POINTING_DEVICE_ROTATION_270_RIGHT) - mouse_report.x = -y; - mouse_report.y = x; -# else -# error "How the heck did you get here?!" -# endif -# endif - // Support Inverting the X and Y Axises -# if defined(POINTING_DEVICE_INVERT_X_RIGHT) - mouse_report.x = -mouse_report.x; -# endif -# if defined(POINTING_DEVICE_INVERT_Y_RIGHT) - mouse_report.y = -mouse_report.y; -# endif - return mouse_report; -} - -/** - * @brief Weak function allowing for keyboard level mouse report modification - * - * Takes 2 report_mouse_t structs allowing individual modification of sides at keyboard level then returns pointing_device_task_combined_user. - * - * NOTE: Only available when using SPLIT_POINTING_ENABLE and POINTING_DEVICE_COMBINED - * - * @param[in] left_report report_mouse_t - * @param[in] right_report report_mouse_t - * @return pointing_device_task_combined_user(left_report, right_report) by default - */ -__attribute__((weak)) report_mouse_t pointing_device_task_combined_kb(report_mouse_t left_report, report_mouse_t right_report) { - return pointing_device_task_combined_user(left_report, right_report); -} - -/** - * @brief Weak function allowing for user level mouse report modification - * - * Takes 2 report_mouse_t structs allowing individual modification of sides at user level then returns pointing_device_combine_reports. - * - * NOTE: Only available when using SPLIT_POINTING_ENABLE and POINTING_DEVICE_COMBINED - * - * @param[in] left_report report_mouse_t - * @param[in] right_report report_mouse_t - * @return pointing_device_combine_reports(left_report, right_report) by default - */ -__attribute__((weak)) report_mouse_t pointing_device_task_combined_user(report_mouse_t left_report, report_mouse_t right_report) { - return pointing_device_combine_reports(left_report, right_report); -} -#endif diff --git a/quantum/pointing_device.h b/quantum/pointing_device.h deleted file mode 100644 index 77db5471ea..0000000000 --- a/quantum/pointing_device.h +++ /dev/null @@ -1,117 +0,0 @@ -/* -Copyright 2017 Joshua Broekhuijsen - -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 -#include "host.h" -#include "report.h" - -#if defined(POINTING_DEVICE_DRIVER_adns5050) -# include "drivers/sensors/adns5050.h" -#elif defined(POINTING_DEVICE_DRIVER_adns9800) -# include "spi_master.h" -# include "drivers/sensors/adns9800.h" -#elif defined(POINTING_DEVICE_DRIVER_analog_joystick) -# include "analog.h" -# include "drivers/sensors/analog_joystick.h" -#elif defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_i2c) || defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_spi) -# include "drivers/sensors/cirque_pinnacle.h" -# include "drivers/sensors/cirque_pinnacle_gestures.h" -# include "pointing_device_gestures.h" -#elif defined(POINTING_DEVICE_DRIVER_paw3204) -# include "drivers/sensors/paw3204.h" -#elif defined(POINTING_DEVICE_DRIVER_pimoroni_trackball) -# include "i2c_master.h" -# include "drivers/sensors/pimoroni_trackball.h" -// support for legacy pimoroni defines -# ifdef PIMORONI_TRACKBALL_INVERT_X -# define POINTING_DEVICE_INVERT_X -# endif -# ifdef PIMORONI_TRACKBALL_INVERT_Y -# define POINTING_DEVICE_INVERT_Y -# endif -# ifdef PIMORONI_TRACKBALL_ROTATE -# define POINTING_DEVICE_ROTATION_90 -# endif -#elif defined(POINTING_DEVICE_DRIVER_pmw3360) || defined(POINTING_DEVICE_DRIVER_pmw3389) -# include "spi_master.h" -# include "drivers/sensors/pmw33xx_common.h" -#else -void pointing_device_driver_init(void); -report_mouse_t pointing_device_driver_get_report(report_mouse_t mouse_report); -uint16_t pointing_device_driver_get_cpi(void); -void pointing_device_driver_set_cpi(uint16_t cpi); -#endif - -typedef struct { - void (*init)(void); - report_mouse_t (*get_report)(report_mouse_t mouse_report); - void (*set_cpi)(uint16_t); - uint16_t (*get_cpi)(void); -} pointing_device_driver_t; - -typedef enum { - POINTING_DEVICE_BUTTON1, - POINTING_DEVICE_BUTTON2, - POINTING_DEVICE_BUTTON3, - POINTING_DEVICE_BUTTON4, - POINTING_DEVICE_BUTTON5, - POINTING_DEVICE_BUTTON6, - POINTING_DEVICE_BUTTON7, - POINTING_DEVICE_BUTTON8, -} pointing_device_buttons_t; - -#ifdef MOUSE_EXTENDED_REPORT -# define XY_REPORT_MIN INT16_MIN -# define XY_REPORT_MAX INT16_MAX -typedef int32_t clamp_range_t; -#else -# define XY_REPORT_MIN INT8_MIN -# define XY_REPORT_MAX INT8_MAX -typedef int16_t clamp_range_t; -#endif - -void pointing_device_init(void); -void pointing_device_task(void); -void pointing_device_send(void); -report_mouse_t pointing_device_get_report(void); -void pointing_device_set_report(report_mouse_t mouse_report); -uint16_t pointing_device_get_cpi(void); -void pointing_device_set_cpi(uint16_t cpi); - -void pointing_device_init_kb(void); -void pointing_device_init_user(void); -report_mouse_t pointing_device_task_kb(report_mouse_t mouse_report); -report_mouse_t pointing_device_task_user(report_mouse_t mouse_report); -uint8_t pointing_device_handle_buttons(uint8_t buttons, bool pressed, pointing_device_buttons_t button); -report_mouse_t pointing_device_adjust_by_defines(report_mouse_t mouse_report); - -#if defined(SPLIT_POINTING_ENABLE) -void pointing_device_set_shared_report(report_mouse_t report); -uint16_t pointing_device_get_shared_cpi(void); -# if !defined(POINTING_DEVICE_TASK_THROTTLE_MS) -# define POINTING_DEVICE_TASK_THROTTLE_MS 1 -# endif -# if defined(POINTING_DEVICE_COMBINED) -void pointing_device_set_cpi_on_side(bool left, uint16_t cpi); -report_mouse_t pointing_device_combine_reports(report_mouse_t left_report, report_mouse_t right_report); -report_mouse_t pointing_device_task_combined_kb(report_mouse_t left_report, report_mouse_t right_report); -report_mouse_t pointing_device_task_combined_user(report_mouse_t left_report, report_mouse_t right_report); -report_mouse_t pointing_device_adjust_by_defines_right(report_mouse_t mouse_report); -# endif // defined(POINTING_DEVICE_COMBINED) -#endif // defined(SPLIT_POINTING_ENABLE) diff --git a/quantum/pointing_device/pointing_device.c b/quantum/pointing_device/pointing_device.c new file mode 100644 index 0000000000..3aa4941687 --- /dev/null +++ b/quantum/pointing_device/pointing_device.c @@ -0,0 +1,470 @@ +/* Copyright 2017 Joshua Broekhuijsen + * Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) + * Copyright 2021 Dasky (@daskygit) + * + * 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 "pointing_device.h" +#include +#include "timer.h" +#ifdef MOUSEKEY_ENABLE +# include "mousekey.h" +#endif +#if (defined(POINTING_DEVICE_ROTATION_90) + defined(POINTING_DEVICE_ROTATION_180) + defined(POINTING_DEVICE_ROTATION_270)) > 1 +# error More than one rotation selected. This is not supported. +#endif + +#if defined(POINTING_DEVICE_LEFT) || defined(POINTING_DEVICE_RIGHT) || defined(POINTING_DEVICE_COMBINED) +# ifndef SPLIT_POINTING_ENABLE +# error "Using POINTING_DEVICE_LEFT or POINTING_DEVICE_RIGHT or POINTING_DEVICE_COMBINED, then SPLIT_POINTING_ENABLE is required but has not been defined" +# endif +#endif + +#if defined(SPLIT_POINTING_ENABLE) +# include "transactions.h" +# include "keyboard.h" + +report_mouse_t shared_mouse_report = {}; +uint16_t shared_cpi = 0; + +/** + * @brief Sets the shared mouse report used be pointing device task + * + * NOTE : Only available when using SPLIT_POINTING_ENABLE + * + * @param[in] new_mouse_report report_mouse_t + */ +void pointing_device_set_shared_report(report_mouse_t new_mouse_report) { + shared_mouse_report = new_mouse_report; +} + +/** + * @brief Gets current pointing device CPI if supported + * + * Gets current cpi of the shared report and returns it as uint16_t + * + * NOTE : Only available when using SPLIT_POINTING_ENABLE + * + * @return cpi value as uint16_t + */ +uint16_t pointing_device_get_shared_cpi(void) { + return shared_cpi; +} + +# if defined(POINTING_DEVICE_LEFT) +# define POINTING_DEVICE_THIS_SIDE is_keyboard_left() +# elif defined(POINTING_DEVICE_RIGHT) +# define POINTING_DEVICE_THIS_SIDE !is_keyboard_left() +# elif defined(POINTING_DEVICE_COMBINED) +# define POINTING_DEVICE_THIS_SIDE true +# endif + +#endif // defined(SPLIT_POINTING_ENABLE) + +static report_mouse_t local_mouse_report = {}; + +extern const pointing_device_driver_t pointing_device_driver; + +/** + * @brief Keyboard level code pointing device initialisation + * + */ +__attribute__((weak)) void pointing_device_init_kb(void) {} + +/** + * @brief User level code pointing device initialisation + * + */ +__attribute__((weak)) void pointing_device_init_user(void) {} + +/** + * @brief Weak function allowing for keyboard level mouse report modification + * + * Takes report_mouse_t struct allowing modification at keyboard level then returns report_mouse_t. + * + * @param[in] mouse_report report_mouse_t + * @return report_mouse_t + */ +__attribute__((weak)) report_mouse_t pointing_device_task_kb(report_mouse_t mouse_report) { + return pointing_device_task_user(mouse_report); +} + +/** + * @brief Weak function allowing for user level mouse report modification + * + * Takes report_mouse_t struct allowing modification at user level then returns report_mouse_t. + * + * @param[in] mouse_report report_mouse_t + * @return report_mouse_t + */ +__attribute__((weak)) report_mouse_t pointing_device_task_user(report_mouse_t mouse_report) { + return mouse_report; +} + +/** + * @brief Handles pointing device buttons + * + * Returns modified button bitmask using bool pressed and selected pointing_device_buttons_t button in uint8_t buttons bitmask. + * + * @param buttons[in] uint8_t bitmask + * @param pressed[in] bool + * @param button[in] pointing_device_buttons_t value + * @return Modified uint8_t bitmask buttons + */ +__attribute__((weak)) uint8_t pointing_device_handle_buttons(uint8_t buttons, bool pressed, pointing_device_buttons_t button) { + if (pressed) { + buttons |= 1 << (button); + } else { + buttons &= ~(1 << (button)); + } + return buttons; +} + +/** + * @brief Initialises pointing device + * + * Initialises pointing device, perform driver init and optional keyboard/user level code. + */ +__attribute__((weak)) void pointing_device_init(void) { +#if defined(SPLIT_POINTING_ENABLE) + if (!(POINTING_DEVICE_THIS_SIDE)) { + return; + } +#endif + pointing_device_driver.init(); +#ifdef POINTING_DEVICE_MOTION_PIN + setPinInputHigh(POINTING_DEVICE_MOTION_PIN); +#endif + pointing_device_init_kb(); + pointing_device_init_user(); +} + +/** + * @brief Sends processed mouse report to host + * + * This sends the mouse report generated by pointing_device_task if changed since the last report. Once send zeros mouse report except buttons. + * + */ +__attribute__((weak)) void pointing_device_send(void) { + static report_mouse_t old_report = {}; + + // If you need to do other things, like debugging, this is the place to do it. + if (has_mouse_report_changed(&local_mouse_report, &old_report)) { + host_mouse_send(&local_mouse_report); + } + // send it and 0 it out except for buttons, so those stay until they are explicity over-ridden using update_pointing_device + local_mouse_report.x = 0; + local_mouse_report.y = 0; + local_mouse_report.v = 0; + local_mouse_report.h = 0; + + memcpy(&old_report, &local_mouse_report, sizeof(local_mouse_report)); +} + +/** + * @brief Adjust mouse report by any optional common pointing configuration defines + * + * This applies rotation or inversion to the mouse report as selected by the pointing device common configuration defines. + * + * @param mouse_report[in] takes a report_mouse_t to be adjusted + * @return report_mouse_t with adjusted values + */ +report_mouse_t pointing_device_adjust_by_defines(report_mouse_t mouse_report) { + // Support rotation of the sensor data +#if defined(POINTING_DEVICE_ROTATION_90) || defined(POINTING_DEVICE_ROTATION_180) || defined(POINTING_DEVICE_ROTATION_270) + mouse_xy_report_t x = mouse_report.x; + mouse_xy_report_t y = mouse_report.y; +# if defined(POINTING_DEVICE_ROTATION_90) + mouse_report.x = y; + mouse_report.y = -x; +# elif defined(POINTING_DEVICE_ROTATION_180) + mouse_report.x = -x; + mouse_report.y = -y; +# elif defined(POINTING_DEVICE_ROTATION_270) + mouse_report.x = -y; + mouse_report.y = x; +# else +# error "How the heck did you get here?!" +# endif +#endif + // Support Inverting the X and Y Axises +#if defined(POINTING_DEVICE_INVERT_X) + mouse_report.x = -mouse_report.x; +#endif +#if defined(POINTING_DEVICE_INVERT_Y) + mouse_report.y = -mouse_report.y; +#endif + return mouse_report; +} + +/** + * @brief Retrieves and processes pointing device data. + * + * This function is part of the keyboard loop and retrieves the mouse report from the pointing device driver. + * It applies any optional configuration e.g. rotation or axis inversion and then initiates a send. + * + */ +__attribute__((weak)) void pointing_device_task(void) { +#if defined(SPLIT_POINTING_ENABLE) + // Don't poll the target side pointing device. + if (!is_keyboard_master()) { + return; + }; +#endif + +#if (POINTING_DEVICE_TASK_THROTTLE_MS > 0) + static uint32_t last_exec = 0; + if (timer_elapsed32(last_exec) < POINTING_DEVICE_TASK_THROTTLE_MS) { + return; + } + last_exec = timer_read32(); +#endif + + // Gather report info +#ifdef POINTING_DEVICE_MOTION_PIN +# if defined(SPLIT_POINTING_ENABLE) +# error POINTING_DEVICE_MOTION_PIN not supported when sharing the pointing device report between sides. +# endif + if (!readPin(POINTING_DEVICE_MOTION_PIN)) +#endif + +#if defined(SPLIT_POINTING_ENABLE) +# if defined(POINTING_DEVICE_COMBINED) + static uint8_t old_buttons = 0; + local_mouse_report.buttons = old_buttons; + local_mouse_report = pointing_device_driver.get_report(local_mouse_report); + old_buttons = local_mouse_report.buttons; +# elif defined(POINTING_DEVICE_LEFT) || defined(POINTING_DEVICE_RIGHT) + local_mouse_report = POINTING_DEVICE_THIS_SIDE ? pointing_device_driver.get_report(local_mouse_report) : shared_mouse_report; +# else +# error "You need to define the side(s) the pointing device is on. POINTING_DEVICE_COMBINED / POINTING_DEVICE_LEFT / POINTING_DEVICE_RIGHT" +# endif +#else + local_mouse_report = pointing_device_driver.get_report(local_mouse_report); +#endif // defined(SPLIT_POINTING_ENABLE) + + // allow kb to intercept and modify report +#if defined(SPLIT_POINTING_ENABLE) && defined(POINTING_DEVICE_COMBINED) + if (is_keyboard_left()) { + local_mouse_report = pointing_device_adjust_by_defines(local_mouse_report); + shared_mouse_report = pointing_device_adjust_by_defines_right(shared_mouse_report); + } else { + local_mouse_report = pointing_device_adjust_by_defines_right(local_mouse_report); + shared_mouse_report = pointing_device_adjust_by_defines(shared_mouse_report); + } + local_mouse_report = is_keyboard_left() ? pointing_device_task_combined_kb(local_mouse_report, shared_mouse_report) : pointing_device_task_combined_kb(shared_mouse_report, local_mouse_report); +#else + local_mouse_report = pointing_device_adjust_by_defines(local_mouse_report); + local_mouse_report = pointing_device_task_kb(local_mouse_report); +#endif + // combine with mouse report to ensure that the combined is sent correctly +#ifdef MOUSEKEY_ENABLE + report_mouse_t mousekey_report = mousekey_get_report(); + local_mouse_report.buttons = local_mouse_report.buttons | mousekey_report.buttons; +#endif + pointing_device_send(); +} + +/** + * @brief Gets current mouse report used by pointing device task + * + * @return report_mouse_t + */ +report_mouse_t pointing_device_get_report(void) { + return local_mouse_report; +} + +/** + * @brief Sets mouse report used be pointing device task + * + * @param[in] mouse_report + */ +void pointing_device_set_report(report_mouse_t mouse_report) { + local_mouse_report = mouse_report; +} + +/** + * @brief Gets current pointing device CPI if supported + * + * Gets current cpi from pointing device driver if supported and returns it as uint16_t + * + * @return cpi value as uint16_t + */ +uint16_t pointing_device_get_cpi(void) { +#if defined(SPLIT_POINTING_ENABLE) + return POINTING_DEVICE_THIS_SIDE ? pointing_device_driver.get_cpi() : shared_cpi; +#else + return pointing_device_driver.get_cpi(); +#endif +} + +/** + * @brief Set pointing device CPI if supported + * + * Takes a uint16_t value to set pointing device cpi if supported by driver. + * + * @param[in] cpi uint16_t value. + */ +void pointing_device_set_cpi(uint16_t cpi) { +#if defined(SPLIT_POINTING_ENABLE) + if (POINTING_DEVICE_THIS_SIDE) { + pointing_device_driver.set_cpi(cpi); + } else { + shared_cpi = cpi; + } +#else + pointing_device_driver.set_cpi(cpi); +#endif +} + +#if defined(SPLIT_POINTING_ENABLE) && defined(POINTING_DEVICE_COMBINED) +/** + * @brief Set pointing device CPI if supported + * + * Takes a bool and uint16_t and allows setting cpi for a single side when using 2 pointing devices with a split keyboard. + * + * NOTE: Only available when using SPLIT_POINTING_ENABLE and POINTING_DEVICE_COMBINED + * + * @param[in] left true = left, false = right. + * @param[in] cpi uint16_t value. + */ +void pointing_device_set_cpi_on_side(bool left, uint16_t cpi) { + bool local = (is_keyboard_left() & left) ? true : false; + if (local) { + pointing_device_driver.set_cpi(cpi); + } else { + shared_cpi = cpi; + } +} + +/** + * @brief clamps int16_t to int8_t + * + * @param[in] int16_t value + * @return int8_t clamped value + */ +static inline int8_t pointing_device_hv_clamp(int16_t value) { + if (value < INT8_MIN) { + return INT8_MIN; + } else if (value > INT8_MAX) { + return INT8_MAX; + } else { + return value; + } +} + +/** + * @brief clamps int16_t to int8_t + * + * @param[in] clamp_range_t value + * @return mouse_xy_report_t clamped value + */ +static inline mouse_xy_report_t pointing_device_xy_clamp(clamp_range_t value) { + if (value < XY_REPORT_MIN) { + return XY_REPORT_MIN; + } else if (value > XY_REPORT_MAX) { + return XY_REPORT_MAX; + } else { + return value; + } +} +/** + * @brief combines 2 mouse reports and returns 2 + * + * Combines 2 report_mouse_t structs, clamping movement values to int8_t and ignores report_id then returns the resulting report_mouse_t struct. + * + * NOTE: Only available when using SPLIT_POINTING_ENABLE and POINTING_DEVICE_COMBINED + * + * @param[in] left_report left report_mouse_t + * @param[in] right_report right report_mouse_t + * @return combined report_mouse_t of left_report and right_report + */ +report_mouse_t pointing_device_combine_reports(report_mouse_t left_report, report_mouse_t right_report) { + left_report.x = pointing_device_xy_clamp((clamp_range_t)left_report.x + right_report.x); + left_report.y = pointing_device_xy_clamp((clamp_range_t)left_report.y + right_report.y); + left_report.h = pointing_device_hv_clamp((int16_t)left_report.h + right_report.h); + left_report.v = pointing_device_hv_clamp((int16_t)left_report.v + right_report.v); + left_report.buttons |= right_report.buttons; + return left_report; +} + +/** + * @brief Adjust mouse report by any optional right pointing configuration defines + * + * This applies rotation or inversion to the mouse report as selected by the pointing device common configuration defines. + * + * NOTE: Only available when using SPLIT_POINTING_ENABLE and POINTING_DEVICE_COMBINED + * + * @param[in] mouse_report report_mouse_t to be adjusted + * @return report_mouse_t with adjusted values + */ +report_mouse_t pointing_device_adjust_by_defines_right(report_mouse_t mouse_report) { + // Support rotation of the sensor data +# if defined(POINTING_DEVICE_ROTATION_90_RIGHT) || defined(POINTING_DEVICE_ROTATION_RIGHT) || defined(POINTING_DEVICE_ROTATION_RIGHT) + mouse_xy_report_t x = mouse_report.x; + mouse_xy_report_t y = mouse_report.y; +# if defined(POINTING_DEVICE_ROTATION_90_RIGHT) + mouse_report.x = y; + mouse_report.y = -x; +# elif defined(POINTING_DEVICE_ROTATION_180_RIGHT) + mouse_report.x = -x; + mouse_report.y = -y; +# elif defined(POINTING_DEVICE_ROTATION_270_RIGHT) + mouse_report.x = -y; + mouse_report.y = x; +# else +# error "How the heck did you get here?!" +# endif +# endif + // Support Inverting the X and Y Axises +# if defined(POINTING_DEVICE_INVERT_X_RIGHT) + mouse_report.x = -mouse_report.x; +# endif +# if defined(POINTING_DEVICE_INVERT_Y_RIGHT) + mouse_report.y = -mouse_report.y; +# endif + return mouse_report; +} + +/** + * @brief Weak function allowing for keyboard level mouse report modification + * + * Takes 2 report_mouse_t structs allowing individual modification of sides at keyboard level then returns pointing_device_task_combined_user. + * + * NOTE: Only available when using SPLIT_POINTING_ENABLE and POINTING_DEVICE_COMBINED + * + * @param[in] left_report report_mouse_t + * @param[in] right_report report_mouse_t + * @return pointing_device_task_combined_user(left_report, right_report) by default + */ +__attribute__((weak)) report_mouse_t pointing_device_task_combined_kb(report_mouse_t left_report, report_mouse_t right_report) { + return pointing_device_task_combined_user(left_report, right_report); +} + +/** + * @brief Weak function allowing for user level mouse report modification + * + * Takes 2 report_mouse_t structs allowing individual modification of sides at user level then returns pointing_device_combine_reports. + * + * NOTE: Only available when using SPLIT_POINTING_ENABLE and POINTING_DEVICE_COMBINED + * + * @param[in] left_report report_mouse_t + * @param[in] right_report report_mouse_t + * @return pointing_device_combine_reports(left_report, right_report) by default + */ +__attribute__((weak)) report_mouse_t pointing_device_task_combined_user(report_mouse_t left_report, report_mouse_t right_report) { + return pointing_device_combine_reports(left_report, right_report); +} +#endif diff --git a/quantum/pointing_device/pointing_device.h b/quantum/pointing_device/pointing_device.h new file mode 100644 index 0000000000..77db5471ea --- /dev/null +++ b/quantum/pointing_device/pointing_device.h @@ -0,0 +1,117 @@ +/* +Copyright 2017 Joshua Broekhuijsen + +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 +#include "host.h" +#include "report.h" + +#if defined(POINTING_DEVICE_DRIVER_adns5050) +# include "drivers/sensors/adns5050.h" +#elif defined(POINTING_DEVICE_DRIVER_adns9800) +# include "spi_master.h" +# include "drivers/sensors/adns9800.h" +#elif defined(POINTING_DEVICE_DRIVER_analog_joystick) +# include "analog.h" +# include "drivers/sensors/analog_joystick.h" +#elif defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_i2c) || defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_spi) +# include "drivers/sensors/cirque_pinnacle.h" +# include "drivers/sensors/cirque_pinnacle_gestures.h" +# include "pointing_device_gestures.h" +#elif defined(POINTING_DEVICE_DRIVER_paw3204) +# include "drivers/sensors/paw3204.h" +#elif defined(POINTING_DEVICE_DRIVER_pimoroni_trackball) +# include "i2c_master.h" +# include "drivers/sensors/pimoroni_trackball.h" +// support for legacy pimoroni defines +# ifdef PIMORONI_TRACKBALL_INVERT_X +# define POINTING_DEVICE_INVERT_X +# endif +# ifdef PIMORONI_TRACKBALL_INVERT_Y +# define POINTING_DEVICE_INVERT_Y +# endif +# ifdef PIMORONI_TRACKBALL_ROTATE +# define POINTING_DEVICE_ROTATION_90 +# endif +#elif defined(POINTING_DEVICE_DRIVER_pmw3360) || defined(POINTING_DEVICE_DRIVER_pmw3389) +# include "spi_master.h" +# include "drivers/sensors/pmw33xx_common.h" +#else +void pointing_device_driver_init(void); +report_mouse_t pointing_device_driver_get_report(report_mouse_t mouse_report); +uint16_t pointing_device_driver_get_cpi(void); +void pointing_device_driver_set_cpi(uint16_t cpi); +#endif + +typedef struct { + void (*init)(void); + report_mouse_t (*get_report)(report_mouse_t mouse_report); + void (*set_cpi)(uint16_t); + uint16_t (*get_cpi)(void); +} pointing_device_driver_t; + +typedef enum { + POINTING_DEVICE_BUTTON1, + POINTING_DEVICE_BUTTON2, + POINTING_DEVICE_BUTTON3, + POINTING_DEVICE_BUTTON4, + POINTING_DEVICE_BUTTON5, + POINTING_DEVICE_BUTTON6, + POINTING_DEVICE_BUTTON7, + POINTING_DEVICE_BUTTON8, +} pointing_device_buttons_t; + +#ifdef MOUSE_EXTENDED_REPORT +# define XY_REPORT_MIN INT16_MIN +# define XY_REPORT_MAX INT16_MAX +typedef int32_t clamp_range_t; +#else +# define XY_REPORT_MIN INT8_MIN +# define XY_REPORT_MAX INT8_MAX +typedef int16_t clamp_range_t; +#endif + +void pointing_device_init(void); +void pointing_device_task(void); +void pointing_device_send(void); +report_mouse_t pointing_device_get_report(void); +void pointing_device_set_report(report_mouse_t mouse_report); +uint16_t pointing_device_get_cpi(void); +void pointing_device_set_cpi(uint16_t cpi); + +void pointing_device_init_kb(void); +void pointing_device_init_user(void); +report_mouse_t pointing_device_task_kb(report_mouse_t mouse_report); +report_mouse_t pointing_device_task_user(report_mouse_t mouse_report); +uint8_t pointing_device_handle_buttons(uint8_t buttons, bool pressed, pointing_device_buttons_t button); +report_mouse_t pointing_device_adjust_by_defines(report_mouse_t mouse_report); + +#if defined(SPLIT_POINTING_ENABLE) +void pointing_device_set_shared_report(report_mouse_t report); +uint16_t pointing_device_get_shared_cpi(void); +# if !defined(POINTING_DEVICE_TASK_THROTTLE_MS) +# define POINTING_DEVICE_TASK_THROTTLE_MS 1 +# endif +# if defined(POINTING_DEVICE_COMBINED) +void pointing_device_set_cpi_on_side(bool left, uint16_t cpi); +report_mouse_t pointing_device_combine_reports(report_mouse_t left_report, report_mouse_t right_report); +report_mouse_t pointing_device_task_combined_kb(report_mouse_t left_report, report_mouse_t right_report); +report_mouse_t pointing_device_task_combined_user(report_mouse_t left_report, report_mouse_t right_report); +report_mouse_t pointing_device_adjust_by_defines_right(report_mouse_t mouse_report); +# endif // defined(POINTING_DEVICE_COMBINED) +#endif // defined(SPLIT_POINTING_ENABLE) diff --git a/quantum/pointing_device/pointing_device_drivers.c b/quantum/pointing_device/pointing_device_drivers.c new file mode 100644 index 0000000000..d0b545d22d --- /dev/null +++ b/quantum/pointing_device/pointing_device_drivers.c @@ -0,0 +1,345 @@ +/* Copyright 2017 Joshua Broekhuijsen + * Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) + * Copyright 2021 Dasky (@daskygit) + * + * 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 "pointing_device.h" +#include "debug.h" +#include "wait.h" +#include "timer.h" +#include + +#define CONSTRAIN_HID(amt) ((amt) < INT8_MIN ? INT8_MIN : ((amt) > INT8_MAX ? INT8_MAX : (amt))) +#define CONSTRAIN_HID_XY(amt) ((amt) < XY_REPORT_MIN ? XY_REPORT_MIN : ((amt) > XY_REPORT_MAX ? XY_REPORT_MAX : (amt))) + +// get_report functions should probably be moved to their respective drivers. + +#if defined(POINTING_DEVICE_DRIVER_adns5050) +report_mouse_t adns5050_get_report(report_mouse_t mouse_report) { + report_adns5050_t data = adns5050_read_burst(); + + if (data.dx != 0 || data.dy != 0) { +# ifdef CONSOLE_ENABLE + if (debug_mouse) dprintf("Raw ] X: %d, Y: %d\n", data.dx, data.dy); +# endif + + mouse_report.x = (mouse_xy_report_t)data.dx; + mouse_report.y = (mouse_xy_report_t)data.dy; + } + + return mouse_report; +} + +// clang-format off +const pointing_device_driver_t pointing_device_driver = { + .init = adns5050_init, + .get_report = adns5050_get_report, + .set_cpi = adns5050_set_cpi, + .get_cpi = adns5050_get_cpi, +}; +// clang-format on + +#elif defined(POINTING_DEVICE_DRIVER_adns9800) + +report_mouse_t adns9800_get_report_driver(report_mouse_t mouse_report) { + report_adns9800_t sensor_report = adns9800_get_report(); + + mouse_report.x = CONSTRAIN_HID_XY(sensor_report.x); + mouse_report.y = CONSTRAIN_HID_XY(sensor_report.y); + + return mouse_report; +} + +// clang-format off +const pointing_device_driver_t pointing_device_driver = { + .init = adns9800_init, + .get_report = adns9800_get_report_driver, + .set_cpi = adns9800_set_cpi, + .get_cpi = adns9800_get_cpi +}; +// clang-format on + +#elif defined(POINTING_DEVICE_DRIVER_analog_joystick) +report_mouse_t analog_joystick_get_report(report_mouse_t mouse_report) { + report_analog_joystick_t data = analog_joystick_read(); + +# ifdef CONSOLE_ENABLE + if (debug_mouse) dprintf("Raw ] X: %d, Y: %d\n", data.x, data.y); +# endif + + mouse_report.x = data.x; + mouse_report.y = data.y; + + mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, data.button, POINTING_DEVICE_BUTTON1); + + return mouse_report; +} + +// clang-format off +const pointing_device_driver_t pointing_device_driver = { + .init = analog_joystick_init, + .get_report = analog_joystick_get_report, + .set_cpi = NULL, + .get_cpi = NULL +}; +// clang-format on + +#elif defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_i2c) || defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_spi) +# ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE +static bool cursor_glide_enable = true; + +static cursor_glide_context_t glide = {.config = { + .coef = 102, /* Good default friction coef */ + .interval = 10, /* 100sps */ + .trigger_px = 10, /* Default threshold in case of hover, set to 0 if you'd like */ + }}; + +void cirque_pinnacle_enable_cursor_glide(bool enable) { + cursor_glide_enable = enable; +} + +void cirque_pinnacle_configure_cursor_glide(float trigger_px) { + glide.config.trigger_px = trigger_px; +} +# endif + +report_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) { + pinnacle_data_t touchData = cirque_pinnacle_read_data(); + mouse_xy_report_t report_x = 0, report_y = 0; + static mouse_xy_report_t x = 0, y = 0; +# ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE + cursor_glide_t glide_report = {0}; + + if (cursor_glide_enable) { + glide_report = cursor_glide_check(&glide); + } +# endif + +# if !CIRQUE_PINNACLE_POSITION_MODE +# error Cirque Pinnacle with relative mode not implemented yet. +# endif + + if (!touchData.valid) { +# ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE + if (cursor_glide_enable && glide_report.valid) { + report_x = glide_report.dx; + report_y = glide_report.dy; + goto mouse_report_update; + } +# endif + return mouse_report; + } + +# if CONSOLE_ENABLE + if (debug_mouse && touchData.touchDown) { + dprintf("cirque_pinnacle touchData x=%4d y=%4d z=%2d\n", touchData.xValue, touchData.yValue, touchData.zValue); + } +# endif + + // Scale coordinates to arbitrary X, Y resolution + cirque_pinnacle_scale_data(&touchData, cirque_pinnacle_get_scale(), cirque_pinnacle_get_scale()); + + if (!cirque_pinnacle_gestures(&mouse_report, touchData)) { + if (x && y && touchData.xValue && touchData.yValue) { + report_x = (mouse_xy_report_t)(touchData.xValue - x); + report_y = (mouse_xy_report_t)(touchData.yValue - y); + } + x = touchData.xValue; + y = touchData.yValue; + +# ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE + if (cursor_glide_enable) { + if (touchData.touchDown) { + cursor_glide_update(&glide, report_x, report_y, touchData.zValue); + } else if (!glide_report.valid) { + glide_report = cursor_glide_start(&glide); + if (glide_report.valid) { + report_x = glide_report.dx; + report_y = glide_report.dy; + } + } + } +# endif + } + +# ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE +mouse_report_update: +# endif + mouse_report.x = report_x; + mouse_report.y = report_y; + + return mouse_report; +} + +uint16_t cirque_pinnacle_get_cpi(void) { + return CIRQUE_PINNACLE_PX_TO_INCH(cirque_pinnacle_get_scale()); +} +void cirque_pinnacle_set_cpi(uint16_t cpi) { + cirque_pinnacle_set_scale(CIRQUE_PINNACLE_INCH_TO_PX(cpi)); +} + +// clang-format off +const pointing_device_driver_t pointing_device_driver = { + .init = cirque_pinnacle_init, + .get_report = cirque_pinnacle_get_report, + .set_cpi = cirque_pinnacle_set_cpi, + .get_cpi = cirque_pinnacle_get_cpi +}; +// clang-format on +#elif defined(POINTING_DEVICE_DRIVER_paw3204) + +report_mouse_t paw3204_get_report(report_mouse_t mouse_report) { + report_paw3204_t data = paw3204_read(); + if (data.isMotion) { +# ifdef CONSOLE_ENABLE + dprintf("Raw ] X: %d, Y: %d\n", data.x, data.y); +# endif + + mouse_report.x = data.x; + mouse_report.y = data.y; + } + + return mouse_report; +} +const pointing_device_driver_t pointing_device_driver = { + .init = paw3204_init, + .get_report = paw3204_get_report, + .set_cpi = paw3204_set_cpi, + .get_cpi = paw3204_get_cpi, +}; +#elif defined(POINTING_DEVICE_DRIVER_pimoroni_trackball) + +mouse_xy_report_t pimoroni_trackball_adapt_values(clamp_range_t* offset) { + if (*offset > XY_REPORT_MAX) { + *offset -= XY_REPORT_MAX; + return (mouse_xy_report_t)XY_REPORT_MAX; + } else if (*offset < XY_REPORT_MIN) { + *offset += XY_REPORT_MAX; + return (mouse_xy_report_t)XY_REPORT_MIN; + } else { + mouse_xy_report_t temp_return = *offset; + *offset = 0; + return temp_return; + } +} + +report_mouse_t pimoroni_trackball_get_report(report_mouse_t mouse_report) { + static uint16_t debounce = 0; + static uint8_t error_count = 0; + pimoroni_data_t pimoroni_data = {0}; + static clamp_range_t x_offset = 0, y_offset = 0; + + if (error_count < PIMORONI_TRACKBALL_ERROR_COUNT) { + i2c_status_t status = read_pimoroni_trackball(&pimoroni_data); + + if (status == I2C_STATUS_SUCCESS) { + error_count = 0; + + if (!(pimoroni_data.click & 128)) { + mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, false, POINTING_DEVICE_BUTTON1); + if (!debounce) { + x_offset += pimoroni_trackball_get_offsets(pimoroni_data.right, pimoroni_data.left, PIMORONI_TRACKBALL_SCALE); + y_offset += pimoroni_trackball_get_offsets(pimoroni_data.down, pimoroni_data.up, PIMORONI_TRACKBALL_SCALE); + mouse_report.x = pimoroni_trackball_adapt_values(&x_offset); + mouse_report.y = pimoroni_trackball_adapt_values(&y_offset); + } else { + debounce--; + } + } else { + mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, true, POINTING_DEVICE_BUTTON1); + debounce = PIMORONI_TRACKBALL_DEBOUNCE_CYCLES; + } + } else { + error_count++; + } + } + return mouse_report; +} + +// clang-format off +const pointing_device_driver_t pointing_device_driver = { + .init = pimoroni_trackball_device_init, + .get_report = pimoroni_trackball_get_report, + .set_cpi = pimoroni_trackball_set_cpi, + .get_cpi = pimoroni_trackball_get_cpi +}; +// clang-format on + +#elif defined(POINTING_DEVICE_DRIVER_pmw3360) || defined(POINTING_DEVICE_DRIVER_pmw3389) +static void pmw33xx_init_wrapper(void) { + pmw33xx_init(0); +} + +static void pmw33xx_set_cpi_wrapper(uint16_t cpi) { + pmw33xx_set_cpi(0, cpi); +} + +static uint16_t pmw33xx_get_cpi_wrapper(void) { + return pmw33xx_get_cpi(0); +} + +report_mouse_t pmw33xx_get_report(report_mouse_t mouse_report) { + pmw33xx_report_t report = pmw33xx_read_burst(0); + static bool in_motion = false; + + if (report.motion.b.is_lifted) { + return mouse_report; + } + + if (!report.motion.b.is_motion) { + in_motion = false; + return mouse_report; + } + + if (!in_motion) { + in_motion = true; + dprintf("PWM3360 (0): starting motion\n"); + } + + mouse_report.x = CONSTRAIN_HID_XY(report.delta_x); + mouse_report.y = CONSTRAIN_HID_XY(report.delta_y); + return mouse_report; +} + +// clang-format off +const pointing_device_driver_t pointing_device_driver = { + .init = pmw33xx_init_wrapper, + .get_report = pmw33xx_get_report, + .set_cpi = pmw33xx_set_cpi_wrapper, + .get_cpi = pmw33xx_get_cpi_wrapper +}; +// clang-format on + +#else +__attribute__((weak)) void pointing_device_driver_init(void) {} +__attribute__((weak)) report_mouse_t pointing_device_driver_get_report(report_mouse_t mouse_report) { + return mouse_report; +} +__attribute__((weak)) uint16_t pointing_device_driver_get_cpi(void) { + return 0; +} +__attribute__((weak)) void pointing_device_driver_set_cpi(uint16_t cpi) {} + +// clang-format off +const pointing_device_driver_t pointing_device_driver = { + .init = pointing_device_driver_init, + .get_report = pointing_device_driver_get_report, + .get_cpi = pointing_device_driver_get_cpi, + .set_cpi = pointing_device_driver_set_cpi +}; +// clang-format on + +#endif diff --git a/quantum/pointing_device/pointing_device_gestures.c b/quantum/pointing_device/pointing_device_gestures.c new file mode 100644 index 0000000000..02b11ebe3f --- /dev/null +++ b/quantum/pointing_device/pointing_device_gestures.c @@ -0,0 +1,133 @@ +/* Copyright 2022 Daniel Kao + * + * 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 +#include "pointing_device_gestures.h" +#include "timer.h" + +#ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE +# ifdef POINTING_DEVICE_MOTION_PIN +# error POINTING_DEVICE_MOTION_PIN not supported when using inertial cursor. Need repeated calls to get_report() to generate glide events. +# endif + +static void cursor_glide_stop(cursor_glide_context_t* glide) { + memset(&glide->status, 0, sizeof(glide->status)); +} + +static cursor_glide_t cursor_glide(cursor_glide_context_t* glide) { + cursor_glide_status_t* status = &glide->status; + cursor_glide_t report; + int32_t p; + int32_t x, y; + + if (status->v0 == 0) { + report.dx = 0; + report.dy = 0; + report.valid = false; + cursor_glide_stop(glide); + goto exit; + } + + status->counter++; + /* Calculate current 1D position */ + p = status->v0 * status->counter - (int32_t)glide->config.coef * status->counter * status->counter / 2; + /* + * Translate to x & y axes + * Done this way instead of applying friction to each axis separately, so we don't end up with the shorter axis stuck at 0 towards the end of diagonal movements. + */ + x = (int32_t)(p * status->dx0 / status->v0); + y = (int32_t)(p * status->dy0 / status->v0); + report.dx = (mouse_xy_report_t)(x - status->x); + report.dy = (mouse_xy_report_t)(y - status->y); + report.valid = true; + if (report.dx <= 1 && report.dx >= -1 && report.dy <= 1 && report.dy >= -1) { + /* Stop gliding once speed is low enough */ + cursor_glide_stop(glide); + goto exit; + } + status->x = x; + status->y = y; + status->timer = timer_read(); + +exit: + return report; +} + +cursor_glide_t cursor_glide_check(cursor_glide_context_t* glide) { + cursor_glide_t invalid_report = {0, 0, false}; + cursor_glide_status_t* status = &glide->status; + + if (status->z || (status->dx0 == 0 && status->dy0 == 0) || timer_elapsed(status->timer) < glide->config.interval) { + return invalid_report; + } else { + return cursor_glide(glide); + } +} + +static inline uint16_t sqrt32(uint32_t x) { + uint32_t l, m, h; + + if (x == 0) { + return 0; + } else if (x > (UINT16_MAX >> 2)) { + /* Safe upper bound to avoid integer overflow with m * m */ + h = UINT16_MAX; + } else { + /* Upper bound based on closest log2 */ + h = (1 << (((__builtin_clzl(1) - __builtin_clzl(x) + 1) + 1) >> 1)); + } + /* Lower bound based on closest log2 */ + l = (1 << ((__builtin_clzl(1) - __builtin_clzl(x)) >> 1)); + + /* Binary search to find integer square root */ + while (l != h - 1) { + m = (l + h) / 2; + if (m * m <= x) { + l = m; + } else { + h = m; + } + } + return l; +} + +cursor_glide_t cursor_glide_start(cursor_glide_context_t* glide) { + cursor_glide_t invalid_report = {0, 0, false}; + cursor_glide_status_t* status = &glide->status; + + status->timer = timer_read(); + status->counter = 0; + status->v0 = (status->dx0 == 0 && status->dy0 == 0) ? 0.0 : sqrt32(((int32_t)status->dx0 * 256 * status->dx0 * 256) + ((int32_t)status->dy0 * 256 * status->dy0 * 256)); // skip trigonometry if not needed, calculate distance in Q8 + status->x = 0; + status->y = 0; + status->z = 0; + + if (status->v0 < ((uint32_t)glide->config.trigger_px * 256)) { /* Q8 comparison */ + /* Not enough velocity to be worth gliding, abort */ + cursor_glide_stop(glide); + return invalid_report; + } + + return cursor_glide(glide); +} + +void cursor_glide_update(cursor_glide_context_t* glide, mouse_xy_report_t dx, mouse_xy_report_t dy, uint16_t z) { + cursor_glide_status_t* status = &glide->status; + + status->dx0 = dx; + status->dy0 = dy; + status->z = z; +} +#endif diff --git a/quantum/pointing_device/pointing_device_gestures.h b/quantum/pointing_device/pointing_device_gestures.h new file mode 100644 index 0000000000..d2ea44971b --- /dev/null +++ b/quantum/pointing_device/pointing_device_gestures.h @@ -0,0 +1,58 @@ +/* Copyright 2022 Daniel Kao + * + * 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 +#include "report.h" + +#ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE +typedef struct { + mouse_xy_report_t dx; + mouse_xy_report_t dy; + bool valid; +} cursor_glide_t; + +typedef struct { + uint16_t trigger_px; /* Pixels of movement needed to trigger cursor glide */ + uint16_t coef; /* Coefficient of friction */ + uint16_t interval; /* Glide report interval, in milliseconds */ +} cursor_glide_config_t; + +typedef struct { + int32_t v0; + int32_t x; + int32_t y; + uint16_t z; + uint16_t timer; + uint16_t counter; + mouse_xy_report_t dx0; + mouse_xy_report_t dy0; +} cursor_glide_status_t; + +typedef struct { + cursor_glide_config_t config; + cursor_glide_status_t status; +} cursor_glide_context_t; + +/* Check glide report conditions, calculates glide coordinates */ +cursor_glide_t cursor_glide_check(cursor_glide_context_t* glide); + +/* Start glide reporting, gives first set of glide coordinates */ +cursor_glide_t cursor_glide_start(cursor_glide_context_t* glide); + +/* Update glide engine on the latest cursor movement, cursor glide is based on the final movement */ +void cursor_glide_update(cursor_glide_context_t* glide, mouse_xy_report_t dx, mouse_xy_report_t dy, uint16_t z); +#endif diff --git a/quantum/pointing_device_drivers.c b/quantum/pointing_device_drivers.c deleted file mode 100644 index d0b545d22d..0000000000 --- a/quantum/pointing_device_drivers.c +++ /dev/null @@ -1,345 +0,0 @@ -/* Copyright 2017 Joshua Broekhuijsen - * Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) - * Copyright 2021 Dasky (@daskygit) - * - * 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 "pointing_device.h" -#include "debug.h" -#include "wait.h" -#include "timer.h" -#include - -#define CONSTRAIN_HID(amt) ((amt) < INT8_MIN ? INT8_MIN : ((amt) > INT8_MAX ? INT8_MAX : (amt))) -#define CONSTRAIN_HID_XY(amt) ((amt) < XY_REPORT_MIN ? XY_REPORT_MIN : ((amt) > XY_REPORT_MAX ? XY_REPORT_MAX : (amt))) - -// get_report functions should probably be moved to their respective drivers. - -#if defined(POINTING_DEVICE_DRIVER_adns5050) -report_mouse_t adns5050_get_report(report_mouse_t mouse_report) { - report_adns5050_t data = adns5050_read_burst(); - - if (data.dx != 0 || data.dy != 0) { -# ifdef CONSOLE_ENABLE - if (debug_mouse) dprintf("Raw ] X: %d, Y: %d\n", data.dx, data.dy); -# endif - - mouse_report.x = (mouse_xy_report_t)data.dx; - mouse_report.y = (mouse_xy_report_t)data.dy; - } - - return mouse_report; -} - -// clang-format off -const pointing_device_driver_t pointing_device_driver = { - .init = adns5050_init, - .get_report = adns5050_get_report, - .set_cpi = adns5050_set_cpi, - .get_cpi = adns5050_get_cpi, -}; -// clang-format on - -#elif defined(POINTING_DEVICE_DRIVER_adns9800) - -report_mouse_t adns9800_get_report_driver(report_mouse_t mouse_report) { - report_adns9800_t sensor_report = adns9800_get_report(); - - mouse_report.x = CONSTRAIN_HID_XY(sensor_report.x); - mouse_report.y = CONSTRAIN_HID_XY(sensor_report.y); - - return mouse_report; -} - -// clang-format off -const pointing_device_driver_t pointing_device_driver = { - .init = adns9800_init, - .get_report = adns9800_get_report_driver, - .set_cpi = adns9800_set_cpi, - .get_cpi = adns9800_get_cpi -}; -// clang-format on - -#elif defined(POINTING_DEVICE_DRIVER_analog_joystick) -report_mouse_t analog_joystick_get_report(report_mouse_t mouse_report) { - report_analog_joystick_t data = analog_joystick_read(); - -# ifdef CONSOLE_ENABLE - if (debug_mouse) dprintf("Raw ] X: %d, Y: %d\n", data.x, data.y); -# endif - - mouse_report.x = data.x; - mouse_report.y = data.y; - - mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, data.button, POINTING_DEVICE_BUTTON1); - - return mouse_report; -} - -// clang-format off -const pointing_device_driver_t pointing_device_driver = { - .init = analog_joystick_init, - .get_report = analog_joystick_get_report, - .set_cpi = NULL, - .get_cpi = NULL -}; -// clang-format on - -#elif defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_i2c) || defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_spi) -# ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE -static bool cursor_glide_enable = true; - -static cursor_glide_context_t glide = {.config = { - .coef = 102, /* Good default friction coef */ - .interval = 10, /* 100sps */ - .trigger_px = 10, /* Default threshold in case of hover, set to 0 if you'd like */ - }}; - -void cirque_pinnacle_enable_cursor_glide(bool enable) { - cursor_glide_enable = enable; -} - -void cirque_pinnacle_configure_cursor_glide(float trigger_px) { - glide.config.trigger_px = trigger_px; -} -# endif - -report_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) { - pinnacle_data_t touchData = cirque_pinnacle_read_data(); - mouse_xy_report_t report_x = 0, report_y = 0; - static mouse_xy_report_t x = 0, y = 0; -# ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE - cursor_glide_t glide_report = {0}; - - if (cursor_glide_enable) { - glide_report = cursor_glide_check(&glide); - } -# endif - -# if !CIRQUE_PINNACLE_POSITION_MODE -# error Cirque Pinnacle with relative mode not implemented yet. -# endif - - if (!touchData.valid) { -# ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE - if (cursor_glide_enable && glide_report.valid) { - report_x = glide_report.dx; - report_y = glide_report.dy; - goto mouse_report_update; - } -# endif - return mouse_report; - } - -# if CONSOLE_ENABLE - if (debug_mouse && touchData.touchDown) { - dprintf("cirque_pinnacle touchData x=%4d y=%4d z=%2d\n", touchData.xValue, touchData.yValue, touchData.zValue); - } -# endif - - // Scale coordinates to arbitrary X, Y resolution - cirque_pinnacle_scale_data(&touchData, cirque_pinnacle_get_scale(), cirque_pinnacle_get_scale()); - - if (!cirque_pinnacle_gestures(&mouse_report, touchData)) { - if (x && y && touchData.xValue && touchData.yValue) { - report_x = (mouse_xy_report_t)(touchData.xValue - x); - report_y = (mouse_xy_report_t)(touchData.yValue - y); - } - x = touchData.xValue; - y = touchData.yValue; - -# ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE - if (cursor_glide_enable) { - if (touchData.touchDown) { - cursor_glide_update(&glide, report_x, report_y, touchData.zValue); - } else if (!glide_report.valid) { - glide_report = cursor_glide_start(&glide); - if (glide_report.valid) { - report_x = glide_report.dx; - report_y = glide_report.dy; - } - } - } -# endif - } - -# ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE -mouse_report_update: -# endif - mouse_report.x = report_x; - mouse_report.y = report_y; - - return mouse_report; -} - -uint16_t cirque_pinnacle_get_cpi(void) { - return CIRQUE_PINNACLE_PX_TO_INCH(cirque_pinnacle_get_scale()); -} -void cirque_pinnacle_set_cpi(uint16_t cpi) { - cirque_pinnacle_set_scale(CIRQUE_PINNACLE_INCH_TO_PX(cpi)); -} - -// clang-format off -const pointing_device_driver_t pointing_device_driver = { - .init = cirque_pinnacle_init, - .get_report = cirque_pinnacle_get_report, - .set_cpi = cirque_pinnacle_set_cpi, - .get_cpi = cirque_pinnacle_get_cpi -}; -// clang-format on -#elif defined(POINTING_DEVICE_DRIVER_paw3204) - -report_mouse_t paw3204_get_report(report_mouse_t mouse_report) { - report_paw3204_t data = paw3204_read(); - if (data.isMotion) { -# ifdef CONSOLE_ENABLE - dprintf("Raw ] X: %d, Y: %d\n", data.x, data.y); -# endif - - mouse_report.x = data.x; - mouse_report.y = data.y; - } - - return mouse_report; -} -const pointing_device_driver_t pointing_device_driver = { - .init = paw3204_init, - .get_report = paw3204_get_report, - .set_cpi = paw3204_set_cpi, - .get_cpi = paw3204_get_cpi, -}; -#elif defined(POINTING_DEVICE_DRIVER_pimoroni_trackball) - -mouse_xy_report_t pimoroni_trackball_adapt_values(clamp_range_t* offset) { - if (*offset > XY_REPORT_MAX) { - *offset -= XY_REPORT_MAX; - return (mouse_xy_report_t)XY_REPORT_MAX; - } else if (*offset < XY_REPORT_MIN) { - *offset += XY_REPORT_MAX; - return (mouse_xy_report_t)XY_REPORT_MIN; - } else { - mouse_xy_report_t temp_return = *offset; - *offset = 0; - return temp_return; - } -} - -report_mouse_t pimoroni_trackball_get_report(report_mouse_t mouse_report) { - static uint16_t debounce = 0; - static uint8_t error_count = 0; - pimoroni_data_t pimoroni_data = {0}; - static clamp_range_t x_offset = 0, y_offset = 0; - - if (error_count < PIMORONI_TRACKBALL_ERROR_COUNT) { - i2c_status_t status = read_pimoroni_trackball(&pimoroni_data); - - if (status == I2C_STATUS_SUCCESS) { - error_count = 0; - - if (!(pimoroni_data.click & 128)) { - mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, false, POINTING_DEVICE_BUTTON1); - if (!debounce) { - x_offset += pimoroni_trackball_get_offsets(pimoroni_data.right, pimoroni_data.left, PIMORONI_TRACKBALL_SCALE); - y_offset += pimoroni_trackball_get_offsets(pimoroni_data.down, pimoroni_data.up, PIMORONI_TRACKBALL_SCALE); - mouse_report.x = pimoroni_trackball_adapt_values(&x_offset); - mouse_report.y = pimoroni_trackball_adapt_values(&y_offset); - } else { - debounce--; - } - } else { - mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, true, POINTING_DEVICE_BUTTON1); - debounce = PIMORONI_TRACKBALL_DEBOUNCE_CYCLES; - } - } else { - error_count++; - } - } - return mouse_report; -} - -// clang-format off -const pointing_device_driver_t pointing_device_driver = { - .init = pimoroni_trackball_device_init, - .get_report = pimoroni_trackball_get_report, - .set_cpi = pimoroni_trackball_set_cpi, - .get_cpi = pimoroni_trackball_get_cpi -}; -// clang-format on - -#elif defined(POINTING_DEVICE_DRIVER_pmw3360) || defined(POINTING_DEVICE_DRIVER_pmw3389) -static void pmw33xx_init_wrapper(void) { - pmw33xx_init(0); -} - -static void pmw33xx_set_cpi_wrapper(uint16_t cpi) { - pmw33xx_set_cpi(0, cpi); -} - -static uint16_t pmw33xx_get_cpi_wrapper(void) { - return pmw33xx_get_cpi(0); -} - -report_mouse_t pmw33xx_get_report(report_mouse_t mouse_report) { - pmw33xx_report_t report = pmw33xx_read_burst(0); - static bool in_motion = false; - - if (report.motion.b.is_lifted) { - return mouse_report; - } - - if (!report.motion.b.is_motion) { - in_motion = false; - return mouse_report; - } - - if (!in_motion) { - in_motion = true; - dprintf("PWM3360 (0): starting motion\n"); - } - - mouse_report.x = CONSTRAIN_HID_XY(report.delta_x); - mouse_report.y = CONSTRAIN_HID_XY(report.delta_y); - return mouse_report; -} - -// clang-format off -const pointing_device_driver_t pointing_device_driver = { - .init = pmw33xx_init_wrapper, - .get_report = pmw33xx_get_report, - .set_cpi = pmw33xx_set_cpi_wrapper, - .get_cpi = pmw33xx_get_cpi_wrapper -}; -// clang-format on - -#else -__attribute__((weak)) void pointing_device_driver_init(void) {} -__attribute__((weak)) report_mouse_t pointing_device_driver_get_report(report_mouse_t mouse_report) { - return mouse_report; -} -__attribute__((weak)) uint16_t pointing_device_driver_get_cpi(void) { - return 0; -} -__attribute__((weak)) void pointing_device_driver_set_cpi(uint16_t cpi) {} - -// clang-format off -const pointing_device_driver_t pointing_device_driver = { - .init = pointing_device_driver_init, - .get_report = pointing_device_driver_get_report, - .get_cpi = pointing_device_driver_get_cpi, - .set_cpi = pointing_device_driver_set_cpi -}; -// clang-format on - -#endif diff --git a/quantum/pointing_device_gestures.c b/quantum/pointing_device_gestures.c deleted file mode 100644 index 02b11ebe3f..0000000000 --- a/quantum/pointing_device_gestures.c +++ /dev/null @@ -1,133 +0,0 @@ -/* Copyright 2022 Daniel Kao - * - * 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 -#include "pointing_device_gestures.h" -#include "timer.h" - -#ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE -# ifdef POINTING_DEVICE_MOTION_PIN -# error POINTING_DEVICE_MOTION_PIN not supported when using inertial cursor. Need repeated calls to get_report() to generate glide events. -# endif - -static void cursor_glide_stop(cursor_glide_context_t* glide) { - memset(&glide->status, 0, sizeof(glide->status)); -} - -static cursor_glide_t cursor_glide(cursor_glide_context_t* glide) { - cursor_glide_status_t* status = &glide->status; - cursor_glide_t report; - int32_t p; - int32_t x, y; - - if (status->v0 == 0) { - report.dx = 0; - report.dy = 0; - report.valid = false; - cursor_glide_stop(glide); - goto exit; - } - - status->counter++; - /* Calculate current 1D position */ - p = status->v0 * status->counter - (int32_t)glide->config.coef * status->counter * status->counter / 2; - /* - * Translate to x & y axes - * Done this way instead of applying friction to each axis separately, so we don't end up with the shorter axis stuck at 0 towards the end of diagonal movements. - */ - x = (int32_t)(p * status->dx0 / status->v0); - y = (int32_t)(p * status->dy0 / status->v0); - report.dx = (mouse_xy_report_t)(x - status->x); - report.dy = (mouse_xy_report_t)(y - status->y); - report.valid = true; - if (report.dx <= 1 && report.dx >= -1 && report.dy <= 1 && report.dy >= -1) { - /* Stop gliding once speed is low enough */ - cursor_glide_stop(glide); - goto exit; - } - status->x = x; - status->y = y; - status->timer = timer_read(); - -exit: - return report; -} - -cursor_glide_t cursor_glide_check(cursor_glide_context_t* glide) { - cursor_glide_t invalid_report = {0, 0, false}; - cursor_glide_status_t* status = &glide->status; - - if (status->z || (status->dx0 == 0 && status->dy0 == 0) || timer_elapsed(status->timer) < glide->config.interval) { - return invalid_report; - } else { - return cursor_glide(glide); - } -} - -static inline uint16_t sqrt32(uint32_t x) { - uint32_t l, m, h; - - if (x == 0) { - return 0; - } else if (x > (UINT16_MAX >> 2)) { - /* Safe upper bound to avoid integer overflow with m * m */ - h = UINT16_MAX; - } else { - /* Upper bound based on closest log2 */ - h = (1 << (((__builtin_clzl(1) - __builtin_clzl(x) + 1) + 1) >> 1)); - } - /* Lower bound based on closest log2 */ - l = (1 << ((__builtin_clzl(1) - __builtin_clzl(x)) >> 1)); - - /* Binary search to find integer square root */ - while (l != h - 1) { - m = (l + h) / 2; - if (m * m <= x) { - l = m; - } else { - h = m; - } - } - return l; -} - -cursor_glide_t cursor_glide_start(cursor_glide_context_t* glide) { - cursor_glide_t invalid_report = {0, 0, false}; - cursor_glide_status_t* status = &glide->status; - - status->timer = timer_read(); - status->counter = 0; - status->v0 = (status->dx0 == 0 && status->dy0 == 0) ? 0.0 : sqrt32(((int32_t)status->dx0 * 256 * status->dx0 * 256) + ((int32_t)status->dy0 * 256 * status->dy0 * 256)); // skip trigonometry if not needed, calculate distance in Q8 - status->x = 0; - status->y = 0; - status->z = 0; - - if (status->v0 < ((uint32_t)glide->config.trigger_px * 256)) { /* Q8 comparison */ - /* Not enough velocity to be worth gliding, abort */ - cursor_glide_stop(glide); - return invalid_report; - } - - return cursor_glide(glide); -} - -void cursor_glide_update(cursor_glide_context_t* glide, mouse_xy_report_t dx, mouse_xy_report_t dy, uint16_t z) { - cursor_glide_status_t* status = &glide->status; - - status->dx0 = dx; - status->dy0 = dy; - status->z = z; -} -#endif diff --git a/quantum/pointing_device_gestures.h b/quantum/pointing_device_gestures.h deleted file mode 100644 index d2ea44971b..0000000000 --- a/quantum/pointing_device_gestures.h +++ /dev/null @@ -1,58 +0,0 @@ -/* Copyright 2022 Daniel Kao - * - * 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 -#include "report.h" - -#ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE -typedef struct { - mouse_xy_report_t dx; - mouse_xy_report_t dy; - bool valid; -} cursor_glide_t; - -typedef struct { - uint16_t trigger_px; /* Pixels of movement needed to trigger cursor glide */ - uint16_t coef; /* Coefficient of friction */ - uint16_t interval; /* Glide report interval, in milliseconds */ -} cursor_glide_config_t; - -typedef struct { - int32_t v0; - int32_t x; - int32_t y; - uint16_t z; - uint16_t timer; - uint16_t counter; - mouse_xy_report_t dx0; - mouse_xy_report_t dy0; -} cursor_glide_status_t; - -typedef struct { - cursor_glide_config_t config; - cursor_glide_status_t status; -} cursor_glide_context_t; - -/* Check glide report conditions, calculates glide coordinates */ -cursor_glide_t cursor_glide_check(cursor_glide_context_t* glide); - -/* Start glide reporting, gives first set of glide coordinates */ -cursor_glide_t cursor_glide_start(cursor_glide_context_t* glide); - -/* Update glide engine on the latest cursor movement, cursor glide is based on the final movement */ -void cursor_glide_update(cursor_glide_context_t* glide, mouse_xy_report_t dx, mouse_xy_report_t dy, uint16_t z); -#endif -- cgit v1.2.3 From 9a31bbb3fa4389ea0d0efcb0d283664eaf684190 Mon Sep 17 00:00:00 2001 From: mknj Date: Wed, 20 Jul 2022 23:59:18 +0200 Subject: fix syntax error (#17732) --- quantum/keymap_extras/keymap_turkish_f.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'quantum') diff --git a/quantum/keymap_extras/keymap_turkish_f.h b/quantum/keymap_extras/keymap_turkish_f.h index f86ef21546..aaee2aa480 100644 --- a/quantum/keymap_extras/keymap_turkish_f.h +++ b/quantum/keymap_extras/keymap_turkish_f.h @@ -187,7 +187,7 @@ #define TR_CURR S(ALGR(TR_4)) // ¤ #define TR_IQUE S(ALGR(TR_SLSH)) // ¿ // Row 2 -#define TR_REGD S(ALGR(TR_I) // ® +#define TR_REGD S(ALGR(TR_I)) // ® // Row 3 #define TR_SECT S(ALGR(TR_IDOT)) // § #define TR_FORD S(ALGR(TR_A)) // ª -- cgit v1.2.3 From 4efe6330c49f076b9c01d3f609dea0cd9f9b0214 Mon Sep 17 00:00:00 2001 From: Wilba Date: Thu, 21 Jul 2022 09:53:42 +1000 Subject: VIA Encoder Map Support (#17734) --- quantum/via.c | 12 ++++++++++++ quantum/via.h | 4 +++- 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'quantum') diff --git a/quantum/via.c b/quantum/via.c index d2ef0862cc..37e2046a10 100644 --- a/quantum/via.c +++ b/quantum/via.c @@ -397,6 +397,18 @@ void raw_hid_receive(uint8_t *data, uint8_t length) { dynamic_keymap_set_buffer(offset, size, &command_data[3]); break; } +#ifdef ENCODER_MAP_ENABLE + case id_dynamic_keymap_get_encoder: { + uint16_t keycode = dynamic_keymap_get_encoder(command_data[0], command_data[1], command_data[2] != 0); + command_data[3] = keycode >> 8; + command_data[4] = keycode & 0xFF; + break; + } + case id_dynamic_keymap_set_encoder: { + dynamic_keymap_set_encoder(command_data[0], command_data[1], command_data[2] != 0, (command_data[3] << 8) | command_data[4]); + break; + } +#endif default: { // The command ID is not known // Return the unhandled state diff --git a/quantum/via.h b/quantum/via.h index 31d9c21af4..558ae95de4 100644 --- a/quantum/via.h +++ b/quantum/via.h @@ -58,7 +58,7 @@ // This is changed only when the command IDs change, // so VIA Configurator can detect compatible firmware. -#define VIA_PROTOCOL_VERSION 0x0009 +#define VIA_PROTOCOL_VERSION 0x000A enum via_command_id { id_get_protocol_version = 0x01, // always 0x01 @@ -80,6 +80,8 @@ enum via_command_id { id_dynamic_keymap_get_layer_count = 0x11, id_dynamic_keymap_get_buffer = 0x12, id_dynamic_keymap_set_buffer = 0x13, + id_dynamic_keymap_get_encoder = 0x14, + id_dynamic_keymap_set_encoder = 0x15, id_unhandled = 0xFF, }; -- cgit v1.2.3 From 5f32690cba8789c1ac98e71cb8ae8a4b6a5ee2d2 Mon Sep 17 00:00:00 2001 From: Drashna Jaelre Date: Thu, 21 Jul 2022 10:16:44 -0700 Subject: Move Pointing Device Initialization (#17740) Move Pointing Device Initialization to after Split Post Initialization If both pointing device and split is enabled, the pointing device init needs to be called after the split post init, otherwise the connection (serial/etc) isn't initialized yet, and any commands that need to send data over (such as calling the set cpi command) never get sent over. --- quantum/keyboard.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'quantum') diff --git a/quantum/keyboard.c b/quantum/keyboard.c index 8273299a9a..2364e3167b 100644 --- a/quantum/keyboard.c +++ b/quantum/keyboard.c @@ -384,9 +384,6 @@ void keyboard_init(void) { #ifdef STENO_ENABLE_ALL steno_init(); #endif -#ifdef POINTING_DEVICE_ENABLE - pointing_device_init(); -#endif #if defined(NKRO_ENABLE) && defined(FORCE_NKRO) keymap_config.nkro = 1; eeconfig_update_keymap(keymap_config.raw); @@ -403,6 +400,10 @@ void keyboard_init(void) { #ifdef SPLIT_KEYBOARD split_post_init(); #endif +#ifdef POINTING_DEVICE_ENABLE + // init after split init + pointing_device_init(); +#endif #if defined(DEBUG_MATRIX_SCAN_RATE) && defined(CONSOLE_ENABLE) debug_enable = true; -- cgit v1.2.3 From a6f3194397a515ade82f10b5547e82e2863c8685 Mon Sep 17 00:00:00 2001 From: jack <0x6A73@pm.me> Date: Sat, 23 Jul 2022 10:43:32 -0600 Subject: Add ability to enter bootloader mode from `QK_MAKE` (#17745) --- quantum/quantum.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'quantum') diff --git a/quantum/quantum.c b/quantum/quantum.c index d1cfb5fbe0..0d5f398585 100644 --- a/quantum/quantum.c +++ b/quantum/quantum.c @@ -400,6 +400,9 @@ bool process_record_quantum(keyrecord_t *record) { SEND_STRING_DELAY(" compile ", TAP_CODE_DELAY); } SEND_STRING_DELAY("-kb " QMK_KEYBOARD " -km " QMK_KEYMAP SS_TAP(X_ENTER), TAP_CODE_DELAY); + if (temp_mod & MOD_MASK_CS) { + reset_keyboard(); + } } #endif } -- cgit v1.2.3 From d02cefe613f5ba252c6917c7b0fc6b5b87993062 Mon Sep 17 00:00:00 2001 From: JayceFayne <13365789+JayceFayne@users.noreply.github.com> Date: Sat, 23 Jul 2022 20:21:20 +0200 Subject: implement `tap_code16_delay` (#17748) --- quantum/quantum.c | 21 ++++++++++++++++----- quantum/quantum.h | 1 + 2 files changed, 17 insertions(+), 5 deletions(-) (limited to 'quantum') diff --git a/quantum/quantum.c b/quantum/quantum.c index 0d5f398585..80fa1a3ced 100644 --- a/quantum/quantum.c +++ b/quantum/quantum.c @@ -93,16 +93,27 @@ __attribute__((weak)) void unregister_code16(uint16_t code) { } } -__attribute__((weak)) void tap_code16(uint16_t code) { +/** \brief Tap a keycode with a delay. + * + * \param code The modded keycode to tap. + * \param delay The amount of time in milliseconds to leave the keycode registered, before unregistering it. + */ +__attribute__((weak)) void tap_code16_delay(uint16_t code, uint16_t delay) { register_code16(code); - if (code == KC_CAPS_LOCK) { - wait_ms(TAP_HOLD_CAPS_DELAY); - } else if (TAP_CODE_DELAY > 0) { - wait_ms(TAP_CODE_DELAY); + for (uint16_t i = delay; i > 0; i--) { + wait_ms(1); } unregister_code16(code); } +/** \brief Tap a keycode with the default delay. + * + * \param code The modded keycode to tap. If `code` is `KC_CAPS_LOCK`, the delay will be `TAP_HOLD_CAPS_DELAY`, otherwise `TAP_CODE_DELAY`, if defined. + */ +__attribute__((weak)) void tap_code16(uint16_t code) { + tap_code16_delay(code, code == KC_CAPS_LOCK ? TAP_HOLD_CAPS_DELAY : TAP_CODE_DELAY); +} + __attribute__((weak)) bool process_action_kb(keyrecord_t *record) { return true; } diff --git a/quantum/quantum.h b/quantum/quantum.h index f3a8a323c7..8d74f2be38 100644 --- a/quantum/quantum.h +++ b/quantum/quantum.h @@ -264,6 +264,7 @@ void shutdown_user(void); void register_code16(uint16_t code); void unregister_code16(uint16_t code); void tap_code16(uint16_t code); +void tap_code16_delay(uint16_t code, uint16_t delay); const char *get_numeric_str(char *buf, size_t buf_len, uint32_t curr_num, char curr_pad); const char *get_u8_str(uint8_t curr_num, char curr_pad); -- cgit v1.2.3 From 325da02e57fe7374e77b82cb00360ba45167e25c Mon Sep 17 00:00:00 2001 From: Drashna Jaelre Date: Mon, 25 Jul 2022 15:06:26 -0700 Subject: Fix QK_MAKE's reboot check (#17795) --- quantum/quantum.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'quantum') diff --git a/quantum/quantum.c b/quantum/quantum.c index 80fa1a3ced..9a0016b150 100644 --- a/quantum/quantum.c +++ b/quantum/quantum.c @@ -411,7 +411,7 @@ bool process_record_quantum(keyrecord_t *record) { SEND_STRING_DELAY(" compile ", TAP_CODE_DELAY); } SEND_STRING_DELAY("-kb " QMK_KEYBOARD " -km " QMK_KEYMAP SS_TAP(X_ENTER), TAP_CODE_DELAY); - if (temp_mod & MOD_MASK_CS) { + if (temp_mod & MOD_MASK_SHIFT && temp_mod & MOD_MASK_CTRL) { reset_keyboard(); } } -- cgit v1.2.3 From 1de6811ebebba77b4604fb01276d42a8d9003374 Mon Sep 17 00:00:00 2001 From: Daniel Kao Date: Mon, 25 Jul 2022 23:19:33 -0700 Subject: Cirque circular scroll: Support POINTING_DEVICE_COMBINED (#17654) --- quantum/pointing_device/pointing_device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'quantum') diff --git a/quantum/pointing_device/pointing_device.c b/quantum/pointing_device/pointing_device.c index 3aa4941687..09f9e5efb7 100644 --- a/quantum/pointing_device/pointing_device.c +++ b/quantum/pointing_device/pointing_device.c @@ -412,7 +412,7 @@ report_mouse_t pointing_device_combine_reports(report_mouse_t left_report, repor */ report_mouse_t pointing_device_adjust_by_defines_right(report_mouse_t mouse_report) { // Support rotation of the sensor data -# if defined(POINTING_DEVICE_ROTATION_90_RIGHT) || defined(POINTING_DEVICE_ROTATION_RIGHT) || defined(POINTING_DEVICE_ROTATION_RIGHT) +# if defined(POINTING_DEVICE_ROTATION_90_RIGHT) || defined(POINTING_DEVICE_ROTATION_180_RIGHT) || defined(POINTING_DEVICE_ROTATION_270_RIGHT) mouse_xy_report_t x = mouse_report.x; mouse_xy_report_t y = mouse_report.y; # if defined(POINTING_DEVICE_ROTATION_90_RIGHT) -- cgit v1.2.3 From c982d6c5e3f5b113322beabb8a51c3153cab3d54 Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Tue, 26 Jul 2022 17:38:28 +0100 Subject: Avoid OOB in dynamic_keymap_reset (#17695) --- quantum/dynamic_keymap.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'quantum') diff --git a/quantum/dynamic_keymap.c b/quantum/dynamic_keymap.c index e80dd6d534..01be9806e4 100644 --- a/quantum/dynamic_keymap.c +++ b/quantum/dynamic_keymap.c @@ -149,18 +149,25 @@ void dynamic_keymap_set_encoder(uint8_t layer, uint8_t encoder_id, bool clockwis void dynamic_keymap_reset(void) { // Reset the keymaps in EEPROM to what is in flash. - // All keyboards using dynamic keymaps should define a layout - // for the same number of layers as DYNAMIC_KEYMAP_LAYER_COUNT. for (int layer = 0; layer < DYNAMIC_KEYMAP_LAYER_COUNT; layer++) { for (int row = 0; row < MATRIX_ROWS; row++) { for (int column = 0; column < MATRIX_COLS; column++) { - dynamic_keymap_set_keycode(layer, row, column, pgm_read_word(&keymaps[layer][row][column])); + if (layer < keymap_layer_count()) { + dynamic_keymap_set_keycode(layer, row, column, pgm_read_word(&keymaps[layer][row][column])); + } else { + dynamic_keymap_set_keycode(layer, row, column, KC_TRANSPARENT); + } } } #ifdef ENCODER_MAP_ENABLE for (int encoder = 0; encoder < NUM_ENCODERS; encoder++) { - dynamic_keymap_set_encoder(layer, encoder, true, pgm_read_word(&encoder_map[layer][encoder][0])); - dynamic_keymap_set_encoder(layer, encoder, false, pgm_read_word(&encoder_map[layer][encoder][1])); + if (layer < encodermap_layer_count()) { + dynamic_keymap_set_encoder(layer, encoder, true, pgm_read_word(&encoder_map[layer][encoder][0])); + dynamic_keymap_set_encoder(layer, encoder, false, pgm_read_word(&encoder_map[layer][encoder][1])); + } else { + dynamic_keymap_set_encoder(layer, encoder, true, KC_TRANSPARENT); + dynamic_keymap_set_encoder(layer, encoder, false, KC_TRANSPARENT); + } } #endif // ENCODER_MAP_ENABLE } -- cgit v1.2.3 From b8b2e9997680bbc0aef074ad0bfb2eb130aafe04 Mon Sep 17 00:00:00 2001 From: Daniel Kao Date: Tue, 26 Jul 2022 11:35:41 -0700 Subject: Constrain Cirque Pinnacle coordinates (#17803) Static x & y should be the same type as touchData.xValue & touchData.yValue: uint16_t. Their delta could be larger than int8_t and should be constrained to mouse_xy_report_t. --- quantum/pointing_device/pointing_device_drivers.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'quantum') diff --git a/quantum/pointing_device/pointing_device_drivers.c b/quantum/pointing_device/pointing_device_drivers.c index d0b545d22d..3780f3d2da 100644 --- a/quantum/pointing_device/pointing_device_drivers.c +++ b/quantum/pointing_device/pointing_device_drivers.c @@ -117,11 +117,11 @@ void cirque_pinnacle_configure_cursor_glide(float trigger_px) { # endif report_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) { - pinnacle_data_t touchData = cirque_pinnacle_read_data(); - mouse_xy_report_t report_x = 0, report_y = 0; - static mouse_xy_report_t x = 0, y = 0; + pinnacle_data_t touchData = cirque_pinnacle_read_data(); + mouse_xy_report_t report_x = 0, report_y = 0; + static uint16_t x = 0, y = 0; # ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE - cursor_glide_t glide_report = {0}; + cursor_glide_t glide_report = {0}; if (cursor_glide_enable) { glide_report = cursor_glide_check(&glide); @@ -154,8 +154,8 @@ report_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) { if (!cirque_pinnacle_gestures(&mouse_report, touchData)) { if (x && y && touchData.xValue && touchData.yValue) { - report_x = (mouse_xy_report_t)(touchData.xValue - x); - report_y = (mouse_xy_report_t)(touchData.yValue - y); + report_x = CONSTRAIN_HID_XY((int16_t)(touchData.xValue - x)); + report_y = CONSTRAIN_HID_XY((int16_t)(touchData.yValue - y)); } x = touchData.xValue; y = touchData.yValue; -- 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') 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') 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 0b726a437b8906fb52662504ccb6e4f052890f3c Mon Sep 17 00:00:00 2001 From: Drzony Date: Sat, 30 Jul 2022 06:20:34 +0200 Subject: Implement relative mode for Cirque trackpad (#17760) --- quantum/pointing_device/pointing_device_drivers.c | 51 ++++++++++++++++------- 1 file changed, 37 insertions(+), 14 deletions(-) (limited to 'quantum') diff --git a/quantum/pointing_device/pointing_device_drivers.c b/quantum/pointing_device/pointing_device_drivers.c index 3780f3d2da..b96f8ff4b3 100644 --- a/quantum/pointing_device/pointing_device_drivers.c +++ b/quantum/pointing_device/pointing_device_drivers.c @@ -116,38 +116,35 @@ void cirque_pinnacle_configure_cursor_glide(float trigger_px) { } # endif +# if CIRQUE_PINNACLE_POSITION_MODE report_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) { pinnacle_data_t touchData = cirque_pinnacle_read_data(); mouse_xy_report_t report_x = 0, report_y = 0; static uint16_t x = 0, y = 0; -# ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE +# ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE cursor_glide_t glide_report = {0}; if (cursor_glide_enable) { glide_report = cursor_glide_check(&glide); } -# endif - -# if !CIRQUE_PINNACLE_POSITION_MODE -# error Cirque Pinnacle with relative mode not implemented yet. -# endif +# endif if (!touchData.valid) { -# ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE +# ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE if (cursor_glide_enable && glide_report.valid) { report_x = glide_report.dx; report_y = glide_report.dy; goto mouse_report_update; } -# endif +# endif return mouse_report; } -# if CONSOLE_ENABLE +# if CONSOLE_ENABLE if (debug_mouse && touchData.touchDown) { dprintf("cirque_pinnacle touchData x=%4d y=%4d z=%2d\n", touchData.xValue, touchData.yValue, touchData.zValue); } -# endif +# endif // Scale coordinates to arbitrary X, Y resolution cirque_pinnacle_scale_data(&touchData, cirque_pinnacle_get_scale(), cirque_pinnacle_get_scale()); @@ -160,7 +157,7 @@ report_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) { x = touchData.xValue; y = touchData.yValue; -# ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE +# ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE if (cursor_glide_enable) { if (touchData.touchDown) { cursor_glide_update(&glide, report_x, report_y, touchData.zValue); @@ -172,12 +169,12 @@ report_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) { } } } -# endif +# endif } -# ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE +# ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE mouse_report_update: -# endif +# endif mouse_report.x = report_x; mouse_report.y = report_y; @@ -199,6 +196,32 @@ const pointing_device_driver_t pointing_device_driver = { .get_cpi = cirque_pinnacle_get_cpi }; // clang-format on +# else +report_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) { + pinnacle_data_t touchData = cirque_pinnacle_read_data(); + + // Scale coordinates to arbitrary X, Y resolution + cirque_pinnacle_scale_data(&touchData, cirque_pinnacle_get_scale(), cirque_pinnacle_get_scale()); + + if (touchData.valid) { + mouse_report.buttons = touchData.buttons; + mouse_report.x = CONSTRAIN_HID_XY(touchData.xDelta); + mouse_report.y = CONSTRAIN_HID_XY(touchData.yDelta); + mouse_report.v = touchData.wheelCount; + } + return mouse_report; +} + +// clang-format off +const pointing_device_driver_t pointing_device_driver = { + .init = cirque_pinnacle_init, + .get_report = cirque_pinnacle_get_report, + .set_cpi = cirque_pinnacle_set_scale, + .get_cpi = cirque_pinnacle_get_scale +}; +// clang-format on +# endif + #elif defined(POINTING_DEVICE_DRIVER_paw3204) report_mouse_t paw3204_get_report(report_mouse_t mouse_report) { -- cgit v1.2.3 From 2bdd73f80135965c1047b24b78fdf15c1ebbbee7 Mon Sep 17 00:00:00 2001 From: David Hoelscher Date: Sun, 31 Jul 2022 21:58:25 -0500 Subject: Add ST7735 driver to Quantum Painter (#17848) --- quantum/painter/qp.h | 4 ++++ quantum/painter/rules.mk | 13 ++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) (limited to 'quantum') diff --git a/quantum/painter/qp.h b/quantum/painter/qp.h index 47f077d0cf..fb6904de22 100644 --- a/quantum/painter/qp.h +++ b/quantum/painter/qp.h @@ -448,6 +448,10 @@ int16_t qp_drawtext_recolor(painter_device_t device, uint16_t x, uint16_t y, pai # include "qp_st7789.h" #endif // QUANTUM_PAINTER_ST7789_ENABLE +#ifdef QUANTUM_PAINTER_ST7735_ENABLE +# include "qp_st7735.h" +#endif // QUANTUM_PAINTER_ST7735_ENABLE + #ifdef QUANTUM_PAINTER_GC9A01_ENABLE # include "qp_gc9a01.h" #endif // QUANTUM_PAINTER_GC9A01_ENABLE diff --git a/quantum/painter/rules.mk b/quantum/painter/rules.mk index 675a1a5460..91787dfe0e 100644 --- a/quantum/painter/rules.mk +++ b/quantum/painter/rules.mk @@ -3,7 +3,7 @@ QUANTUM_PAINTER_DRIVERS ?= QUANTUM_PAINTER_ANIMATIONS_ENABLE ?= yes # The list of permissible drivers that can be listed in QUANTUM_PAINTER_DRIVERS -VALID_QUANTUM_PAINTER_DRIVERS := ili9163_spi ili9341_spi ili9488_spi st7789_spi gc9a01_spi ssd1351_spi +VALID_QUANTUM_PAINTER_DRIVERS := ili9163_spi ili9341_spi ili9488_spi st7789_spi st7735_spi gc9a01_spi ssd1351_spi #------------------------------------------------------------------------------- @@ -83,6 +83,17 @@ define handle_quantum_painter_driver $(DRIVER_PATH)/painter/tft_panel/qp_tft_panel.c \ $(DRIVER_PATH)/painter/st77xx/qp_st7789.c + else ifeq ($$(strip $$(CURRENT_PAINTER_DRIVER)),st7735_spi) + QUANTUM_PAINTER_NEEDS_COMMS_SPI := yes + QUANTUM_PAINTER_NEEDS_COMMS_SPI_DC_RESET := yes + OPT_DEFS += -DQUANTUM_PAINTER_ST7735_ENABLE -DQUANTUM_PAINTER_ST7735_SPI_ENABLE + COMMON_VPATH += \ + $(DRIVER_PATH)/painter/tft_panel \ + $(DRIVER_PATH)/painter/st77xx + SRC += \ + $(DRIVER_PATH)/painter/tft_panel/qp_tft_panel.c \ + $(DRIVER_PATH)/painter/st77xx/qp_st7735.c + else ifeq ($$(strip $$(CURRENT_PAINTER_DRIVER)),gc9a01_spi) QUANTUM_PAINTER_NEEDS_COMMS_SPI := yes QUANTUM_PAINTER_NEEDS_COMMS_SPI_DC_RESET := yes -- cgit v1.2.3 From ed9bdcbc3608819e17ff7a11221e651bf51ec1cc Mon Sep 17 00:00:00 2001 From: Stefan Kerkmann Date: Sat, 6 Aug 2022 10:46:59 +0200 Subject: [Core] guard RPC invocation by checking RPC info against crc checksum (#17840) --- quantum/split_common/transactions.c | 18 ++++++++++++------ quantum/split_common/transport.h | 9 ++++++--- 2 files changed, 18 insertions(+), 9 deletions(-) (limited to 'quantum') diff --git a/quantum/split_common/transactions.c b/quantum/split_common/transactions.c index 9e3df534e3..719068908f 100644 --- a/quantum/split_common/transactions.c +++ b/quantum/split_common/transactions.c @@ -694,7 +694,7 @@ split_transaction_desc_t split_transaction_table[NUM_TOTAL_TRANSACTIONS] = { #if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) [PUT_RPC_INFO] = trans_initiator2target_initializer_cb(rpc_info, slave_rpc_info_callback), [PUT_RPC_REQ_DATA] = trans_initiator2target_initializer(rpc_m2s_buffer), - [EXECUTE_RPC] = trans_initiator2target_initializer_cb(rpc_info.transaction_id, slave_rpc_exec_callback), + [EXECUTE_RPC] = trans_initiator2target_initializer_cb(rpc_info.payload.transaction_id, slave_rpc_exec_callback), [GET_RPC_RESP_DATA] = trans_target2initiator_initializer(rpc_s2m_buffer), #endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) }; @@ -760,7 +760,8 @@ bool transaction_rpc_exec(int8_t transaction_id, uint8_t initiator2target_buffer if (target2initiator_buffer_size > RPC_S2M_BUFFER_SIZE) return false; // Prepare the metadata block - rpc_sync_info_t info = {.transaction_id = transaction_id, .m2s_length = initiator2target_buffer_size, .s2m_length = target2initiator_buffer_size}; + rpc_sync_info_t info = {.payload = {.transaction_id = transaction_id, .m2s_length = initiator2target_buffer_size, .s2m_length = target2initiator_buffer_size}}; + info.checksum = crc8(&info.payload, sizeof(info.payload)); // Make sure the local side knows that we're not sending the full block of data split_transaction_table[PUT_RPC_REQ_DATA].initiator2target_buffer_size = initiator2target_buffer_size; @@ -791,18 +792,23 @@ void slave_rpc_info_callback(uint8_t initiator2target_buffer_size, const void *i // Ignore the args -- the `split_shmem` already has the info, we just need to act upon it. // We must keep the `split_transaction_table` non-const, so that it is able to be modified at runtime. - split_transaction_table[PUT_RPC_REQ_DATA].initiator2target_buffer_size = split_shmem->rpc_info.m2s_length; - split_transaction_table[GET_RPC_RESP_DATA].target2initiator_buffer_size = split_shmem->rpc_info.s2m_length; + split_transaction_table[PUT_RPC_REQ_DATA].initiator2target_buffer_size = split_shmem->rpc_info.payload.m2s_length; + split_transaction_table[GET_RPC_RESP_DATA].target2initiator_buffer_size = split_shmem->rpc_info.payload.s2m_length; } void slave_rpc_exec_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer) { // We can assume that the buffer lengths are correctly set, now, given that sequentially the rpc_info callback was already executed. // Go through the rpc_info and execute _that_ transaction's callback, with the scratch buffers as inputs. - int8_t transaction_id = split_shmem->rpc_info.transaction_id; + // As a safety precaution we check that the received payload matches its checksum first. + if (crc8(&split_shmem->rpc_info.payload, sizeof(split_shmem->rpc_info.payload)) != split_shmem->rpc_info.checksum) { + return; + } + + int8_t transaction_id = split_shmem->rpc_info.payload.transaction_id; if (transaction_id < NUM_TOTAL_TRANSACTIONS) { split_transaction_desc_t *trans = &split_transaction_table[transaction_id]; if (trans->slave_callback) { - trans->slave_callback(split_shmem->rpc_info.m2s_length, split_shmem->rpc_m2s_buffer, split_shmem->rpc_info.s2m_length, split_shmem->rpc_s2m_buffer); + trans->slave_callback(split_shmem->rpc_info.payload.m2s_length, split_shmem->rpc_m2s_buffer, split_shmem->rpc_info.payload.s2m_length, split_shmem->rpc_s2m_buffer); } } } diff --git a/quantum/split_common/transport.h b/quantum/split_common/transport.h index e62679990a..06778ad14a 100644 --- a/quantum/split_common/transport.h +++ b/quantum/split_common/transport.h @@ -116,9 +116,12 @@ typedef struct _split_slave_pointing_sync_t { #if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) typedef struct _rpc_sync_info_t { - int8_t transaction_id; - uint8_t m2s_length; - uint8_t s2m_length; + uint8_t checksum; + struct { + int8_t transaction_id; + uint8_t m2s_length; + uint8_t s2m_length; + } payload; } rpc_sync_info_t; #endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) -- cgit v1.2.3 From f27b617f36d55ac5469247016a1b79304f892366 Mon Sep 17 00:00:00 2001 From: Stefan Kerkmann Date: Sat, 6 Aug 2022 12:51:13 +0200 Subject: [Core] Process all changed keys in one scan loop, deprecate `QMK_KEYS_PER_SCAN` (#15292) --- quantum/keyboard.c | 138 ++++++++++++++++++++++++++++------------------------- quantum/keyboard.h | 8 +++- 2 files changed, 81 insertions(+), 65 deletions(-) (limited to 'quantum') diff --git a/quantum/keyboard.c b/quantum/keyboard.c index 2364e3167b..1c62a43d9d 100644 --- a/quantum/keyboard.c +++ b/quantum/keyboard.c @@ -212,6 +212,12 @@ static inline bool has_ghost_in_row(uint8_t row, matrix_row_t rowdata) { return false; } +#else + +static inline bool has_ghost_in_row(uint8_t row, matrix_row_t rowdata) { + return false; +} + #endif /** \brief matrix_setup @@ -426,64 +432,74 @@ void switch_events(uint8_t row, uint8_t col, bool pressed) { #endif } -/** \brief Perform scan of keyboard matrix +/** + * @brief Generates a tick event at a maximum rate of 1KHz that drives the + * internal QMK state machine. + */ +static inline void generate_tick_event(void) { + static uint16_t last_tick = 0; + const uint16_t now = timer_read(); + if (TIMER_DIFF_16(now, last_tick) != 0) { + action_exec(TICK_EVENT); + last_tick = now; + } +} + +/** + * @brief This task scans the keyboards matrix and processes any key presses + * that occur. * - * Any detected changes in state are sent out as part of the processing + * @return true Matrix did change + * @return false Matrix didn't change */ -bool matrix_scan_task(void) { - static matrix_row_t matrix_prev[MATRIX_ROWS]; - matrix_row_t matrix_row = 0; - matrix_row_t matrix_change = 0; -#ifdef QMK_KEYS_PER_SCAN - uint8_t keys_processed = 0; -#endif +static bool matrix_task(void) { + static matrix_row_t matrix_previous[MATRIX_ROWS]; - uint8_t matrix_changed = matrix_scan(); - if (matrix_changed) last_matrix_activity_trigger(); + matrix_scan(); - for (uint8_t r = 0; r < MATRIX_ROWS; r++) { - matrix_row = matrix_get_row(r); - matrix_change = matrix_row ^ matrix_prev[r]; - if (matrix_change) { -#ifdef MATRIX_HAS_GHOST - if (has_ghost_in_row(r, matrix_row)) { - continue; - } -#endif - if (debug_matrix) matrix_print(); - matrix_row_t col_mask = 1; - for (uint8_t c = 0; c < MATRIX_COLS; c++, col_mask <<= 1) { - if (matrix_change & col_mask) { - if (should_process_keypress()) { - action_exec((keyevent_t){ - .key = (keypos_t){.row = r, .col = c}, .pressed = (matrix_row & col_mask), .time = (timer_read() | 1) /* time should not be 0 */ - }); - } - // record a processed key - matrix_prev[r] ^= col_mask; - - switch_events(r, c, (matrix_row & col_mask)); - -#ifdef QMK_KEYS_PER_SCAN - // only jump out if we have processed "enough" keys. - if (++keys_processed >= QMK_KEYS_PER_SCAN) -#endif - // process a key per task call - goto MATRIX_LOOP_END; + bool matrix_changed = false; + for (uint8_t row = 0; row < MATRIX_ROWS && !matrix_changed; row++) { + matrix_changed |= matrix_previous[row] ^ matrix_get_row(row); + } + + matrix_scan_perf_task(); + + // Short-circuit the complete matrix processing if it is not necessary + if (!matrix_changed) { + generate_tick_event(); + return matrix_changed; + } + + if (debug_config.matrix) { + matrix_print(); + } + + const bool process_keypress = should_process_keypress(); + + for (uint8_t row = 0; row < MATRIX_ROWS; row++) { + const matrix_row_t current_row = matrix_get_row(row); + const matrix_row_t row_changes = current_row ^ matrix_previous[row]; + + if (!row_changes || has_ghost_in_row(row, current_row)) { + continue; + } + + matrix_row_t col_mask = 1; + for (uint8_t col = 0; col < MATRIX_COLS; col++, col_mask <<= 1) { + if (row_changes & col_mask) { + const bool key_pressed = current_row & col_mask; + + if (process_keypress) { + action_exec(MAKE_KEYEVENT(row, col, key_pressed)); } + + switch_events(row, col, key_pressed); } } - } - // call with pseudo tick event when no real key event. -#ifdef QMK_KEYS_PER_SCAN - // we can get here with some keys processed now. - if (!keys_processed) -#endif - action_exec(TICK_EVENT); -MATRIX_LOOP_END: + matrix_previous[row] = current_row; + } - matrix_scan_perf_task(); return matrix_changed; } @@ -562,20 +578,12 @@ void quantum_task(void) { #endif } -/** \brief Keyboard task: Do keyboard routine jobs - * - * Do routine keyboard jobs: - * - * * scan matrix - * * handle mouse movements - * * handle midi commands - * * light LEDs - * - * This is repeatedly called as fast as possible. - */ +/** \brief Main task that is repeatedly called as fast as possible. */ void keyboard_task(void) { - bool matrix_changed = matrix_scan_task(); - (void)matrix_changed; + const bool matrix_changed = matrix_task(); + if (matrix_changed) { + last_matrix_activity_trigger(); + } quantum_task(); @@ -597,8 +605,10 @@ void keyboard_task(void) { #endif #ifdef ENCODER_ENABLE - bool encoders_changed = encoder_read(); - if (encoders_changed) last_encoder_activity_trigger(); + const bool encoders_changed = encoder_read(); + if (encoders_changed) { + last_encoder_activity_trigger(); + } #endif #ifdef OLED_ENABLE diff --git a/quantum/keyboard.h b/quantum/keyboard.h index fe0736a515..86ce65aac1 100644 --- a/quantum/keyboard.h +++ b/quantum/keyboard.h @@ -71,9 +71,15 @@ static inline bool IS_RELEASED(keyevent_t event) { /* Common keyevent object factory */ #define MAKE_KEYPOS(row_num, col_num) ((keypos_t){.row = (row_num), .col = (col_num)}) + +/** + * @brief Constructs a key event for a pressed or released key. + */ #define MAKE_KEYEVENT(row_num, col_num, press) ((keyevent_t){.key = MAKE_KEYPOS((row_num), (col_num)), .pressed = (press), .time = (timer_read() | 1)}) -/* Tick event */ +/** + * @brief Constructs a internal tick event that is used to drive the internal QMK state machine. + */ #define TICK_EVENT MAKE_KEYEVENT(KEYLOC_TICK, KEYLOC_TICK, false) #ifdef ENCODER_MAP_ENABLE -- cgit v1.2.3 From ac25109312a49b6b50e54d61430c959652307810 Mon Sep 17 00:00:00 2001 From: Drashna Jaelre Date: Sun, 7 Aug 2022 02:18:03 -0700 Subject: Always run pointing device init (#17936) --- quantum/pointing_device/pointing_device.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'quantum') diff --git a/quantum/pointing_device/pointing_device.c b/quantum/pointing_device/pointing_device.c index 09f9e5efb7..505a7a6ffd 100644 --- a/quantum/pointing_device/pointing_device.c +++ b/quantum/pointing_device/pointing_device.c @@ -139,14 +139,15 @@ __attribute__((weak)) uint8_t pointing_device_handle_buttons(uint8_t buttons, bo */ __attribute__((weak)) void pointing_device_init(void) { #if defined(SPLIT_POINTING_ENABLE) - if (!(POINTING_DEVICE_THIS_SIDE)) { - return; - } + if ((POINTING_DEVICE_THIS_SIDE)) #endif - pointing_device_driver.init(); + { + pointing_device_driver.init(); #ifdef POINTING_DEVICE_MOTION_PIN - setPinInputHigh(POINTING_DEVICE_MOTION_PIN); + setPinInputHigh(POINTING_DEVICE_MOTION_PIN); #endif + } + pointing_device_init_kb(); pointing_device_init_user(); } -- cgit v1.2.3 From 5ecd745514c4832f1082a81d30a209e47513aded Mon Sep 17 00:00:00 2001 From: Takeshi ISHII <2170248+mtei@users.noreply.github.com> Date: Mon, 8 Aug 2022 04:32:59 +0900 Subject: fix comment space code in quantum/matrix_common.c (#17942) --- quantum/matrix_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'quantum') diff --git a/quantum/matrix_common.c b/quantum/matrix_common.c index 2cf8484347..3000d3eb70 100644 --- a/quantum/matrix_common.c +++ b/quantum/matrix_common.c @@ -122,7 +122,7 @@ bool matrix_post_scan(void) { } #endif -/* `matrix_io_delay ()` exists for backwards compatibility. From now on, use matrix_output_unselect_delay(). */ +/* `matrix_io_delay ()` exists for backwards compatibility. From now on, use matrix_output_unselect_delay(). */ __attribute__((weak)) void matrix_io_delay(void) { wait_us(MATRIX_IO_DELAY); } -- cgit v1.2.3 From cde9dd8b90ed41bd3f960833b7ceab891baf7560 Mon Sep 17 00:00:00 2001 From: Albert Y <76888457+filterpaper@users.noreply.github.com> Date: Fri, 12 Aug 2022 05:41:42 +0800 Subject: Refactor Pixel Fractal effect (#17602) * Refactor effect with smaller array * Add RGB_MATRIX_USE_LIMITS call * Remove spaces Co-authored-by: Drashna Jaelre Co-authored-by: Drashna Jaelre --- quantum/rgb_matrix/animations/pixel_fractal_anim.h | 78 ++++++++-------------- 1 file changed, 28 insertions(+), 50 deletions(-) (limited to 'quantum') diff --git a/quantum/rgb_matrix/animations/pixel_fractal_anim.h b/quantum/rgb_matrix/animations/pixel_fractal_anim.h index 906da1a48e..b26d5c4100 100644 --- a/quantum/rgb_matrix/animations/pixel_fractal_anim.h +++ b/quantum/rgb_matrix/animations/pixel_fractal_anim.h @@ -1,19 +1,5 @@ -/* Copyright (C) 2021 @filterpaper - * - * 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 . - */ - +// Copyright (C) 2022 @filterpaper +// SPDX-License-Identifier: GPL-2.0-or-later // Inspired from 4x12 fractal created by @schwarzgrau #ifdef ENABLE_RGB_MATRIX_PIXEL_FRACTAL @@ -22,12 +8,8 @@ RGB_MATRIX_EFFECT(PIXEL_FRACTAL) static bool PIXEL_FRACTAL(effect_params_t* params) { # define MID_COL MATRIX_COLS / 2 - static bool led[MATRIX_ROWS][MATRIX_COLS]; - + static bool led[MATRIX_ROWS][MID_COL]; static uint32_t wait_timer = 0; - if (wait_timer > g_rgb_timer) { - return false; - } inline uint32_t interval(void) { return 3000 / scale16by8(qadd8(rgb_matrix_config.speed, 16), 16); @@ -37,44 +19,40 @@ static bool PIXEL_FRACTAL(effect_params_t* params) { rgb_matrix_set_color_all(0, 0, 0); } - RGB rgb = rgb_matrix_hsv_to_rgb(rgb_matrix_config.hsv); - for (uint8_t h = 0; h < MATRIX_ROWS; ++h) { - for (uint8_t l = 0; l < MID_COL - 1; ++l) { // Light and move left columns outwards - if (led[h][l]) { - rgb_matrix_set_color(g_led_config.matrix_co[h][l], rgb.r, rgb.g, rgb.b); - } else { - rgb_matrix_set_color(g_led_config.matrix_co[h][l], 0, 0, 0); + RGB_MATRIX_USE_LIMITS(led_min, led_max); + + if (g_rgb_timer > wait_timer) { + RGB rgb = rgb_matrix_hsv_to_rgb(rgb_matrix_config.hsv); + for (uint8_t h = 0; h < MATRIX_ROWS; ++h) { + // Light and copy columns outward + for (uint8_t l = 0; l < MID_COL - 1; ++l) { + if (led[h][l]) { + rgb_matrix_set_color(g_led_config.matrix_co[h][l], rgb.r, rgb.g, rgb.b); + rgb_matrix_set_color(g_led_config.matrix_co[h][MATRIX_COLS - 1 - l], rgb.r, rgb.g, rgb.b); + } else { + rgb_matrix_set_color(g_led_config.matrix_co[h][l], 0, 0, 0); + rgb_matrix_set_color(g_led_config.matrix_co[h][MATRIX_COLS - 1 - l], 0, 0, 0); + } + led[h][l] = led[h][l + 1]; } - led[h][l] = led[h][l + 1]; - } - for (uint8_t r = MATRIX_COLS - 1; r > MID_COL; --r) { // Light and move right columns outwards - if (led[h][r]) { - rgb_matrix_set_color(g_led_config.matrix_co[h][r], rgb.r, rgb.g, rgb.b); + // Light both middle columns + if (led[h][MID_COL - 1]) { + rgb_matrix_set_color(g_led_config.matrix_co[h][MID_COL - 1], rgb.r, rgb.g, rgb.b); + rgb_matrix_set_color(g_led_config.matrix_co[h][MATRIX_COLS - MID_COL], rgb.r, rgb.g, rgb.b); } else { - rgb_matrix_set_color(g_led_config.matrix_co[h][r], 0, 0, 0); + rgb_matrix_set_color(g_led_config.matrix_co[h][MID_COL - 1], 0, 0, 0); + rgb_matrix_set_color(g_led_config.matrix_co[h][MATRIX_COLS - MID_COL], 0, 0, 0); } - led[h][r] = led[h][r - 1]; - } - // Light both middle columns - if (led[h][MID_COL]) { - rgb_matrix_set_color(g_led_config.matrix_co[h][MID_COL], rgb.r, rgb.g, rgb.b); - } else { - rgb_matrix_set_color(g_led_config.matrix_co[h][MID_COL], 0, 0, 0); - } - if (led[h][MID_COL - 1]) { - rgb_matrix_set_color(g_led_config.matrix_co[h][MID_COL - 1], rgb.r, rgb.g, rgb.b); - } else { - rgb_matrix_set_color(g_led_config.matrix_co[h][MID_COL - 1], 0, 0, 0); + // Generate new random fractal column + led[h][MID_COL - 1] = (random8() & 3) ? false : true; } - // Generate new random fractal columns - led[h][MID_COL] = led[h][MID_COL - 1] = (random8() & 3) ? false : true; + wait_timer = g_rgb_timer + interval(); } - wait_timer = g_rgb_timer + interval(); - return false; + return rgb_matrix_check_finished_leds(led_max); } # endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS #endif // ENABLE_RGB_MATRIX_PIXEL_FRACTAL -- cgit v1.2.3 From 7e7387c0d140c36b1b0058f8aa20f49a5959d535 Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Fri, 12 Aug 2022 03:03:36 +0100 Subject: Remove legacy keycode use from command (#18002) --- quantum/command.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'quantum') diff --git a/quantum/command.h b/quantum/command.h index 676507d3bd..a63f9ec7a7 100644 --- a/quantum/command.h +++ b/quantum/command.h @@ -146,7 +146,7 @@ bool command_proc(uint8_t code); #endif #ifndef MAGIC_KEY_EEPROM_CLEAR -# define MAGIC_KEY_EEPROM_CLEAR BSPACE +# define MAGIC_KEY_EEPROM_CLEAR BACKSPACE #endif #ifndef MAGIC_KEY_NKRO -- cgit v1.2.3 From db4c4b1f7868a99c094ee277f7b17ffffe2fea7b Mon Sep 17 00:00:00 2001 From: precondition <57645186+precondition@users.noreply.github.com> Date: Sat, 13 Aug 2022 04:13:22 +0200 Subject: =?UTF-8?q?Fix=20B=C3=A9po's=20BP=5FNNBS=20(narrow=20non-breaking?= =?UTF-8?q?=20space)=20(#17999)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ryan --- quantum/keymap_extras/keymap_bepo.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'quantum') diff --git a/quantum/keymap_extras/keymap_bepo.h b/quantum/keymap_extras/keymap_bepo.h index 72d5b81f32..6361823242 100644 --- a/quantum/keymap_extras/keymap_bepo.h +++ b/quantum/keymap_extras/keymap_bepo.h @@ -237,4 +237,4 @@ #define BP_DDAG S(ALGR(BP_H)) // ‡ #define BP_FORD S(ALGR(BP_F)) // ª // Row 5 -#define BP_NNBS S(ALGR(BP_)) //   (narrow non-breaking space) +#define BP_NNBS S(ALGR(KC_SPC)) //   (narrow non-breaking space) -- cgit v1.2.3 From b4fbb340c51dc4353ec821a70adc529698549ef0 Mon Sep 17 00:00:00 2001 From: precondition <57645186+precondition@users.noreply.github.com> Date: Sat, 13 Aug 2022 04:13:43 +0200 Subject: Use LT_ZCAR in place of LT_PLUS for modded kc definitions (#18000) --- quantum/keymap_extras/keymap_lithuanian_qwerty.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'quantum') diff --git a/quantum/keymap_extras/keymap_lithuanian_qwerty.h b/quantum/keymap_extras/keymap_lithuanian_qwerty.h index 2dbb3649f7..fb0468a20a 100644 --- a/quantum/keymap_extras/keymap_lithuanian_qwerty.h +++ b/quantum/keymap_extras/keymap_lithuanian_qwerty.h @@ -137,7 +137,7 @@ #define LT_6 ALGR(LT_SCAR) // 6 #define LT_7 ALGR(LT_UOGO) // 7 #define LT_8 ALGR(LT_UMAC) // 8 -#define LT_EQL ALGR(LT_PLUS) // = +#define LT_EQL ALGR(LT_ZCAR) // = // Row 2 #define LT_EURO ALGR(LT_E) // € @@ -163,4 +163,4 @@ #define LT_CIRC S(ALGR(LT_SCAR)) // ^ #define LT_AMPR S(ALGR(LT_UOGO)) // & #define LT_ASTR S(ALGR(LT_UMAC)) // * -#define LT_PLUS S(ALGR(LT_PLUS)) // + +#define LT_PLUS S(ALGR(LT_ZCAR)) // + -- cgit v1.2.3 From 20f0f402207e38d074aa4ca713fcc0d9c2e391c9 Mon Sep 17 00:00:00 2001 From: precondition <57645186+precondition@users.noreply.github.com> Date: Sat, 13 Aug 2022 04:16:50 +0200 Subject: Remove invisible variation selector-15 from keymap_japanese.h (#18007) --- quantum/keymap_extras/keymap_japanese.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'quantum') diff --git a/quantum/keymap_extras/keymap_japanese.h b/quantum/keymap_extras/keymap_japanese.h index d10feb5856..df78af8399 100644 --- a/quantum/keymap_extras/keymap_japanese.h +++ b/quantum/keymap_extras/keymap_japanese.h @@ -28,7 +28,7 @@ /* * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ - * │Z↔︎H│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ ^ │ ¥ │ │ + * │Z↔H│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ ^ │ ¥ │ │ * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤ * │ │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ @ │ [ │ │ * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ │ @@ -40,7 +40,7 @@ * └─────┴───┴─────┴─────┴─────────┴─────┴───┴───┴───┴───┴─────┘ */ // Row 1 -#define JP_ZKHK KC_GRV // Zenkaku ↔︎ Hankaku ↔ Kanji (半角 ↔ 全角 ↔ 漢字) +#define JP_ZKHK KC_GRV // Zenkaku ↔ Hankaku ↔ Kanji (半角 ↔ 全角 ↔ 漢字) #define JP_1 KC_1 // 1 #define JP_2 KC_2 // 2 #define JP_3 KC_3 // 3 -- cgit v1.2.3 From e3415d6b973868e99eef83a9bb92726eba796e4f Mon Sep 17 00:00:00 2001 From: precondition <57645186+precondition@users.noreply.github.com> Date: Sat, 13 Aug 2022 04:20:35 +0200 Subject: =?UTF-8?q?define=20CZ=5FPERC=20S(CZ=5FPLUS)=20=E2=86=92=20define?= =?UTF-8?q?=20CZ=5FPERC=20S(CZ=5FEQL)=20(#18008)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- quantum/keymap_extras/keymap_czech.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'quantum') diff --git a/quantum/keymap_extras/keymap_czech.h b/quantum/keymap_extras/keymap_czech.h index 2b1fb5da29..a8f522d31c 100644 --- a/quantum/keymap_extras/keymap_czech.h +++ b/quantum/keymap_extras/keymap_czech.h @@ -111,7 +111,7 @@ #define CZ_8 S(CZ_AACU) // 8 #define CZ_9 S(CZ_IACU) // 9 #define CZ_0 S(CZ_EACU) // 0 -#define CZ_PERC S(CZ_PLUS) // % +#define CZ_PERC S(CZ_EQL) // % #define CZ_CARN S(CZ_ACUT) // ˇ (dead) // Row 2 #define CZ_SLSH S(CZ_UACU) // / -- cgit v1.2.3 From 4ff39b5652b7f3338ac6e046e784da7a3d351d58 Mon Sep 17 00:00:00 2001 From: precondition <57645186+precondition@users.noreply.github.com> Date: Sat, 13 Aug 2022 04:20:58 +0200 Subject: =?UTF-8?q?KR=5FDQUO=20S(KR=5FCOLN)=20=E2=86=92=20KR=5FDQUO=20S(KR?= =?UTF-8?q?=5FQUOT)=20(#18011)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- quantum/keymap_extras/keymap_korean.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'quantum') diff --git a/quantum/keymap_extras/keymap_korean.h b/quantum/keymap_extras/keymap_korean.h index 74be122dad..3d41a98b88 100644 --- a/quantum/keymap_extras/keymap_korean.h +++ b/quantum/keymap_extras/keymap_korean.h @@ -121,7 +121,7 @@ #define KR_PIPE S(KR_WON) // | // Row 3 #define KR_COLN S(KR_SCLN) // : -#define KR_DQUO S(KR_COLN) // " +#define KR_DQUO S(KR_QUOT) // " // Row 4 #define KR_LABK S(KR_COMM) // < #define KR_RABK S(KR_DOT) // > -- cgit v1.2.3 From a02aff9c77c15ca9e248f84b09a88386a8f4b0a6 Mon Sep 17 00:00:00 2001 From: lokher Date: Sat, 13 Aug 2022 21:39:06 +0800 Subject: Add led matrix support for CKLED2001 (#17643) --- quantum/led_matrix/led_matrix.h | 3 ++ quantum/led_matrix/led_matrix_drivers.c | 51 ++++++++++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 1 deletion(-) (limited to 'quantum') diff --git a/quantum/led_matrix/led_matrix.h b/quantum/led_matrix/led_matrix.h index b2abec7eb1..446f293c78 100644 --- a/quantum/led_matrix/led_matrix.h +++ b/quantum/led_matrix/led_matrix.h @@ -33,6 +33,9 @@ #ifdef IS31FL3733 # include "is31fl3733-simple.h" #endif +#ifdef CKLED2001 +# include "ckled2001-simple.h" +#endif #ifndef LED_MATRIX_LED_FLUSH_LIMIT # define LED_MATRIX_LED_FLUSH_LIMIT 16 diff --git a/quantum/led_matrix/led_matrix_drivers.c b/quantum/led_matrix/led_matrix_drivers.c index 847ca1c310..f01b395c15 100644 --- a/quantum/led_matrix/led_matrix_drivers.c +++ b/quantum/led_matrix/led_matrix_drivers.c @@ -25,7 +25,7 @@ * in their own files. */ -#if defined(IS31FL3731) || defined(IS31FL3733) || defined(IS31FLCOMMON) +#if defined(IS31FL3731) || defined(IS31FL3733) || defined(IS31FLCOMMON) || defined(CKLED2001) # include "i2c_master.h" static void init(void) { @@ -78,6 +78,22 @@ static void init(void) { # endif # endif # endif +# elif defined(CKLED2001) +# if defined(LED_DRIVER_SHUTDOWN_PIN) + setPinOutput(LED_DRIVER_SHUTDOWN_PIN); + writePinHigh(LED_DRIVER_SHUTDOWN_PIN); +# endif + + CKLED2001_init(DRIVER_ADDR_1); +# if defined(DRIVER_ADDR_2) + CKLED2001_init(DRIVER_ADDR_2); +# if defined(DRIVER_ADDR_3) + CKLED2001_init(DRIVER_ADDR_3); +# if defined(DRIVER_ADDR_4) + CKLED2001_init(DRIVER_ADDR_4); +# endif +# endif +# endif # endif for (int index = 0; index < DRIVER_LED_TOTAL; index++) { @@ -87,6 +103,8 @@ static void init(void) { IS31FL3733_set_led_control_register(index, true); # elif defined(IS31FLCOMMON) IS31FL_simple_set_scaling_buffer(index, true); +# elif defined(CKLED2001) + CKLED2001_set_led_control_register(index, true); # endif } @@ -129,6 +147,17 @@ static void init(void) { # endif # endif # endif +# elif defined(CKLED2001) + CKLED2001_update_led_control_registers(DRIVER_ADDR_1, 0); +# if defined(DRIVER_ADDR_2) + CKLED2001_update_led_control_registers(DRIVER_ADDR_2, 1); +# if defined(DRIVER_ADDR_3) + CKLED2001_update_led_control_registers(DRIVER_ADDR_3, 2); +# if defined(DRIVER_ADDR_4) + CKLED2001_update_led_control_registers(DRIVER_ADDR_4, 3); +# endif +# endif +# endif # endif } @@ -194,5 +223,25 @@ const led_matrix_driver_t led_matrix_driver = { .set_value = IS31FL_simple_set_brightness, .set_value_all = IS31FL_simple_set_brigntness_all, }; +# elif defined(CKLED2001) +static void flush(void) { + CKLED2001_update_pwm_buffers(DRIVER_ADDR_1, 0); +# if defined(DRIVER_ADDR_2) + CKLED2001_update_pwm_buffers(DRIVER_ADDR_2, 1); +# if defined(DRIVER_ADDR_3) + CKLED2001_update_pwm_buffers(DRIVER_ADDR_3, 2); +# if defined(DRIVER_ADDR_4) + CKLED2001_update_pwm_buffers(DRIVER_ADDR_4, 3); +# endif +# endif +# endif +} + +const led_matrix_driver_t led_matrix_driver = { + .init = init, + .flush = flush, + .set_value = CKLED2001_set_value, + .set_value_all = CKLED2001_set_value_all, +}; # endif #endif -- 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') 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 96731ba88e84e0cdd8144e6009fb086a9ccce743 Mon Sep 17 00:00:00 2001 From: precondition <57645186+precondition@users.noreply.github.com> Date: Sat, 13 Aug 2022 19:24:15 +0200 Subject: Fix LV_CCAR and LV_NCED (#18025) --- quantum/keymap_extras/keymap_latvian.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'quantum') diff --git a/quantum/keymap_extras/keymap_latvian.h b/quantum/keymap_extras/keymap_latvian.h index 3eaa2df274..087138cb72 100644 --- a/quantum/keymap_extras/keymap_latvian.h +++ b/quantum/keymap_extras/keymap_latvian.h @@ -161,8 +161,8 @@ #define LV_ACUT ALGR(LV_QUOT) // ´ (dead) // Row 4 #define LV_ZCAR ALGR(LV_Z) // Ž -#define LV_CCAR ALGR(LV_Z) // Č -#define LV_NCED ALGR(LV_Z) // Ņ +#define LV_CCAR ALGR(LV_C) // Č +#define LV_NCED ALGR(LV_N) // Ņ /* Shift+AltGr symbols * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ -- cgit v1.2.3 From 2c3b447641821c12b967058be6442fcfbc6c27d1 Mon Sep 17 00:00:00 2001 From: precondition <57645186+precondition@users.noreply.github.com> Date: Sun, 14 Aug 2022 02:50:32 +0200 Subject: Use ANSI ASCII art and fix comments for LT_COLN and LT_UNDS in keymap_lithuanian_qwerty.h (#18028) --- quantum/keymap_extras/keymap_lithuanian_qwerty.h | 34 ++++++++++++------------ 1 file changed, 17 insertions(+), 17 deletions(-) (limited to 'quantum') diff --git a/quantum/keymap_extras/keymap_lithuanian_qwerty.h b/quantum/keymap_extras/keymap_lithuanian_qwerty.h index fb0468a20a..afca2dc750 100644 --- a/quantum/keymap_extras/keymap_lithuanian_qwerty.h +++ b/quantum/keymap_extras/keymap_lithuanian_qwerty.h @@ -24,10 +24,10 @@ * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ * │ ` │ Ą │ Č │ Ę │ Ė │ Į │ Š │ Ų │ Ū │ 9 │ 0 │ - │ Ž │ │ * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ - * │ │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │ │ - * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ │ - * │ │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ \ │ │ - * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤ + * │ │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │ \ │ + * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ + * │ │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ │ + * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤ * │ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ │ * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤ * │ │ │ │ │ │ │ │ │ @@ -72,7 +72,7 @@ #define LT_L KC_L // L #define LT_SCLN KC_SCLN // ; #define LT_QUOT KC_QUOT // ' -#define LT_BSLS KC_NUHS // (backslash) +#define LT_BSLS KC_BSLS // (backslash) // Row 4 #define LT_Z KC_Z // Z #define LT_X KC_X // X @@ -89,10 +89,10 @@ * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ * │ ~ │ │ │ │ │ │ │ │ │ ( │ ) │ _ │ │ │ * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ - * │ │ │ │ │ │ │ │ │ │ │ │ { │ } │ │ - * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ │ - * │ │ │ │ │ │ │ │ │ │ │ : │ " │ | │ │ - * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤ + * │ │ │ │ │ │ │ │ │ │ │ │ { │ } │ | │ + * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ + * │ │ │ │ │ │ │ │ │ │ │ : │ " │ │ + * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤ * │ │ │ │ │ │ │ │ │ < │ > │ ? │ │ * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤ * │ │ │ │ │ │ │ │ │ @@ -102,12 +102,12 @@ #define LT_TILD S(LT_GRV) // ~ #define LT_LPRN S(LT_9) // ( #define LT_RPRN S(LT_0) // ) -#define LT_UNDS S(LT_MINS) // * +#define LT_UNDS S(LT_MINS) // _ // Row 2 #define LT_LCBR S(LT_LBRC) // { #define LT_RCBR S(LT_RBRC) // } // Row 3 -#define LT_COLN S(LT_SCLN) // ; +#define LT_COLN S(LT_SCLN) // : #define LT_DQUO S(LT_QUOT) // " #define LT_PIPE S(LT_BSLS) // | // Row 4 @@ -120,9 +120,9 @@ * │ │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ │ │ │ = │ │ * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ * │ │ │ │ € │ │ │ │ │ │ │ │ │ │ │ - * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ │ - * │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ - * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤ + * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ + * │ │ │ │ │ │ │ │ │ │ │ │ │ │ + * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤ * │ │ │ │ │ │ │ │ │ │ │ │ │ * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤ * │ │ │ │ │ │ │ │ │ @@ -146,9 +146,9 @@ * │ │ ! │ @ │ # │ $ │ % │ ^ │ & │ * │ │ │ │ + │ │ * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ * │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ - * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ │ - * │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ - * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤ + * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ + * │ │ │ │ │ │ │ │ │ │ │ │ │ │ + * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤ * │ │ │ │ │ │ │ │ │ │ │ │ │ * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤ * │ │ │ │ │ │ │ │ │ -- cgit v1.2.3 From ba04ecfabd4f254bb89ccd7d1de9ac7fb228ce5b Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Sun, 14 Aug 2022 12:25:46 +0100 Subject: Align TO() max layers with other keycodes (#17989) --- quantum/action_code.h | 1 + quantum/keymap_common.c | 8 +++----- quantum/quantum_keycodes.h | 11 ++--------- 3 files changed, 6 insertions(+), 14 deletions(-) (limited to 'quantum') diff --git a/quantum/action_code.h b/quantum/action_code.h index 20b3e459d2..e107f0a740 100644 --- a/quantum/action_code.h +++ b/quantum/action_code.h @@ -234,6 +234,7 @@ enum layer_param_tap_op { #define ACTION_LAYER_INVERT(layer, on) ACTION_LAYER_BIT_XOR((layer) / 4, 1 << ((layer) % 4), (on)) #define ACTION_LAYER_ON(layer, on) ACTION_LAYER_BIT_OR((layer) / 4, 1 << ((layer) % 4), (on)) #define ACTION_LAYER_OFF(layer, on) ACTION_LAYER_BIT_AND((layer) / 4, ~(1 << ((layer) % 4)), (on)) +#define ACTION_LAYER_GOTO(layer) ACTION_LAYER_SET(layer, ON_PRESS) #define ACTION_LAYER_SET(layer, on) ACTION_LAYER_BIT_SET((layer) / 4, 1 << ((layer) % 4), (on)) #define ACTION_LAYER_ON_OFF(layer) ACTION_LAYER_TAP((layer), OP_ON_OFF) #define ACTION_LAYER_OFF_ON(layer) ACTION_LAYER_TAP((layer), OP_OFF_ON) diff --git a/quantum/keymap_common.c b/quantum/keymap_common.c index c1940f0fd3..8d7a8bda9a 100644 --- a/quantum/keymap_common.c +++ b/quantum/keymap_common.c @@ -47,10 +47,9 @@ action_t action_for_keycode(uint16_t keycode) { keycode = keycode_config(keycode); action_t action = {}; - uint8_t action_layer, when, mod; + uint8_t action_layer, mod; (void)action_layer; - (void)when; (void)mod; switch (keycode) { @@ -85,9 +84,8 @@ action_t action_for_keycode(uint16_t keycode) { break; case QK_TO ... QK_TO_MAX:; // Layer set "GOTO" - when = (keycode >> 0x4) & 0x3; - action_layer = keycode & 0xF; - action.code = ACTION_LAYER_SET(action_layer, when); + action_layer = keycode & 0xFF; + action.code = ACTION_LAYER_GOTO(action_layer); break; case QK_MOMENTARY ... QK_MOMENTARY_MAX:; // Momentary action_layer diff --git a/quantum/quantum_keycodes.h b/quantum/quantum_keycodes.h index 456fad6f1b..7228ee9e08 100644 --- a/quantum/quantum_keycodes.h +++ b/quantum/quantum_keycodes.h @@ -797,15 +797,8 @@ enum quantum_keycodes { #define EH_LEFT MAGIC_EE_HANDS_LEFT #define EH_RGHT MAGIC_EE_HANDS_RIGHT -// GOTO layer - 16 layers max -// when: -// ON_PRESS = 1 -// ON_RELEASE = 2 -// Unless you have a good reason not to do so, prefer ON_PRESS (1) as your default. -// In fact, we changed it to assume ON_PRESS for sanity/simplicity. If needed, you can add your own -// keycode modeled after the old version, kept below for this. -/* #define TO(layer, when) (QK_TO | (when << 0x4) | (layer & 0xFF)) */ -#define TO(layer) (QK_TO | (ON_PRESS << 0x4) | ((layer)&0xFF)) +// GOTO layer - 256 layer max +#define TO(layer) (QK_TO | ((layer)&0xFF)) // Momentary switch layer - 256 layer max #define MO(layer) (QK_MOMENTARY | ((layer)&0xFF)) -- cgit v1.2.3 From 0a6327d20c9cb63c80da2aff6e84c5b2d62c66e3 Mon Sep 17 00:00:00 2001 From: precondition <57645186+precondition@users.noreply.github.com> Date: Sun, 14 Aug 2022 18:18:42 +0200 Subject: Replace ; by : in the shifted symbols ASCII art of keymap_norman (#18029) Thanks! --- quantum/keymap_extras/keymap_norman.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'quantum') diff --git a/quantum/keymap_extras/keymap_norman.h b/quantum/keymap_extras/keymap_norman.h index 7a5c1cbeb2..9f20f66873 100644 --- a/quantum/keymap_extras/keymap_norman.h +++ b/quantum/keymap_extras/keymap_norman.h @@ -89,7 +89,7 @@ * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ * │ ~ │ ! │ @ │ # │ $ │ % │ ^ │ & │ * │ ( │ ) │ _ │ + │ │ * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ - * │ │ │ │ │ │ │ │ │ │ │ ; │ { │ } │ | │ + * │ │ │ │ │ │ │ │ │ │ │ : │ { │ } │ | │ * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ * │ │ │ │ │ │ │ │ │ │ │ │ " │ │ * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤ -- 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 + quantum/quantum_keycodes.h | 3 ++ 3 files changed, 38 insertions(+), 6 deletions(-) (limited to 'quantum') 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) }; diff --git a/quantum/quantum_keycodes.h b/quantum/quantum_keycodes.h index 7228ee9e08..c8f03fa1ce 100644 --- a/quantum/quantum_keycodes.h +++ b/quantum/quantum_keycodes.h @@ -609,6 +609,8 @@ enum quantum_keycodes { MAGIC_UNSWAP_ESCAPE_CAPSLOCK, MAGIC_TOGGLE_ESCAPE_CAPSLOCK, + UNICODE_MODE_EMACS, + // Start of custom keycode range for keyboards and keymaps - always leave at the end SAFE_RANGE }; @@ -894,6 +896,7 @@ enum quantum_keycodes { #define UC_M_WI UNICODE_MODE_WIN #define UC_M_BS UNICODE_MODE_BSD #define UC_M_WC UNICODE_MODE_WINC +#define UC_M_EM UNICODE_MODE_EMACS // Swap Hands #define SH_T(kc) (QK_SWAP_HANDS | (kc)) -- 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') 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 8e9ee29fe352b98ee1380213fc2e2b0e945cdf3f Mon Sep 17 00:00:00 2001 From: precondition <57645186+precondition@users.noreply.github.com> Date: Mon, 15 Aug 2022 07:25:37 +0200 Subject: Remove duplicate COMBINING HORN in keymap_us_extended.h (#18045) --- quantum/keymap_extras/keymap_us_extended.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'quantum') diff --git a/quantum/keymap_extras/keymap_us_extended.h b/quantum/keymap_extras/keymap_us_extended.h index fb3e9c7d36..8e71a8de25 100644 --- a/quantum/keymap_extras/keymap_us_extended.h +++ b/quantum/keymap_extras/keymap_us_extended.h @@ -145,7 +145,7 @@ #define US_CURR ALGR(US_4) // ¤ #define US_EURO ALGR(US_5) // € #define US_DCIR ALGR(US_6) // ^ (dead) -#define US_HORN ALGR(US_7) // ̛̛ (dead) +#define US_HORN ALGR(US_7) // ̛ (dead) #define US_OGON ALGR(US_8) // ˛ (dead) #define US_LSQU ALGR(US_9) // ‘ #define US_RSQU ALGR(US_0) // ’ -- cgit v1.2.3 From 8ce946b5c8e7026b5d7337becf4719e2795af9bb Mon Sep 17 00:00:00 2001 From: Stefan Kerkmann Date: Mon, 15 Aug 2022 16:40:51 +0200 Subject: [Bug] Add key event check to `is_tap_record` and remove `is_tap_key` (#18063) --- quantum/action.c | 13 ++++--------- quantum/action.h | 1 - 2 files changed, 4 insertions(+), 10 deletions(-) (limited to 'quantum') diff --git a/quantum/action.c b/quantum/action.c index 83f6e2a970..6b2e9104e0 100644 --- a/quantum/action.c +++ b/quantum/action.c @@ -1085,20 +1085,15 @@ void clear_keyboard_but_mods_and_keys() { #endif } -/** \brief Utilities for actions. (FIXME: Needs better description) - * - * FIXME: Needs documentation. - */ -bool is_tap_key(keypos_t key) { - action_t action = layer_switch_get_action(key); - return is_tap_action(action); -} - /** \brief Utilities for actions. (FIXME: Needs better description) * * FIXME: Needs documentation. */ bool is_tap_record(keyrecord_t *record) { + if (IS_NOEVENT(record->event)) { + return false; + } + #ifdef COMBO_ENABLE action_t action; if (record->keycode) { diff --git a/quantum/action.h b/quantum/action.h index 08e1f6ac29..2bc46429b2 100644 --- a/quantum/action.h +++ b/quantum/action.h @@ -105,7 +105,6 @@ void clear_keyboard(void); void clear_keyboard_but_mods(void); void clear_keyboard_but_mods_and_keys(void); void layer_switch(uint8_t new_layer); -bool is_tap_key(keypos_t key); bool is_tap_record(keyrecord_t *record); bool is_tap_action(action_t action); -- cgit v1.2.3 From 5021cf58ade469560fa0fd018f6b35b2b6f6907f Mon Sep 17 00:00:00 2001 From: precondition <57645186+precondition@users.noreply.github.com> Date: Mon, 15 Aug 2022 19:41:05 +0200 Subject: Fix DV_SCLN and DV_COLN in keymap_spanish_dvorak.h (#18043) --- quantum/keymap_extras/keymap_spanish_dvorak.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'quantum') diff --git a/quantum/keymap_extras/keymap_spanish_dvorak.h b/quantum/keymap_extras/keymap_spanish_dvorak.h index 29c4f1c44a..ea0c93f86a 100644 --- a/quantum/keymap_extras/keymap_spanish_dvorak.h +++ b/quantum/keymap_extras/keymap_spanish_dvorak.h @@ -114,8 +114,8 @@ #define DV_QUES S(DV_QUOT) // ? #define DV_IQUE S(DV_IEXL) // ¿ // Row 2 -#define DV_COLN S(KC_DOT) // : -#define DV_SCLN S(KC_COMM) // ; +#define DV_COLN S(DV_DOT) // : +#define DV_SCLN S(DV_COMM) // ; #define DV_CIRC S(DV_GRV) // ^ (dead) #define DV_ASTR S(DV_PLUS) // * // Row 3 -- cgit v1.2.3 From a2765bfe1998abdd4571556bff236a3d89b0c16d Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Sat, 27 Aug 2022 00:41:10 +0100 Subject: Add missing SS_LOPT and SS_ROPT defines (#18175) --- quantum/send_string/send_string_keycodes.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'quantum') diff --git a/quantum/send_string/send_string_keycodes.h b/quantum/send_string/send_string_keycodes.h index 7017e03d5a..802d9a8240 100644 --- a/quantum/send_string/send_string_keycodes.h +++ b/quantum/send_string/send_string_keycodes.h @@ -419,6 +419,7 @@ #define SS_LSFT(string) SS_DOWN(X_LSFT) string SS_UP(X_LSFT) #define SS_LALT(string) SS_DOWN(X_LALT) string SS_UP(X_LALT) #define SS_LGUI(string) SS_DOWN(X_LGUI) string SS_UP(X_LGUI) +#define SS_LOPT(string) SS_LALT(string) #define SS_LCMD(string) SS_LGUI(string) #define SS_LWIN(string) SS_LGUI(string) @@ -427,6 +428,7 @@ #define SS_RALT(string) SS_DOWN(X_RALT) string SS_UP(X_RALT) #define SS_RGUI(string) SS_DOWN(X_RGUI) string SS_UP(X_RGUI) #define SS_ALGR(string) SS_RALT(string) +#define SS_ROPT(string) SS_RALT(string) #define SS_RCMD(string) SS_RGUI(string) #define SS_RWIN(string) SS_RGUI(string) -- cgit v1.2.3 From efdd4f4d50e09e0f71d09333d15acf197406a09c Mon Sep 17 00:00:00 2001 From: James Young <18669334+noroadsleft@users.noreply.github.com> Date: Sat, 27 Aug 2022 23:13:44 -0700 Subject: Clean-up of `#define _ADJUST 16` instances (#18182) * enum layer_names: 40percentclub/nori default keymap * enum layer_names: ergotravel default keymap * enum layer_names: handwired/atreus50 default keymap * enum layer_names: handwired/ortho5x13 default keymap * enum layer_names: keebio/levinson default keymap * enum layer_names: keebio/nyquist default keymap * enum layer_names: keebio/rorschach default keymap * enum layer_names: keebio/viterbi default keymap * enum layer_names: keebio/wavelet default keymap * enum layer_names: lets_split default keymap * enum layer_names: maple_computing/launchpad reference keymaps Update `default` and `default_rgb` keymaps. * enum layer_names: maple_computing/minidox default keymap * enum layer_names: miniaxe reference keymaps Update `default` and `underglow` keymaps. * enum layer_names: omkbd/ergodash/mini default keymap * enum layer_names: omkbd/ergodash/rev1 default keymap * enum layer_names: orthodox default keymap * enum layer_names: unikeyboard/divergetm2 default keymap * enum layer_names: woodkeys/scarletbandana default keymap * add _Static_assert to keymap_introspection.c --- quantum/keymap_introspection.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'quantum') diff --git a/quantum/keymap_introspection.c b/quantum/keymap_introspection.c index 7a96f802ef..179b5eb037 100644 --- a/quantum/keymap_introspection.c +++ b/quantum/keymap_introspection.c @@ -17,6 +17,8 @@ uint8_t keymap_layer_count(void) { return NUM_KEYMAP_LAYERS; } +_Static_assert(NUM_KEYMAP_LAYERS <= MAX_LAYER, "Number of keymap layers exceeds maximum set by LAYER_STATE_(8|16|32)BIT"); + #if defined(ENCODER_ENABLE) && defined(ENCODER_MAP_ENABLE) # define NUM_ENCODERMAP_LAYERS ((uint8_t)(sizeof(encoder_map) / ((NUM_ENCODERS) * (2) * sizeof(uint16_t)))) -- 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') 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 From 652d1d8a6d0a98ab2cff285dc46b91b702adedc6 Mon Sep 17 00:00:00 2001 From: Marius Renner Date: Mon, 19 Sep 2022 20:34:29 +0200 Subject: Fix int8_t overflow in RGB heatmap effect (#18410) --- quantum/rgb_matrix/animations/typing_heatmap_anim.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'quantum') diff --git a/quantum/rgb_matrix/animations/typing_heatmap_anim.h b/quantum/rgb_matrix/animations/typing_heatmap_anim.h index a05c07760e..00d137f1a6 100644 --- a/quantum/rgb_matrix/animations/typing_heatmap_anim.h +++ b/quantum/rgb_matrix/animations/typing_heatmap_anim.h @@ -29,7 +29,7 @@ void process_rgb_matrix_typing_heatmap(uint8_t row, uint8_t col) { if (i_row == row && i_col == col) { g_rgb_frame_buffer[row][col] = qadd8(g_rgb_frame_buffer[row][col], 32); } else { -# define LED_DISTANCE(led_a, led_b) sqrt16(((int8_t)(led_a.x - led_b.x) * (int8_t)(led_a.x - led_b.x)) + ((int8_t)(led_a.y - led_b.y) * (int8_t)(led_a.y - led_b.y))) +# define LED_DISTANCE(led_a, led_b) sqrt16(((int16_t)(led_a.x - led_b.x) * (int16_t)(led_a.x - led_b.x)) + ((int16_t)(led_a.y - led_b.y) * (int16_t)(led_a.y - led_b.y))) uint8_t distance = LED_DISTANCE(g_led_config.point[g_led_config.matrix_co[row][col]], g_led_config.point[g_led_config.matrix_co[i_row][i_col]]); # undef LED_DISTANCE if (distance <= RGB_MATRIX_TYPING_HEATMAP_SPREAD) { -- cgit v1.2.3 From 959f4cbb621868a4f0c1701f54ad2a1f45d889af Mon Sep 17 00:00:00 2001 From: George Rodrigues Date: Wed, 5 Oct 2022 13:32:07 -0300 Subject: docs: some typos (#18582) probably hacktoberism --- quantum/action_layer.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'quantum') diff --git a/quantum/action_layer.c b/quantum/action_layer.c index 473e0e948d..8ef337a690 100644 --- a/quantum/action_layer.c +++ b/quantum/action_layer.c @@ -71,21 +71,21 @@ void default_layer_set(layer_state_t state) { #ifndef NO_ACTION_LAYER /** \brief Default Layer Or * - * Turns on the default layer based on matching bits between specifed layer and existing layer state + * Turns on the default layer based on matching bits between specified layer and existing layer state */ void default_layer_or(layer_state_t state) { default_layer_state_set(default_layer_state | state); } /** \brief Default Layer And * - * Turns on default layer based on matching enabled bits between specifed layer and existing layer state + * Turns on default layer based on matching enabled bits between specified layer and existing layer state */ void default_layer_and(layer_state_t state) { default_layer_state_set(default_layer_state & state); } /** \brief Default Layer Xor * - * Turns on default layer based on non-matching bits between specifed layer and existing layer state + * Turns on default layer based on non-matching bits between specified layer and existing layer state */ void default_layer_xor(layer_state_t state) { default_layer_state_set(default_layer_state ^ state); @@ -115,7 +115,7 @@ __attribute__((weak)) layer_state_t layer_state_set_kb(layer_state_t state) { /** \brief Layer state set * - * Sets the layer to match the specifed state (a bitmask) + * Sets the layer to match the specified state (a bitmask) */ void layer_state_set(layer_state_t state) { state = layer_state_set_kb(state); @@ -193,21 +193,21 @@ void layer_invert(uint8_t layer) { /** \brief Layer or * - * Turns on layers based on matching bits between specifed layer and existing layer state + * Turns on layers based on matching bits between specified layer and existing layer state */ void layer_or(layer_state_t state) { layer_state_set(layer_state | state); } /** \brief Layer and * - * Turns on layers based on matching enabled bits between specifed layer and existing layer state + * Turns on layers based on matching enabled bits between specified layer and existing layer state */ void layer_and(layer_state_t state) { layer_state_set(layer_state & state); } /** \brief Layer xor * - * Turns on layers based on non-matching bits between specifed layer and existing layer state + * Turns on layers based on non-matching bits between specified layer and existing layer state */ void layer_xor(layer_state_t state) { layer_state_set(layer_state ^ state); -- cgit v1.2.3