Added snap click, per-key/mixed rgb, custom debounce, wireless config feature

This commit is contained in:
lokher
2025-05-30 23:55:10 +08:00
parent b507ea2216
commit c9049679ac
81 changed files with 3696 additions and 251 deletions
@@ -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 <http://www.gnu.org/licenses/>.
*/
#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)
@@ -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 <http://www.gnu.org/licenses/>.
*/
#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 <lib/lib8tion/lib8tion.h>
#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], &regions[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(&regions[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
@@ -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 <http://www.gnu.org/licenses/>.
*/
#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;
+191
View File
@@ -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 <http://www.gnu.org/licenses/>.
*/
#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; i<EFFECT_LAYERS; i++) {
layer_effect_index[i] = 0;
layer_effect_timer[i] = timer_read32();
if (effect_list[i][0].effect == RGB_MATRIX_TYPING_HEATMAP) typingHeatmap |= 0x01 << i;
}
}
void update_mixed_rgb_effect_count(void) {
for (int8_t layer=0; layer<EFFECT_LAYERS; layer++) {
layer_effect_count[layer] = 0;
for (uint8_t i=0; i<EFFECTS_PER_LAYER; i++) {
if (effect_list[layer][i].effect != 0) ++layer_effect_count[layer];
}
}
mixed_rgb_reset();
}
bool mixed_rgb(effect_params_t *params) {
bool ret;
extern uint8_t rgb_regions[RGB_MATRIX_LED_COUNT];
if (params->init) {
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; i<EFFECTS_PER_LAYER; i++) {
if (effect_list[layer][i].effect != 0) {
effect_index = layer_effect_index[params->region] = 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
+160
View File
@@ -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 <http://www.gnu.org/licenses/>.
*/
#include "quantum.h"
#include "rgb_matrix.h"
#include "keychron_rgb_type.h"
#include <math.h>
#include <lib/lib8tion/lib8tion.h>
#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
+185
View File
@@ -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 <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#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
@@ -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 <http://www.gnu.org/licenses/>.
*/
#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);
+14
View File
@@ -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)
@@ -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 <http://www.gnu.org/licenses/>.
*/
#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
@@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "config.h"
#ifndef EFFECT_LAYERS
#define EFFECT_LAYERS 2
#endif
#ifndef EFFECTS_PER_LAYER
#define EFFECTS_PER_LAYER 5
#endif