/* 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