diff --git a/keyboards/keychron/common/common.mk b/keyboards/keychron/common/common.mk index d7610bce69..d3f283ef9a 100644 --- a/keyboards/keychron/common/common.mk +++ b/keyboards/keychron/common/common.mk @@ -2,3 +2,5 @@ COMMON_DIR = common SRC += $(COMMON_DIR)/matrix.c VPATH += $(TOP_DIR)/keyboards/keychron/$(COMMON_DIR) + +include $(TOP_DIR)/keyboards/keychron/$(COMMON_DIR)/debounce/debounce.mk diff --git a/keyboards/keychron/common/debounce/asym_eager_defer_pk.c b/keyboards/keychron/common/debounce/asym_eager_defer_pk.c new file mode 100644 index 0000000000..db24cb9359 --- /dev/null +++ b/keyboards/keychron/common/debounce/asym_eager_defer_pk.c @@ -0,0 +1,170 @@ +/* + * Copyright 2017 Alex Ong + * Copyright 2020 Andrei Purdea + * Copyright 2021 Simon Arlott + * Copyright 2024 @ keychron (https://www.keychron.com) + * + * 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 . + */ + +/* +Basic symmetric per-key algorithm. Uses an 8-bit counter per key. +When no state changes have occured for DEBOUNCE milliseconds, we push the state. +*/ + +#include "debounce.h" +#include "timer.h" +#include + +#ifdef PROTOCOL_CHIBIOS +# if CH_CFG_USE_MEMCORE == FALSE +# error ChibiOS is configured without a memory allocator. Your keyboard may have set `#define CH_CFG_USE_MEMCORE FALSE`, which is incompatible with this debounce algorithm. +# endif +#endif + +#define ROW_SHIFTER ((matrix_row_t)1) + +typedef struct { + bool pressed : 1; + uint8_t time : 7; +} debounce_counter_t; + +extern uint8_t debounce_time; + +static debounce_counter_t *debounce_counters = NULL; +static fast_timer_t last_time; +static bool counters_need_update; +static bool matrix_need_update; +static bool cooked_changed; + +# define DEBOUNCE_ELAPSED 0 + +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); +static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows); + +// we use num_rows rather than MATRIX_ROWS to support split keyboards +void asym_eager_defer_pk_debounce_init(uint8_t num_rows) { + debounce_counters = malloc(num_rows * MATRIX_COLS * sizeof(debounce_counter_t)); + + int i = 0; + for (uint8_t r = 0; r < num_rows; r++) { + for (uint8_t c = 0; c < MATRIX_COLS; c++) { + debounce_counters[i++].time = DEBOUNCE_ELAPSED; + } + } +} + +void asym_eager_defer_pk_debounce_free(void) { + if (debounce_counters != NULL) { + free(debounce_counters); + debounce_counters = NULL; + } +} + +bool asym_eager_defer_pk_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(); + fast_timer_t elapsed_time = TIMER_DIFF_FAST(now, last_time); + + last_time = now; + updated_last = true; + if (elapsed_time > UINT8_MAX) { + elapsed_time = UINT8_MAX; + } + + if (elapsed_time > 0) { + update_debounce_counters_and_transfer_if_expired(raw, cooked, num_rows, elapsed_time); + } + } + + if (changed || matrix_need_update) { + if (!updated_last) { + last_time = timer_read_fast(); + } + + 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) { + debounce_counter_t *debounce_pointer = debounce_counters; + + counters_need_update = false; + matrix_need_update = false; + + for (uint8_t row = 0; row < num_rows; row++) { + for (uint8_t col = 0; col < MATRIX_COLS; col++) { + matrix_row_t col_mask = (ROW_SHIFTER << col); + + if (debounce_pointer->time != DEBOUNCE_ELAPSED) { + if (debounce_pointer->time <= elapsed_time) { + debounce_pointer->time = DEBOUNCE_ELAPSED; + + if (debounce_pointer->pressed) { + // key-down: eager + matrix_need_update = true; + } else { + // key-up: defer + 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; + counters_need_update = true; + } + } + debounce_pointer++; + } + } +} + +static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) { + debounce_counter_t *debounce_pointer = debounce_counters; + + matrix_need_update = false; + + for (uint8_t row = 0; row < num_rows; row++) { + matrix_row_t delta = raw[row] ^ cooked[row]; + for (uint8_t col = 0; col < MATRIX_COLS; col++) { + matrix_row_t col_mask = (ROW_SHIFTER << col); + + if (delta & col_mask) { + if (debounce_pointer->time == DEBOUNCE_ELAPSED) { + debounce_pointer->pressed = (raw[row] & col_mask); + debounce_pointer->time = debounce_time;; + counters_need_update = true; + + if (debounce_pointer->pressed) { + // key-down: eager + cooked[row] ^= col_mask; + cooked_changed = true; + } + } + } else if (debounce_pointer->time != DEBOUNCE_ELAPSED) { + if (!debounce_pointer->pressed) { + // key-up: defer + debounce_pointer->time = DEBOUNCE_ELAPSED; + } + } + debounce_pointer++; + } + } +} diff --git a/keyboards/keychron/common/debounce/debounce.mk b/keyboards/keychron/common/debounce/debounce.mk new file mode 100644 index 0000000000..d84c479ab5 --- /dev/null +++ b/keyboards/keychron/common/debounce/debounce.mk @@ -0,0 +1,14 @@ +DEBOUNCE_DIR = common/debounce +SRC += \ + $(DEBOUNCE_DIR)/sym_defer_g.c \ + $(DEBOUNCE_DIR)/sym_defer_pr.c \ + $(DEBOUNCE_DIR)/sym_defer_pk.c \ + $(DEBOUNCE_DIR)/sym_eager_pr.c \ + $(DEBOUNCE_DIR)/sym_eager_pk.c \ + $(DEBOUNCE_DIR)/asym_eager_defer_pk.c \ + $(DEBOUNCE_DIR)/none.c \ + $(DEBOUNCE_DIR)/keychron_debounce.c + +VPATH += $(TOP_DIR)/keyboards/keychron/$(DEBOUNCE_DIR) + +OPT_DEFS += -DDYNAMIC_DEBOUNCE_ENABLE diff --git a/keyboards/keychron/common/debounce/eeconfig_debounce.h b/keyboards/keychron/common/debounce/eeconfig_debounce.h new file mode 100644 index 0000000000..7c12f0f189 --- /dev/null +++ b/keyboards/keychron/common/debounce/eeconfig_debounce.h @@ -0,0 +1,20 @@ +/* Copyright 2024 @ Keychron (https://www.keychron.com) + * + * 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 + +#define EECONFIG_SIZE_DEBOUNCE 2 + diff --git a/keyboards/keychron/common/debounce/keychron_debounce.c b/keyboards/keychron/common/debounce/keychron_debounce.c new file mode 100644 index 0000000000..6296d85b81 --- /dev/null +++ b/keyboards/keychron/common/debounce/keychron_debounce.c @@ -0,0 +1,214 @@ + +/* Copyright 2024 @ Keychron (https://www.keychron.com) + * + * 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 "keychron_debounce.h" +#include "raw_hid.h" +#include "quantum.h" +#include "eeconfig.h" +#include "eeconfig_kb.h" +#include "keychron_raw_hid.h" + +#ifdef SPLIT_KEYBOARD +# pragma(error "Split keyboard is not supported") +#endif + +#ifndef DEBOUNCE +# define DEBOUNCE 5 +#endif + +// Maximum debounce: 255ms +#if DEBOUNCE > UINT8_MAX +# undef DEBOUNCE +# define DEBOUNCE UINT8_MAX +#endif + +#ifndef DEFAULT_DEBOUNCE_TYPE + #define DEFAULT_DEBOUNCE_TYPE DEBOUNCE_SYM_EAGER_PER_KEY +#endif + +#define DEBOUNCE_SET_QMK 0 +#define OFFSET_DEBOUNCE ((uint8_t *)(EECONFIG_BASE_DYNAMIC_DEBOUNCE)) + +static uint8_t debounce_type = 0; +uint8_t debounce_time = 0; +static debounce_t debounce_func = {NULL, NULL, NULL}; + +extern void sym_defer_g_debounce_init(uint8_t num_rows); +extern bool sym_defer_g_debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed); +extern void sym_defer_g_debounce_free(void); + +extern void sym_defer_pr_debounce_init(uint8_t num_rows); +extern bool sym_defer_pr_debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed); +extern void sym_defer_pr_debounce_free(void); + +extern void sym_defer_pk_debounce_init(uint8_t num_rows); +extern bool sym_defer_pk_debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed); +extern void sym_defer_pk_debounce_free(void); + +extern void sym_eager_pr_debounce_init(uint8_t num_rows); +extern bool sym_eager_pr_debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed); +extern void sym_eager_pr_debounce_free(void); + +extern void sym_eager_pk_debounce_init(uint8_t num_rows); +extern bool sym_eager_pk_debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed); +extern void sym_eager_pk_debounce_free(void); + +extern void asym_eager_defer_pk_debounce_init(uint8_t num_rows); +extern bool asym_eager_defer_pk_debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed); +extern void asym_eager_defer_pk_debounce_free(void); + +extern void none_debounce_init(uint8_t num_rows); +extern bool none_debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed); +extern void none_debounce_free(void); + +void debounce_set(uint8_t new_debounce_type, uint8_t time, bool force); + +/** + * @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) { + if (debounce_func.debounce) debounce_func.debounce(raw, cooked, num_rows, changed); + + return false; +} + +void debounce_init(uint8_t num_rows) { + debounce_type = 0; + + // debounce_set(DEBOUNCE_SYM_EAGER_PER_KEY, DEBOUNCE); + if (!eeconfig_is_enabled()) { + eeconfig_init(); + } + uint8_t type = eeprom_read_byte(OFFSET_DEBOUNCE); + uint8_t time = eeprom_read_byte(OFFSET_DEBOUNCE + 1); + + if (type >= DEBOUNCE_MAX) type = DEFAULT_DEBOUNCE_TYPE; + + debounce_set(type, time, debounce_type == type); +} + +void debounce_free(void) { + if (debounce_func.debounce_free) debounce_func.debounce_free(); +} + +static bool debounce_save(void) { + eeprom_update_byte(OFFSET_DEBOUNCE, debounce_type); + eeprom_update_byte(OFFSET_DEBOUNCE + 1, debounce_time); + return true; +} + +void debounce_config_reset(void) { + debounce_set(DEFAULT_DEBOUNCE_TYPE, DEBOUNCE, true); + debounce_save(); +} + +void debounce_set(uint8_t new_debounce_type, uint8_t time, bool force) { + if (new_debounce_type == debounce_type && time == debounce_time && !force) return; + + debounce_free(); + + debounce_type = new_debounce_type; + debounce_time = time; + + if (debounce_time == 0) new_debounce_type = DEBOUNCE_NONE; + + switch (new_debounce_type) { + case DEBOUNCE_SYM_DEFER_GLOBAL: + debounce_func.debounce_init = sym_defer_g_debounce_init; + debounce_func.debounce = sym_defer_g_debounce; + debounce_func.debounce_free = sym_defer_g_debounce_free; + break; + + case DEBOUNCE_SYM_DEFER_PER_ROW: + debounce_func.debounce_init = sym_defer_pr_debounce_init; + debounce_func.debounce = sym_defer_pr_debounce; + debounce_func.debounce_free = sym_defer_pr_debounce_free; + break; + + case DEBOUNCE_SYM_DEFER_PER_KEY: + debounce_func.debounce_init = sym_defer_pk_debounce_init; + debounce_func.debounce = sym_defer_pk_debounce; + debounce_func.debounce_free = sym_defer_pk_debounce_free; + break; + + case DEBOUNCE_SYM_EAGER_PER_ROW: + debounce_func.debounce_init = sym_eager_pr_debounce_init; + debounce_func.debounce = sym_eager_pr_debounce; + debounce_func.debounce_free = sym_eager_pr_debounce_free; + break; + + case DEBOUNCE_SYM_EAGER_PER_KEY: + debounce_func.debounce_init = sym_eager_pk_debounce_init; + debounce_func.debounce = sym_eager_pk_debounce; + debounce_func.debounce_free = sym_eager_pk_debounce_free; + break; + + case DEBOUNCE_ASYM_EAGER_DEFER_PER_KEY: + debounce_func.debounce_init = asym_eager_defer_pk_debounce_init; + debounce_func.debounce = asym_eager_defer_pk_debounce; + debounce_func.debounce_free = asym_eager_defer_pk_debounce_free; + if (debounce_time > 127) debounce_time = 127; + break; + + case DEBOUNCE_NONE: + debounce_func.debounce_init = none_debounce_init; + debounce_func.debounce = none_debounce; + debounce_func.debounce_free = none_debounce_free; + break; + } + + if (debounce_func.debounce_init) debounce_func.debounce_init(MATRIX_ROWS); +} + +void debounce_time_set(uint8_t time) { + debounce_time = time; +} + +void debounce_rx(uint8_t *data, uint8_t length) { + uint8_t cmd = data[1]; + switch (cmd) { + case DEBOUNCE_GET: + data[2] = 0; + data[3] = DEBOUNCE_SET_QMK; + data[4] = debounce_type; + data[5] = debounce_time; + break; + + case DEBOUNCE_SET: { + uint8_t type = data[2]; + uint8_t time = data[3]; + if (type < DEBOUNCE_MAX) { + data[2] = 0; + debounce_set(type, time, false); + debounce_save(); + } else + data[2] = 1; + } break; + + default: + data[0] = 0xFF; + break; + } +} diff --git a/keyboards/keychron/common/debounce/keychron_debounce.h b/keyboards/keychron/common/debounce/keychron_debounce.h new file mode 100644 index 0000000000..40ea5800e2 --- /dev/null +++ b/keyboards/keychron/common/debounce/keychron_debounce.h @@ -0,0 +1,56 @@ +/* Copyright 2024 @ Keychron (https://www.keychron.com) + * + * 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 +#include "matrix.h" + +enum { + DEBOUNCE_SYM_DEFER_GLOBAL, + DEBOUNCE_SYM_DEFER_PER_ROW, + DEBOUNCE_SYM_DEFER_PER_KEY, + DEBOUNCE_SYM_EAGER_PER_ROW, + DEBOUNCE_SYM_EAGER_PER_KEY, + DEBOUNCE_ASYM_EAGER_DEFER_PER_KEY, + DEBOUNCE_NONE, + DEBOUNCE_MAX, +}; + +typedef struct { + void (*debounce_init)(uint8_t); + bool (*debounce)(matrix_row_t [], matrix_row_t [], uint8_t, bool); + void (*debounce_free)(void); +} debounce_t; + +/** + * @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); +void debounce_config_reset(void); + +void debounce_free(void); +void debounce_rx(uint8_t *data, uint8_t length); diff --git a/keyboards/keychron/common/debounce/none.c b/keyboards/keychron/common/debounce/none.c new file mode 100644 index 0000000000..244236803c --- /dev/null +++ b/keyboards/keychron/common/debounce/none.c @@ -0,0 +1,36 @@ +/* Copyright 2021 Simon Arlott + * + * 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 "debounce.h" +#include + +void none_debounce_init(uint8_t num_rows) {} + +bool none_debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { + bool cooked_changed = false; + + if (changed) { + size_t matrix_size = num_rows * sizeof(matrix_row_t); + if (memcmp(cooked, raw, matrix_size) != 0) { + memcpy(cooked, raw, matrix_size); + cooked_changed = true; + } + } + + return cooked_changed; +} + +void none_debounce_free(void) {} diff --git a/keyboards/keychron/common/debounce/none.h b/keyboards/keychron/common/debounce/none.h new file mode 100644 index 0000000000..244236803c --- /dev/null +++ b/keyboards/keychron/common/debounce/none.h @@ -0,0 +1,36 @@ +/* Copyright 2021 Simon Arlott + * + * 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 "debounce.h" +#include + +void none_debounce_init(uint8_t num_rows) {} + +bool none_debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { + bool cooked_changed = false; + + if (changed) { + size_t matrix_size = num_rows * sizeof(matrix_row_t); + if (memcmp(cooked, raw, matrix_size) != 0) { + memcpy(cooked, raw, matrix_size); + cooked_changed = true; + } + } + + return cooked_changed; +} + +void none_debounce_free(void) {} diff --git a/keyboards/keychron/common/debounce/sym_defer_g.c b/keyboards/keychron/common/debounce/sym_defer_g.c new file mode 100644 index 0000000000..f1b475846c --- /dev/null +++ b/keyboards/keychron/common/debounce/sym_defer_g.c @@ -0,0 +1,51 @@ +/* +Copyright 2017 Alex Ong +Copyright 2021 Simon Arlott +Copyright 2024 @ keychron (https://www.keychron.com) + +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 . +*/ + +/* +Basic global debounce algorithm. Used in 99% of keyboards at time of implementation +When no state changes have occured for DEBOUNCE milliseconds, we push the state. +*/ +#include "debounce.h" +#include "timer.h" +#include + +extern uint8_t debounce_time; +static bool debouncing = false; +static fast_timer_t debouncing_time; + +void sym_defer_g_debounce_init(uint8_t num_rows) {} + +bool sym_defer_g_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(); + } else if (debouncing && timer_elapsed_fast(debouncing_time) >= debounce_time) { + size_t matrix_size = num_rows * sizeof(matrix_row_t); + if (memcmp(cooked, raw, matrix_size) != 0) { + memcpy(cooked, raw, matrix_size); + cooked_changed = true; + } + debouncing = false; + } + + return cooked_changed; +} + +void sym_defer_g_debounce_free(void) {} + diff --git a/keyboards/keychron/common/debounce/sym_defer_pk.c b/keyboards/keychron/common/debounce/sym_defer_pk.c new file mode 100644 index 0000000000..633d5c3ebc --- /dev/null +++ b/keyboards/keychron/common/debounce/sym_defer_pk.c @@ -0,0 +1,137 @@ +/* +Copyright 2017 Alex Ong +Copyright 2020 Andrei Purdea +Copyright 2021 Simon Arlott +Copyright 2024 @ keychron (https://www.keychron.com) + +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 . +*/ + +/* +Basic symmetric per-key algorithm. Uses an 8-bit counter per key. +When no state changes have occured for DEBOUNCE milliseconds, we push the state. +*/ + +#include "debounce.h" +#include "timer.h" +#include + +#ifdef PROTOCOL_CHIBIOS +# if CH_CFG_USE_MEMCORE == FALSE +# error ChibiOS is configured without a memory allocator. Your keyboard may have set `#define CH_CFG_USE_MEMCORE FALSE`, which is incompatible with this debounce algorithm. +# endif +#endif + + +#define ROW_SHIFTER ((matrix_row_t)1) + +typedef uint8_t debounce_counter_t; + +extern uint8_t debounce_time; + +static debounce_counter_t *debounce_counters = NULL; +static fast_timer_t last_time; +static bool counters_need_update; +static bool cooked_changed; + +# define DEBOUNCE_ELAPSED 0 + +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); +static void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows); + +// we use num_rows rather than MATRIX_ROWS to support split keyboards +void sym_defer_pk_debounce_init(uint8_t num_rows) { + debounce_counters = (debounce_counter_t *)malloc(num_rows * MATRIX_COLS * sizeof(debounce_counter_t)); + + int i = 0; + for (uint8_t r = 0; r < num_rows; r++) { + for (uint8_t c = 0; c < MATRIX_COLS; c++) { + debounce_counters[i++] = DEBOUNCE_ELAPSED; + } + } +} + +void sym_defer_pk_debounce_free(void) { + if (debounce_counters != NULL) { + free(debounce_counters); + debounce_counters = NULL; + } +} + +bool sym_defer_pk_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(); + fast_timer_t elapsed_time = TIMER_DIFF_FAST(now, last_time); + + last_time = now; + updated_last = true; + if (elapsed_time > UINT8_MAX) { + elapsed_time = UINT8_MAX; + } + + if (elapsed_time > 0) { + update_debounce_counters_and_transfer_if_expired(raw, cooked, num_rows, elapsed_time); + } + } + + if (changed) { + if (!updated_last) { + last_time = timer_read_fast(); + } + + 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) { + counters_need_update = false; + debounce_counter_t *debounce_pointer = debounce_counters; + for (uint8_t row = 0; row < num_rows; row++) { + for (uint8_t col = 0; col < MATRIX_COLS; col++) { + if (*debounce_pointer != DEBOUNCE_ELAPSED) { + if (*debounce_pointer <= elapsed_time) { + *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; + } + } + debounce_pointer++; + } + } +} + +static void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) { + debounce_counter_t *debounce_pointer = debounce_counters; + for (uint8_t row = 0; row < num_rows; row++) { + matrix_row_t delta = raw[row] ^ cooked[row]; + for (uint8_t col = 0; col < MATRIX_COLS; col++) { + if (delta & (ROW_SHIFTER << col)) { + if (*debounce_pointer == DEBOUNCE_ELAPSED) { + *debounce_pointer = debounce_time;; + counters_need_update = true; + } + } else { + *debounce_pointer = DEBOUNCE_ELAPSED; + } + debounce_pointer++; + } + } +} diff --git a/keyboards/keychron/common/debounce/sym_defer_pr.c b/keyboards/keychron/common/debounce/sym_defer_pr.c new file mode 100644 index 0000000000..c295855061 --- /dev/null +++ b/keyboards/keychron/common/debounce/sym_defer_pr.c @@ -0,0 +1,80 @@ +/* +Copyright 2021 Chad Austin +Copyright 2024 @ keychron (https://www.keychron.com) + +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 . +*/ + +/* +Symmetric per-row debounce algorithm. Changes only apply when +DEBOUNCE milliseconds have elapsed since the last change. +*/ + +#include "debounce.h" +#include "timer.h" +#include + +extern uint8_t debounce_time; + +static uint16_t last_time; +// [row] milliseconds until key's state is considered debounced. +static uint8_t* countdowns = NULL; +// [row] +static matrix_row_t* last_raw = NULL; + +void sym_defer_pr_debounce_init(uint8_t num_rows) { + countdowns = (uint8_t*)calloc(num_rows, sizeof(uint8_t)); + last_raw = (matrix_row_t*)calloc(num_rows, sizeof(matrix_row_t)); + last_time = timer_read(); +} + +void sym_defer_pr_debounce_free(void) { + if (countdowns != NULL) { + free(countdowns); + countdowns = NULL; + } + if (last_raw != NULL) { + free(last_raw); + last_raw = NULL; + } +} + +bool sym_defer_pr_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; + + for (uint8_t row = 0; row < num_rows; ++row, ++countdown) { + matrix_row_t raw_row = raw[row]; + + if (raw_row != last_raw[row]) { + *countdown = debounce_time; + last_raw[row] = raw_row; + } 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) { + return true; +} diff --git a/keyboards/keychron/common/debounce/sym_eager_pk.c b/keyboards/keychron/common/debounce/sym_eager_pk.c new file mode 100644 index 0000000000..fe3655172c --- /dev/null +++ b/keyboards/keychron/common/debounce/sym_eager_pk.c @@ -0,0 +1,142 @@ +/* +Copyright 2017 Alex Ong +Copyright 2021 Simon Arlott +Copyright 2024 @ keychron (https://www.keychron.com) + +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 . +*/ + +/* +Basic per-key algorithm. Uses an 8-bit counter per key. +After pressing a key, it immediately changes state, and sets a counter. +No further inputs are accepted until DEBOUNCE milliseconds have occurred. +*/ + +#include "debounce.h" +#include "timer.h" +#include + +#ifdef PROTOCOL_CHIBIOS +# if CH_CFG_USE_MEMCORE == FALSE +# error ChibiOS is configured without a memory allocator. Your keyboard may have set `#define CH_CFG_USE_MEMCORE FALSE`, which is incompatible with this debounce algorithm. +# endif +#endif + +extern uint8_t debounce_time; + +#define ROW_SHIFTER ((matrix_row_t)1) + +typedef uint8_t debounce_counter_t; + + +static debounce_counter_t *debounce_counters = NULL; +static fast_timer_t last_time; +static bool counters_need_update; +static bool matrix_need_update; +static bool cooked_changed; + +# define DEBOUNCE_ELAPSED 0 + +static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time); +static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows); + +// we use num_rows rather than MATRIX_ROWS to support split keyboards +void sym_eager_pk_debounce_init(uint8_t num_rows) { + debounce_counters = (debounce_counter_t *)malloc(num_rows * MATRIX_COLS * sizeof(debounce_counter_t)); + int i = 0; + for (uint8_t r = 0; r < num_rows; r++) { + for (uint8_t c = 0; c < MATRIX_COLS; c++) { + debounce_counters[i++] = DEBOUNCE_ELAPSED; + } + } +} + +void sym_eager_pk_debounce_free(void) { + if (debounce_counters != NULL) { + free(debounce_counters); + debounce_counters = NULL; + } +} + +bool sym_eager_pk_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(); + fast_timer_t elapsed_time = TIMER_DIFF_FAST(now, last_time); + + last_time = now; + updated_last = true; + if (elapsed_time > UINT8_MAX) { + elapsed_time = UINT8_MAX; + } + + if (elapsed_time > 0) { + update_debounce_counters(num_rows, elapsed_time); + } + } + + if (changed || matrix_need_update) { + if (!updated_last) { + last_time = timer_read_fast(); + } + + transfer_matrix_values(raw, cooked, num_rows); + } + + return cooked_changed; +} + +// If the current time is > debounce counter, set the counter to enable input. +static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time) { + counters_need_update = false; + matrix_need_update = false; + debounce_counter_t *debounce_pointer = debounce_counters; + for (uint8_t row = 0; row < num_rows; row++) { + for (uint8_t col = 0; col < MATRIX_COLS; col++) { + if (*debounce_pointer != DEBOUNCE_ELAPSED) { + if (*debounce_pointer <= elapsed_time) { + *debounce_pointer = DEBOUNCE_ELAPSED; + matrix_need_update = true; + } else { + *debounce_pointer -= elapsed_time; + counters_need_update = true; + } + } + debounce_pointer++; + } + } +} + +// upload from raw_matrix to final matrix; +static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) { + matrix_need_update = false; + debounce_counter_t *debounce_pointer = debounce_counters; + for (uint8_t row = 0; row < num_rows; row++) { + matrix_row_t delta = raw[row] ^ cooked[row]; + matrix_row_t existing_row = cooked[row]; + for (uint8_t col = 0; col < MATRIX_COLS; col++) { + matrix_row_t col_mask = (ROW_SHIFTER << col); + if (delta & col_mask) { + if (*debounce_pointer == DEBOUNCE_ELAPSED) { + *debounce_pointer = debounce_time; + counters_need_update = true; + existing_row ^= col_mask; // flip the bit. + cooked_changed = true; + } + } + debounce_pointer++; + } + cooked[row] = existing_row; + } +} diff --git a/keyboards/keychron/common/debounce/sym_eager_pr.c b/keyboards/keychron/common/debounce/sym_eager_pr.c new file mode 100644 index 0000000000..85488f1063 --- /dev/null +++ b/keyboards/keychron/common/debounce/sym_eager_pr.c @@ -0,0 +1,134 @@ +/* +Copyright 2019 Alex Ong +Copyright 2021 Simon Arlott +Copyright 2024 @ keychron (https://www.keychron.com) + +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 . +*/ + +/* +Basic per-row algorithm. Uses an 8-bit counter per row. +After pressing a key, it immediately changes state, and sets a counter. +No further inputs are accepted until DEBOUNCE milliseconds have occurred. +*/ + +#include "debounce.h" +#include "timer.h" +#include + +#ifdef PROTOCOL_CHIBIOS +# if CH_CFG_USE_MEMCORE == FALSE +# error ChibiOS is configured without a memory allocator. Your keyboard may have set `#define CH_CFG_USE_MEMCORE FALSE`, which is incompatible with this debounce algorithm. +# endif +#endif + + +typedef uint8_t debounce_counter_t; + +extern uint8_t debounce_time; + +static bool matrix_need_update; + +static debounce_counter_t *debounce_counters = NULL; +static fast_timer_t last_time; +static bool counters_need_update; +static bool cooked_changed; + +# define DEBOUNCE_ELAPSED 0 + +static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time); +static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows); + +// we use num_rows rather than MATRIX_ROWS to support split keyboards +void sym_eager_pr_debounce_init(uint8_t num_rows) { + debounce_counters = (debounce_counter_t *)malloc(num_rows * sizeof(debounce_counter_t)); + for (uint8_t r = 0; r < num_rows; r++) { + debounce_counters[r] = DEBOUNCE_ELAPSED; + } +} + +void sym_eager_pr_debounce_free(void) { + if (debounce_counters != NULL) { + free(debounce_counters); + debounce_counters = NULL; + } +} + +bool sym_eager_pr_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(); + fast_timer_t elapsed_time = TIMER_DIFF_FAST(now, last_time); + + last_time = now; + updated_last = true; + if (elapsed_time > UINT8_MAX) { + elapsed_time = UINT8_MAX; + } + + if (elapsed_time > 0) { + update_debounce_counters(num_rows, elapsed_time); + } + } + + if (changed || matrix_need_update) { + if (!updated_last) { + last_time = timer_read_fast(); + } + + transfer_matrix_values(raw, cooked, num_rows); + } + + return cooked_changed; +} + +// If the current time is > debounce counter, set the counter to enable input. +static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time) { + counters_need_update = false; + matrix_need_update = false; + debounce_counter_t *debounce_pointer = debounce_counters; + for (uint8_t row = 0; row < num_rows; row++) { + if (*debounce_pointer != DEBOUNCE_ELAPSED) { + if (*debounce_pointer <= elapsed_time) { + *debounce_pointer = DEBOUNCE_ELAPSED; + matrix_need_update = true; + } else { + *debounce_pointer -= elapsed_time; + counters_need_update = true; + } + } + debounce_pointer++; + } +} + +// upload from raw_matrix to final matrix; +static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) { + matrix_need_update = false; + debounce_counter_t *debounce_pointer = debounce_counters; + for (uint8_t row = 0; row < num_rows; row++) { + matrix_row_t existing_row = cooked[row]; + matrix_row_t raw_row = raw[row]; + + // determine new value basd on debounce pointer + raw value + if (existing_row != raw_row) { + if (*debounce_pointer == DEBOUNCE_ELAPSED) { + *debounce_pointer = debounce_time; + cooked_changed |= cooked[row] ^ raw_row; + cooked[row] = raw_row; + counters_need_update = true; + } + } + debounce_pointer++; + } +} diff --git a/keyboards/keychron/common/dfu_info.c b/keyboards/keychron/common/dfu_info.c new file mode 100644 index 0000000000..47c6012046 --- /dev/null +++ b/keyboards/keychron/common/dfu_info.c @@ -0,0 +1,49 @@ +/* Copyright 2024 @ Keychron (https://www.keychron.com) + * + * 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" + +enum { + DFU_INFO_CHIP = 1, + DFU_INFO_TYPE, +}; + +enum { + BL_TYPE_STM32 = 1, + BL_TYPE_WB32, +}; + +void dfu_info_rx(uint8_t *data, uint8_t length) { + uint8_t i = 2; + + data[i++] = 0; // success + data[i++] = DFU_INFO_CHIP, + data[i++] = strlen(STR(QMK_MCU)); + memcpy(&data[i], STR(QMK_MCU), strlen(STR(QMK_MCU))); + i += strlen(STR(QMK_MCU)); + data[i++] = DFU_INFO_TYPE; + data[i++] = 1; + data[i++] = +#if defined(BOOTLOADER_STM32_DFU) + BL_TYPE_STM32 +#elif defined(BOOTLOADER_WB32_DFU) + BL_TYPE_WB32 +#else + 0 +#endif + ; +} diff --git a/keyboards/keychron/common/eeconfig_kb.c b/keyboards/keychron/common/eeconfig_kb.c new file mode 100644 index 0000000000..a0a15828ac --- /dev/null +++ b/keyboards/keychron/common/eeconfig_kb.c @@ -0,0 +1,35 @@ +/* Copyright 2024 @ Keychron (https://www.keychron.com) + * + * 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 "eeconfig_kb.h" +#ifdef DYNAMIC_DEBOUNCE_ENABLE +# include "keychron_debounce.h" +#endif + +void eeconfig_init_kb_datablock(void) { +#ifdef DYNAMIC_DEBOUNCE_ENABLE + extern void debounce_config_reset(void); + debounce_config_reset(); +#endif +#if defined(SNAP_CLICK_ENABLE) + extern void snap_click_config_reset(void); + snap_click_config_reset(); +#endif +#if defined(KEYCHRON_RGB_ENABLE) && defined(RGB_MATRIX_ENABLE) + extern void eeconfig_reset_custom_rgb(void); + eeconfig_reset_custom_rgb(); +#endif +} diff --git a/keyboards/keychron/common/eeconfig_kb.h b/keyboards/keychron/common/eeconfig_kb.h new file mode 100644 index 0000000000..dd995bb993 --- /dev/null +++ b/keyboards/keychron/common/eeconfig_kb.h @@ -0,0 +1,61 @@ +/* Copyright 2024 @ Keychron (https://www.keychron.com) + * + * 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 "eeconfig_language.h" + +#define EECONFIG_BASE_LANGUAGE 37 +#define EECONFIG_END_LANGUAGE (EECONFIG_BASE_LANGUAGE + EECONFIG_SIZE_LANGUAGE) + +#ifdef DYNAMIC_DEBOUNCE_ENABLE +# include "eeconfig_debounce.h" +# define __EECONFIG_SIZE_DEBOUNCE EECONFIG_SIZE_DEBOUNCE +#else +# define __EECONFIG_SIZE_DEBOUNCE 0 +#endif +#define EECONFIG_BASE_DYNAMIC_DEBOUNCE EECONFIG_END_LANGUAGE +#define EECONFIG_END_DYNAMIC_DEBOUNCE (EECONFIG_BASE_DYNAMIC_DEBOUNCE + __EECONFIG_SIZE_DEBOUNCE) + +#ifdef SNAP_CLICK_ENABLE +# include "eeconfig_snap_click.h" +# define __EECONFIG_SIZE_SNAP_CLICK EECONFIG_SIZE_SNAP_CLICK +#else +# define __EECONFIG_SIZE_SNAP_CLICK 0 +#endif +#define EECONFIG_BASE_SNAP_CLICK (EECONFIG_END_DYNAMIC_DEBOUNCE) +#define EECONFIG_END_SNAP_CLICK (EECONFIG_BASE_SNAP_CLICK + __EECONFIG_SIZE_SNAP_CLICK) + +#if defined(KEYCHRON_RGB_ENABLE) && defined(RGB_MATRIX_ENABLE) +# include "eeconfig_custom_rgb.h" +# define __EECONFIG_SIZE_CUSTOM_RGB EECONFIG_SIZE_CUSTOM_RGB +#else +# define __EECONFIG_SIZE_CUSTOM_RGB 0 +#endif +#define EECONFIG_BASE_CUSTOM_RGB EECONFIG_END_SNAP_CLICK +#define EECONFIG_END_CUSTOM_RGB (EECONFIG_BASE_CUSTOM_RGB + __EECONFIG_SIZE_CUSTOM_RGB) + +#if defined(WIRELESS_CONFIG_ENABLE) +# include "eeconfig_wireless.h" +# define __EECONFIG_SIZE_WIRELESS_CONFIG EECONFIG_SIZE_WIRELESS_CONFIG +#else +# define __EECONFIG_SIZE_WIRELESS_CONFIG 0 +#endif +#define EECONFIG_BASE_WIRELESS_CONFIG EECONFIG_END_CUSTOM_RGB +#define EECONFIG_END_WIRELESS_CONFIG (EECONFIG_BASE_WIRELESS_CONFIG + __EECONFIG_SIZE_WIRELESS_CONFIG) + +#define EECONFIG_KB_DATA_SIZE (EECONFIG_END_WIRELESS_CONFIG - EECONFIG_BASE_LANGUAGE) + diff --git a/keyboards/keychron/common/factory_test.c b/keyboards/keychron/common/factory_test.c index 4f9e8c531f..5296222925 100644 --- a/keyboards/keychron/common/factory_test.c +++ b/keyboards/keychron/common/factory_test.c @@ -1,4 +1,4 @@ -/* Copyright 2021 @ Keychron (https://www.keychron.com) +/* Copyright 2021~2025 @ Keychron (https://www.keychron.com) * * 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 @@ -26,6 +26,12 @@ # include "lkbt51.h" # include "indicator.h" #endif +#ifdef DYNAMIC_DEBOUNCE_ENABLE +# include "keychron_debounce.h" +#endif +#ifdef SNAP_CLICK_ENABLE +# include "snap_click.h" +#endif #include "config.h" #include "version.h" @@ -92,7 +98,9 @@ static uint8_t backlight_test_mode = BACKLIGHT_TEST_OFF; static uint32_t factory_reset_ind_timer = 0; static uint8_t factory_reset_ind_state = 0; static bool report_os_sw_state = false; -static bool keys_released = true; +static uint8_t keys_released = 0; + +extern void eeconfig_reset_custom_rgb(void); void factory_timer_start(void) { factory_reset_timer = timer_read32(); @@ -112,6 +120,12 @@ static inline void factory_timer_check(void) { eeconfig_init(); keymap_config.raw = eeconfig_read_keymap(); default_layer_set(default_layer_tmp); +#ifdef DYNAMIC_DEBOUNCE_ENABLE + debounce_config_reset(); +#endif +#ifdef SNAP_CLICK_ENABLE + snap_click_config_reset(); +#endif #ifdef LED_MATRIX_ENABLE if (!led_matrix_is_enabled()) led_matrix_enable(); led_matrix_init(); @@ -119,8 +133,15 @@ static inline void factory_timer_check(void) { #ifdef RGB_MATRIX_ENABLE if (!rgb_matrix_is_enabled()) rgb_matrix_enable(); rgb_matrix_init(); +#if defined(KEYCHRON_RGB_ENABLE) && defined(EECONFIG_SIZE_CUSTOM_RGB) + eeconfig_reset_custom_rgb(); +#endif #endif #ifdef LK_WIRELESS_ENABLE +#ifdef EECONFIG_SIZE_WIRELESS_CONFIG + wireless_config_reset(); +#endif + wait_ms(50); lkbt51_factory_reset(P2P4G_CELAR_MASK); #endif } else if (factory_reset_state == KEY_PRESS_BACKLIGTH_TEST) { @@ -168,13 +189,21 @@ bool process_record_factory_test(uint16_t keycode, keyrecord_t *record) { break; #endif case KC_J: +#if defined(FN_J_KEY) + case FN_J_KEY: +#endif if (record->event.pressed) { factory_reset_state |= KEY_PRESS_J; if (factory_reset_state == 0x07) factory_timer_start(); - if (factory_reset_state & KEY_PRESS_FN) return false; + if ((factory_reset_state & KEY_PRESS_FN) && keycode == KC_J) return false; } else { factory_reset_state &= ~KEY_PRESS_J; factory_reset_timer = 0; + /* Avoid changing backlight effect on key released if FN_Z_KEY is mode*/ + if (keys_released & KEY_PRESS_J) { + keys_released &= ~KEY_PRESS_J; + if (keycode >= QK_BACKLIGHT_ON && keycode <= RGB_MODE_TWINKLE) return false; + } } break; case KC_Z: @@ -189,10 +218,9 @@ bool process_record_factory_test(uint16_t keycode, keyrecord_t *record) { factory_reset_state &= ~KEY_PRESS_Z; factory_reset_timer = 0; /* Avoid changing backlight effect on key released if FN_Z_KEY is mode*/ - - if (!keys_released && keycode >= QK_BACKLIGHT_ON && keycode <= RGB_MODE_TWINKLE) { - keys_released = true; - return false; + if (keys_released & KEY_PRESS_Z) { + keys_released &= ~KEY_PRESS_Z; + if (keycode >= QK_BACKLIGHT_ON && keycode <= RGB_MODE_TWINKLE) return false; } } break; @@ -327,10 +355,8 @@ void factory_test_rx(uint8_t *data, uint8_t length) { /* Verify checksum */ if ((checksum & 0xFF) != data[RAW_EPSIZE - 2] || checksum >> 8 != data[RAW_EPSIZE - 1]) return; -#ifdef LK_WIRELESS_ENABLE uint8_t payload[32]; uint8_t len = 0; -#endif switch (data[1]) { case FACTORY_TEST_CMD_BACKLIGHT: diff --git a/keyboards/keychron/common/keychron_common.c b/keyboards/keychron/common/keychron_common.c index 7a039854a0..043960454d 100644 --- a/keyboards/keychron/common/keychron_common.c +++ b/keyboards/keychron/common/keychron_common.c @@ -16,16 +16,18 @@ #include QMK_KEYBOARD_H #include "keychron_common.h" -#include "raw_hid.h" -#include "version.h" - #ifdef FACTORY_TEST_ENABLE # include "factory_test.h" -# include "keychron_common.h" #endif - +#ifdef RETAIL_DEMO_ENABLE +# include "retail_demo.h" +#endif #ifdef LK_WIRELESS_ENABLE # include "lkbt51.h" +# include "wireless.h" +#endif +#ifdef LED_MATRIX_ENABLE +# include "led_matrix.h" #endif bool is_siri_active = false; @@ -53,6 +55,35 @@ static key_combination_t key_comb_list[] = { }; // clang-format on +void keychron_common_init(void) { +#ifdef SNAP_CLICK_ENABLE + extern void snap_click_init(void); + snap_click_init(); +#endif +#if defined(RGB_MATRIX_ENABLE) && defined(KEYCHRON_RGB_ENABLE) + extern void eeconfig_init_custom_rgb(void); + eeconfig_init_custom_rgb(); +#endif +#ifdef LK_WIRELESS_ENABLE +# ifdef P2P4_MODE_SELECT_PIN + palSetLineMode(P2P4_MODE_SELECT_PIN, PAL_MODE_INPUT); +# endif +# ifdef BT_MODE_SELECT_PIN + palSetLineMode(BT_MODE_SELECT_PIN, PAL_MODE_INPUT); +# endif +# ifdef BAT_LOW_LED_PIN + writePin(BAT_LOW_LED_PIN, BAT_LOW_LED_PIN_ON_STATE); +# endif + + lkbt51_init(false); + wireless_init(); +#endif + +#ifdef ENCODER_ENABLE + encoder_cb_init(); +#endif +} + bool process_record_keychron_common(uint16_t keycode, keyrecord_t *record) { switch (keycode) { case KC_MCTRL: @@ -111,9 +142,18 @@ bool process_record_keychron_common(uint16_t keycode, keyrecord_t *record) { } } return false; // Skip all further processing of this key +#ifdef LED_MATRIX_ENABLE + case BL_SPI: + led_matrix_increase_speed(); + break; + case BL_SPD: + led_matrix_decrease_speed(); + break; +#endif default: return true; // Process all other keycodes normally } + return true; } void keychron_common_task(void) { @@ -134,105 +174,11 @@ static void encoder_pad_cb(void *param) { void encoder_cb_init(void) { pin_t encoders_pad_a[] = ENCODERS_PAD_A; pin_t encoders_pad_b[] = ENCODERS_PAD_B; - for (uint32_t i=0; i> 12), (char *)&data[i++], 16); - itoa((DEVICE_VER >> 8) & 0xF, (char *)&data[i++], 16); - data[i++] = '.'; - itoa((DEVICE_VER >> 4) & 0xF, (char *)&data[i++], 16); - data[i++] = '.'; - itoa(DEVICE_VER & 0xF, (char *)&data[i++], 16); - data[i++] = ' '; - memcpy(&data[i], QMK_BUILDDATE, sizeof(QMK_BUILDDATE)); - i += sizeof(QMK_BUILDDATE); - raw_hid_send(data, length); - } break; - - case kc_get_support_feature: - get_support_feature(&data[1]); - raw_hid_send(data, length); - break; - - case kc_get_default_layer: - data[1] = get_highest_layer(default_layer_state); - raw_hid_send(data, length); - break; - -#ifdef ANANLOG_MATRIX - case 0xA9: - analog_matrix_rx(data, length); - break; -#endif -#ifdef LK_WIRELESS_ENABLE - case 0xAA: - lkbt51_dfu_rx(data, length); - break; -#endif -#ifdef FACTORY_TEST_ENABLE - case 0xAB: - factory_test_rx(data, length); - break; -#endif - default: - return false; - } - - return true; -} - -#if defined(VIA_ENABLE) -bool via_command_kb(uint8_t *data, uint8_t length) { - return kc_raw_hid_rx(data, length); -} -#else -void raw_hid_receive(uint8_t *data, uint8_t length) { - kc_raw_hid_rx(data, length); -} -#endif - diff --git a/keyboards/keychron/common/keychron_common.h b/keyboards/keychron/common/keychron_common.h index 4ba6f35f26..e2a4a70480 100644 --- a/keyboards/keychron/common/keychron_common.h +++ b/keyboards/keychron/common/keychron_common.h @@ -54,6 +54,10 @@ enum { PROF1, PROF2, PROF3, +#endif +#ifdef LED_MATRIX_ENABLE + BL_SPI, + BL_SPD, #endif NEW_SAFE_RANGE, }; @@ -83,10 +87,10 @@ typedef struct PACKED { uint8_t keycode[3]; } key_combination_t; +void keychron_common_init(void); bool process_record_keychron_common(uint16_t keycode, keyrecord_t *record); void keychron_common_task(void); #ifdef ENCODER_ENABLE void encoder_cb_init(void); #endif - diff --git a/keyboards/keychron/common/keychron_common.mk b/keyboards/keychron/common/keychron_common.mk index 4598cee79e..3b1bf6d240 100644 --- a/keyboards/keychron/common/keychron_common.mk +++ b/keyboards/keychron/common/keychron_common.mk @@ -1,10 +1,31 @@ -OPT_DEFS += -DFACTORY_TEST_ENABLE +OPT_DEFS += -DFACTORY_TEST_ENABLE -DAPDAPTIVE_NKRO_ENABLE KEYCHRON_COMMON_DIR = common SRC += \ - $(KEYCHRON_COMMON_DIR)/keychron_task.c \ + $(KEYCHRON_COMMON_DIR)/keychron_task.c \ $(KEYCHRON_COMMON_DIR)/keychron_common.c \ - $(KEYCHRON_COMMON_DIR)/factory_test.c + $(KEYCHRON_COMMON_DIR)/keychron_raw_hid.c \ + $(KEYCHRON_COMMON_DIR)/factory_test.c \ + $(KEYCHRON_COMMON_DIR)/eeconfig_kb.c \ + $(KEYCHRON_COMMON_DIR)/dfu_info.c VPATH += $(TOP_DIR)/keyboards/keychron/$(KEYCHRON_COMMON_DIR) +INFO_RULES_MK = $(shell $(QMK_BIN) generate-rules-mk --quiet --escape --keyboard $(KEYBOARD) --output $(INTERMEDIATE_OUTPUT)/src/info_rules.mk) +include $(INFO_RULES_MK) + +include $(TOP_DIR)/keyboards/keychron/$(KEYCHRON_COMMON_DIR)/language/language.mk + +ifeq ($(strip $(DEBOUNCE_TYPE)), custom) +include $(TOP_DIR)/keyboards/keychron/$(KEYCHRON_COMMON_DIR)/debounce/debounce.mk +endif + +ifeq ($(strip $(SNAP_CLICK_ENABLE)), yes) +include $(TOP_DIR)/keyboards/keychron/$(KEYCHRON_COMMON_DIR)/snap_click/snap_click.mk +endif + +ifeq ($(strip $(KEYCHRON_RGB_ENABLE)), yes) +ifeq ($(strip $(RGB_MATRIX_ENABLE)), yes) +include $(TOP_DIR)/keyboards/keychron/$(KEYCHRON_COMMON_DIR)/rgb/rgb.mk +endif +endif diff --git a/keyboards/keychron/common/keychron_raw_hid.c b/keyboards/keychron/common/keychron_raw_hid.c new file mode 100644 index 0000000000..e21776f5c8 --- /dev/null +++ b/keyboards/keychron/common/keychron_raw_hid.c @@ -0,0 +1,203 @@ +/* Copyright 2024 @ Keychron (https://www.keychron.com) + * + * 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 QMK_KEYBOARD_H +#include "keychron_common.h" +#include "keychron_raw_hid.h" +#include "raw_hid.h" +#include "version.h" +#include "language.h" +#ifdef FACTORY_TEST_ENABLE +# include "factory_test.h" +#endif +#ifdef LK_WIRELESS_ENABLE +# include "lkbt51.h" +#endif +#ifdef ANANLOG_MATRIX +# include "analog_matrix.h" +#endif +#ifdef DYNAMIC_DEBOUNCE_ENABLE +# include "keychron_debounce.h" +#endif +#ifdef SNAP_CLICK_ENABLE +# include "snap_click.h" +#endif +#ifdef LK_WIRELESS_ENABLE +# include "wireless.h" +#endif + +extern void dfu_info_rx(uint8_t *data, uint8_t length); + +void get_support_feature(uint8_t *data) { + data[0] = 0; + data[1] = FEATURE_DEFAULT_LAYER +#ifdef KC_BLUETOOTH_ENABLE + | FEATURE_BLUETOOTH +#endif +#ifdef LK_WIRELESS_ENABLE + | FEATURE_BLUETOOTH | FEATURE_P24G +#endif +#ifdef ANANLOG_MATRIX + | FEATURE_ANALOG_MATRIX +#endif +#ifdef INFO_CHAGNED_NOTIFY_ENABLE + | FEATURE_INFO_CHAGNED_NOTIFY +#endif +#ifdef DYNAMIC_DEBOUNCE_ENABLE + | FEATURE_DYNAMIC_DEBOUNCE +#endif +#ifdef SNAP_CLICK_ENABLE + | FEATURE_SNAP_CLICK +#endif +#ifdef KEYCHRON_RGB_ENABLE + | FEATURE_KEYCHRON_RGB +#endif + ; +} + +void get_firmware_version(uint8_t *data) { + uint8_t i = 0; + data[i++] = 'v'; + if ((DEVICE_VER & 0xF000) != 0) itoa((DEVICE_VER >> 12), (char *)&data[i++], 16); + itoa((DEVICE_VER >> 8) & 0xF, (char *)&data[i++], 16); + data[i++] = '.'; + itoa((DEVICE_VER >> 4) & 0xF, (char *)&data[i++], 16); + data[i++] = '.'; + itoa(DEVICE_VER & 0xF, (char *)&data[i++], 16); + data[i++] = ' '; + memcpy(&data[i], QMK_BUILDDATE, sizeof(QMK_BUILDDATE)); + i += sizeof(QMK_BUILDDATE); +} + + +__attribute__((weak)) void kc_rgb_matrix_rx(uint8_t *data, uint8_t length) {} + +bool kc_raw_hid_rx(uint8_t *data, uint8_t length) { + switch (data[0]) { + case KC_GET_PROTOCOL_VERSION: + data[1] = PROTOCOL_VERSION; + data[2] = 0; + data[3] = QMK_COMMAND_SET; + break; + + case KC_GET_FIRMWARE_VERSION: + get_firmware_version(&data[1]); + break; + + case KC_GET_SUPPORT_FEATURE: + get_support_feature(&data[1]); + break; + + case KC_GET_DEFAULT_LAYER: + data[1] = get_highest_layer(default_layer_state); + break; + + case 0xA7: + switch (data[1]) { + case MISC_GET_PROTOCOL_VER: + data[2] = 0; + data[3] = MISC_PROTOCOL_VERSION & 0xFF; + data[4] = (MISC_PROTOCOL_VERSION >> 8) & 0xFF; + data[5] = MISC_DFU_INFO | MISC_LANGUAGE +#ifdef DYNAMIC_DEBOUNCE_ENABLE + | MISC_DEBOUNCE +#endif +#ifdef SNAP_CLICK_ENABLE + | MISC_SNAP_CLICK +#endif +#ifdef LK_WIRELESS_ENABLE + | MISC_WIRELESS_LPM +#endif +#ifdef HSUSB_8K_ENABLE + | MISC_REPORT_REATE +#endif + ; + break; + + case DFU_INFO_GET: + dfu_info_rx(data, length); + break; + case LANGUAGE_GET ... LANGUAGE_SET: + language_rx(data, length); + break; + +#if defined(DYNAMIC_DEBOUNCE_ENABLE) + case DEBOUNCE_GET ... DEBOUNCE_SET: + debounce_rx(data, length); + break; +#endif +#if defined(SNAP_CLICK_ENABLE) + case SNAP_CLICK_GET_INFO ... SNAP_CLICK_SAVE: + snap_click_rx(data, length); + break; +#endif +#if defined(LK_WIRELESS_ENABLE) && defined(EECONFIG_BASE_WIRELESS_CONFIG) + case WIRELESS_LPM_GET ... WIRELESS_LPM_SET: + wireless_raw_hid_rx(data, length); + break; +#endif +#if defined(HSUSB_8K_ENABLE) + case REPORT_RATE_GET ... REPORT_RATE_SET: + report_rate_hid_rx(data, length); + break; +#endif + default: + data[0] = 0xFF; + data[1] = 0; + break; + } + break; + +#if defined(KEYCHRON_RGB_ENABLE) + case 0xA8: + kc_rgb_matrix_rx(data, length); + break; +#endif + +#ifdef ANANLOG_MATRIX + case 0xA9: + analog_matrix_rx(data, length); + return true; +#endif +#ifdef LK_WIRELESS_ENABLE + case 0xAA: + lkbt51_dfu_rx(data, length); + return true; + +#endif +#ifdef FACTORY_TEST_ENABLE + case 0xAB: + factory_test_rx(data, length); + return true; + +#endif + default: + return false; + } + + raw_hid_send(data, length); + return true; +} + +#if defined(VIA_ENABLE) +bool via_command_kb(uint8_t *data, uint8_t length) { + return kc_raw_hid_rx(data, length); +} +#else +void raw_hid_receive(uint8_t *data, uint8_t length) { + kc_raw_hid_rx(data, length); +} +#endif diff --git a/keyboards/keychron/common/keychron_raw_hid.h b/keyboards/keychron/common/keychron_raw_hid.h new file mode 100644 index 0000000000..4419f0f73f --- /dev/null +++ b/keyboards/keychron/common/keychron_raw_hid.h @@ -0,0 +1,65 @@ +/* Copyright 2024 @ Keychron (https://www.keychron.com) + * + * 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 + +#define PROTOCOL_VERSION 0x02 +#define MISC_PROTOCOL_VERSION 0x0002 +#define QMK_COMMAND_SET 2 + +enum { + KC_GET_PROTOCOL_VERSION = 0xA0, + KC_GET_FIRMWARE_VERSION = 0xA1, + KC_GET_SUPPORT_FEATURE = 0xA2, + KC_GET_DEFAULT_LAYER = 0xA3, +}; + +enum { + FEATURE_DEFAULT_LAYER = 0x01U << 0, + FEATURE_BLUETOOTH = 0x01U << 1, + FEATURE_P24G = 0x01U << 2, + FEATURE_ANALOG_MATRIX = 0x01U << 3, + FEATURE_INFO_CHAGNED_NOTIFY = 0x01U << 4, + FEATURE_DYNAMIC_DEBOUNCE = 0x01U << 5, + FEATURE_SNAP_CLICK = 0x01U << 6, + FEATURE_KEYCHRON_RGB = 0x01U << 7, +}; + +enum { + MISC_DFU_INFO = 0x01 << 0, + MISC_LANGUAGE = 0x01 << 1, + MISC_DEBOUNCE = 0x01 << 2, + MISC_SNAP_CLICK = 0x01 << 3, + MISC_WIRELESS_LPM = 0x01 << 4, + MISC_REPORT_REATE = 0x01 << 5, +}; + +enum { + MISC_GET_PROTOCOL_VER = 0x01, + DFU_INFO_GET, + LANGUAGE_GET, + LANGUAGE_SET, + DEBOUNCE_GET, // 5 + DEBOUNCE_SET, + SNAP_CLICK_GET_INFO, + SNAP_CLICK_GET, + SNAP_CLICK_SET, + SNAP_CLICK_SAVE, // A + WIRELESS_LPM_GET, + WIRELESS_LPM_SET, + REPORT_RATE_GET, + REPORT_RATE_SET, +}; diff --git a/keyboards/keychron/common/keychron_task.c b/keyboards/keychron/common/keychron_task.c index e8407bf1fe..95fca0a9e5 100644 --- a/keyboards/keychron/common/keychron_task.c +++ b/keyboards/keychron/common/keychron_task.c @@ -1,4 +1,4 @@ -/* Copyright 2023 @ Keychron (https://www.keychron.com) +/* Copyright 2023~2025 @ Keychron (https://www.keychron.com) * * 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 @@ -21,6 +21,9 @@ #ifdef FACTORY_TEST_ENABLE # include "factory_test.h" #endif +#ifdef RETAIL_DEMO_ENABLE +# include "retail_demo.h" +#endif __attribute__((weak)) bool process_record_keychron_kb(uint16_t keycode, keyrecord_t *record) { return true; @@ -34,10 +37,27 @@ bool process_record_keychron(uint16_t keycode, keyrecord_t *record) { #ifdef FACTORY_TEST_ENABLE if (!process_record_factory_test(keycode, record)) return false; #endif - // extern bool process_record_keychron_kb(uint16_t keycode, keyrecord_t *record); + +#ifdef SNAP_CLICK_ENABLE + extern bool process_record_snap_click(uint16_t keycode, keyrecord_t * record); + if (!process_record_snap_click(keycode, record)) return false; +#endif if (!process_record_keychron_kb(keycode, record)) return false; +#if defined(KEYCHRON_RGB_ENABLE) && defined(EECONFIG_SIZE_CUSTOM_RGB) +# if defined(RETAIL_DEMO_ENABLE) + if (!process_record_retail_demo(keycode, record)) { + return false; + } +# endif + + extern bool process_record_keychron_rgb(uint16_t keycode, keyrecord_t *record); + if (!process_record_keychron_rgb(keycode, record)) { + return false; + } +#endif + return true; } @@ -79,6 +99,10 @@ void keychron_task(void) { #ifdef FACTORY_TEST_ENABLE factory_test_task(); #endif +#if defined(RETAIL_DEMO_ENABLE) && defined(KEYCHRON_RGB_ENABLE) && defined(EECONFIG_SIZE_CUSTOM_RGB) + retail_demo_task(); +#endif + keychron_common_task(); keychron_task_kb(); @@ -86,7 +110,7 @@ void keychron_task(void) { bool process_record_kb(uint16_t keycode, keyrecord_t *record) { if (!process_record_user(keycode, record)) return false; - + if (!process_record_keychron(keycode, record)) return false; return true; diff --git a/keyboards/keychron/common/language/eeconfig_language.h b/keyboards/keychron/common/language/eeconfig_language.h new file mode 100644 index 0000000000..16aefcd147 --- /dev/null +++ b/keyboards/keychron/common/language/eeconfig_language.h @@ -0,0 +1,20 @@ +/* Copyright 2024 @ Keychron (https://www.keychron.com) + * + * 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 + +#define EECONFIG_SIZE_LANGUAGE 1 + diff --git a/keyboards/keychron/common/language/language.c b/keyboards/keychron/common/language/language.c new file mode 100644 index 0000000000..934cc31cdf --- /dev/null +++ b/keyboards/keychron/common/language/language.c @@ -0,0 +1,60 @@ +/* Copyright 2024 @ Keychron (https://www.keychron.com) + * + * 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 "eeconfig_kb.h" +#include "raw_hid.h" +#include "eeconfig.h" +#include "matrix.h" +#include "quantum.h" +#include "keychron_raw_hid.h" + +static uint8_t lang; + +static bool language_get(uint8_t *data) { + eeprom_read_block(&lang, (uint8_t *)(EECONFIG_BASE_LANGUAGE), sizeof(lang)); + data[1] = lang; + + return true; +} + +static bool language_set(uint8_t *data) { + lang = data[0]; + eeprom_update_block(&lang, (uint8_t *)(EECONFIG_BASE_LANGUAGE), sizeof(lang)); + + return true; +} + +void language_rx(uint8_t *data, uint8_t length) { + uint8_t cmd = data[1]; + bool success = true; + + switch (cmd) { + case LANGUAGE_GET: + success = language_get(&data[2]); + break; + + case LANGUAGE_SET: + success = language_set(&data[2]); + break; + + default: + data[0] = 0xFF; + break; + } + + data[2] = success ? 0 : 1; +} diff --git a/keyboards/keychron/common/language/language.h b/keyboards/keychron/common/language/language.h new file mode 100644 index 0000000000..0a320d7903 --- /dev/null +++ b/keyboards/keychron/common/language/language.h @@ -0,0 +1,21 @@ +/* Copyright 2024 @ Keychron (https://www.keychron.com) + * + * 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 + +void language_config_reset(void); +void language_rx(uint8_t *data, uint8_t length); + diff --git a/keyboards/keychron/common/language/language.mk b/keyboards/keychron/common/language/language.mk new file mode 100644 index 0000000000..6a9d01d0a1 --- /dev/null +++ b/keyboards/keychron/common/language/language.mk @@ -0,0 +1,7 @@ +LANGUAGE_DIR = common/language +SRC += \ + $(LANGUAGE_DIR)/language.c \ + +VPATH += $(TOP_DIR)/keyboards/keychron/$(LANGUAGE_DIR) + +OPT_DEFS += -DLANGUAGE_ENABLE diff --git a/keyboards/keychron/common/rgb/eeconfig_custom_rgb.h b/keyboards/keychron/common/rgb/eeconfig_custom_rgb.h new file mode 100644 index 0000000000..bc0cf34f49 --- /dev/null +++ b/keyboards/keychron/common/rgb/eeconfig_custom_rgb.h @@ -0,0 +1,39 @@ +/* Copyright 2024 @ Keychron (https://www.keychron.com) + * + * 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 "rgb_matrix_kb_config.h" + +#define OS_INDICATOR_CONFIG_SIZE 4 // sizeof(os_indicator_config_t) + +//#define OS_INDICATOR_CONFIG_OFFSET (PER_KEY_RGB_LED_COLOR_LIST_SIZE + RGB_MATRIX_LED_COUNT) +#define RETAIL_DEMO_SIZE 1 // sizeof(retail_demo_enable) + +#define PER_KEY_RGB_TYPE_SIZE 1 +#define PER_KEY_RGB_LED_COLOR_LIST_SIZE (RGB_MATRIX_LED_COUNT * 3) + +#define MIX_RGB_LAYER_FLAG_SIZE RGB_MATRIX_LED_COUNT +#define EFFECT_CONFIG_SIZE 8 // sizeof(effect_config_t) +#define EFFECT_LIST_SIZE (EFFECT_LAYERS * EFFECTS_PER_LAYER * EFFECT_CONFIG_SIZE) + +#define EECONFIG_SIZE_CUSTOM_RGB ( \ + OS_INDICATOR_CONFIG_SIZE \ + + RETAIL_DEMO_SIZE \ + + PER_KEY_RGB_TYPE_SIZE \ + + PER_KEY_RGB_LED_COLOR_LIST_SIZE \ + + MIX_RGB_LAYER_FLAG_SIZE \ + + EFFECT_LIST_SIZE) diff --git a/keyboards/keychron/common/rgb/keychron_rgb.c b/keyboards/keychron/common/rgb/keychron_rgb.c new file mode 100644 index 0000000000..83d9451d44 --- /dev/null +++ b/keyboards/keychron/common/rgb/keychron_rgb.c @@ -0,0 +1,494 @@ +/* Copyright 2024 @ Keychron (https://www.keychron.com) + * + * 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 QMK_KEYBOARD_H +#include "raw_hid.h" +#include "keychron_common.h" +#include "keychron_rgb_type.h" +#include "eeconfig_kb.h" +#include "usb_main.h" +#include "color.h" +#ifdef LK_WIRELESS_ENABLE +#include "transport.h" +#endif +#include + +#if defined(KEYCHRON_RGB_ENABLE) && defined(EECONFIG_SIZE_CUSTOM_RGB) + +# define PER_KEY_RGB_VER 0x0001 + +# define OFFSET_OS_INDICATOR ((uint8_t *)(EECONFIG_BASE_CUSTOM_RGB)) +# define OFFSET_RETAIL_DEMO (OFFSET_OS_INDICATOR + sizeof(os_indicator_config_t)) +# define OFFSET_PER_KEY_RGB_TYPE (OFFSET_RETAIL_DEMO + sizeof(retail_demo_enable)) +# define OFFSET_PER_KEY_RGBS (OFFSET_PER_KEY_RGB_TYPE + sizeof(per_key_rgb_type)) +# define OFFSET_LAYER_FLAGS (OFFSET_PER_KEY_RGBS + sizeof(per_key_led)) +# define OFFSET_EFFECT_LIST (OFFSET_LAYER_FLAGS + sizeof(regions)) + +enum { + RGB_GET_PROTOCOL_VER = 0x01, + RGB_SAVE, + GET_INDICATORS_CONFIG, + SET_INDICATORS_CONFIG, + RGB_GET_LED_COUNT, + RGB_GET_LED_IDX, + PER_KEY_RGB_GET_TYPE, + PER_KEY_RGB_SET_TYPE, + PER_KEY_RGB_GET_COLOR, + PER_KEY_RGB_SET_COLOR, //10 + MIXED_EFFECT_RGB_GET_INFO, + MIXED_EFFECT_RGB_GET_REGIONS, + MIXED_EFFECT_RGB_SET_REGIONS, + MIXED_EFFECT_RGB_GET_EFFECT_LIST, + MIXED_EFFECT_RGB_SET_EFFECT_LIST, +}; + +extern uint8_t retail_demo_enable; +extern uint8_t per_key_rgb_type; +extern HSV per_key_led[RGB_MATRIX_LED_COUNT]; +extern HSV default_per_key_led[RGB_MATRIX_LED_COUNT]; + +extern uint8_t regions[RGB_MATRIX_LED_COUNT]; +extern uint8_t rgb_regions[RGB_MATRIX_LED_COUNT]; +extern effect_config_t effect_list[EFFECT_LAYERS][EFFECTS_PER_LAYER]; +extern uint8_t default_region[RGB_MATRIX_LED_COUNT]; + +os_indicator_config_t os_ind_cfg; + +extern void update_mixed_rgb_effect_count(void); + +void eeconfig_reset_custom_rgb(void) { + os_ind_cfg.disable.raw = 0; + os_ind_cfg.hsv.s = 0; + os_ind_cfg.hsv.h = os_ind_cfg.hsv.v = 0xFF; + + eeprom_update_block(&os_ind_cfg, OFFSET_OS_INDICATOR, sizeof(os_ind_cfg)); + retail_demo_enable = 0; + eeprom_read_block(&retail_demo_enable, (uint8_t *)(OFFSET_RETAIL_DEMO), sizeof(retail_demo_enable)); + per_key_rgb_type = 0; + eeprom_update_block(&per_key_rgb_type, OFFSET_PER_KEY_RGB_TYPE, sizeof(per_key_rgb_type)); + + memcpy(per_key_led, default_per_key_led, sizeof(per_key_led)); + eeprom_update_block(per_key_led, OFFSET_PER_KEY_RGBS, sizeof(per_key_led)); + + memcpy(regions, default_region, RGB_MATRIX_LED_COUNT); + eeprom_update_block(regions, OFFSET_LAYER_FLAGS, sizeof(regions)); + + memset(effect_list, 0, sizeof(effect_list)); + + effect_list[0][0].effect = 5; + effect_list[0][0].sat = 255; + effect_list[0][0].speed = 127; + effect_list[0][0].time = 5000; + + effect_list[1][0].effect = 2; + effect_list[1][0].hue = 0; + effect_list[1][0].sat = 255; + effect_list[1][0].speed = 127; + effect_list[1][0].time = 5000; + + eeprom_update_block(effect_list, OFFSET_EFFECT_LIST, sizeof(effect_list)); + update_mixed_rgb_effect_count(); +} + +void eeconfig_init_custom_rgb(void) { + memcpy(per_key_led, default_per_key_led, sizeof(per_key_led)); + eeprom_update_dword(EECONFIG_KEYBOARD, (EECONFIG_KB_DATA_VERSION)); + + eeprom_read_block(&os_ind_cfg, OFFSET_OS_INDICATOR, sizeof(os_ind_cfg)); + eeprom_read_block(&retail_demo_enable, (uint8_t *)(OFFSET_RETAIL_DEMO), sizeof(retail_demo_enable)); + + if (os_ind_cfg.hsv.v < 128) os_ind_cfg.hsv.v = 128; + // Load per key rgb led + eeprom_read_block(&per_key_rgb_type, OFFSET_PER_KEY_RGB_TYPE, sizeof(per_key_rgb_type)); + eeprom_read_block(per_key_led, OFFSET_PER_KEY_RGBS, sizeof(per_key_led)); + // Load mixed rgb + eeprom_read_block(regions, OFFSET_LAYER_FLAGS, sizeof(regions)); + eeprom_read_block(effect_list, OFFSET_EFFECT_LIST, sizeof(effect_list)); + update_mixed_rgb_effect_count(); + +} + +void rgb_save_retail_demo(void) { + eeprom_update_block(&retail_demo_enable, (uint8_t *)(OFFSET_RETAIL_DEMO), sizeof(retail_demo_enable)); +} + +static bool rgb_get_version(uint8_t *data) { + data[1] = PER_KEY_RGB_VER & 0xFF; + data[2] = (PER_KEY_RGB_VER >> 8) & 0xFF; + + return true; +} + +static bool rgb_get_led_count(uint8_t *data) { + data[1] = RGB_MATRIX_LED_COUNT; + + return true; +} + +static bool rgb_get_led_idx(uint8_t *data) { + uint8_t row = data[0]; + if (row > MATRIX_ROWS) return false; + + uint8_t led_idx[128]; + uint32_t row_mask = 0; + memcpy(&row_mask, &data[1], 3); + + for (uint8_t c = 0; c < MATRIX_COLS; c++) { + led_idx[0] = 0xFF; + if (row_mask & (0x01 << c)) { + rgb_matrix_map_row_column_to_led(row, c, led_idx); + } + data[1 + c] = led_idx[0]; + } + + return true; +} + +static bool per_key_rgb_get_type(uint8_t *data) { + extern uint8_t per_key_rgb_type; + data[1] = per_key_rgb_type; + + return true; +} + +static bool per_key_rgb_set_type(uint8_t *data) { + uint8_t type = data[0]; + + if (type >= PER_KEY_RGB_MAX) return false; + + per_key_rgb_type = data[0]; + + return true; +} + +static bool per_key_rgb_get_led_color(uint8_t *data) { + uint8_t start = data[0]; + uint8_t count = data[1]; + + if (count > 9) return false; + + for (uint8_t i = 0; i < count; i++) { + data[1 + i * 3] = per_key_led[start + i].h; + data[2 + i * 3] = per_key_led[start + i].s; + data[3 + i * 3] = per_key_led[start + i].v; + } + + return true; +} + +static bool per_key_rgb_set_led_color(uint8_t *data) { + uint8_t start = data[0]; + uint8_t count = data[1]; + + if (count > 9) return false; + + for (uint8_t i = 0; i < count; i++) { + per_key_led[start + i].h = data[2 + i * 3]; + per_key_led[start + i].s = data[3 + i * 3]; + per_key_led[start + i].v = data[4 + i * 3]; + } + + return true; +} + +static bool mixed_rgb_get_effect_info(uint8_t *data) { + data[1] = EFFECT_LAYERS; + data[2] = EFFECTS_PER_LAYER; + + return true; +} + +static bool mixed_rgb_get_regions(uint8_t *data) { + uint8_t start = data[0]; + uint8_t count = data[1]; + + if (count > 29 || start + count > RGB_MATRIX_LED_COUNT) return false; + memcpy(&data[1], ®ions[start], count); + + return true; +} + +bool mixed_rgb_set_regions(uint8_t *data) { + uint8_t start = data[0]; + uint8_t count = data[1]; + + if (count > 28 || start + count > RGB_MATRIX_LED_COUNT) return false; + for (uint8_t i = 0; i < count; i++) + if (data[2 + i] >= EFFECT_LAYERS) return false; + + memcpy(®ions[start], &data[2], count); + memcpy(&rgb_regions[start], &data[2], count); + + return true; +} +#define EFFECT_DATA_LEN 8 + +static bool mixed_rgb_get_effect_list(uint8_t *data) { + uint8_t region = data[0]; + uint8_t start = data[1]; + uint8_t count = data[2]; + + if (count > 3 || region > EFFECT_LAYERS || start + count > EFFECTS_PER_LAYER) return false; + + for (uint8_t i = 0; i < count; i++) { + data[1 + i * EFFECT_DATA_LEN] = effect_list[region][start + i].effect; + data[2 + i * EFFECT_DATA_LEN] = effect_list[region][start + i].hue; + data[3 + i * EFFECT_DATA_LEN] = effect_list[region][start + i].sat; + data[4 + i * EFFECT_DATA_LEN] = effect_list[region][start + i].speed; + memcpy(&data[5 + i * EFFECT_DATA_LEN], &effect_list[region][start + i].time, 4); + } + + return true; +} + +bool mixed_rgb_set_effect_list(uint8_t *data) { + uint8_t region = data[0]; + uint8_t start = data[1]; + uint8_t count = data[2]; + + if (count > 3 || region > EFFECT_LAYERS || start + count > EFFECTS_PER_LAYER) return false; + for (uint8_t i = 0; i < count; i++) { + if (data[3 + i * EFFECT_DATA_LEN] >= RGB_MATRIX_CUSTOM_MIXED_RGB) return false; + } + + for (uint8_t i = 0; i < count; i++) { + effect_list[region][start + i].effect = data[3 + i * EFFECT_DATA_LEN]; + effect_list[region][start + i].hue = data[4 + i * EFFECT_DATA_LEN]; + effect_list[region][start + i].sat = data[5 + i * EFFECT_DATA_LEN]; + effect_list[region][start + i].speed = data[6 + i * EFFECT_DATA_LEN]; + memcpy(&effect_list[region][start + i].time, &data[7 + i * EFFECT_DATA_LEN], 4); + } + update_mixed_rgb_effect_count(); + + return true; +} + +static bool kc_rgb_save(void) { + eeprom_update_block(&os_ind_cfg, OFFSET_OS_INDICATOR, sizeof(os_ind_cfg)); + eeprom_update_block(&per_key_rgb_type, OFFSET_PER_KEY_RGB_TYPE, sizeof(per_key_rgb_type)); + eeprom_update_block(per_key_led, OFFSET_PER_KEY_RGBS, RGB_MATRIX_LED_COUNT * sizeof(rgb_led_t)); + eeprom_update_block(regions, OFFSET_LAYER_FLAGS, RGB_MATRIX_LED_COUNT); + eeprom_update_block(effect_list, OFFSET_EFFECT_LIST, sizeof(effect_list)); + + return true; +} + +static bool get_indicators_config(uint8_t *data) { + data[1] = 0 +#if defined(NUM_LOCK_INDEX) && !defined(DIM_NUM_LOCK) + | (1 << 0x00) +#endif +#if defined(CAPS_LOCK_INDEX) && !defined(DIM_CAPS_LOCK) + | (1 << 0x01) +#endif +#if defined(SCROLL_LOCK_INDEX) + | (1 << 0x02) +#endif +#if defined(COMPOSE_LOCK_INDEX) + | (1 << 0x03) +#endif +#if defined(KANA_LOCK_INDEX) + | (1 << 0x04) +#endif +; + data[2] = os_ind_cfg.disable.raw; + data[3] = os_ind_cfg.hsv.h; + data[4] = os_ind_cfg.hsv.s; + data[5] = os_ind_cfg.hsv.v; + + return true; +} + +static bool set_indicators_config(uint8_t *data) { + os_ind_cfg.disable.raw = data[0]; + os_ind_cfg.hsv.h = data[1]; + os_ind_cfg.hsv.s = data[2]; + os_ind_cfg.hsv.v = data[3]; + + if (os_ind_cfg.hsv.v < 128) os_ind_cfg.hsv.v = 128; + led_update_kb(host_keyboard_led_state()); + + return true; +} + +void kc_rgb_matrix_rx(uint8_t *data, uint8_t length) { + uint8_t cmd = data[1]; + bool success = true; + + switch (cmd) { + case RGB_GET_PROTOCOL_VER: + success = rgb_get_version(&data[2]); + break; + + case RGB_SAVE: + success = kc_rgb_save(); + break; + + case GET_INDICATORS_CONFIG: + success = get_indicators_config(&data[2]); + break; + + case SET_INDICATORS_CONFIG: + success = set_indicators_config(&data[2]); + break; + + case RGB_GET_LED_COUNT: + success = rgb_get_led_count(&data[2]); + break; + + case RGB_GET_LED_IDX: + success = rgb_get_led_idx(&data[2]); + break; + + case PER_KEY_RGB_GET_TYPE: + success = per_key_rgb_get_type(&data[2]); + break; + + case PER_KEY_RGB_SET_TYPE: + success = per_key_rgb_set_type(&data[2]); + break; + + case PER_KEY_RGB_GET_COLOR: + success = per_key_rgb_get_led_color(&data[2]); + break; + + case PER_KEY_RGB_SET_COLOR: + success = per_key_rgb_set_led_color(&data[2]); + break; + + case MIXED_EFFECT_RGB_GET_INFO: + success = mixed_rgb_get_effect_info(&data[2]); + break; + + case MIXED_EFFECT_RGB_GET_REGIONS: + success = mixed_rgb_get_regions(&data[2]); + break; + + case MIXED_EFFECT_RGB_SET_REGIONS: + success = mixed_rgb_set_regions(&data[2]); + break; + + case MIXED_EFFECT_RGB_GET_EFFECT_LIST: + success = mixed_rgb_get_effect_list(&data[2]); + break; + + case MIXED_EFFECT_RGB_SET_EFFECT_LIST: + success = mixed_rgb_set_effect_list(&data[2]); + break; + + default: + data[0] = 0xFF; + break; + } + + data[2] = success ? 0 : 1; +} + +void os_state_indicate(void) { +# if defined(RGB_DISABLE_WHEN_USB_SUSPENDED) || defined(LED_DISABLE_WHEN_USB_SUSPENDED) + if (get_transport() == TRANSPORT_USB && USB_DRIVER.state == USB_SUSPENDED) return; +# endif + + RGB rgb = hsv_to_rgb(os_ind_cfg.hsv); + +# if defined(NUM_LOCK_INDEX) + if (host_keyboard_led_state().num_lock && !os_ind_cfg.disable.num_lock) { + rgb_matrix_set_color(NUM_LOCK_INDEX, rgb.r, rgb.g, rgb.b); + } +# endif +# if defined(CAPS_LOCK_INDEX) + if (host_keyboard_led_state().caps_lock && !os_ind_cfg.disable.caps_lock) { + rgb_matrix_set_color(CAPS_LOCK_INDEX, rgb.r, rgb.g, rgb.b); + } +# endif +# if defined(SCROLL_LOCK_INDEX) + if (host_keyboard_led_state().compose && !os_ind_cfg.disable.scroll_lock) { + rgb_matrix_set_color(SCROLL_LOCK_INDEX, rgb.r, rgb.g, rgb.b); + } +# endif +# if defined(COMPOSE_LOCK_INDEX) + if (host_keyboard_led_state().compose && !os_ind_cfg.disable.compose) { + rgb_matrix_set_color(COMPOSE_LOCK_INDEX, rgb.r, rgb.g, rgb.b); + } +# endif +# if defined(KANA_LOCK_INDEX) + if (host_keyboard_led_state().kana && !os_ind_cfg.disable.kana) { + rgb_matrix_set_color(KANA_LOCK_INDEX, rgb.r, rgb.g, rgb.b); + } +# endif + (void)rgb; +} + +bool process_record_keychron_rgb(uint16_t keycode, keyrecord_t *record) { + if (rgb_matrix_get_mode() == RGB_MATRIX_CUSTOM_MIXED_RGB || rgb_matrix_get_mode() == RGB_MATRIX_CUSTOM_PER_KEY_RGB) { + switch (keycode) { + case RGB_HUI ... RGB_SAD: + return false; + + case RGB_SPI: + if (rgb_matrix_get_mode() == RGB_MATRIX_CUSTOM_MIXED_RGB) { + return false; + } else { + rgb_matrix_config.speed = qadd8(rgb_matrix_config.speed, RGB_MATRIX_SPD_STEP); + eeprom_write_byte((uint8_t *)EECONFIG_RGB_MATRIX + offsetof(rgb_config_t, speed), rgb_matrix_config.speed); + } + break; + case RGB_SPD: + if (rgb_matrix_get_mode() == RGB_MATRIX_CUSTOM_MIXED_RGB) { + return false; + } else { + rgb_matrix_config.speed = qsub8(rgb_matrix_config.speed, RGB_MATRIX_SPD_STEP); + eeprom_write_byte((uint8_t *)EECONFIG_RGB_MATRIX + offsetof(rgb_config_t, speed), rgb_matrix_config.speed); + } + break; + + case RGB_VAI: +# ifdef RGB_MATRIX_BRIGHTNESS_TURN_OFF_VAL + if (!rgb_matrix_config.enable) { + rgb_matrix_toggle(); + return false; + } +# endif + rgb_matrix_config.hsv.v = qadd8(rgb_matrix_config.hsv.v, RGB_MATRIX_VAL_STEP); +# ifdef RGB_MATRIX_BRIGHTNESS_TURN_OFF_VAL + while (rgb_matrix_config.hsv.v <= RGB_MATRIX_BRIGHTNESS_TURN_OFF_VAL) + rgb_matrix_config.hsv.v = qadd8(rgb_matrix_config.hsv.v, RGB_MATRIX_VAL_STEP); +# endif + eeprom_write_byte((uint8_t *)EECONFIG_RGB_MATRIX + offsetof(rgb_config_t, hsv.v), rgb_matrix_config.hsv.v); + return false; + + case RGB_VAD: +# ifdef RGB_MATRIX_BRIGHTNESS_TURN_OFF_VAL + if (rgb_matrix_config.enable && rgb_matrix_config.hsv.v > RGB_MATRIX_BRIGHTNESS_TURN_OFF_VAL) +# endif + { + rgb_matrix_config.hsv.v = qsub8(rgb_matrix_config.hsv.v, RGB_MATRIX_VAL_STEP); + eeprom_write_byte((uint8_t *)EECONFIG_RGB_MATRIX + offsetof(rgb_config_t, hsv.v), rgb_matrix_config.hsv.v); + } +# ifdef RGB_MATRIX_BRIGHTNESS_TURN_OFF_VAL + if (rgb_matrix_config.enable && rgb_matrix_config.hsv.v <= RGB_MATRIX_BRIGHTNESS_TURN_OFF_VAL) { + rgb_matrix_toggle(); + } +# endif + return false; + + default: + break; + } + } + return true; +} +#endif diff --git a/keyboards/keychron/common/rgb/keychron_rgb_type.h b/keyboards/keychron/common/rgb/keychron_rgb_type.h new file mode 100644 index 0000000000..f9e17d548c --- /dev/null +++ b/keyboards/keychron/common/rgb/keychron_rgb_type.h @@ -0,0 +1,59 @@ +/* Copyright 2024 @ Keychron (https://www.keychron.com) + * + * 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 "color.h" + +enum { + PER_KEY_RGB_SOLID, + PER_KEY_RGB_BREATHING, + PER_KEY_RGB_REATIVE_SIMPLE, + PER_KEY_RGB_REATIVE_MULTI_WIDE, + PER_KEY_RGB_REATIVE_SPLASH, + PER_KEY_RGB_MAX, +}; + +typedef struct PACKED { + uint8_t effect; + uint8_t hue; + uint8_t sat; + uint8_t speed; + uint32_t time; +} effect_config_t; + +typedef union { + uint8_t raw; + struct { + bool num_lock : 1; + bool caps_lock : 1; + bool scroll_lock : 1; + bool compose : 1; + bool kana : 1; + uint8_t reserved : 3; + }; +} os_led_t; + +// TODO: +// typedef struct PACKED HSV2 { +// uint8_t h; +// uint8_t s; +// uint8_t v; +// } HSV2; + +typedef struct PACKED { + os_led_t disable; + HSV hsv; +} os_indicator_config_t; diff --git a/keyboards/keychron/common/rgb/mixed_rgb.c b/keyboards/keychron/common/rgb/mixed_rgb.c new file mode 100644 index 0000000000..16fcafa0ac --- /dev/null +++ b/keyboards/keychron/common/rgb/mixed_rgb.c @@ -0,0 +1,191 @@ +/* Copyright 2024 @ Keychron (https://www.keychron.com) + * + * 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 . + */ + +#if defined(KEYCHRON_RGB_ENABLE) && defined(EECONFIG_SIZE_CUSTOM_RGB) + +#include "quantum.h" +#include "rgb_matrix.h" +#include "keychron_rgb_type.h" + +#define RGB_MATRIX_EFFECT(name, ...) \ + extern bool name(effect_params_t *params); +#include "rgb_matrix_effects.inc" +#include "rgb_matrix_kb.inc" +#undef RGB_MATRIX_EFFECT + +// PER_KEY_RGB data +extern uint8_t per_key_rgb_type; + +// MIXED_RGB data +extern uint8_t rgb_regions[RGB_MATRIX_LED_COUNT]; +uint8_t regions[RGB_MATRIX_LED_COUNT] = {0}; // +effect_config_t effect_list[EFFECT_LAYERS][EFFECTS_PER_LAYER]; + +uint8_t layer_effect_count[EFFECT_LAYERS] = {0}; +uint8_t layer_effect_index[EFFECT_LAYERS] = {0}; +uint32_t layer_effect_timer[EFFECT_LAYERS] = {0}; + +// Typing heatmap +uint8_t typingHeatmap = 0; + +static bool multiple_rgb_effect_runner(effect_params_t *params); + +void mixed_rgb_reset(void) { + typingHeatmap = 0; + for (uint8_t i=0; iinit) { + memcpy(rgb_regions, regions, RGB_MATRIX_LED_COUNT); + memset(layer_effect_index, 0, sizeof(layer_effect_index)); + + mixed_rgb_reset(); + } + + for (int8_t i=EFFECT_LAYERS-1; i>=0; i--) { + params->region = i; + ret = multiple_rgb_effect_runner(params); + } + + return ret; +} + +#define TRANSITION_TIME 1000 + +bool multiple_rgb_effect_runner(effect_params_t *params) { + HSV hsv= rgb_matrix_get_hsv(); + uint8_t backup_value = hsv.v; + + bool transation = false; + bool rendering = false; + uint8_t layer = params->region; + + uint8_t effect_index = layer_effect_index[layer]; + + if (effect_list[layer][effect_index].effect == RGB_MATRIX_TYPING_HEATMAP) + typingHeatmap |= 0x01 << layer; + else + typingHeatmap &= ~(0x01 << layer); + + uint8_t last_effect = effect_list[layer][layer_effect_index[layer]].effect; + + if (layer_effect_count[layer] > 1) { + if (timer_elapsed32(layer_effect_timer[layer]) > effect_list[layer][effect_index].time) { + layer_effect_timer[layer] = timer_read32(); + if (++layer_effect_index[layer] >= EFFECTS_PER_LAYER) layer_effect_index[layer] = 0; + + effect_index = layer_effect_index[layer]; + + if (effect_list[layer][effect_index].time == 0) return true; // + } + else if (timer_elapsed32(layer_effect_timer[layer]) > effect_list[layer][effect_index].time - TRANSITION_TIME) + { + hsv.v = backup_value*(effect_list[layer][effect_index].time - timer_elapsed32(layer_effect_timer[layer]))/TRANSITION_TIME; + transation = true; + } + + if (timer_elapsed32(layer_effect_timer[layer]) < TRANSITION_TIME) + { + hsv.v = backup_value*timer_elapsed32(layer_effect_timer[layer])/TRANSITION_TIME; + transation = true; + } + } else if (layer_effect_count[layer] == 1 && effect_list[layer][effect_index].effect == 0) { + for (uint8_t i=0; iregion] = i; + break; + } + } + } + + uint8_t effect = effect_list[layer][effect_index].effect; + if (effect == 0) ++layer_effect_index[layer]; // Skip effect 0 + if (layer_effect_index[layer] >= EFFECTS_PER_LAYER) layer_effect_index[layer] = 0; + + effect = effect_list[layer][effect_index].effect; + hsv.h = effect_list[layer][effect_index].hue; + hsv.s = effect_list[layer][effect_index].sat; + rgb_matrix_sethsv_noeeprom(hsv.h, hsv.s, hsv.v); + + rgb_matrix_set_speed_noeeprom(effect_list[layer][effect_index].speed); + + params->init = last_effect != effect; + + // each effect can opt to do calculations + // and/or request PWM buffer updates. + switch (effect) { + // --------------------------------------------- + // -----Begin rgb effect switch case macros----- +#define RGB_MATRIX_EFFECT(name, ...) \ + case RGB_MATRIX_##name: \ + rendering = name(params); \ + break; +#include "rgb_matrix_effects.inc" +#undef RGB_MATRIX_EFFECT + +#if defined(RGB_MATRIX_CUSTOM_KB) || defined(RGB_MATRIX_CUSTOM_USER) +# define RGB_MATRIX_EFFECT(name, ...) \ + case RGB_MATRIX_CUSTOM_##name: \ + rendering = name(params); \ + break; +# ifdef RGB_MATRIX_CUSTOM_KB +# include "rgb_matrix_kb.inc" +# endif +# undef RGB_MATRIX_EFFECT +#endif + // -----End rgb effect switch case macros------- + // --------------------------------------------- + } + + if (transation) { + rgb_matrix_sethsv_noeeprom(hsv.h, hsv.s, backup_value); + } + + return rendering; + +} + +void process_rgb_matrix_kb(uint8_t row, uint8_t col, bool pressed) { + if (pressed) + { + if (rgb_matrix_config.mode == RGB_MATRIX_CUSTOM_MIXED_RGB) { + extern void process_rgb_matrix_typing_heatmap(uint8_t row, uint8_t col); + if (typingHeatmap) process_rgb_matrix_typing_heatmap(row, col); + } + } +} +#endif diff --git a/keyboards/keychron/common/rgb/per_key_rgb.c b/keyboards/keychron/common/rgb/per_key_rgb.c new file mode 100644 index 0000000000..8cf18af824 --- /dev/null +++ b/keyboards/keychron/common/rgb/per_key_rgb.c @@ -0,0 +1,160 @@ +/* Copyright 2024 @ Keychron (https://www.keychron.com) + * + * 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 "quantum.h" +#include "rgb_matrix.h" +#include "keychron_rgb_type.h" +#include +#include + +#if defined(KEYCHRON_RGB_ENABLE) + +// PER_KEY_RGB data +uint8_t per_key_rgb_type; +HSV per_key_led[RGB_MATRIX_LED_COUNT] = {0}; + +bool per_key_rgb_solid(effect_params_t *params) { + RGB_MATRIX_USE_LIMITS(led_min, led_max); + HSV hsv; + + for (uint8_t i = led_min; i < led_max; i++) { + hsv = per_key_led[i]; + hsv.v = rgb_matrix_config.hsv.v; + RGB rgb = hsv_to_rgb(hsv); + rgb_matrix_region_set_color(params->region, i, rgb.r, rgb.g, rgb.b); + } + return rgb_matrix_check_finished_leds(led_max); +} + +bool per_key_rgb_breahting(effect_params_t *params) { + RGB_MATRIX_USE_LIMITS(led_min, led_max); + HSV hsv; + uint16_t time = scale16by8(g_rgb_timer, rgb_matrix_config.speed / 8); + + for (uint8_t i = led_min; i < led_max; i++) { + hsv = per_key_led[i]; + hsv.v = scale8(abs8(sin8(time) - 128) * 2, rgb_matrix_config.hsv.v); + RGB rgb = hsv_to_rgb(hsv); + RGB_MATRIX_TEST_LED_FLAGS(); + rgb_matrix_region_set_color(params->region, i, rgb.r, rgb.g, rgb.b); + } + + return rgb_matrix_check_finished_leds(led_max); +} + +bool per_key_rgb_reactive_simple(effect_params_t *params) { + RGB_MATRIX_USE_LIMITS(led_min, led_max); + + uint16_t max_tick = 65535 / qadd8(rgb_matrix_config.speed, 1); + for (uint8_t i = led_min; i < led_max; i++) { + RGB_MATRIX_TEST_LED_FLAGS(); + uint16_t tick = max_tick; + // Reverse search to find most recent key hit + for (int8_t j = g_last_hit_tracker.count - 1; j >= 0; j--) { + if (g_last_hit_tracker.index[j] == i && g_last_hit_tracker.tick[j] < tick) { + tick = g_last_hit_tracker.tick[j]; + break; + } + } + + uint16_t offset = scale16by8(tick, qadd8(rgb_matrix_config.speed, 1)); + HSV hsv = per_key_led[i]; + + hsv.v = scale8(255 - offset, rgb_matrix_config.hsv.v); + if (per_key_led[i].v < hsv.v) + hsv.v = per_key_led[i].v; + + RGB rgb = hsv_to_rgb(hsv); + rgb_matrix_region_set_color(params->region, i, rgb.r, rgb.g, rgb.b); + } + return rgb_matrix_check_finished_leds(led_max); + +} + +typedef HSV (*reactive_splash_f)(HSV hsv, int16_t dx, int16_t dy, uint8_t dist, uint16_t tick); + +bool per_key_rgb_effect_runner_reactive_splash(uint8_t start, effect_params_t* params, reactive_splash_f effect_func) { + RGB_MATRIX_USE_LIMITS(led_min, led_max); + + uint8_t count = g_last_hit_tracker.count; + for (uint8_t i = led_min; i < led_max; i++) { + RGB_MATRIX_TEST_LED_FLAGS(); + HSV hsv = rgb_matrix_config.hsv; + hsv.v = 0; + for (uint8_t j = start; j < count; j++) { + int16_t dx = g_led_config.point[i].x - g_last_hit_tracker.x[j]; + int16_t dy = g_led_config.point[i].y - g_last_hit_tracker.y[j]; + uint8_t dist = sqrt16(dx * dx + dy * dy); + uint16_t tick = scale16by8(g_last_hit_tracker.tick[j], qadd8(rgb_matrix_config.speed, 1)); + hsv = effect_func(hsv, dx, dy, dist, tick); + } + hsv.h = per_key_led[i].h; + hsv.s = per_key_led[i].s; + hsv.v = scale8(hsv.v, rgb_matrix_config.hsv.v); + if (per_key_led[i].v < hsv.v) + hsv.v = per_key_led[i].v; + RGB rgb = hsv_to_rgb(hsv); + rgb_matrix_region_set_color(params->region, i, rgb.r, rgb.g, rgb.b); + } + return rgb_matrix_check_finished_leds(led_max); +} + +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, qadd8(rgb_matrix_config.speed, 8) >> 4); +# endif + hsv.v = qadd8(hsv.v, 255 - effect); + return hsv; +} + +bool per_key_rgb_reactive_multi_wide(effect_params_t *params) { + return per_key_rgb_effect_runner_reactive_splash(0, params, &solid_reactive_wide_math); +} + +static HSV SPLASH_math(HSV hsv, int16_t dx, int16_t dy, uint8_t dist, uint16_t tick) { + uint16_t effect = tick - dist; + if (effect > 255) effect = 255; + hsv.h += effect; + hsv.v = qadd8(hsv.v, 255 - effect); + return hsv; +} + +bool per_key_rgb_reactive_splash(effect_params_t *params) { + return per_key_rgb_effect_runner_reactive_splash(qsub8(g_last_hit_tracker.count, 1), params, &SPLASH_math); +} + +bool per_key_rgb(effect_params_t *params) { + switch (per_key_rgb_type) { + case PER_KEY_RGB_BREATHING: + return per_key_rgb_breahting(params); + + case PER_KEY_RGB_REATIVE_SIMPLE: + return per_key_rgb_reactive_simple(params); + + case PER_KEY_RGB_REATIVE_MULTI_WIDE: + return per_key_rgb_reactive_multi_wide(params); + + case PER_KEY_RGB_REATIVE_SPLASH: + return per_key_rgb_reactive_splash(params); + + default: + return per_key_rgb_solid(params); + } +} + +#endif diff --git a/keyboards/keychron/common/rgb/retail_demo.c b/keyboards/keychron/common/rgb/retail_demo.c new file mode 100644 index 0000000000..03f5360706 --- /dev/null +++ b/keyboards/keychron/common/rgb/retail_demo.c @@ -0,0 +1,185 @@ +/* Copyright 2024 @ Keychron (https://www.keychron.com) + * + * 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 "eeconfig_kb.h" +#include "retail_demo.h" +#include "eeconfig.h" +#include "matrix.h" +#include "quantum.h" +#ifdef LK_WIRELESS_ENABLE +# include "transport.h" +#endif + +#if defined(RETAIL_DEMO_ENABLE) && defined(KEYCHRON_RGB_ENABLE) && defined(EECONFIG_SIZE_CUSTOM_RGB) + +# ifndef RETAIL_DEMO_KEY_1 +# ifdef RGB_MATRIX_ENABLE +# define RETAIL_DEMO_KEY_1 RGB_HUI +# else +# define RETAIL_DEMO_KEY_1 KC_D +# endif +# endif + +# ifndef RETAIL_DEMO_KEY_2 +# ifdef RGB_MATRIX_ENABLE +# define RETAIL_DEMO_KEY_2 RGB_HUD +# else +# define RETAIL_DEMO_KEY_2 KC_E +# endif +# endif + +# ifndef EFFECT_DURATION +# define EFFECT_DURATION 10000 +# endif + +enum { + KEY_PRESS_FN = 0x01 << 0, + KEY_PRESS_D = 0x01 << 1, + KEY_PRESS_E = 0x01 << 2, + KEY_PRESS_RETAIL_DEMO = KEY_PRESS_FN | KEY_PRESS_D | KEY_PRESS_E, +}; + +uint8_t retail_demo_enable = 0; +static uint8_t retail_demo_combo = 0; +static uint32_t retail_demo_timer = 0; + +extern void rgb_save_retail_demo(void); + +bool process_record_retail_demo(uint16_t keycode, keyrecord_t *record) { + switch (keycode) { + case MO(0)... MO(15): + if (record->event.pressed) + retail_demo_combo |= KEY_PRESS_FN; + else + retail_demo_combo &= ~KEY_PRESS_FN; + break; + + case RETAIL_DEMO_KEY_1: + if (record->event.pressed) { + retail_demo_combo |= KEY_PRESS_D; + if (retail_demo_combo == KEY_PRESS_RETAIL_DEMO) retail_demo_timer = timer_read32(); + } else { + retail_demo_combo &= ~KEY_PRESS_D; + retail_demo_timer = 0; + } + break; + + case RETAIL_DEMO_KEY_2: + if (record->event.pressed) { + retail_demo_combo |= KEY_PRESS_E; + if (retail_demo_combo == KEY_PRESS_RETAIL_DEMO) retail_demo_timer = timer_read32(); + } else { + retail_demo_combo &= ~KEY_PRESS_E; + retail_demo_timer = 0; + } + break; + } + + if (retail_demo_enable && keycode >= RGB_TOG && keycode <= RGB_SPD) return false; + + return true; +} + +void retail_demo_start(void) { + extern bool mixed_rgb_set_regions(uint8_t * data); + extern bool mixed_rgb_set_effect_list(uint8_t * data); + + uint8_t index = 0; + uint8_t this_count = 28; + uint8_t data[31] = {0}; + + // Set all LED to region 0 + while (index < RGB_MATRIX_LED_COUNT - 1) { + memset(data, 0, 31); + + if ((index + this_count) >= RGB_MATRIX_LED_COUNT) + this_count = RGB_MATRIX_LED_COUNT - 1 - index; + else + this_count = 28; + + data[0] = index; + data[1] = this_count; + mixed_rgb_set_regions(data); + + index += this_count; + } + + uint8_t effect_list[5] = {4, 7, 8, 11, 14}; + // Set effect list + for (uint8_t i = 0; i < 5; i++) { + data[0] = 0; // regsion + data[1] = i; // start + data[2] = 1; // count + data[3] = effect_list[i]; // effect + data[4] = 0; // hue + data[5] = 255; // sat + data[6] = 127; // speed; + data[7] = EFFECT_DURATION & 0xFF; + data[8] = (EFFECT_DURATION >> 8) & 0xFF; + data[9] = (EFFECT_DURATION >> 16) & 0xFF; + data[10] = (EFFECT_DURATION >> 24) & 0xFF; + + mixed_rgb_set_effect_list(data); + } + + HSV hsv = rgb_matrix_get_hsv(); + hsv.v = hsv.s = UINT8_MAX; + rgb_matrix_sethsv_noeeprom(hsv.h, hsv.s, hsv.v); + rgb_matrix_set_speed_noeeprom(RGB_MATRIX_DEFAULT_SPD); + rgb_matrix_mode_noeeprom(RGB_MATRIX_CUSTOM_MIXED_RGB); +} + +void retail_demo_stop(void) { + retail_demo_enable = false; + rgb_save_retail_demo(); + eeprom_read_block(&rgb_matrix_config, EECONFIG_RGB_MATRIX, sizeof(rgb_matrix_config)); +} + +static inline void retail_demo_timer_check(void) { + if (timer_elapsed32(retail_demo_timer) > 5000) { + retail_demo_timer = 0; + + if (retail_demo_combo == KEY_PRESS_RETAIL_DEMO) { + retail_demo_combo = 0; + retail_demo_enable = !retail_demo_enable; + + if (retail_demo_enable) { +# ifdef LK_WIRELESS_ENABLE + // Retail demo is allowed only in wireless mode + if (get_transport() != TRANSPORT_USB) { + retail_demo_enable = false; + return; + } +# endif + } else { + eeprom_read_block(&rgb_matrix_config, EECONFIG_RGB_MATRIX, sizeof(rgb_matrix_config)); + } + rgb_save_retail_demo(); + + if (!retail_demo_enable) { + extern void eeconfig_init_custom_rgb(void); + eeconfig_init_custom_rgb(); + } + } + } +} + +void retail_demo_task(void) { + if (retail_demo_timer) retail_demo_timer_check(); + if (retail_demo_enable && rgb_matrix_get_mode() != RGB_MATRIX_CUSTOM_MIXED_RGB) retail_demo_start(); +} +#endif diff --git a/keyboards/keychron/common/rgb/retail_demo.h b/keyboards/keychron/common/rgb/retail_demo.h new file mode 100644 index 0000000000..2870a91b60 --- /dev/null +++ b/keyboards/keychron/common/rgb/retail_demo.h @@ -0,0 +1,25 @@ +/* Copyright 2024 @ Keychron (https://www.keychron.com) + * + * 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 "stdint.h" +#include "action.h" + +void retail_demo_start(void); +void retail_demo_stop(void); + +bool process_record_retail_demo(uint16_t keycode, keyrecord_t * record); +void retail_demo_task(void); diff --git a/keyboards/keychron/common/rgb/rgb.mk b/keyboards/keychron/common/rgb/rgb.mk new file mode 100644 index 0000000000..93e0b7ad21 --- /dev/null +++ b/keyboards/keychron/common/rgb/rgb.mk @@ -0,0 +1,14 @@ +OPT_DEFS += -DKEYCHRON_RGB_ENABLE -DRETAIL_DEMO_ENABLE + +RGB_MATRIX_CUSTOM_KB = yes +RGB_MATRIX_DIR = common/rgb + +SRC += \ + $(RGB_MATRIX_DIR)/keychron_rgb.c \ + $(RGB_MATRIX_DIR)/per_key_rgb.c \ + $(RGB_MATRIX_DIR)/mixed_rgb.c \ + $(RGB_MATRIX_DIR)/retail_demo.c + +VPATH += $(TOP_DIR)/keyboards/keychron/$(RGB_MATRIX_DIR) + + diff --git a/keyboards/keychron/common/rgb/rgb_matrix_kb.inc b/keyboards/keychron/common/rgb/rgb_matrix_kb.inc new file mode 100644 index 0000000000..4a538f0cac --- /dev/null +++ b/keyboards/keychron/common/rgb/rgb_matrix_kb.inc @@ -0,0 +1,39 @@ +/* Copyright 2024 @ Keychron (https://www.keychron.com) + * + * 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 "rgb_matrix_kb_config.h" + +#if defined(KEYCHRON_RGB_ENABLE) && defined(EECONFIG_SIZE_CUSTOM_RGB) +//extern bool MIXED_RGB(effect_params_t *params); + +RGB_MATRIX_EFFECT(PER_KEY_RGB) +RGB_MATRIX_EFFECT(MIXED_RGB) + +# ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS + +bool PER_KEY_RGB(effect_params_t *params) { + extern bool per_key_rgb(effect_params_t *params); + return per_key_rgb(params); +} + +bool MIXED_RGB(effect_params_t *params) { + extern bool mixed_rgb(effect_params_t *params); + return mixed_rgb(params); +} + +#endif + +#endif diff --git a/keyboards/keychron/common/rgb/rgb_matrix_kb_config.h b/keyboards/keychron/common/rgb/rgb_matrix_kb_config.h new file mode 100644 index 0000000000..530a46b11c --- /dev/null +++ b/keyboards/keychron/common/rgb/rgb_matrix_kb_config.h @@ -0,0 +1,26 @@ +/* Copyright 2024 @ Keychron (https://www.keychron.com) + * + * 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 "config.h" + +#ifndef EFFECT_LAYERS +#define EFFECT_LAYERS 2 +#endif + +#ifndef EFFECTS_PER_LAYER +#define EFFECTS_PER_LAYER 5 +#endif diff --git a/keyboards/keychron/common/snap_click/eeconfig_snap_click.h b/keyboards/keychron/common/snap_click/eeconfig_snap_click.h new file mode 100644 index 0000000000..d53879ca92 --- /dev/null +++ b/keyboards/keychron/common/snap_click/eeconfig_snap_click.h @@ -0,0 +1,26 @@ +/* Copyright 2024 @ Keychron (https://www.keychron.com) + * + * 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 + +#ifndef SNAP_CLICK_COUNT +# define SNAP_CLICK_COUNT 20 +#endif + +#define SIZE_OF_SNAP_CLICK_CONFIG_T 3 + +#define EECONFIG_SIZE_SNAP_CLICK (SNAP_CLICK_COUNT * SIZE_OF_SNAP_CLICK_CONFIG_T) + diff --git a/keyboards/keychron/common/snap_click/snap_click.c b/keyboards/keychron/common/snap_click/snap_click.c new file mode 100644 index 0000000000..b795df70b1 --- /dev/null +++ b/keyboards/keychron/common/snap_click/snap_click.c @@ -0,0 +1,201 @@ +/* Copyright 2024 @ Keychron (https://www.keychron.com) + * + * 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 "eeconfig_kb.h" +#include "snap_click.h" +#include "raw_hid.h" +#include "eeconfig.h" +#include "matrix.h" +#include "quantum.h" +#include "keychron_raw_hid.h" + +#if defined(SNAP_CLICK_ENABLE) &&defined(EECONFIG_SIZE_SNAP_CLICK) + +enum { + SNAP_CLICK_TYPE_NONE = 0, + SNAP_CLICK_TYPE_REGULAR, + SNAP_CLICK_TYPE_LAST_INPUT, + SNAP_CLICK_TYPE_FIRST_KEY, + SNAP_CLICK_TYPE_SECOND_KEY, + SNAP_CLICK_TYPE_NEUTRAL, + SNAP_CLICK_TYPE_MAX, +}; + +#define SC_MASK_BOTH_KEYS_PRESSED 3 + +snap_click_config_t snap_click_pair[SNAP_CLICK_COUNT]; +snap_click_state_t snap_click_state[SNAP_CLICK_COUNT]; + +void snap_click_config_reset(void) { + memset(snap_click_pair, 0, sizeof(snap_click_pair)); + eeprom_update_block(snap_click_pair, (uint8_t *)(EECONFIG_BASE_SNAP_CLICK), sizeof(snap_click_pair)); +} + +void snap_click_init(void) { + eeprom_read_block(snap_click_pair, (uint8_t *)(EECONFIG_BASE_SNAP_CLICK), sizeof(snap_click_pair)); + memset(snap_click_state, 0, sizeof(snap_click_state)); +} + +bool process_record_snap_click(uint16_t keycode, keyrecord_t * record) +{ + for (uint8_t i=0; itype && (keycode == p->key[0] || keycode == p->key[1])) + { + snap_click_state_t *pState = &snap_click_state[i]; + uint8_t index = keycode == p->key[1]; // 0 or 1 of key pair + + if (record->event.pressed) { + uint8_t state = 0x01 << index; + + if (pState->state == 0) { + // Single key down + pState->state_keys = pState->last_single_key = state; + } else if ((state & pState->state_keys) == 0) { // TODO: do we need checking? + // Both keys are pressed + pState->state_keys = SC_MASK_BOTH_KEYS_PRESSED; + switch (p->type) { + case SNAP_CLICK_TYPE_REGULAR: + case SNAP_CLICK_TYPE_LAST_INPUT: + unregister_code(p->key[1-index]); + register_code(p->key[index]); + break; + case SNAP_CLICK_TYPE_FIRST_KEY: + unregister_code(p->key[1]); + register_code(p->key[0]); + break; + case SNAP_CLICK_TYPE_SECOND_KEY: + unregister_code(p->key[0]); + register_code(p->key[1]); + break; + case SNAP_CLICK_TYPE_NEUTRAL: + unregister_code(p->key[1-index]); + break; + } + return false; + } + } else { + if (pState->state_keys == SC_MASK_BOTH_KEYS_PRESSED) { + // Snap click active + uint8_t state = 0x01 << (1-index); + pState->state_keys = pState->last_single_key = state; + + switch (p->type) { + case SNAP_CLICK_TYPE_REGULAR: + unregister_code(p->key[index]); + break; + case SNAP_CLICK_TYPE_LAST_INPUT: + case SNAP_CLICK_TYPE_FIRST_KEY: + case SNAP_CLICK_TYPE_SECOND_KEY: + if (is_key_pressed(p->key[index])) { + unregister_code(p->key[index]); + } + if (!is_key_pressed(p->key[1-index])) { + register_code(p->key[1-index]); + } + break; + case SNAP_CLICK_TYPE_NEUTRAL: + register_code(p->key[1-index]); + break; + } + + return false; + } else { + pState->state = 0; + } + } + } + } + + return true; +} + +static bool snap_click_get_info(uint8_t *data) { + data[1] = SNAP_CLICK_COUNT; + + return true; +} + +static bool snap_click_get(uint8_t *data) { + uint8_t start = data[0]; + uint8_t count = data[1]; + + if (count > 9 || start + count > SNAP_CLICK_COUNT) return false; + memcpy(&data[1], &snap_click_pair[start], count * sizeof(snap_click_config_t)); + + return true; +} + +static bool snap_click_set(uint8_t *data) { + uint8_t start = data[0]; + uint8_t count = data[1]; + + if (count > 9 || start + count > SNAP_CLICK_COUNT) return false; + for (uint8_t i=0; i= SNAP_CLICK_TYPE_MAX) + return false; + + if (type != 0 && (keycode1 == 0 || keycode2 == 0)) + return false; + } + memcpy(&snap_click_pair[start], &data[2], count * sizeof(snap_click_config_t)); + + return true; +} + +static bool snap_click_save(uint8_t *data) { + eeprom_update_block(snap_click_pair, (uint8_t *)(EECONFIG_BASE_SNAP_CLICK), sizeof(snap_click_pair)); + + return true; +} + +void snap_click_rx(uint8_t *data, uint8_t length) { + uint8_t cmd = data[1]; + bool success = true; + + switch (cmd) { + case SNAP_CLICK_GET_INFO: + success = snap_click_get_info(&data[2]); + break; + + case SNAP_CLICK_GET: + success = snap_click_get(&data[2]); + break; + + case SNAP_CLICK_SET: + success = snap_click_set(&data[2]); + break; + + case SNAP_CLICK_SAVE: + success = snap_click_save(&data[2]); + break; + + default: + data[0] = 0xFF; + break; + } + + data[2] = success ? 0 : 1; +} +#endif diff --git a/keyboards/keychron/common/snap_click/snap_click.h b/keyboards/keychron/common/snap_click/snap_click.h new file mode 100644 index 0000000000..6432116909 --- /dev/null +++ b/keyboards/keychron/common/snap_click/snap_click.h @@ -0,0 +1,43 @@ +/* Copyright 2024 @ Keychron (https://www.keychron.com) + * + * 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 + +typedef struct __attribute__((__packed__)) { + uint8_t type; + uint8_t key[2]; +} snap_click_config_t; +// size = 3 bytes + +typedef union { + uint8_t state; + struct { + uint8_t state_key_1:1; + uint8_t state_key_2:1; + uint8_t last_single_key_1:1; + uint8_t last_single_key_2:1; + uint8_t reserved:4; + }; + struct { + uint8_t state_keys:2; + uint8_t last_single_key:2; + uint8_t reserved2:4; + }; +} snap_click_state_t; + +void snap_click_config_reset(void); +void snap_click_rx(uint8_t *data, uint8_t length); + diff --git a/keyboards/keychron/common/snap_click/snap_click.mk b/keyboards/keychron/common/snap_click/snap_click.mk new file mode 100644 index 0000000000..cf86fe732d --- /dev/null +++ b/keyboards/keychron/common/snap_click/snap_click.mk @@ -0,0 +1,7 @@ +SNAP_CLICK_DIR = common/snap_click +SRC += \ + $(SNAP_CLICK_DIR)/snap_click.c \ + +VPATH += $(TOP_DIR)/keyboards/keychron/$(SNAP_CLICK_DIR) + +OPT_DEFS += -DSNAP_CLICK_ENABLE diff --git a/keyboards/keychron/common/wireless/bat_level_animation.c b/keyboards/keychron/common/wireless/bat_level_animation.c index 2c63ec6cf7..43269d6836 100644 --- a/keyboards/keychron/common/wireless/bat_level_animation.c +++ b/keyboards/keychron/common/wireless/bat_level_animation.c @@ -1,4 +1,20 @@ +/* Copyright 2023~2025 @ Keychron (https://www.keychron.com) + * + * 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 "quantum.h" #include "wireless.h" #include "indicator.h" diff --git a/keyboards/keychron/common/wireless/bat_level_animation.h b/keyboards/keychron/common/wireless/bat_level_animation.h index 716e924103..d8e0e9f2ce 100644 --- a/keyboards/keychron/common/wireless/bat_level_animation.h +++ b/keyboards/keychron/common/wireless/bat_level_animation.h @@ -1,4 +1,4 @@ -/* Copyright 2022 @ lokher (https://www.keychron.com) +/* Copyright 2023~2025 @ lokher (https://www.keychron.com) * * 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 diff --git a/keyboards/keychron/common/wireless/eeconfig_wireless.h b/keyboards/keychron/common/wireless/eeconfig_wireless.h new file mode 100644 index 0000000000..109e6d3c9a --- /dev/null +++ b/keyboards/keychron/common/wireless/eeconfig_wireless.h @@ -0,0 +1,20 @@ +/* Copyright 2024 @ Keychron (https://www.keychron.com) + * + * 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 + +#define EECONFIG_SIZE_WIRELESS_CONFIG 4 //sizeof(backlit_disable_time) + sizeof (connected_idle_time) + diff --git a/keyboards/keychron/common/wireless/indicator.c b/keyboards/keychron/common/wireless/indicator.c index 5c9024660a..6271466fe2 100644 --- a/keyboards/keychron/common/wireless/indicator.c +++ b/keyboards/keychron/common/wireless/indicator.c @@ -1,4 +1,4 @@ -/* Copyright 2023 @ lokher (https://www.keychron.com) +/* Copyright 2023~2025 @ lokher (https://www.keychron.com) * * 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 @@ -60,6 +60,8 @@ enum { BACKLIGHT_ON_UNCONNECTED = 0x02, }; +extern uint16_t backlit_disable_time; + static indicator_config_t pairing_config = INDICATOR_CONFIG_PARING; static indicator_config_t connected_config = INDICATOR_CONFIG_CONNECTD; static indicator_config_t reconnecting_config = INDICATOR_CONFIG_RECONNECTING; @@ -201,12 +203,13 @@ inline void indicator_disable(void) { LED_DRIVER_DISABLE_NOEEPROM(); } -void indicator_set_backlit_timeout(uint32_t time) { - LED_DRIVER_DISABLE_TIMEOUT_SET(time); +void indicator_reset_backlit_time(void) { + LED_DRIVER_DISABLE_TIME_RESET(); } -static inline void indicator_reset_backlit_time(void) { - LED_DRIVER_DISABLE_TIME_RESET(); +void indicator_set_backlit_timeout(uint32_t time) { + LED_DRIVER_DISABLE_TIMEOUT_SET(time); + indicator_reset_backlit_time(); } bool indicator_is_enabled(void) { @@ -443,7 +446,7 @@ void indicator_set(wt_state_t state, uint8_t host_index) { indicator_timer_cb((void *)&indicator_config.type); } #if defined(LED_MATRIX_ENABLE) || defined(RGB_MATRIX_ENABLE) - indicator_set_backlit_timeout(DECIDE_TIME(CONNECTED_BACKLIGHT_DISABLE_TIMEOUT * 1000, indicator_config.duration)); + indicator_set_backlit_timeout(DECIDE_TIME(backlit_disable_time * 1000, indicator_config.duration)); #endif break; @@ -541,6 +544,9 @@ void indicator_battery_low_enable(bool enable) { } else { rtc_time = 0; bat_low_ind_state = 0; +# if defined(BAT_LOW_LED_PIN) + writePin(BAT_LOW_LED_PIN, !BAT_LOW_LED_PIN_ON_STATE); +# endif # if defined(SPACE_KEY_LOW_BAT_IND) indicator_eeconfig_reload(); if (!LED_DRIVER_IS_ENABLED()) indicator_disable(); @@ -552,7 +558,8 @@ void indicator_battery_low_enable(bool enable) { void indicator_battery_low(void) { #if defined(BAT_LOW_LED_PIN) || defined(SPACE_KEY_LOW_BAT_IND) if (bat_low_ind_state) { - if ((bat_low_ind_state & 0x0F) <= (LOW_BAT_LED_BLINK_TIMES) && timer_elapsed32(bat_low_backlit_indicator) > (LOW_BAT_LED_BLINK_PERIOD)) { + if ((bat_low_ind_state & 0x0F) <= (LOW_BAT_LED_BLINK_TIMES) && + timer_elapsed32(bat_low_backlit_indicator) > (LOW_BAT_LED_BLINK_PERIOD)) { if (bat_low_ind_state & 0x80) { bat_low_ind_state &= 0x7F; bat_low_ind_state++; diff --git a/keyboards/keychron/common/wireless/indicator.h b/keyboards/keychron/common/wireless/indicator.h index 1632356af8..6bbae137c4 100644 --- a/keyboards/keychron/common/wireless/indicator.h +++ b/keyboards/keychron/common/wireless/indicator.h @@ -1,4 +1,4 @@ -/* Copyright 2023 @ lokher (https://www.keychron.com) +/* Copyright 2023~2025 @ lokher (https://www.keychron.com) * * 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 @@ -101,7 +101,7 @@ typedef struct { void indicator_init(void); void indicator_set(wt_state_t state, uint8_t host_index); void indicator_set_backlit_timeout(uint32_t time); -void indicator_backlight_timer_reset(bool enable); +void indicator_reset_backlit_time(void); bool indicator_hook_key(uint16_t keycode); void indicator_enable(void); void indicator_disable(void); diff --git a/keyboards/keychron/common/wireless/keychron_wireless_common.c b/keyboards/keychron/common/wireless/keychron_wireless_common.c index 12fa367320..2fa6273fa6 100644 --- a/keyboards/keychron/common/wireless/keychron_wireless_common.c +++ b/keyboards/keychron/common/wireless/keychron_wireless_common.c @@ -1,4 +1,4 @@ -/* Copyright 2022 @ Keychron (https://www.keychron.com) +/* Copyright 2022~2025 @ Keychron (https://www.keychron.com) * * 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 @@ -32,6 +32,7 @@ bool firstDisconnect = true; static uint32_t pairing_key_timer; static uint8_t host_idx = 0; +extern uint32_t connected_idle_time; bool process_record_keychron_wireless(uint16_t keycode, keyrecord_t *record) { static uint8_t host_idx; @@ -84,7 +85,7 @@ void lkbt51_param_init(void) { // clang-format off /* Set bluetooth parameters */ module_param_t param = {.event_mode = 0x02, - .connected_idle_timeout = 7200, + .connected_idle_timeout = connected_idle_time, .pairing_timeout = 180, .pairing_mode = 0, .reconnect_timeout = 5, diff --git a/keyboards/keychron/common/wireless/lpm.c b/keyboards/keychron/common/wireless/lpm.c index 121e9cf783..c6f9a12c1c 100644 --- a/keyboards/keychron/common/wireless/lpm.c +++ b/keyboards/keychron/common/wireless/lpm.c @@ -194,6 +194,11 @@ static inline void lpm_wakeup(void) { halInit(); +#if defined(DIP_SWITCH_PINS) + /* Init dip switch as early as possible, and read it later. */ + dip_switch_init(); +#endif + #ifdef ENCODER_ENABLE encoder_cb_init(); #endif @@ -227,15 +232,30 @@ static inline void lpm_wakeup(void) { #endif -#if defined(DIP_SWITCH_PINS) - dip_switch_init(); - dip_switch_read(true); -#endif /* Call debounce_free() to avoiding memory leak of debounce_counters as debounce_init() invoked in matrix_init() alloc new memory to debounce_counters */ debounce_free(); matrix_init(); + +#ifdef ENABLE_RGB_MATRIX_PIXEL_RAIN + extern void PIXEL_RAIN_init(void); + PIXEL_RAIN_init(); +#endif + +#ifdef ENABLE_RGB_MATRIX_PIXEL_FLOW + extern void PIXEL_FLOW_init(void); + PIXEL_FLOW_init(); +#endif + +#ifdef ENABLE_RGB_MATRIX_PIXEL_FRACTAL + extern void PIXEL_FRACTAL_init(void); + PIXEL_FRACTAL_init(); +#endif + +#if defined(DIP_SWITCH_PINS) + dip_switch_read(true); +#endif } void lpm_task(void) { diff --git a/keyboards/keychron/common/wireless/wireless.c b/keyboards/keychron/common/wireless/wireless.c index 02099e8e63..24e9b18fdb 100644 --- a/keyboards/keychron/common/wireless/wireless.c +++ b/keyboards/keychron/common/wireless/wireless.c @@ -24,6 +24,8 @@ #include "rtc_timer.h" #include "keychron_wireless_common.h" #include "keychron_task.h" +#include "wireless_config.h" +#include "keychron_raw_hid.h" extern uint8_t pairing_indication; extern host_driver_t chibios_driver; @@ -39,6 +41,9 @@ static wt_state_t wireless_state = WT_RESET; static bool pincodeEntry = false; uint8_t wireless_report_protocol = true; +uint16_t backlit_disable_time = CONNECTED_BACKLIGHT_DISABLE_TIMEOUT; +uint16_t connected_idle_time = CONNECTED_IDLE_TIME; + /* declarations */ uint8_t wreless_keyboard_leds(void); void wireless_send_keyboard(report_keyboard_t *report); @@ -55,6 +60,8 @@ wireless_event_t wireless_event_queue[WT_EVENT_QUEUE_SIZE]; uint8_t wireless_event_queue_head; uint8_t wireless_event_queue_tail; +bool wireless_lpm_set(uint8_t *data); + void wireless_event_queue_init(void) { // Initialise the event queue memset(&wireless_event_queue, 0, sizeof(wireless_event_queue)); @@ -82,6 +89,41 @@ static inline bool wireless_event_dequeue(wireless_event_t *event) { return true; } +#if defined(EECONFIG_BASE_WIRELESS_CONFIG) +void wireless_config_reset(void) { + uint8_t data[4] = { 0 }; + + uint16_t backlit_disable_time = CONNECTED_BACKLIGHT_DISABLE_TIMEOUT; + uint16_t connected_idle_time = CONNECTED_IDLE_TIME; + + memcpy(&data[0], &backlit_disable_time, sizeof(backlit_disable_time)); + memcpy(&data[2], &connected_idle_time, sizeof(connected_idle_time)); + wireless_lpm_set(data); +} + +void wireless_config_load(void) { + uint8_t offset = 0; + eeprom_read_block(&backlit_disable_time, (uint8_t *)(EECONFIG_BASE_WIRELESS_CONFIG+offset), sizeof(backlit_disable_time)); + offset += sizeof(backlit_disable_time); + eeprom_read_block(&connected_idle_time, (uint8_t *)(EECONFIG_BASE_WIRELESS_CONFIG+offset), sizeof(connected_idle_time)); + + if (backlit_disable_time == 0) + backlit_disable_time = CONNECTED_BACKLIGHT_DISABLE_TIMEOUT; + else if (backlit_disable_time < 5 ) backlit_disable_time = 5; + + if (connected_idle_time == 0) + connected_idle_time = CONNECTED_IDLE_TIME; + else if (connected_idle_time < 30 ) connected_idle_time = 30; +} + +void wireless_config_save(void) { + uint8_t offset = 0; + eeprom_update_block(&backlit_disable_time, (uint8_t *)(EECONFIG_BASE_WIRELESS_CONFIG+offset), sizeof(backlit_disable_time)); + offset += sizeof(backlit_disable_time); + eeprom_update_block(&connected_idle_time, (uint8_t *)(EECONFIG_BASE_WIRELESS_CONFIG+offset), sizeof(connected_idle_time)); +} +#endif + /* * Bluetooth init. */ @@ -102,6 +144,10 @@ void wireless_init(void) { #if HAL_USE_RTC rtc_timer_init(); #endif + +#if defined(EECONFIG_BASE_WIRELESS_CONFIG) + wireless_config_load(); +#endif } /* @@ -253,9 +299,10 @@ static void wireless_enter_disconnected(uint8_t host_idx, uint8_t reason) { indicator_set(WT_SUSPEND, host_idx); } else { indicator_set(wireless_state, host_idx); -#if defined(RGB_MATRIX) || defined(LED_MATRIX) - if (reason && (get_transport() & TRANSPORT_WIRELESS)) +#if defined(RGB_MATRIX_ENABLE) || defined(LED_MATRIX_ENABLE) + if (reason && (get_transport() & TRANSPORT_WIRELESS)) { indicator_set_backlit_timeout(DISCONNECTED_BACKLIGHT_DISABLE_TIMEOUT*1000); + } #endif } @@ -539,3 +586,72 @@ bool process_record_wireless(uint16_t keycode, keyrecord_t *record) { return true; } + +#if defined(EECONFIG_BASE_WIRELESS_CONFIG) +bool wireless_lpm_get(uint8_t *data) { + uint8_t index = 1; + memcpy(&data[index], &backlit_disable_time, sizeof(backlit_disable_time)); + index += sizeof(backlit_disable_time); + memcpy(&data[index], &connected_idle_time, sizeof(connected_idle_time)); + + return true; +} + +bool wireless_lpm_set(uint8_t *data) { + uint8_t index = 0; + + memcpy(&backlit_disable_time, &data[index], sizeof(backlit_disable_time)); + index += sizeof(backlit_disable_time); + memcpy(&connected_idle_time, &data[index], sizeof(connected_idle_time)); + + if (backlit_disable_time < 5 || connected_idle_time < 60) { + wireless_config_load(); + return false; + } + + wireless_config_save(); + + // Reset backlight timeout + if ((get_transport() & TRANSPORT_WIRELESS) && wireless_state == WT_CONNECTED) + { + indicator_set_backlit_timeout(backlit_disable_time*1000); + indicator_reset_backlit_time(); + + // Wiggle mouse to reset bluetooth module timer + mousekey_on(KC_MS_LEFT); + mousekey_send(); + wait_ms(10); + mousekey_on(KC_MS_RIGHT); + mousekey_send(); + wait_ms(10); + mousekey_off((KC_MS_RIGHT)); + mousekey_send(); + wait_ms(10); + } + + // Update bluetooth module param + lkbt51_param_init(); + return true; +} + +void wireless_raw_hid_rx(uint8_t *data, uint8_t length) { + uint8_t cmd = data[1]; + bool success = true; + + switch (cmd) { + case WIRELESS_LPM_GET: + success = wireless_lpm_get(&data[2]); + break; + + case WIRELESS_LPM_SET: + success = wireless_lpm_set(&data[2]); + break; + + default: + data[0] = 0xFF; + break; + } + + data[2] = success ? 0 : 1; +} +#endif diff --git a/keyboards/keychron/common/wireless/wireless.h b/keyboards/keychron/common/wireless/wireless.h index 04d6008318..0ead527d47 100644 --- a/keyboards/keychron/common/wireless/wireless.h +++ b/keyboards/keychron/common/wireless/wireless.h @@ -1,4 +1,4 @@ -/* Copyright 2023 @ lokher (https://www.keychron.com) +/* Copyright 2023~2025 @ lokher (https://www.keychron.com) * * 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 @@ -67,6 +67,8 @@ typedef struct { extern void register_wt_tasks(void); void wireless_init(void); +void wireless_config_reset(void); + void wireless_set_transport(wt_func_t *transport); void wireless(void); @@ -99,3 +101,6 @@ wt_state_t wireless_get_state(void); void wireless_low_battery_shutdown(void); bool process_record_wireless(uint16_t keycode, keyrecord_t *record); + +void wireless_raw_hid_rx(uint8_t *data, uint8_t length); + diff --git a/keyboards/keychron/common/wireless/wireless.mk b/keyboards/keychron/common/wireless/wireless.mk index 43373fdac3..b575624b06 100644 --- a/keyboards/keychron/common/wireless/wireless.mk +++ b/keyboards/keychron/common/wireless/wireless.mk @@ -1,4 +1,4 @@ -OPT_DEFS += -DLK_WIRELESS_ENABLE +OPT_DEFS += -DLK_WIRELESS_ENABLE -DWIRELESS_CONFIG_ENABLE OPT_DEFS += -DNO_USB_STARTUP_CHECK OPT_DEFS += -DCORTEX_ENABLE_WFI_IDLE=TRUE diff --git a/keyboards/keychron/common/wireless/wireless_config.h b/keyboards/keychron/common/wireless/wireless_config.h index e55ad24290..f1305f3fd1 100644 --- a/keyboards/keychron/common/wireless/wireless_config.h +++ b/keyboards/keychron/common/wireless/wireless_config.h @@ -1,4 +1,4 @@ -/* Copyright 2023 @ lokher (https://www.keychron.com) +/* Copyright 2023~2025 @ lokher (https://www.keychron.com) * * 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 @@ -25,12 +25,7 @@ #define P2P4G_HOST_DEVICES_COUNT 1 -// Uint: Second -#ifndef DISCONNECTED_BACKLIGHT_OFF_DELAY_TIME -# define DISCONNECTED_BACKLIGHT_OFF_DELAY_TIME 40 -#endif - // Uint: Second, the timer restarts on key activities. -#ifndef CONNECTED_BACKLIGHT_OFF_DELAY_TIME -# define CONNECTED_BACKLIGHT_OFF_DELAY_TIME 600 +#ifndef CONNECTED_IDLE_TIME +# define CONNECTED_IDLE_TIME 7200 #endif diff --git a/keyboards/keychron/common/wireless/wireless_event_type.h b/keyboards/keychron/common/wireless/wireless_event_type.h index d2d39de410..430ace916f 100644 --- a/keyboards/keychron/common/wireless/wireless_event_type.h +++ b/keyboards/keychron/common/wireless/wireless_event_type.h @@ -1,4 +1,4 @@ -/* Copyright 2023 @ lokher (https://www.keychron.com) +/* Copyright 2023~2025 @ lokher (https://www.keychron.com) * * 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 diff --git a/quantum/rgb_matrix/animations/alpha_mods_anim.h b/quantum/rgb_matrix/animations/alpha_mods_anim.h index 59b8381d69..8e4a70d5a0 100644 --- a/quantum/rgb_matrix/animations/alpha_mods_anim.h +++ b/quantum/rgb_matrix/animations/alpha_mods_anim.h @@ -14,9 +14,9 @@ bool ALPHAS_MODS(effect_params_t* params) { for (uint8_t i = led_min; i < led_max; i++) { RGB_MATRIX_TEST_LED_FLAGS(); if (HAS_FLAGS(g_led_config.flags[i], LED_FLAG_MODIFIER)) { - rgb_matrix_set_color(i, rgb2.r, rgb2.g, rgb2.b); + rgb_matrix_region_set_color(params->region, i, rgb2.r, rgb2.g, rgb2.b); } else { - rgb_matrix_set_color(i, rgb1.r, rgb1.g, rgb1.b); + rgb_matrix_region_set_color(params->region, i, rgb1.r, rgb1.g, rgb1.b); } } return rgb_matrix_check_finished_leds(led_max); diff --git a/quantum/rgb_matrix/animations/breathing_anim.h b/quantum/rgb_matrix/animations/breathing_anim.h index e9a3c96e1b..ac73a7cd83 100644 --- a/quantum/rgb_matrix/animations/breathing_anim.h +++ b/quantum/rgb_matrix/animations/breathing_anim.h @@ -11,7 +11,7 @@ bool BREATHING(effect_params_t* params) { RGB rgb = rgb_matrix_hsv_to_rgb(hsv); for (uint8_t i = led_min; i < led_max; i++) { RGB_MATRIX_TEST_LED_FLAGS(); - rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); + rgb_matrix_region_set_color(params->region, i, rgb.r, rgb.g, rgb.b); } return rgb_matrix_check_finished_leds(led_max); } diff --git a/quantum/rgb_matrix/animations/digital_rain_anim.h b/quantum/rgb_matrix/animations/digital_rain_anim.h index 7d3b22f697..10dbf56dbd 100644 --- a/quantum/rgb_matrix/animations/digital_rain_anim.h +++ b/quantum/rgb_matrix/animations/digital_rain_anim.h @@ -7,76 +7,108 @@ RGB_MATRIX_EFFECT(DIGITAL_RAIN) # define RGB_DIGITAL_RAIN_DROPS 24 # endif +uint8_t rain_rgb_frame_buffer[MATRIX_ROWS][MATRIX_COLS] = {{0}}; + bool DIGITAL_RAIN(effect_params_t* params) { // algorithm ported from https://github.com/tremby/Kaleidoscope-LEDEffect-DigitalRain const uint8_t drop_ticks = 28; const uint8_t pure_green_intensity = (((uint16_t)rgb_matrix_config.hsv.v) * 3) >> 2; const uint8_t max_brightness_boost = (((uint16_t)rgb_matrix_config.hsv.v) * 3) >> 2; - const uint8_t max_intensity = rgb_matrix_config.hsv.v; + static uint8_t max_intensity = RGB_MATRIX_MAXIMUM_BRIGHTNESS; const uint8_t decay_ticks = 0xff / max_intensity; static uint8_t drop = 0; static uint8_t decay = 0; + static bool render = true; + + RGB_MATRIX_USE_LIMITS(led_min, led_max); if (params->init) { - rgb_matrix_set_color_all(0, 0, 0); - memset(g_rgb_frame_buffer, 0, sizeof(g_rgb_frame_buffer)); + rgb_matrix_region_set_color_all(params->region, 0, 0, 0); + memset(rain_rgb_frame_buffer, 0, sizeof(rain_rgb_frame_buffer)); drop = 0; } - decay++; - for (uint8_t col = 0; col < MATRIX_COLS; col++) { - for (uint8_t row = 0; row < MATRIX_ROWS; row++) { - if (row == 0 && drop == 0 && rand() < RAND_MAX / RGB_DIGITAL_RAIN_DROPS) { - // top row, pixels have just fallen and we're - // making a new rain drop in this column - g_rgb_frame_buffer[row][col] = max_intensity; - } else if (g_rgb_frame_buffer[row][col] > 0 && g_rgb_frame_buffer[row][col] < max_intensity) { - // neither fully bright nor dark, decay it - if (decay == decay_ticks) { - g_rgb_frame_buffer[row][col]--; + if (params->iter == 0) { + + if (max_intensity != rgb_matrix_config.hsv.v) { + // Check if value is decreased + if (max_intensity > rgb_matrix_config.hsv.v) { + for (uint8_t col = 0; col < MATRIX_COLS; col++) { + for (uint8_t row = 0; row < MATRIX_ROWS; row++) { + rain_rgb_frame_buffer[row][col] = rain_rgb_frame_buffer[row][col] * (uint16_t)rgb_matrix_config.hsv.v / max_intensity; + } } } - // 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); - // TODO: multiple leds are supported mapped to the same row/column - if (led_count > 0) { - if (g_rgb_frame_buffer[row][col] > pure_green_intensity) { - const uint8_t boost = (uint8_t)((uint16_t)max_brightness_boost * (g_rgb_frame_buffer[row][col] - pure_green_intensity) / (max_intensity - pure_green_intensity)); - rgb_matrix_set_color(led[0], boost, max_intensity, boost); - } else { - const uint8_t green = (uint8_t)((uint16_t)max_intensity * g_rgb_frame_buffer[row][col] / pure_green_intensity); - rgb_matrix_set_color(led[0], 0, green, 0); + max_intensity = rgb_matrix_config.hsv.v; + } + + if (render) + decay++; + + for (uint8_t col = 0; col < MATRIX_COLS; col++) { + for (uint8_t row = 0; row < MATRIX_ROWS; row++) { + if (render) { + if (row == 0 && drop == 0 && rand() < RAND_MAX / RGB_DIGITAL_RAIN_DROPS) { + // top row, pixels have just fallen and we're + // making a new rain drop in this column + rain_rgb_frame_buffer[row][col] = max_intensity; + } else if (rain_rgb_frame_buffer[row][col] > 0 && rain_rgb_frame_buffer[row][col] < max_intensity) { + // neither fully bright nor dark, decay it + if (decay == decay_ticks) { + rain_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); + + // TODO: multiple leds are supported mapped to the same row/column + if (led_count > 0) { + if (rain_rgb_frame_buffer[row][col] > pure_green_intensity) { + const uint8_t boost = (uint8_t)((uint16_t)max_brightness_boost * (rain_rgb_frame_buffer[row][col] - pure_green_intensity) / (max_intensity - pure_green_intensity)); + rgb_matrix_region_set_color(params->region, led[0], boost, max_intensity, boost); + } else { + const uint8_t green = (uint8_t)((uint16_t)max_intensity * rain_rgb_frame_buffer[row][col] / pure_green_intensity); + rgb_matrix_region_set_color(params->region, led[0], 0, green, 0); + } } } } - } - if (decay == decay_ticks) { - decay = 0; - } - if (++drop > drop_ticks) { - // reset drop timer - drop = 0; - for (uint8_t row = MATRIX_ROWS - 1; row > 0; row--) { - for (uint8_t col = 0; col < MATRIX_COLS; col++) { - // if ths is on the bottom row and bright allow decay - if (row == MATRIX_ROWS - 1 && g_rgb_frame_buffer[row][col] == max_intensity) { - g_rgb_frame_buffer[row][col]--; - } - // check if the pixel above is bright - if (g_rgb_frame_buffer[row - 1][col] >= max_intensity) { // Note: can be larger than max_intensity if val was recently decreased - // allow old bright pixel to decay - g_rgb_frame_buffer[row - 1][col] = max_intensity - 1; - // make this pixel bright - g_rgb_frame_buffer[row][col] = max_intensity; + if (render) { + if (decay == decay_ticks) { + decay = 0; + } + + if (++drop > drop_ticks) { + // reset drop timer + drop = 0; + for (uint8_t row = MATRIX_ROWS - 1; row > 0; row--) { + for (uint8_t col = 0; col < MATRIX_COLS; col++) { + // if ths is on the bottom row and bright allow decay + if (row == MATRIX_ROWS - 1 && rain_rgb_frame_buffer[row][col] == max_intensity) { + rain_rgb_frame_buffer[row][col]--; + } + // check if the pixel above is bright + if (rain_rgb_frame_buffer[row - 1][col] >= max_intensity) { // Note: can be larger than max_intensity if val was recently decreased + // allow old bright pixel to decay + rain_rgb_frame_buffer[row - 1][col] = max_intensity - 1; + // make this pixel bright + rain_rgb_frame_buffer[row][col] = max_intensity; + } + } } } } + render = false; + } else { + render = true; } - return false; + + return rgb_matrix_check_finished_leds(led_max); } # endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS diff --git a/quantum/rgb_matrix/animations/flower_blooming_anim.h b/quantum/rgb_matrix/animations/flower_blooming_anim.h index 7629fde858..135bff7e70 100644 --- a/quantum/rgb_matrix/animations/flower_blooming_anim.h +++ b/quantum/rgb_matrix/animations/flower_blooming_anim.h @@ -28,10 +28,10 @@ bool effect_runner_bloom(effect_params_t* params, flower_blooming_f effect_func) RGB_MATRIX_TEST_LED_FLAGS(); if (g_led_config.point[i].y > k_rgb_matrix_center.y) { RGB bgr = rgb_matrix_hsv_to_rgb(effect_func(rgb_matrix_config.hsv, i, time)); - rgb_matrix_set_color(i, bgr.b, bgr.g, bgr.r); + rgb_matrix_region_set_color(params->region, i, bgr.b, bgr.g, bgr.r); } else { RGB rgb = rgb_matrix_hsv_to_rgb(effect_func(rgb_matrix_config.hsv, i, time)); - rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); + rgb_matrix_region_set_color(params->region, i, rgb.r, rgb.g, rgb.b); } } return rgb_matrix_check_finished_leds(led_max); diff --git a/quantum/rgb_matrix/animations/gradient_left_right_anim.h b/quantum/rgb_matrix/animations/gradient_left_right_anim.h index ebb06f59f2..191ef00725 100644 --- a/quantum/rgb_matrix/animations/gradient_left_right_anim.h +++ b/quantum/rgb_matrix/animations/gradient_left_right_anim.h @@ -13,7 +13,7 @@ bool GRADIENT_LEFT_RIGHT(effect_params_t* params) { // Relies on hue being 8-bit and wrapping hsv.h = rgb_matrix_config.hsv.h + (scale * g_led_config.point[i].x >> 5); RGB rgb = rgb_matrix_hsv_to_rgb(hsv); - rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); + rgb_matrix_region_set_color(params->region, i, rgb.r, rgb.g, rgb.b); } return rgb_matrix_check_finished_leds(led_max); } diff --git a/quantum/rgb_matrix/animations/gradient_up_down_anim.h b/quantum/rgb_matrix/animations/gradient_up_down_anim.h index febc3919a8..3680323818 100644 --- a/quantum/rgb_matrix/animations/gradient_up_down_anim.h +++ b/quantum/rgb_matrix/animations/gradient_up_down_anim.h @@ -13,7 +13,7 @@ bool GRADIENT_UP_DOWN(effect_params_t* params) { // Relies on hue being 8-bit and wrapping hsv.h = rgb_matrix_config.hsv.h + scale * (g_led_config.point[i].y >> 4); RGB rgb = rgb_matrix_hsv_to_rgb(hsv); - rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); + rgb_matrix_region_set_color(params->region, i, rgb.r, rgb.g, rgb.b); } return rgb_matrix_check_finished_leds(led_max); } diff --git a/quantum/rgb_matrix/animations/hue_breathing_anim.h b/quantum/rgb_matrix/animations/hue_breathing_anim.h index 8537762832..0d9c4d651c 100644 --- a/quantum/rgb_matrix/animations/hue_breathing_anim.h +++ b/quantum/rgb_matrix/animations/hue_breathing_anim.h @@ -13,7 +13,7 @@ bool HUE_BREATHING(effect_params_t* params) { RGB rgb = hsv_to_rgb(hsv); for (uint8_t i = led_min; i < led_max; i++) { RGB_MATRIX_TEST_LED_FLAGS(); - rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); + rgb_matrix_region_set_color(params->region, i, rgb.r, rgb.g, rgb.b); } return rgb_matrix_check_finished_leds(led_max); } diff --git a/quantum/rgb_matrix/animations/jellybean_raindrops_anim.h b/quantum/rgb_matrix/animations/jellybean_raindrops_anim.h index 5d3df1059e..55ae931d4d 100644 --- a/quantum/rgb_matrix/animations/jellybean_raindrops_anim.h +++ b/quantum/rgb_matrix/animations/jellybean_raindrops_anim.h @@ -6,7 +6,7 @@ static void jellybean_raindrops_set_color(int i, effect_params_t* params) { if (!HAS_ANY_FLAGS(g_led_config.flags[i], params->flags)) return; HSV hsv = {random8(), random8_min_max(127, 255), rgb_matrix_config.hsv.v}; RGB rgb = rgb_matrix_hsv_to_rgb(hsv); - rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); + rgb_matrix_region_set_color(params->region, i, rgb.r, rgb.g, rgb.b); } bool JELLYBEAN_RAINDROPS(effect_params_t* params) { diff --git a/quantum/rgb_matrix/animations/pixel_flow_anim.h b/quantum/rgb_matrix/animations/pixel_flow_anim.h index 27567b4f3a..2a8a2e7d6c 100644 --- a/quantum/rgb_matrix/animations/pixel_flow_anim.h +++ b/quantum/rgb_matrix/animations/pixel_flow_anim.h @@ -5,12 +5,17 @@ RGB_MATRIX_EFFECT(PIXEL_FLOW) # ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS +static uint32_t flow_wait_timer = 0; + +void PIXEL_FLOW_init(void) { + flow_wait_timer = 0; +} + static bool PIXEL_FLOW(effect_params_t* params) { // LED state array static RGB led[RGB_MATRIX_LED_COUNT]; - static uint32_t wait_timer = 0; - if (wait_timer > g_rgb_timer) { + if (flow_wait_timer > g_rgb_timer) { return false; } @@ -20,7 +25,7 @@ static bool PIXEL_FLOW(effect_params_t* params) { if (params->init) { // Clear LEDs and fill the state array - rgb_matrix_set_color_all(0, 0, 0); + rgb_matrix_region_set_color_all(params->region, 0, 0, 0); for (uint8_t j = 0; j < RGB_MATRIX_LED_COUNT; ++j) { led[j] = (random8() & 2) ? (RGB){0, 0, 0} : hsv_to_rgb((HSV){random8(), random8_min_max(127, 255), rgb_matrix_config.hsv.v}); } @@ -30,7 +35,7 @@ static bool PIXEL_FLOW(effect_params_t* params) { // Light LEDs based on state array for (uint8_t i = led_min; i < led_max; ++i) { RGB_MATRIX_TEST_LED_FLAGS(); - rgb_matrix_set_color(i, led[i].r, led[i].g, led[i].b); + rgb_matrix_region_set_color(params->region, i, led[i].r, led[i].g, led[i].b); } if (!rgb_matrix_check_finished_leds(led_max)) { @@ -41,7 +46,7 @@ static bool PIXEL_FLOW(effect_params_t* params) { // Fill last LED led[led_max - 1] = (random8() & 2) ? (RGB){0, 0, 0} : hsv_to_rgb((HSV){random8(), random8_min_max(127, 255), rgb_matrix_config.hsv.v}); // Set pulse timer - wait_timer = g_rgb_timer + interval(); + flow_wait_timer = g_rgb_timer + interval(); } return rgb_matrix_check_finished_leds(led_max); diff --git a/quantum/rgb_matrix/animations/pixel_fractal_anim.h b/quantum/rgb_matrix/animations/pixel_fractal_anim.h index 4cd1d9b861..0e55d15d0c 100644 --- a/quantum/rgb_matrix/animations/pixel_fractal_anim.h +++ b/quantum/rgb_matrix/animations/pixel_fractal_anim.h @@ -6,6 +6,13 @@ RGB_MATRIX_EFFECT(PIXEL_FRACTAL) # ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS + +static uint32_t fractal_wait_timer = 0; + +void PIXEL_FRACTAL_init(void) { + fractal_wait_timer = 0; +} + static bool PIXEL_FRACTAL(effect_params_t* params) { # if MATRIX_COLS < 2 # define MID_COL 1 @@ -13,47 +20,46 @@ static bool PIXEL_FRACTAL(effect_params_t* params) { # define MID_COL MATRIX_COLS / 2 # endif static bool led[MATRIX_ROWS][MID_COL]; - static uint32_t wait_timer = 0; inline uint32_t interval(void) { return 3000 / scale16by8(qadd8(rgb_matrix_config.speed, 16), 16); } if (params->init) { - rgb_matrix_set_color_all(0, 0, 0); + rgb_matrix_region_set_color_all(params->region 0, 0, 0); } RGB_MATRIX_USE_LIMITS(led_min, led_max); - if (g_rgb_timer > wait_timer) { + if (g_rgb_timer > fractal_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); + rgb_matrix_region_set_color(params->region, g_led_config.matrix_co[h][l], rgb.r, rgb.g, rgb.b); + rgb_matrix_region_set_color(params->region, 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); + rgb_matrix_region_set_color(params->region, g_led_config.matrix_co[h][l], 0, 0, 0); + rgb_matrix_region_set_color(params->region, g_led_config.matrix_co[h][MATRIX_COLS - 1 - l], 0, 0, 0); } led[h][l] = led[h][l + 1]; } // 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); + rgb_matrix_region_set_color(params->region, g_led_config.matrix_co[h][MID_COL - 1], rgb.r, rgb.g, rgb.b); + rgb_matrix_region_set_color(params->region, 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][MID_COL - 1], 0, 0, 0); - rgb_matrix_set_color(g_led_config.matrix_co[h][MATRIX_COLS - MID_COL], 0, 0, 0); + rgb_matrix_region_set_color(params->region, g_led_config.matrix_co[h][MID_COL - 1], 0, 0, 0); + rgb_matrix_region_set_color(params->region, g_led_config.matrix_co[h][MATRIX_COLS - MID_COL], 0, 0, 0); } // Generate new random fractal column led[h][MID_COL - 1] = (random8() & 3) ? false : true; } - wait_timer = g_rgb_timer + interval(); + fractal_wait_timer = g_rgb_timer + interval(); } return rgb_matrix_check_finished_leds(led_max); diff --git a/quantum/rgb_matrix/animations/pixel_rain_anim.h b/quantum/rgb_matrix/animations/pixel_rain_anim.h index 26cd73b578..9089416bd7 100644 --- a/quantum/rgb_matrix/animations/pixel_rain_anim.h +++ b/quantum/rgb_matrix/animations/pixel_rain_anim.h @@ -5,8 +5,13 @@ RGB_MATRIX_EFFECT(PIXEL_RAIN) # ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS -static bool PIXEL_RAIN(effect_params_t* params) { - static uint32_t wait_timer = 0; +static uint32_t rain_wait_timer = 0; + +void PIXEL_RAIN_init(void) { + rain_wait_timer = 0; +} + +bool PIXEL_RAIN(effect_params_t* params) { inline uint32_t interval(void) { return 500 / scale16by8(qadd8(rgb_matrix_config.speed, 16), 16); @@ -18,12 +23,12 @@ static bool PIXEL_RAIN(effect_params_t* params) { } HSV hsv = (random8() & 2) ? (HSV){0, 0, 0} : (HSV){random8(), random8_min_max(127, 255), rgb_matrix_config.hsv.v}; RGB rgb = rgb_matrix_hsv_to_rgb(hsv); - rgb_matrix_set_color(led_index, rgb.r, rgb.g, rgb.b); - wait_timer = g_rgb_timer + interval(); + rgb_matrix_region_set_color(params->region, led_index, rgb.r, rgb.g, rgb.b); + rain_wait_timer = g_rgb_timer + interval(); } RGB_MATRIX_USE_LIMITS(led_min, led_max); - if (g_rgb_timer > wait_timer) { + if (g_rgb_timer > rain_wait_timer) { rain_pixel(random8_max(RGB_MATRIX_LED_COUNT)); } return rgb_matrix_check_finished_leds(led_max); diff --git a/quantum/rgb_matrix/animations/raindrops_anim.h b/quantum/rgb_matrix/animations/raindrops_anim.h index e8e1f6de04..57b2219ddd 100644 --- a/quantum/rgb_matrix/animations/raindrops_anim.h +++ b/quantum/rgb_matrix/animations/raindrops_anim.h @@ -16,7 +16,7 @@ static void raindrops_set_color(int i, effect_params_t* params) { 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); + rgb_matrix_region_set_color(params->region, i, rgb.r, rgb.g, rgb.b); } bool RAINDROPS(effect_params_t* params) { diff --git a/quantum/rgb_matrix/animations/riverflow_anim.h b/quantum/rgb_matrix/animations/riverflow_anim.h index 79a38e7f6e..bd21e58232 100644 --- a/quantum/rgb_matrix/animations/riverflow_anim.h +++ b/quantum/rgb_matrix/animations/riverflow_anim.h @@ -12,7 +12,7 @@ bool RIVERFLOW(effect_params_t* params) { hsv.v = scale8(abs8(sin8(time) - 128) * 2, hsv.v); RGB rgb = rgb_matrix_hsv_to_rgb(hsv); RGB_MATRIX_TEST_LED_FLAGS(); - rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); + rgb_matrix_region_set_color(params->region, params->region, i, rgb.r, rgb.g, rgb.b); } return rgb_matrix_check_finished_leds(led_max); diff --git a/quantum/rgb_matrix/animations/runners/effect_runner_dx_dy.h b/quantum/rgb_matrix/animations/runners/effect_runner_dx_dy.h index 2ad0f22c28..0469ed1c1b 100644 --- a/quantum/rgb_matrix/animations/runners/effect_runner_dx_dy.h +++ b/quantum/rgb_matrix/animations/runners/effect_runner_dx_dy.h @@ -11,7 +11,7 @@ bool effect_runner_dx_dy(effect_params_t* params, dx_dy_f effect_func) { int16_t dx = g_led_config.point[i].x - k_rgb_matrix_center.x; int16_t dy = g_led_config.point[i].y - k_rgb_matrix_center.y; RGB rgb = rgb_matrix_hsv_to_rgb(effect_func(rgb_matrix_config.hsv, dx, dy, time)); - rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); + rgb_matrix_region_set_color(params->region, i, rgb.r, rgb.g, rgb.b); } return rgb_matrix_check_finished_leds(led_max); } diff --git a/quantum/rgb_matrix/animations/runners/effect_runner_dx_dy_dist.h b/quantum/rgb_matrix/animations/runners/effect_runner_dx_dy_dist.h index bcae7c79b6..a5dd84f640 100644 --- a/quantum/rgb_matrix/animations/runners/effect_runner_dx_dy_dist.h +++ b/quantum/rgb_matrix/animations/runners/effect_runner_dx_dy_dist.h @@ -12,7 +12,7 @@ bool effect_runner_dx_dy_dist(effect_params_t* params, dx_dy_dist_f effect_func) int16_t dy = g_led_config.point[i].y - k_rgb_matrix_center.y; uint8_t dist = sqrt16(dx * dx + dy * dy); RGB rgb = rgb_matrix_hsv_to_rgb(effect_func(rgb_matrix_config.hsv, dx, dy, dist, time)); - rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); + rgb_matrix_region_set_color(params->region, i, rgb.r, rgb.g, rgb.b); } return rgb_matrix_check_finished_leds(led_max); } diff --git a/quantum/rgb_matrix/animations/runners/effect_runner_i.h b/quantum/rgb_matrix/animations/runners/effect_runner_i.h index b4de2992b6..4914714994 100644 --- a/quantum/rgb_matrix/animations/runners/effect_runner_i.h +++ b/quantum/rgb_matrix/animations/runners/effect_runner_i.h @@ -9,7 +9,7 @@ bool effect_runner_i(effect_params_t* params, i_f effect_func) { for (uint8_t i = led_min; i < led_max; i++) { RGB_MATRIX_TEST_LED_FLAGS(); RGB rgb = rgb_matrix_hsv_to_rgb(effect_func(rgb_matrix_config.hsv, i, time)); - rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); + rgb_matrix_region_set_color(params->region, i, rgb.r, rgb.g, rgb.b); } return rgb_matrix_check_finished_leds(led_max); } diff --git a/quantum/rgb_matrix/animations/runners/effect_runner_reactive.h b/quantum/rgb_matrix/animations/runners/effect_runner_reactive.h index f9584d7071..baca2fbc54 100644 --- a/quantum/rgb_matrix/animations/runners/effect_runner_reactive.h +++ b/quantum/rgb_matrix/animations/runners/effect_runner_reactive.h @@ -21,7 +21,7 @@ bool effect_runner_reactive(effect_params_t* params, reactive_f effect_func) { uint16_t offset = scale16by8(tick, qadd8(rgb_matrix_config.speed, 1)); RGB rgb = rgb_matrix_hsv_to_rgb(effect_func(rgb_matrix_config.hsv, offset)); - rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); + rgb_matrix_region_set_color(params->region, i, rgb.r, rgb.g, rgb.b); } return rgb_matrix_check_finished_leds(led_max); } diff --git a/quantum/rgb_matrix/animations/runners/effect_runner_reactive_splash.h b/quantum/rgb_matrix/animations/runners/effect_runner_reactive_splash.h index 41020eb47f..8e549e32fc 100644 --- a/quantum/rgb_matrix/animations/runners/effect_runner_reactive_splash.h +++ b/quantum/rgb_matrix/animations/runners/effect_runner_reactive_splash.h @@ -21,7 +21,7 @@ bool effect_runner_reactive_splash(uint8_t start, effect_params_t* params, react } hsv.v = scale8(hsv.v, rgb_matrix_config.hsv.v); RGB rgb = rgb_matrix_hsv_to_rgb(hsv); - rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); + rgb_matrix_region_set_color(params->region, i, rgb.r, rgb.g, rgb.b); } return rgb_matrix_check_finished_leds(led_max); } diff --git a/quantum/rgb_matrix/animations/runners/effect_runner_sin_cos_i.h b/quantum/rgb_matrix/animations/runners/effect_runner_sin_cos_i.h index 7776491d51..27cd9f4d02 100644 --- a/quantum/rgb_matrix/animations/runners/effect_runner_sin_cos_i.h +++ b/quantum/rgb_matrix/animations/runners/effect_runner_sin_cos_i.h @@ -11,7 +11,7 @@ bool effect_runner_sin_cos_i(effect_params_t* params, sin_cos_i_f effect_func) { for (uint8_t i = led_min; i < led_max; i++) { RGB_MATRIX_TEST_LED_FLAGS(); RGB rgb = rgb_matrix_hsv_to_rgb(effect_func(rgb_matrix_config.hsv, cos_value, sin_value, i, time)); - rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); + rgb_matrix_region_set_color(params->region, i, rgb.r, rgb.g, rgb.b); } return rgb_matrix_check_finished_leds(led_max); } diff --git a/quantum/rgb_matrix/animations/solid_color_anim.h b/quantum/rgb_matrix/animations/solid_color_anim.h index c8762dcbc2..9ddfddb76a 100644 --- a/quantum/rgb_matrix/animations/solid_color_anim.h +++ b/quantum/rgb_matrix/animations/solid_color_anim.h @@ -7,7 +7,7 @@ bool SOLID_COLOR(effect_params_t* params) { RGB rgb = rgb_matrix_hsv_to_rgb(rgb_matrix_config.hsv); for (uint8_t i = led_min; i < led_max; i++) { RGB_MATRIX_TEST_LED_FLAGS(); - rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); + rgb_matrix_region_set_color(params->region, i, rgb.r, rgb.g, rgb.b); } return rgb_matrix_check_finished_leds(led_max); } diff --git a/quantum/rgb_matrix/animations/starlight_anim.h b/quantum/rgb_matrix/animations/starlight_anim.h index 33f0b61a91..db94bb3f0b 100644 --- a/quantum/rgb_matrix/animations/starlight_anim.h +++ b/quantum/rgb_matrix/animations/starlight_anim.h @@ -7,7 +7,7 @@ void set_starlight_color(int i, effect_params_t* params) { HSV hsv = rgb_matrix_config.hsv; hsv.v = scale8(abs8(sin8(time) - 128) * 2, hsv.v); RGB rgb = hsv_to_rgb(hsv); - rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); + rgb_matrix_region_set_color(params->region, i, rgb.r, rgb.g, rgb.b); } bool STARLIGHT(effect_params_t* params) { @@ -27,4 +27,4 @@ bool STARLIGHT(effect_params_t* params) { } # endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS -#endif // ENABLE_RGB_MATRIX_STARLIGHT \ No newline at end of file +#endif // ENABLE_RGB_MATRIX_STARLIGHT diff --git a/quantum/rgb_matrix/animations/starlight_dual_hue_anim.h b/quantum/rgb_matrix/animations/starlight_dual_hue_anim.h index df6461b8b7..d9ccdfb7b0 100644 --- a/quantum/rgb_matrix/animations/starlight_dual_hue_anim.h +++ b/quantum/rgb_matrix/animations/starlight_dual_hue_anim.h @@ -8,7 +8,7 @@ void set_starlight_dual_hue_color(int i, effect_params_t* params) { hsv.v = scale8(abs8(sin8(time) - 128) * 2, hsv.v); hsv.h = hsv.h + (rand() % (30 + 1 - -30) + -30); RGB rgb = hsv_to_rgb(hsv); - rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); + rgb_matrix_region_set_color(params->region, i, rgb.r, rgb.g, rgb.b); } bool STARLIGHT_DUAL_HUE(effect_params_t* params) { @@ -28,4 +28,4 @@ bool STARLIGHT_DUAL_HUE(effect_params_t* params) { } # endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS -#endif // ENABLE_RGB_MATRIX_STARLIGHT_DUAL_HUE \ No newline at end of file +#endif // ENABLE_RGB_MATRIX_STARLIGHT_DUAL_HUE diff --git a/quantum/rgb_matrix/animations/starlight_dual_sat_anim.h b/quantum/rgb_matrix/animations/starlight_dual_sat_anim.h index f6ecd48aa1..b01229b09a 100644 --- a/quantum/rgb_matrix/animations/starlight_dual_sat_anim.h +++ b/quantum/rgb_matrix/animations/starlight_dual_sat_anim.h @@ -8,7 +8,7 @@ void set_starlight_dual_sat_color(int i, effect_params_t* params) { hsv.v = scale8(abs8(sin8(time) - 128) * 2, hsv.v); hsv.s = hsv.s + (rand() % (30 + 1 - -30) + -30); RGB rgb = hsv_to_rgb(hsv); - rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); + rgb_matrix_region_set_color(params->region, i, rgb.r, rgb.g, rgb.b); } bool STARLIGHT_DUAL_SAT(effect_params_t* params) { @@ -28,4 +28,4 @@ bool STARLIGHT_DUAL_SAT(effect_params_t* params) { } # endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS -#endif // ENABLE_RGB_MATRIX_STARLIGHT_DUAL_SAT \ No newline at end of file +#endif // ENABLE_RGB_MATRIX_STARLIGHT_DUAL_SAT diff --git a/quantum/rgb_matrix/animations/typing_heatmap_anim.h b/quantum/rgb_matrix/animations/typing_heatmap_anim.h index d09bdc4631..556e29a1f7 100644 --- a/quantum/rgb_matrix/animations/typing_heatmap_anim.h +++ b/quantum/rgb_matrix/animations/typing_heatmap_anim.h @@ -52,25 +52,29 @@ void process_rgb_matrix_typing_heatmap(uint8_t row, uint8_t col) { static uint16_t heatmap_decrease_timer; // Whether we should decrement the heatmap values during the next update. static bool decrease_heatmap_values; +static uint8_t loop = 0; bool TYPING_HEATMAP(effect_params_t* params) { RGB_MATRIX_USE_LIMITS(led_min, led_max); if (params->init) { - rgb_matrix_set_color_all(0, 0, 0); + rgb_matrix_region_set_color_all(params->region, 0, 0, 0); memset(g_rgb_frame_buffer, 0, sizeof g_rgb_frame_buffer); } // The heatmap animation might run in several iterations depending on // `RGB_MATRIX_LED_PROCESS_LIMIT`, therefore we only want to update the // timer when the animation starts. - if (params->iter == 0) { + if (params->iter == 0 && loop == 0) { + loop |= 1 << params->region; decrease_heatmap_values = timer_elapsed(heatmap_decrease_timer) >= RGB_MATRIX_TYPING_HEATMAP_DECREASE_DELAY_MS; // Restart the timer if we are going to decrease the heatmap this frame. if (decrease_heatmap_values) { heatmap_decrease_timer = timer_read(); } + } else if (params->iter == 4) { + loop &= ~(1 << params->region); } // Render heatmap & decrease @@ -84,7 +88,7 @@ bool TYPING_HEATMAP(effect_params_t* params) { 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); + rgb_matrix_region_set_color(params->region, 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); diff --git a/quantum/rgb_matrix/rgb_matrix.c b/quantum/rgb_matrix/rgb_matrix.c index 690f59241e..4e8d2bacb5 100644 --- a/quantum/rgb_matrix/rgb_matrix.c +++ b/quantum/rgb_matrix/rgb_matrix.c @@ -80,7 +80,7 @@ static bool driver_shutdown = false; static bool suspend_state = false; static uint8_t rgb_last_enable = UINT8_MAX; static uint8_t rgb_last_effect = UINT8_MAX; -static effect_params_t rgb_effect_params = {0, LED_FLAG_ALL, false}; +static effect_params_t rgb_effect_params = {0, LED_FLAG_ALL, false, 0}; static rgb_task_states rgb_task_state = SYNCING; #if RGB_MATRIX_TIMEOUT > 0 static uint32_t rgb_anykey_timer; @@ -98,6 +98,8 @@ static last_hit_t last_hit_buffer; const uint8_t k_rgb_matrix_split[2] = RGB_MATRIX_SPLIT; #endif +uint8_t rgb_regions[RGB_MATRIX_LED_COUNT]; + EECONFIG_DEBOUNCE_HELPER(rgb_matrix, EECONFIG_RGB_MATRIX, rgb_matrix_config); void rgb_matrix_increase_val_helper(bool write_to_eeprom); @@ -168,6 +170,20 @@ void rgb_matrix_set_color_all(uint8_t red, uint8_t green, uint8_t blue) { #endif } +void rgb_matrix_region_set_color(uint8_t region, int index, uint8_t red, uint8_t green, uint8_t blue) { + if (rgb_regions[index] == region) { + rgb_matrix_driver.set_color(index, red, green, blue); + } +} + +void rgb_matrix_region_set_color_all(uint8_t region, uint8_t red, uint8_t green, uint8_t blue) { + for (uint8_t i = 0; i < RGB_MATRIX_LED_COUNT; i++) + if (((g_led_config.flags[i] & 0xF0) >> 4) == region) + rgb_matrix_set_color(i, red, green, blue); +} + +__attribute__((weak)) void process_rgb_matrix_kb(uint8_t row, uint8_t col, bool pressed) {} + void process_rgb_matrix(uint8_t row, uint8_t col, bool pressed) { #ifndef RGB_MATRIX_SPLIT if (!is_keyboard_master()) return; @@ -219,6 +235,7 @@ void process_rgb_matrix(uint8_t row, uint8_t col, bool pressed) { } } #endif // defined(RGB_MATRIX_FRAMEBUFFER_EFFECTS) && defined(ENABLE_RGB_MATRIX_TYPING_HEATMAP) + process_rgb_matrix_kb(row, col, pressed); } void rgb_matrix_test(void) { @@ -313,6 +330,14 @@ static void rgb_task_start(void) { static void rgb_task_render(uint8_t effect) { bool rendering = false; rgb_effect_params.init = (effect != rgb_last_effect) || (rgb_matrix_config.enable != rgb_last_enable); + if (effect != rgb_last_effect) { + memset(rgb_regions, 0, RGB_MATRIX_LED_COUNT); + rgb_config_t rgb_cfg; + eeprom_read_block(&rgb_cfg, EECONFIG_RGB_MATRIX, sizeof(rgb_cfg)); + rgb_matrix_config.hsv = rgb_cfg.hsv; + rgb_matrix_config.speed = rgb_cfg.speed; + } + if (rgb_effect_params.flags != rgb_matrix_config.flags) { rgb_effect_params.flags = rgb_matrix_config.flags; rgb_matrix_set_color_all(0, 0, 0); @@ -537,7 +562,10 @@ bool rgb_matrix_get_suspend_state(void) { void rgb_matrix_toggle_eeprom_helper(bool write_to_eeprom) { rgb_matrix_config.enable ^= 1; rgb_task_state = STARTING; - eeconfig_flag_rgb_matrix(write_to_eeprom); + if (write_to_eeprom) { + uint8_t mode = (rgb_matrix_config.mode << 2) | rgb_matrix_config.enable; + eeprom_write_byte((uint8_t*)EECONFIG_RGB_MATRIX, mode); + } dprintf("rgb matrix toggle [%s]: rgb_matrix_config.enable = %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", rgb_matrix_config.enable); #ifdef RGB_MATRIX_BRIGHTNESS_TURN_OFF_VAL while (rgb_matrix_config.enable && rgb_matrix_config.hsv.v < RGB_MATRIX_BRIGHTNESS_TURN_OFF_VAL) { @@ -554,7 +582,8 @@ void rgb_matrix_toggle(void) { void rgb_matrix_enable(void) { rgb_matrix_enable_noeeprom(); - eeconfig_flag_rgb_matrix(true); + uint8_t mode = (rgb_matrix_config.mode << 2) | rgb_matrix_config.enable; + eeprom_write_byte((uint8_t*)EECONFIG_RGB_MATRIX, mode); #ifdef RGB_MATRIX_BRIGHTNESS_TURN_OFF_VAL while (rgb_matrix_config.hsv.v < RGB_MATRIX_BRIGHTNESS_TURN_OFF_VAL) { rgb_matrix_increase_val_helper(true); @@ -574,7 +603,8 @@ void rgb_matrix_enable_noeeprom(void) { void rgb_matrix_disable(void) { rgb_matrix_disable_noeeprom(); - eeconfig_flag_rgb_matrix(true); + uint8_t mode = (rgb_matrix_config.mode << 2) | rgb_matrix_config.enable; + eeprom_write_byte((uint8_t*)EECONFIG_RGB_MATRIX, mode); } void rgb_matrix_disable_noeeprom(void) { @@ -598,7 +628,11 @@ void rgb_matrix_mode_eeprom_helper(uint8_t mode, bool write_to_eeprom) { rgb_matrix_config.mode = mode; } rgb_task_state = STARTING; - eeconfig_flag_rgb_matrix(write_to_eeprom); + + if (write_to_eeprom) { + uint8_t mode = (rgb_matrix_config.mode << 2) | rgb_matrix_config.enable; + eeprom_write_byte((uint8_t*)EECONFIG_RGB_MATRIX, mode); + } dprintf("rgb matrix mode [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", rgb_matrix_config.mode); } void rgb_matrix_mode_noeeprom(uint8_t mode) { @@ -641,7 +675,10 @@ void rgb_matrix_sethsv_eeprom_helper(uint16_t hue, uint8_t sat, uint8_t val, boo rgb_matrix_config.hsv.h = hue; rgb_matrix_config.hsv.s = sat; rgb_matrix_config.hsv.v = (val > RGB_MATRIX_MAXIMUM_BRIGHTNESS) ? RGB_MATRIX_MAXIMUM_BRIGHTNESS : val; - eeconfig_flag_rgb_matrix(write_to_eeprom); + if (write_to_eeprom) { + uint8_t *addr = (uint8_t*)EECONFIG_RGB_MATRIX + offsetof(rgb_config_t, hsv); + eeprom_write_block(&rgb_matrix_config.hsv, addr, sizeof(rgb_matrix_config.hsv)); + } dprintf("rgb matrix set hsv [%s]: %u,%u,%u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", rgb_matrix_config.hsv.h, rgb_matrix_config.hsv.s, rgb_matrix_config.hsv.v); } void rgb_matrix_sethsv_noeeprom(uint16_t hue, uint8_t sat, uint8_t val) { @@ -737,7 +774,10 @@ void rgb_matrix_decrease_val(void) { void rgb_matrix_set_speed_eeprom_helper(uint8_t speed, bool write_to_eeprom) { rgb_matrix_config.speed = speed; - eeconfig_flag_rgb_matrix(write_to_eeprom); + if (write_to_eeprom) { + uint8_t *addr = (uint8_t*)EECONFIG_RGB_MATRIX + offsetof(rgb_config_t, speed); + eeprom_write_byte(addr, rgb_matrix_config.speed); + } dprintf("rgb matrix set speed [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", rgb_matrix_config.speed); } void rgb_matrix_set_speed_noeeprom(uint8_t speed) { @@ -773,7 +813,10 @@ void rgb_matrix_decrease_speed(void) { 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); + if (write_to_eeprom) { + uint8_t *addr = (uint8_t*)EECONFIG_RGB_MATRIX + offsetof(rgb_config_t, flags); + eeprom_write_byte(addr, rgb_matrix_config.flags); + } dprintf("rgb matrix set flags [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", rgb_matrix_config.flags); } diff --git a/quantum/rgb_matrix/rgb_matrix.h b/quantum/rgb_matrix/rgb_matrix.h index 69a2a49b85..8248598159 100644 --- a/quantum/rgb_matrix/rgb_matrix.h +++ b/quantum/rgb_matrix/rgb_matrix.h @@ -168,6 +168,8 @@ uint8_t rgb_matrix_map_row_column_to_led(uint8_t row, uint8_t column, uint8_t *l void rgb_matrix_set_color(int index, uint8_t red, uint8_t green, uint8_t blue); void rgb_matrix_set_color_all(uint8_t red, uint8_t green, uint8_t blue); +void rgb_matrix_region_set_color(uint8_t region, int index, uint8_t red, uint8_t green, uint8_t blue); +void rgb_matrix_region_set_color_all(uint8_t region, uint8_t red, uint8_t green, uint8_t blue); void process_rgb_matrix(uint8_t row, uint8_t col, bool pressed); diff --git a/quantum/rgb_matrix/rgb_matrix_types.h b/quantum/rgb_matrix/rgb_matrix_types.h index 0a3fd7cc0d..de8f65acf7 100644 --- a/quantum/rgb_matrix/rgb_matrix_types.h +++ b/quantum/rgb_matrix/rgb_matrix_types.h @@ -48,6 +48,7 @@ typedef struct PACKED { uint8_t iter; led_flags_t flags; bool init; + uint8_t region; } effect_params_t; typedef struct PACKED { diff --git a/quantum/via.c b/quantum/via.c index 643d7aa3c3..1bc49fa621 100644 --- a/quantum/via.c +++ b/quantum/via.c @@ -633,7 +633,7 @@ void via_qmk_rgblight_save(void) { #endif // QMK_RGBLIGHT_ENABLE #if defined(RGB_MATRIX_ENABLE) - +static uint8_t rgb_matrix_value_id_mask; void via_qmk_rgb_matrix_command(uint8_t *data, uint8_t length) { // data = [ command_id, channel_id, value_id, value_data ] uint8_t *command_id = &(data[0]); @@ -689,6 +689,7 @@ void via_qmk_rgb_matrix_set_value(uint8_t *data) { // data = [ value_id, value_data ] uint8_t *value_id = &(data[0]); uint8_t *value_data = &(data[1]); + rgb_matrix_value_id_mask |= 0x01U << *value_id; switch (*value_id) { case id_qmk_rgb_matrix_brightness: { rgb_matrix_sethsv_noeeprom(rgb_matrix_get_hue(), rgb_matrix_get_sat(), scale8(value_data[0], RGB_MATRIX_MAXIMUM_BRIGHTNESS)); @@ -715,7 +716,30 @@ void via_qmk_rgb_matrix_set_value(uint8_t *data) { } void via_qmk_rgb_matrix_save(void) { - eeconfig_update_rgb_matrix(); + + if (rgb_matrix_value_id_mask == (0x01U << id_qmk_rgb_matrix_brightness)) { + uint8_t *addr = (uint8_t*)EECONFIG_RGB_MATRIX + offsetof(rgb_config_t, hsv.v); + eeprom_write_byte(addr, rgb_matrix_config.hsv.v); + } + + if (rgb_matrix_value_id_mask == (0x01U << id_qmk_rgb_matrix_effect)) { + uint8_t mode = (rgb_matrix_config.mode << 2) | rgb_matrix_config.enable; + eeprom_write_byte((uint8_t*)EECONFIG_RGB_MATRIX, mode); + } + + if (rgb_matrix_value_id_mask == (0x01U << id_qmk_rgb_matrix_color)) { + uint8_t *addr = (uint8_t*)EECONFIG_RGB_MATRIX + offsetof(rgb_config_t, hsv.h); + eeprom_write_byte(addr, rgb_matrix_config.hsv.h); + addr = (uint8_t*)EECONFIG_RGB_MATRIX + offsetof(rgb_config_t, hsv.s); + eeprom_write_byte(addr, rgb_matrix_config.hsv.s); + } + + if (rgb_matrix_value_id_mask == (0x01U << id_qmk_rgb_matrix_effect_speed)) { + uint8_t *addr = (uint8_t*)EECONFIG_RGB_MATRIX + offsetof(rgb_config_t, speed); + eeprom_write_byte(addr, rgb_matrix_config.speed); + } + + rgb_matrix_value_id_mask = 0; } #endif // RGB_MATRIX_ENABLE