~ruther/qmk_firmware

645c5fabf24411fe65d2172a3509c26e053651a2 — Jumail Mundekkat 5 years ago 7f7b6b0
Add VIA support to MxSS, plus minor tweaks (#7809)

* Add VIA support to mxss and general cleanup

* Add support for RGB test for FLEDs

* Add LAYOUT_all to allow for more configuration

* Remove blank layers

* Updated readme

* Improve use of EEPROM

* Credit where its due

* Use the latest iteration of rgblight code

* Keep the RGB timer running if the front LED is in RGB mode

* Fix RGB breathing animation

* Better supported RGB animation
Only thing not working is alternating, but that's not too important

* Abstract front LED handlers from main kb code

* Add support for indicator LED color changing

* Remove debug statement

* Persist indicator LED colors

* Mark custom sections in rgblight.c

* Light commenting

* Fix up keymaps

* Add/update comments

* Remove bloat from default hex

* Tidy a stray tab

* Out with the old, in with the new

* Out with the old, in with the new

* Add LAYER_STATE_8BIT for VIA keymap
M keyboards/mxss/config.h => keyboards/mxss/config.h +10 -3
@@ 21,12 21,12 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
#include "config_common.h"

/* USB Device descriptor parameter */
#define VENDOR_ID       0xFEED
#define PRODUCT_ID      0x6060
#define VENDOR_ID       0x4D78 // "Mx" -> MxBlue
#define PRODUCT_ID      0x5353 // "SS" -> MxSS
#define DEVICE_VER      0x0001
#define MANUFACTURER    MxBlue
#define PRODUCT         MxSS
#define DESCRIPTION     Custom Polycarb Keyboard
#define DESCRIPTION     MxSS Polycarb Keyboard

/* key matrix size */
#define MATRIX_ROWS 5


@@ 56,6 56,13 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
/* Locking resynchronize hack */
#define LOCKING_RESYNC_ENABLE

// Just for posterity, define bootlite matrix pos
#define BOOTMAGIC_LITE_ROW 0
#define BOOTMAGIC_LITE_COLUMN 0

// FLED config takes up 1 byte, stored color count takes 1, stored colors take up to 8
#define VIA_EEPROM_CUSTOM_CONFIG_SIZE 10

/* If defined, GRAVE_ESC will always act as ESC when CTRL is held.
 * This is userful for the Windows task manager shortcut (ctrl+shift+esc).
 */

M keyboards/mxss/keymaps/default/keymap.c => keyboards/mxss/keymaps/default/keymap.c +10 -9
@@ 15,31 15,32 @@
 */
#include QMK_KEYBOARD_H

hs_set caps_color = { .hue = 0, .sat = 255 };

// Colors for layers
// Format: {hue, saturation}
// {0, 0} to turn off the LED
// Add additional rows to handle more layers
const hs_set layer_colors[] = {
    [0] = {0,     0},  // Color for Layer 0
    [1] = {86,    255},  // Color for Layer 1
    [2] = {36,    255},  // Color for Layer 2
    [3] = {185,   255},  // Color for Layer 3
hs_set layer_colors[4] = {
    [0] = {.hue = 0,     .sat = 0},  // Color for Layer 0
    [1] = {.hue = 86,    .sat = 255},  // Color for Layer 1
    [2] = {.hue = 36,    .sat = 255},  // Color for Layer 2
    [3] = {.hue = 185,   .sat = 255},  // Color for Layer 3
};
const size_t lc_size = sizeof(layer_colors) / sizeof(uint16_t);
size_t lc_size = sizeof(layer_colors) / sizeof(uint16_t);

// Use NEW_SAFE_RANGE to define new custom keycodes in order to not overwrite the ones used for front LED control
enum custom_keycodes {
  MY_KEYCODE = NEW_SAFE_RANGE, 
  MY_KEYCODE = NEW_SAFE_RANGE,
};


const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
	LAYOUT( /* Base */
	KC_GESC, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC,
    KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_BSLS, KC_DEL,
    KC_CAPSLOCK, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_ENT, KC_PGUP,
    KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP, KC_PGDN,
    KC_LCTL, KC_LGUI, KC_LALT,           KC_SPACE,           KC_RALT, MO(1), KC_LEFT, KC_DOWN, KC_RGHT 
    KC_LCTL, KC_LGUI, KC_LALT,           KC_SPACE,           KC_RALT, MO(1), KC_LEFT, KC_DOWN, KC_RGHT
),

    LAYOUT( /* L1 */

A keyboards/mxss/keymaps/via/config.h => keyboards/mxss/keymaps/via/config.h +19 -0
@@ 0,0 1,19 @@
/* Copyright 2018 Jumail Mundekkat / MxBlue
 *
 * 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

#define LAYER_STATE_8BIT

A keyboards/mxss/keymaps/via/keymap.c => keyboards/mxss/keymaps/via/keymap.c +33 -0
@@ 0,0 1,33 @@
/* Copyright 2018 Jumail Mundekkat / MxBlue
 *
 * 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

const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
	LAYOUT_all( /* L0 */
        KC_GESC, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_NO, KC_BSPC,
        KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_BSLS, KC_DEL,
        KC_CAPSLOCK, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_NO, KC_ENT, KC_PGUP,
        KC_LSFT, KC_NO, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP, KC_PGDN,
        KC_LCTL, KC_LGUI, KC_LALT, KC_SPACE, KC_SPACE, KC_SPACE, KC_RALT, MO(1), KC_LEFT, KC_DOWN, KC_RGHT
    ),
    LAYOUT_all( /* L1 */
        KC_GRV, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, KC_TRNS, KC_TRNS,
        KC_TRNS, KC_MPLY, KC_MPRV, KC_MNXT, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_HOME, KC_PSCR, FLED_VAD, FLED_VAI, FLED_MOD, RGB_VAI,
        RESET, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_END, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, RGB_VAD,
        KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_MUTE, KC_VOLD, KC_VOLU, KC_TRNS, RGB_MOD, RGB_SAI, RGB_TOG,
        KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,  KC_TRNS, KC_TRNS,  KC_TRNS, KC_TRNS, RGB_HUD, RGB_SAD, RGB_HUI
    )
};

A keyboards/mxss/keymaps/via/rules.mk => keyboards/mxss/keymaps/via/rules.mk +1 -0
@@ 0,0 1,1 @@
VIA_ENABLE = yes

M keyboards/mxss/mxss.c => keyboards/mxss/mxss.c +37 -168
@@ 1,4 1,4 @@
/* Copyright 2018 Jumail Mundekkat / MxBlue
/* Copyright 2020 Jumail Mundekkat / MxBlue
 *
 * 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


@@ 12,63 12,29 @@
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * EEPROM management code from ../cannonkeys/stm32f072/keyboard.c
 */
 

#include QMK_KEYBOARD_H
#include "tmk_core/common/eeprom.h"
#include "tmk_core/common/action_layer.h"
#include "rgblight.h"
#include "via.h"
#include "version.h" // for QMK_BUILDDATE used in EEPROM magic

// Variables for controlling front LED application
uint8_t fled_mode;  // Mode for front LEDs
uint8_t fled_val;   // Brightness for front leds (0 - 255)
LED_TYPE fleds[2];  // Front LED rgb values for indicator mode use

// Predefined colors for layers
// Format: {hue, saturation}
// {0, 0} to turn off the LED
// Add additional rows to handle more layers
__attribute__ ((weak))
const hs_set layer_colors[] = {
    [0] = {0,     0},  // Color for Layer 0
    [1] = {86,    255},  // Color for Layer 1
    [2] = {36,    255},  // Color for Layer 2
    [3] = {185,   255},  // Color for Layer 3
};

__attribute__ ((weak))
const size_t lc_size = sizeof(layer_colors) / sizeof(uint16_t);
void via_init_kb(void) {
	fled_init();
}

void matrix_init_kb(void) {
    // If EEPROM config exists, load it
    if (eeprom_is_valid()) {
        fled_config fled_conf;
        fled_conf.raw = eeprom_read_byte(EEPROM_FRONTLED_ADDR);
        fled_mode = fled_conf.mode;
        fled_val = fled_conf.val * FLED_VAL_STEP;
    // Else, default config
    } else {
        fled_mode = FLED_RGB;
        fled_val = 10 * FLED_VAL_STEP;
        eeprom_update_conf();   // Store default config to EEPROM
    }
    
    // Set default values for leds
    setrgb(0, 0, 0, &fleds[0]);
    setrgb(0, 0, 0, &fleds[1]);
    
    // Handle lighting for indicator mode
    if (fled_mode == FLED_INDI) {
        // Enable capslock led if enabled on host
        if (host_keyboard_leds() & (1<<USB_LED_CAPS_LOCK))
            sethsv(FLED_CAPS_H, FLED_CAPS_S, fled_val, &fleds[0]);
        
        // Determine and set colour of layer LED according to current layer
        // if hue = sat = 0, leave LED off
        uint8_t layer = biton32(layer_state);
        if (layer < lc_size && !(layer_colors[layer].hue == 0 && layer_colors[layer].hue == 0))
            sethsv(layer_colors[layer].hue, layer_colors[layer].sat, fled_val, &fleds[1]);
    }
    // If VIA is disabled, we still need to load settings
	// Call via_init_kb() the same way as via_init(), with setting
	// EEPROM valid afterwards.
#ifndef VIA_ENABLE
    fled_init();
    via_eeprom_set_valid(true);
#endif // VIA_ENABLE

	matrix_init_user();
}


@@ 82,132 48,35 @@ void matrix_scan_kb(void) {

bool process_record_kb(uint16_t keycode, keyrecord_t *record) {
    // Handle custom keycodes for front LED operation
    switch (keycode) {
        case FLED_MOD: // Change between front LED operation modes (off, indicator, RGB)
        if (record->event.pressed)
            fled_mode_cycle();
        break;
        
        case FLED_VAI: // Increase the brightness of the front LEDs by FLED_VAL_STEP
        if (record->event.pressed)
            fled_val_increase();
        break;
        
        case FLED_VAD: // Decrease the brightness of the front LEDs by FLED_VAL_STEP
        if (record->event.pressed)
            fled_val_decrease();
        break;
        
        default:
        break; // Process all other keycodes normally
      }

    process_record_fled(keycode, record);
	return process_record_user(keycode, record);
}

void led_set_kb(uint8_t usb_led) {
    // Set indicator LED appropriately, whether it is used or not
    if (usb_led & (1 << USB_LED_CAPS_LOCK)) {
        sethsv(FLED_CAPS_H, FLED_CAPS_S, fled_val, &fleds[0]);
    } else {
        setrgb(0, 0, 0, &fleds[0]);
    }

    rgblight_set();
	led_set_user(usb_led);
}

uint32_t layer_state_set_kb(uint32_t state) {
    // Determine and set colour of layer LED according to current layer
    // if hue = sat = 0, leave LED off
    uint8_t layer = biton32(state);
    
    if (layer < lc_size && !(layer_colors[layer].hue == 0 && layer_colors[layer].hue == 0))
        sethsv(layer_colors[layer].hue, layer_colors[layer].sat, fled_val, &fleds[1]);
    else
        setrgb(0, 0, 0, &fleds[1]);
    
    return state;
}

// EEPROM Management

// Test if magic value is present at expected location
bool eeprom_is_valid(void)
{
	return (eeprom_read_word(EEPROM_MAGIC_ADDR) == EEPROM_MAGIC);
}

// Set magic value at expected location
void eeprom_set_valid(bool valid)
{
	eeprom_update_word(EEPROM_MAGIC_ADDR, valid ? EEPROM_MAGIC : 0xFFFF);
bool led_update_kb(led_t led_state) {
    fled_lock_update(led_state);
	return led_update_user(led_state);
}

// Store current front led config in EEPROM
void eeprom_update_conf(void)
{
    // Create storage struct and set values
    fled_config conf;
    conf.mode = fled_mode;
    
    // Small hack to ensure max value is stored correctly
    if (fled_val == 255)
        conf.val = 256 / FLED_VAL_STEP;
    else
        conf.val = fled_val / FLED_VAL_STEP;
    
    // Set magic value and store config
    eeprom_set_valid(true);
	eeprom_update_byte(EEPROM_FRONTLED_ADDR, conf.raw);
layer_state_t layer_state_set_kb(layer_state_t state) {
    fled_layer_update(state);
    return layer_state_set_user(state);
}

// Custom keycode functions
// Fallback eeprom functions if VIA is not enabled
#ifndef VIA_ENABLE

void fled_mode_cycle(void)
// Sets VIA/keyboard level usage of EEPROM to valid/invalid
// Keyboard level code (eg. via_init_kb()) should not call this
void via_eeprom_set_valid(bool valid)
{
    // FLED -> FLED_RGB -> FLED_INDI
    switch (fled_mode) {
        case FLED_OFF:
        fled_mode = FLED_RGB;
        break;
        
        case FLED_RGB:
        fled_mode = FLED_INDI;
        break;
        
        case FLED_INDI:
        fled_mode = FLED_OFF;
        break;
    }
    
    // Update stored config
    eeprom_update_conf();
    rgblight_set();
    char *p = QMK_BUILDDATE; // e.g. "2019-11-05-11:29:54"
    uint8_t magic0 = ( ( p[2] & 0x0F ) << 4 ) | ( p[3]  & 0x0F );
    uint8_t magic1 = ( ( p[5] & 0x0F ) << 4 ) | ( p[6]  & 0x0F );
    uint8_t magic2 = ( ( p[8] & 0x0F ) << 4 ) | ( p[9]  & 0x0F );

    eeprom_update_byte( (void*)VIA_EEPROM_MAGIC_ADDR+0, valid ? magic0 : 0xFF);
    eeprom_update_byte( (void*)VIA_EEPROM_MAGIC_ADDR+1, valid ? magic1 : 0xFF);
    eeprom_update_byte( (void*)VIA_EEPROM_MAGIC_ADDR+2, valid ? magic2 : 0xFF);
}

void fled_val_increase(void)
{
    // Increase val by FLED_VAL_STEP, handling the upper edge case
    if (fled_val + FLED_VAL_STEP > 255)
        fled_val = 255;
    else
        fled_val += FLED_VAL_STEP;
    
    // Update stored config
    eeprom_update_conf();
    rgblight_set();
}

void fled_val_decrease(void)
{
    // Decrease val by FLED_VAL_STEP, handling the lower edge case
    if (fled_val - FLED_VAL_STEP > 255)
        fled_val = 255;
    else
        fled_val -= FLED_VAL_STEP;
    
    // Update stored config
    eeprom_update_conf();
    rgblight_set();
}
#endif

M keyboards/mxss/mxss.h => keyboards/mxss/mxss.h +16 -3
@@ 13,8 13,7 @@
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef MXSS_H
#define MXSS_H
#pragma once

#include "quantum.h"
#include "mxss_frontled.h"


@@ 204,4 203,18 @@
    { k40, k41, k42, k43, KC_NO, KC_NO, k46, KC_NO, k48, KC_NO, k4A, k4B, k4C, k4D, k4E }, \
}

#endif
// All the gubs
#define LAYOUT_all( \
    k00, k01, k02, k03, k04, k05, k06, k07, k08, k09, k0A, k0B, k0C, k0D, k0E, \
    k10, k11, k12, k13, k14, k15, k16, k17, k18, k19, k1A, k1B, k1C, k1D, k1E, \
    k20, k21, k22, k23, k24, k25, k26, k27, k28, k29, k2A, k2B, k2C, k2D, k2E, \
    k30, k31, k32, k33, k34, k35, k36, k37, k38, k39, k3A, k3B, k3C, k3D, k3E, \
    k40, k41, k42,      k43,      k46,      k48,      k4A, k4B, k4C, k4D, k4E \
) \
{ \
    { k00, k01, k02, k03, k04, k05, k06, k07, k08, k09, k0A, k0B, k0C, k0D, k0E }, \
    { k10, k11, k12, k13, k14, k15, k16, k17, k18, k19, k1A, k1B, k1C, k1D, k1E }, \
    { k20, k21, k22, k23, k24, k25, k26, k27, k28, k29, k2A, k2B, k2C, k2D, k2E }, \
    { k30, k31, k32, k33, k34, k35, k36, k37, k38, k39, k3A, k3B, k3C, k3D, k3E }, \
    { k40, k41, k42, k43, KC_NO, KC_NO, k46, KC_NO, k48, KC_NO, k4A, k4B, k4C, k4D, k4E }, \
}

A keyboards/mxss/mxss_frontled.c => keyboards/mxss/mxss_frontled.c +264 -0
@@ 0,0 1,264 @@
/* Copyright 2020 Jumail Mundekkat / MxBlue
 *
 * 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/>.
 *
 * Extended from the work done by fcoury: https://github.com/qmk/qmk_firmware/pull/4915
 */

#include "mxss_frontled.h"
#include "tmk_core/common/eeprom.h"
#include "rgblight.h"
#include "via.h"
#include "version.h" // for QMK_BUILDDATE used in EEPROM magic

// Variables for controlling front LED application
uint8_t fled_mode;  // Mode for front LEDs
uint8_t fled_val;   // Brightness for front leds (0 - 255)
LED_TYPE fleds[2];  // Front LED rgb values for indicator mode use

// Layer indicator colors
__attribute__ ((weak))
hs_set layer_colors[FRONTLED_COLOR_MAXCNT];

// Caps lock indicator color
__attribute__ ((weak))
hs_set caps_color;

__attribute__ ((weak))
size_t lc_size = sizeof(layer_colors) / sizeof(hs_set);

void fled_init(void) {
    // If EEPROM config exists, load it
    // If VIA EEPROM exists, FLED config should too
    if (via_eeprom_is_valid()) {
        fled_load_conf();
    // Else, default config
    } else {
        // Default mode/brightness
        fled_mode = FLED_RGB;
        fled_val = 10 * FLED_VAL_STEP;

        // Default colors
        caps_color.hue = 0;
        caps_color.sat = 255;
        layer_colors[0].hue = 0;
        layer_colors[0].sat = 0;
        layer_colors[1].hue = 86;
        layer_colors[1].sat = 255;
        layer_colors[2].hue = 36;
        layer_colors[2].sat = 255;
        layer_colors[3].hue = 185;
        layer_colors[3].sat = 255;

        fled_update_conf();   // Store default config to EEPROM
    }

    // Set default values for leds
    setrgb(0, 0, 0, &fleds[0]);
    setrgb(0, 0, 0, &fleds[1]);

    // Handle lighting for indicator mode
    if (fled_mode == FLED_INDI) {
        fled_lock_update(host_keyboard_led_state());
        fled_layer_update(layer_state);
    }
}

void  process_record_fled(uint16_t keycode, keyrecord_t *record) {
    // Handle custom keycodes for front LED operation
    switch (keycode) {
        case FLED_MOD: // Change between front LED operation modes (off, indicator, RGB)
        if (record->event.pressed)
            fled_mode_cycle();
        break;

        case FLED_VAI: // Increase the brightness of the front LEDs by FLED_VAL_STEP
        if (record->event.pressed)
            fled_val_increase();
        break;

        case FLED_VAD: // Decrease the brightness of the front LEDs by FLED_VAL_STEP
        if (record->event.pressed)
            fled_val_decrease();
        break;

        default:
        break; // Process all other keycodes normally
      }

	return;
}

void fled_load_conf(void) {
    // Load config
    fled_config fled_conf;
    fled_conf.raw = eeprom_read_byte(FRONTLED_CONF_ADDR);
    fled_mode = fled_conf.mode;
    fled_val = fled_conf.val * FLED_VAL_STEP;

    // Load color data
    uint8_t stored_cnt = eeprom_read_byte(FRONTLED_COLOR_CNT_ADDR);
    uint16_t *color_ptr = FRONTLED_COLOR_ADDR;
    caps_color.raw = eeprom_read_word(color_ptr); // Should always store at least 1 color
    for (uint8_t i = 1; i < stored_cnt; i++) {
        if (i == lc_size) // Can't load more layers than we have available
            break;
        layer_colors[i].raw = eeprom_read_word(&color_ptr[i]);
    }
    layer_colors[0].raw = 0; // hue = sat = 0 for layer 0
}

// Store current front led config in EEPROM
void fled_update_conf(void)
{
    // Create storage struct and set values
    fled_config conf;
    conf.mode = fled_mode;

    // Small hack to ensure max value is stored correctly
    if (fled_val == 255)
        conf.val = 256 / FLED_VAL_STEP;
    else
        conf.val = fled_val / FLED_VAL_STEP;

    // Store config
    eeprom_update_byte(FRONTLED_CONF_ADDR, conf.raw);

    // Store color data
    uint16_t *color_ptr = FRONTLED_COLOR_ADDR;
    eeprom_update_word(color_ptr, caps_color.raw);
    // Start from 1, layer 0 is not modifiable and therefore not persisted
    uint8_t i = 1;
    for (; i < lc_size; i++) {
        if (i == FRONTLED_COLOR_MAXCNT) // Can't store more than the EEPROM we have available
            break;
        eeprom_update_word(&color_ptr[i], layer_colors[i].raw);
    }
    eeprom_update_byte(FRONTLED_COLOR_CNT_ADDR, i); // For safety, store the count of colors stored
}

// Custom keycode functions

void fled_mode_cycle(void)
{
    // FLED -> FLED_RGB -> FLED_INDI
    switch (fled_mode) {
        case FLED_OFF:
        fled_mode = FLED_RGB;
        rgblight_timer_enable();
        break;

        case FLED_RGB:
        fled_mode = FLED_INDI;
        break;

        case FLED_INDI:
        fled_mode = FLED_OFF;
        break;
    }

    // Update stored config
    fled_update_conf();
    rgblight_set();
}

void fled_val_increase(void)
{
    // Increase val by FLED_VAL_STEP, handling the upper edge case
    if (fled_val + FLED_VAL_STEP > 255)
        fled_val = 255;
    else
        fled_val += FLED_VAL_STEP;

    // Update stored config
    fled_update_conf();
    rgblight_set();
}

void fled_val_decrease(void)
{
    // Decrease val by FLED_VAL_STEP, handling the lower edge case
    if (fled_val - FLED_VAL_STEP > 255)
        fled_val = 255;
    else
        fled_val -= FLED_VAL_STEP;

    // Update stored config
    fled_update_conf();
    rgblight_set();
}

void fled_layer_update(layer_state_t state) {
    // Determine and set colour of layer LED according to current layer
    // if hue = sat = 0, leave LED off
    uint8_t layer = get_highest_layer(state);

    if (layer < lc_size && !(layer_colors[layer].hue == 0 && layer_colors[layer].sat == 0)) {
        sethsv(layer_colors[layer].hue, layer_colors[layer].sat, fled_val, &fleds[1]);
    } else {
        setrgb(0, 0, 0, &fleds[1]);
    }
}

void fled_lock_update(led_t led_state) {
    // Set indicator LED appropriately, whether it is used or not
    if (led_state.caps_lock) {
        sethsv(caps_color.hue, caps_color.sat, fled_val, &fleds[0]);
    } else {
        setrgb(0, 0, 0, &fleds[0]);
    }

    rgblight_set();
}

void set_fled_layer_color(uint8_t layer, hs_set hs) {
    // Update layer colors and refresh LEDs
    layer_colors[layer] = hs;
    fled_layer_update(layer_state);
    fled_update_conf();
}

hs_set get_fled_layer_color(uint8_t layer) {
    return layer_colors[layer];
}

void set_fled_caps_color(hs_set hs) {
    // Update caplock color and refresh LEDs
    caps_color = hs;
    fled_lock_update(host_keyboard_led_state());
    fled_update_conf();
}

hs_set get_fled_caps_color(void) {
    return caps_color;
}

// Fallback eeprom functions if VIA is not enabled
#ifndef VIA_ENABLE

// Can be called in an overriding via_init_kb() to test if keyboard level code usage of
// EEPROM is invalid and use/save defaults.
bool via_eeprom_is_valid(void)
{
    char *p = QMK_BUILDDATE; // e.g. "2019-11-05-11:29:54"
    uint8_t magic0 = ( ( p[2] & 0x0F ) << 4 ) | ( p[3]  & 0x0F );
    uint8_t magic1 = ( ( p[5] & 0x0F ) << 4 ) | ( p[6]  & 0x0F );
    uint8_t magic2 = ( ( p[8] & 0x0F ) << 4 ) | ( p[9]  & 0x0F );

    return (eeprom_read_byte( (void*)VIA_EEPROM_MAGIC_ADDR+0 ) == magic0 &&
            eeprom_read_byte( (void*)VIA_EEPROM_MAGIC_ADDR+1 ) == magic1 &&
            eeprom_read_byte( (void*)VIA_EEPROM_MAGIC_ADDR+2 ) == magic2 );
}

#endif

M keyboards/mxss/mxss_frontled.h => keyboards/mxss/mxss_frontled.h +32 -25
@@ 1,4 1,4 @@
/* Copyright 2018 Jumail Mundekkat / MxBlue
/* Copyright 2020 Jumail Mundekkat / MxBlue
 *
 * 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


@@ 16,11 16,12 @@

// EEPROM management code taken from Wilba6582
// https://github.com/Wilba6582/qmk_firmware/blob/zeal60/keyboards/zeal60/zeal_eeprom.h
 
#ifndef MXSS_FRONTLED_H
#define MXSS_FRONTLED_H

#pragma once

#include "quantum.h"
#include "quantum_keycodes.h"
#include "via.h"

// RGBLED index for front LEDs
#define RGBLIGHT_FLED1 14


@@ 29,13 30,13 @@
// Brightness increase step for front LEDs
#define FLED_VAL_STEP 8

// QMK never uses more then 32bytes of EEPROM, so our region starts there
// Magic value to verify the state of the EEPROM
#define EEPROM_MAGIC 0xC3E7
#define EEPROM_MAGIC_ADDR ((void*)32)

// Front LED settings
#define EEPROM_FRONTLED_ADDR ((void*)34)
#define FRONTLED_CONF_ADDR ((uint8_t*) VIA_EEPROM_CUSTOM_CONFIG_ADDR)
#define FRONTLED_COLOR_CNT_ADDR (FRONTLED_CONF_ADDR + 1)
#define FRONTLED_COLOR_ADDR ((uint16_t*)(FRONTLED_COLOR_CNT_ADDR + 1))

// No point persisting more 4, VIA only allows editing of 3 + 1 for caps
#define FRONTLED_COLOR_MAXCNT 4

// Modes for front LEDs
#define FLED_OFF    0b00


@@ 43,10 44,6 @@
#define FLED_RGB    0b10
#define FLED_UNDEF  0b11

// Hard-coded color for capslock indicator in FLED_INDI mode, H:0% S:100% = Red
#define FLED_CAPS_H 0
#define FLED_CAPS_S 255

// Config storage format for EEPROM
typedef union {
  uint8_t raw;


@@ 57,25 54,35 @@ typedef union {
} fled_config;

// Structure to store hue and saturation values
typedef struct _hs_set {
    uint16_t hue; 
    uint8_t sat;
typedef union {
    uint16_t raw;
    struct {
        uint8_t hue;
        uint8_t sat;
    };
} hs_set;

// Custom keycodes for front LED control
enum fled_keycodes {
  FLED_MOD = SAFE_RANGE,
  FLED_VAI,
  FLED_VAD,
  NEW_SAFE_RANGE // define a new safe range
    FLED_MOD = USER00, // USER00 = VIA custom keycode start
    FLED_VAI,
    FLED_VAD,
    NEW_SAFE_RANGE // define a new safe range
};

bool eeprom_is_valid(void);         // Check if EEPROM has been set up
void eeprom_set_valid(bool valid);  // Change validity state of EEPROM
void eeprom_update_conf(void);      // Store current front LED config to EEPROM
void fled_init(void);                // Run init functions for front LEDs
void process_record_fled(uint16_t keycode, keyrecord_t* record); // Process keycodes for front LEDs
void fled_load_conf(void);          // Load front LED config from EEPROM
void fled_update_conf(void);        // Store current front LED config to EEPROM

void fled_mode_cycle(void);         // Cycle between the 3 modes for the front LEDs
void fled_val_increase(void);       // Increase the brightness of the front LEDs
void fled_val_decrease(void);       // Decrease the brightness of the front LEDs

#endif //MXSS_FRONTLED_H
\ No newline at end of file
void fled_layer_update(layer_state_t state);   // Process layer update for front LEDs
void fled_lock_update(led_t led_state);     // Process lock update for front LEDs

void set_fled_layer_color(uint8_t layer, hs_set hs); // Set color for a given layer
void set_fled_caps_color(hs_set hs);        // Set color for the capslock indicator
hs_set get_fled_caps_color(void);           // Get color for the capslock indicator
hs_set get_fled_layer_color(uint8_t layer);       // Get color for a given layer

M keyboards/mxss/readme.md => keyboards/mxss/readme.md +6 -1
@@ 2,6 2,11 @@

![MxSS - Polycarb 65% Kit for MX/SMK](https://i.imgur.com/WDTWcmU.jpg)

### Important Note:
**This PCB supports the VIA configurator, and this is the recommended way to configure the keymap on this keyboard. Building the firmware should only be necessary if you would like to change the colours of the front LEDs in indicator mode.**

**For more information about the VIA configurator, [see here](https://caniusevia.com/).**

### Information:

 - Case: Frosted Polycarbonate, CNC milled


@@ 51,4 56,4 @@ Colors for FLED_INDI mode are hardcoded as hue/saturation values, the caps lock 
 
 # Further Notes
 
 As SAFE_RANGE is used for defining the custom keycodes seen above, please use NEW_SAFE_RANGE as the starting value for any custom keycodes in keymap.c, as per the example.
\ No newline at end of file
 As SAFE_RANGE is used for defining the custom keycodes seen above, please use NEW_SAFE_RANGE as the starting value for any custom keycodes in keymap.c, as per the example.

M keyboards/mxss/rgblight.c => keyboards/mxss/rgblight.c +990 -638
@@ 14,814 14,1166 @@
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include <math.h>
#include <avr/eeprom.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <string.h>
#ifdef __AVR__
#    include <avr/eeprom.h>
#    include <avr/interrupt.h>
#endif
#ifdef STM32_EEPROM_ENABLE
#    include "hal.h"
#    include "eeprom.h"
#    include "eeprom_stm32.h"
#endif
#include "wait.h"
#include "progmem.h"
#include "timer.h"
#include "rgblight.h"
#include "color.h"
#include "debug.h"
#include "led_tables.h"
#include "lib/lib8tion/lib8tion.h"
#ifdef VELOCIKEY_ENABLE
#    include "velocikey.h"
#endif
// MxSS custom
#include "mxss_frontled.h"

#ifndef RGBLIGHT_LIMIT_VAL
#define RGBLIGHT_LIMIT_VAL 255
#ifdef RGBLIGHT_SPLIT
/* for split keyboard */
#    define RGBLIGHT_SPLIT_SET_CHANGE_MODE rgblight_status.change_flags |= RGBLIGHT_STATUS_CHANGE_MODE
#    define RGBLIGHT_SPLIT_SET_CHANGE_HSVS rgblight_status.change_flags |= RGBLIGHT_STATUS_CHANGE_HSVS
#    define RGBLIGHT_SPLIT_SET_CHANGE_MODEHSVS rgblight_status.change_flags |= (RGBLIGHT_STATUS_CHANGE_MODE | RGBLIGHT_STATUS_CHANGE_HSVS)
#    define RGBLIGHT_SPLIT_SET_CHANGE_TIMER_ENABLE rgblight_status.change_flags |= RGBLIGHT_STATUS_CHANGE_TIMER
#    define RGBLIGHT_SPLIT_ANIMATION_TICK rgblight_status.change_flags |= RGBLIGHT_STATUS_ANIMATION_TICK
#else
#    define RGBLIGHT_SPLIT_SET_CHANGE_MODE
#    define RGBLIGHT_SPLIT_SET_CHANGE_HSVS
#    define RGBLIGHT_SPLIT_SET_CHANGE_MODEHSVS
#    define RGBLIGHT_SPLIT_SET_CHANGE_TIMER_ENABLE
#    define RGBLIGHT_SPLIT_ANIMATION_TICK
#endif

#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))

#define LED_PTRTOIND(ptr) ((uint32_t) (ptr - led)/sizeof(LED_TYPE))

void copyrgb(LED_TYPE *src, LED_TYPE *dst);
#define _RGBM_SINGLE_STATIC(sym) RGBLIGHT_MODE_##sym,
#define _RGBM_SINGLE_DYNAMIC(sym)
#define _RGBM_MULTI_STATIC(sym) RGBLIGHT_MODE_##sym,
#define _RGBM_MULTI_DYNAMIC(sym)
#define _RGBM_TMP_STATIC(sym, msym) RGBLIGHT_MODE_##sym,
#define _RGBM_TMP_DYNAMIC(sym, msym)
static uint8_t static_effect_table[] = {
#include "rgblight_modes.h"
};

#define _RGBM_SINGLE_STATIC(sym) RGBLIGHT_MODE_##sym,
#define _RGBM_SINGLE_DYNAMIC(sym) RGBLIGHT_MODE_##sym,
#define _RGBM_MULTI_STATIC(sym) RGBLIGHT_MODE_##sym,
#define _RGBM_MULTI_DYNAMIC(sym) RGBLIGHT_MODE_##sym,
#define _RGBM_TMP_STATIC(sym, msym) RGBLIGHT_MODE_##msym,
#define _RGBM_TMP_DYNAMIC(sym, msym) RGBLIGHT_MODE_##msym,
static uint8_t mode_base_table[] = {
    0,  // RGBLIGHT_MODE_zero
#include "rgblight_modes.h"
};

static inline int is_static_effect(uint8_t mode) { return memchr(static_effect_table, mode, sizeof(static_effect_table)) != NULL; }

#ifdef RGBLIGHT_LED_MAP
const uint8_t led_map[] PROGMEM = RGBLIGHT_LED_MAP;
#endif

__attribute__ ((weak))
const uint8_t RGBLED_BREATHING_INTERVALS[] PROGMEM = {30, 20, 10, 5};
__attribute__ ((weak))
const uint8_t RGBLED_RAINBOW_MOOD_INTERVALS[] PROGMEM = {120, 60, 30};
__attribute__ ((weak))
const uint8_t RGBLED_RAINBOW_SWIRL_INTERVALS[] PROGMEM = {100, 50, 20};
__attribute__ ((weak))
const uint8_t RGBLED_SNAKE_INTERVALS[] PROGMEM = {100, 50, 20};
__attribute__ ((weak))
const uint8_t RGBLED_KNIGHT_INTERVALS[] PROGMEM = {127, 63, 31};
__attribute__ ((weak))
const uint16_t RGBLED_GRADIENT_RANGES[] PROGMEM = {360, 240, 180, 120, 90};
__attribute__ ((weak))
const uint16_t RGBLED_RGBTEST_INTERVALS[] PROGMEM = {1024};
#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT
__attribute__((weak)) const uint8_t RGBLED_GRADIENT_RANGES[] PROGMEM = {255, 170, 127, 85, 64};
#endif

rgblight_config_t rgblight_config;
rgblight_status_t rgblight_status         = {.timer_enabled = false};
bool              is_rgblight_initialized = false;

#ifdef RGBLIGHT_USE_TIMER
animation_status_t animation_status = {};
#endif

#ifndef LED_ARRAY
LED_TYPE led[RGBLED_NUM];
#    define LED_ARRAY led
#endif

bool rgblight_timer_enabled = false;
static uint8_t clipping_start_pos = 0;
static uint8_t clipping_num_leds  = RGBLED_NUM;
static uint8_t effect_start_pos   = 0;
static uint8_t effect_end_pos     = RGBLED_NUM;
static uint8_t effect_num_leds    = RGBLED_NUM;

// MxSS custom
extern uint8_t fled_mode;
extern uint8_t fled_val;
extern LED_TYPE fleds[2];
hs_set fled_hs[2];

void sethsv(uint16_t hue, uint8_t sat, uint8_t val, LED_TYPE *led1) {
  uint8_t r = 0, g = 0, b = 0, base, color;
  
  // if led is front leds, cache the hue and sat values
  if (led1 == &led[RGBLIGHT_FLED1]) {
      fled_hs[0].hue = hue;
      fled_hs[0].sat = sat;
  } else if (led1 == &led[RGBLIGHT_FLED2]) {
      fled_hs[1].hue = hue;
      fled_hs[1].sat = sat;
  }
void copyrgb(LED_TYPE *src, LED_TYPE *dst) {
  (*dst).r = (*src).r;
  (*dst).g = (*src).g;
  (*dst).b = (*src).b;
}

  if (val > RGBLIGHT_LIMIT_VAL) {
      val=RGBLIGHT_LIMIT_VAL; // limit the val
  }
void rgblight_set_clipping_range(uint8_t start_pos, uint8_t num_leds) {
    clipping_start_pos = start_pos;
    clipping_num_leds  = num_leds;
}

  if (sat == 0) { // Acromatic color (gray). Hue doesn't mind.
    r = val;
    g = val;
    b = val;
  } else {
    base = ((255 - sat) * val) >> 8;
    color = (val - base) * (hue % 60) / 60;

    switch (hue / 60) {
      case 0:
        r = val;
        g = base + color;
        b = base;
        break;
      case 1:
        r = val - color;
        g = val;
        b = base;
        break;
      case 2:
        r = base;
        g = val;
        b = base + color;
        break;
      case 3:
        r = base;
        g = val - color;
        b = val;
        break;
      case 4:
        r = base + color;
        g = base;
        b = val;
        break;
      case 5:
        r = val;
        g = base;
        b = val - color;
        break;
    }
  }
  r = pgm_read_byte(&CIE1931_CURVE[r]);
  g = pgm_read_byte(&CIE1931_CURVE[g]);
  b = pgm_read_byte(&CIE1931_CURVE[b]);
void rgblight_set_effect_range(uint8_t start_pos, uint8_t num_leds) {
    if (start_pos >= RGBLED_NUM) return;
    if (start_pos + num_leds > RGBLED_NUM) return;
    effect_start_pos = start_pos;
    effect_end_pos   = start_pos + num_leds;
    effect_num_leds  = num_leds;
}

  setrgb(r, g, b, led1);
void sethsv_raw(uint8_t hue, uint8_t sat, uint8_t val, LED_TYPE *led1) {
    HSV hsv = {hue, sat, val};
    // MxSS custom
    // if led is front leds, cache the hue and sat values
    if (led1 == &led[RGBLIGHT_FLED1]) {
        fled_hs[0].hue = hue;
        fled_hs[0].sat = sat;
    } else if (led1 == &led[RGBLIGHT_FLED2]) {
        fled_hs[1].hue = hue;
        fled_hs[1].sat = sat;
    }  RGB rgb = hsv_to_rgb(hsv);
    setrgb(rgb.r, rgb.g, rgb.b, led1);
}

void sethsv(uint8_t hue, uint8_t sat, uint8_t val, LED_TYPE *led1) { sethsv_raw(hue, sat, val > RGBLIGHT_LIMIT_VAL ? RGBLIGHT_LIMIT_VAL : val, led1); }

void setrgb(uint8_t r, uint8_t g, uint8_t b, LED_TYPE *led1) {
  (*led1).r = r;
  (*led1).g = g;
  (*led1).b = b;
    (*led1).r = r;
    (*led1).g = g;
    (*led1).b = b;
#ifdef RGBW
    (*led1).w = 0;
#endif
}

void copyrgb(LED_TYPE *src, LED_TYPE *dst) {
  (*dst).r = (*src).r;
  (*dst).g = (*src).g;
  (*dst).b = (*src).b;
void rgblight_check_config(void) {
    /* Add some out of bound checks for RGB light config */

    if (rgblight_config.mode < RGBLIGHT_MODE_STATIC_LIGHT) {
        rgblight_config.mode = RGBLIGHT_MODE_STATIC_LIGHT;
    } else if (rgblight_config.mode > RGBLIGHT_MODES) {
        rgblight_config.mode = RGBLIGHT_MODES;
    }

    if (rgblight_config.val > RGBLIGHT_LIMIT_VAL) {
        rgblight_config.val = RGBLIGHT_LIMIT_VAL;
    }
}

uint32_t eeconfig_read_rgblight(void) {
  return eeprom_read_dword(EECONFIG_RGBLIGHT);
#if defined(__AVR__) || defined(STM32_EEPROM_ENABLE) || defined(PROTOCOL_ARM_ATSAM) || defined(EEPROM_SIZE)
    return eeprom_read_dword(EECONFIG_RGBLIGHT);
#else
    return 0;
#endif
}

void eeconfig_update_rgblight(uint32_t val) {
  eeprom_update_dword(EECONFIG_RGBLIGHT, val);
#if defined(__AVR__) || defined(STM32_EEPROM_ENABLE) || defined(PROTOCOL_ARM_ATSAM) || defined(EEPROM_SIZE)
    rgblight_check_config();
    eeprom_update_dword(EECONFIG_RGBLIGHT, val);
#endif
}

void eeconfig_update_rgblight_default(void) {
  dprintf("eeconfig_update_rgblight_default\n");
  rgblight_config.enable = 1;
  rgblight_config.mode = 1;
  rgblight_config.hue = 0;
  rgblight_config.sat = 255;
  rgblight_config.val = RGBLIGHT_LIMIT_VAL;
  rgblight_config.speed = 0;
  eeconfig_update_rgblight(rgblight_config.raw);
    rgblight_config.enable = 1;
    rgblight_config.mode   = RGBLIGHT_MODE_STATIC_LIGHT;
    rgblight_config.hue    = 0;
    rgblight_config.sat    = UINT8_MAX;
    rgblight_config.val    = RGBLIGHT_LIMIT_VAL;
    rgblight_config.speed  = 0;
    RGBLIGHT_SPLIT_SET_CHANGE_MODEHSVS;
    eeconfig_update_rgblight(rgblight_config.raw);
}

void eeconfig_debug_rgblight(void) {
  dprintf("rgblight_config eprom\n");
  dprintf("rgblight_config.enable = %d\n", rgblight_config.enable);
  dprintf("rghlight_config.mode = %d\n", rgblight_config.mode);
  dprintf("rgblight_config.hue = %d\n", rgblight_config.hue);
  dprintf("rgblight_config.sat = %d\n", rgblight_config.sat);
  dprintf("rgblight_config.val = %d\n", rgblight_config.val);
  dprintf("rgblight_config.speed = %d\n", rgblight_config.speed);
    dprintf("rgblight_config EEPROM:\n");
    dprintf("rgblight_config.enable = %d\n", rgblight_config.enable);
    dprintf("rghlight_config.mode = %d\n", rgblight_config.mode);
    dprintf("rgblight_config.hue = %d\n", rgblight_config.hue);
    dprintf("rgblight_config.sat = %d\n", rgblight_config.sat);
    dprintf("rgblight_config.val = %d\n", rgblight_config.val);
    dprintf("rgblight_config.speed = %d\n", rgblight_config.speed);
}

void rgblight_init(void) {
  debug_enable = 1; // Debug ON!
  dprintf("rgblight_init called.\n");
  dprintf("rgblight_init start!\n");
  if (!eeconfig_is_enabled()) {
    dprintf("rgblight_init eeconfig is not enabled.\n");
    eeconfig_init();
    eeconfig_update_rgblight_default();
  }
  rgblight_config.raw = eeconfig_read_rgblight();
  if (!rgblight_config.mode) {
    dprintf("rgblight_init rgblight_config.mode = 0. Write default values to EEPROM.\n");
    eeconfig_update_rgblight_default();
    /* if already initialized, don't do it again.
       If you must do it again, extern this and set to false, first.
       This is a dirty, dirty hack until proper hooks can be added for keyboard startup. */
    if (is_rgblight_initialized) {
        return;
    }

    dprintf("rgblight_init called.\n");
    dprintf("rgblight_init start!\n");
    if (!eeconfig_is_enabled()) {
        dprintf("rgblight_init eeconfig is not enabled.\n");
        eeconfig_init();
        eeconfig_update_rgblight_default();
    }
    rgblight_config.raw = eeconfig_read_rgblight();
  }
  eeconfig_debug_rgblight(); // display current eeprom values
    RGBLIGHT_SPLIT_SET_CHANGE_MODEHSVS;
    if (!rgblight_config.mode) {
        dprintf("rgblight_init rgblight_config.mode = 0. Write default values to EEPROM.\n");
        eeconfig_update_rgblight_default();
        rgblight_config.raw = eeconfig_read_rgblight();
    }
    rgblight_check_config();

  #ifdef RGBLIGHT_ANIMATIONS
    rgblight_timer_init(); // setup the timer
  #endif
    eeconfig_debug_rgblight();  // display current eeprom values

  if (rgblight_config.enable) {
    rgblight_mode_noeeprom(rgblight_config.mode);
  }
#ifdef RGBLIGHT_USE_TIMER
    rgblight_timer_init();  // setup the timer
#endif

    if (rgblight_config.enable) {
        rgblight_mode_noeeprom(rgblight_config.mode);
    }

    is_rgblight_initialized = true;
}

uint32_t rgblight_read_dword(void) { return rgblight_config.raw; }

void rgblight_update_dword(uint32_t dword) {
  rgblight_config.raw = dword;
  eeconfig_update_rgblight(rgblight_config.raw);
  if (rgblight_config.enable)
    rgblight_mode(rgblight_config.mode);
  else {
    #ifdef RGBLIGHT_ANIMATIONS
      rgblight_timer_disable();
    #endif
      rgblight_set();
  }
    RGBLIGHT_SPLIT_SET_CHANGE_MODEHSVS;
    rgblight_config.raw = dword;
    if (rgblight_config.enable)
        rgblight_mode_noeeprom(rgblight_config.mode);
    else {
#ifdef RGBLIGHT_USE_TIMER
        rgblight_timer_disable();
#endif
        rgblight_set();
    }
}

void rgblight_increase(void) {
  uint8_t mode = 0;
  if (rgblight_config.mode < RGBLIGHT_MODES) {
    mode = rgblight_config.mode + 1;
  }
  rgblight_mode(mode);
    uint8_t mode = 0;
    if (rgblight_config.mode < RGBLIGHT_MODES) {
        mode = rgblight_config.mode + 1;
    }
    rgblight_mode(mode);
}
void rgblight_decrease(void) {
  uint8_t mode = 0;
  // Mode will never be < 1. If it ever is, eeprom needs to be initialized.
  if (rgblight_config.mode > 1) {
    mode = rgblight_config.mode - 1;
  }
  rgblight_mode(mode);
    uint8_t mode = 0;
    // Mode will never be < 1. If it ever is, eeprom needs to be initialized.
    if (rgblight_config.mode > RGBLIGHT_MODE_STATIC_LIGHT) {
        mode = rgblight_config.mode - 1;
    }
    rgblight_mode(mode);
}
void rgblight_step(void) {
  uint8_t mode = 0;
  mode = rgblight_config.mode + 1;
  if (mode > RGBLIGHT_MODES) {
    mode = 1;
  }
  rgblight_mode(mode);
void rgblight_step_helper(bool write_to_eeprom) {
    uint8_t mode = 0;
    mode         = rgblight_config.mode + 1;
    if (mode > RGBLIGHT_MODES) {
        mode = 1;
    }
    rgblight_mode_eeprom_helper(mode, write_to_eeprom);
}
void rgblight_step_reverse(void) {
  uint8_t mode = 0;
  mode = rgblight_config.mode - 1;
  if (mode < 1) {
    mode = RGBLIGHT_MODES;
  }
  rgblight_mode(mode);
void rgblight_step_noeeprom(void) { rgblight_step_helper(false); }
void rgblight_step(void) { rgblight_step_helper(true); }
void rgblight_step_reverse_helper(bool write_to_eeprom) {
    uint8_t mode = 0;
    mode         = rgblight_config.mode - 1;
    if (mode < 1) {
        mode = RGBLIGHT_MODES;
    }
    rgblight_mode_eeprom_helper(mode, write_to_eeprom);
}
void rgblight_step_reverse_noeeprom(void) { rgblight_step_reverse_helper(false); }
void rgblight_step_reverse(void) { rgblight_step_reverse_helper(true); }

uint8_t rgblight_get_mode(void) {
  if (!rgblight_config.enable) {
    return false;
  }
    if (!rgblight_config.enable) {
        return false;
    }

  return rgblight_config.mode;
    return rgblight_config.mode;
}

void rgblight_mode_eeprom_helper(uint8_t mode, bool write_to_eeprom) {
  if (!rgblight_config.enable) {
    return;
  }
  if (mode < 1) {
    rgblight_config.mode = 1;
  } else if (mode > RGBLIGHT_MODES) {
    rgblight_config.mode = RGBLIGHT_MODES;
  } else {
    rgblight_config.mode = mode;
  }
  if (write_to_eeprom) {
    eeconfig_update_rgblight(rgblight_config.raw);
    xprintf("rgblight mode [EEPROM]: %u\n", rgblight_config.mode);
  } else {
    xprintf("rgblight mode [NOEEPROM]: %u\n", rgblight_config.mode);
  }
  if (rgblight_config.mode == 1) {
    #ifdef RGBLIGHT_ANIMATIONS
      rgblight_timer_disable();
    #endif
  } else if ((rgblight_config.mode >= 2 && rgblight_config.mode <= 24) ||
	     rgblight_config.mode == 35 ) {
    // MODE 2-5, breathing
    // MODE 6-8, rainbow mood
    // MODE 9-14, rainbow swirl
    // MODE 15-20, snake
    // MODE 21-23, knight
    // MODE 24, xmas
    // MODE 35  RGB test

    #ifdef RGBLIGHT_ANIMATIONS
      rgblight_timer_enable();
    #endif
  } else if (rgblight_config.mode >= 25 && rgblight_config.mode <= 34) {
    // MODE 25-34, static gradient

    #ifdef RGBLIGHT_ANIMATIONS
      rgblight_timer_disable();
    #endif
  }
  rgblight_sethsv_noeeprom(rgblight_config.hue, rgblight_config.sat, rgblight_config.val);
}

void rgblight_mode(uint8_t mode) {
  rgblight_mode_eeprom_helper(mode, true);
    if (!rgblight_config.enable) {
        return;
    }
    if (mode < RGBLIGHT_MODE_STATIC_LIGHT) {
        rgblight_config.mode = RGBLIGHT_MODE_STATIC_LIGHT;
    } else if (mode > RGBLIGHT_MODES) {
        rgblight_config.mode = RGBLIGHT_MODES;
    } else {
        rgblight_config.mode = mode;
    }
    RGBLIGHT_SPLIT_SET_CHANGE_MODE;
    if (write_to_eeprom) {
        eeconfig_update_rgblight(rgblight_config.raw);
        dprintf("rgblight mode [EEPROM]: %u\n", rgblight_config.mode);
    } else {
        dprintf("rgblight mode [NOEEPROM]: %u\n", rgblight_config.mode);
    }
    if (is_static_effect(rgblight_config.mode)) {
#ifdef RGBLIGHT_USE_TIMER
        rgblight_timer_disable();
#endif
    } else {
#ifdef RGBLIGHT_USE_TIMER
        rgblight_timer_enable();
#endif
    }
#ifdef RGBLIGHT_USE_TIMER
    animation_status.restart = true;
#endif
    rgblight_sethsv_noeeprom(rgblight_config.hue, rgblight_config.sat, rgblight_config.val);
}

void rgblight_mode_noeeprom(uint8_t mode) {
  rgblight_mode_eeprom_helper(mode, false);
}
void rgblight_mode(uint8_t mode) { rgblight_mode_eeprom_helper(mode, true); }

void rgblight_mode_noeeprom(uint8_t mode) { rgblight_mode_eeprom_helper(mode, false); }

void rgblight_toggle(void) {
  xprintf("rgblight toggle [EEPROM]: rgblight_config.enable = %u\n", !rgblight_config.enable);
  if (rgblight_config.enable) {
    rgblight_disable();
  }
  else {
    rgblight_enable();
  }
    dprintf("rgblight toggle [EEPROM]: rgblight_config.enable = %u\n", !rgblight_config.enable);
    if (rgblight_config.enable) {
        rgblight_disable();
    } else {
        rgblight_enable();
    }
}

void rgblight_toggle_noeeprom(void) {
  xprintf("rgblight toggle [NOEEPROM]: rgblight_config.enable = %u\n", !rgblight_config.enable);
  if (rgblight_config.enable) {
    rgblight_disable_noeeprom();
  }
  else {
    rgblight_enable_noeeprom();
  }
    dprintf("rgblight toggle [NOEEPROM]: rgblight_config.enable = %u\n", !rgblight_config.enable);
    if (rgblight_config.enable) {
        rgblight_disable_noeeprom();
    } else {
        rgblight_enable_noeeprom();
    }
}

void rgblight_enable(void) {
  rgblight_config.enable = 1;
  // No need to update EEPROM here. rgblight_mode() will do that, actually
  //eeconfig_update_rgblight(rgblight_config.raw);
  xprintf("rgblight enable [EEPROM]: rgblight_config.enable = %u\n", rgblight_config.enable);
  rgblight_mode(rgblight_config.mode);
    rgblight_config.enable = 1;
    // No need to update EEPROM here. rgblight_mode() will do that, actually
    // eeconfig_update_rgblight(rgblight_config.raw);
    dprintf("rgblight enable [EEPROM]: rgblight_config.enable = %u\n", rgblight_config.enable);
    rgblight_mode(rgblight_config.mode);
}

void rgblight_enable_noeeprom(void) {
  rgblight_config.enable = 1;
  xprintf("rgblight enable [NOEEPROM]: rgblight_config.enable = %u\n", rgblight_config.enable);
  rgblight_mode_noeeprom(rgblight_config.mode);
    rgblight_config.enable = 1;
    dprintf("rgblight enable [NOEEPROM]: rgblight_config.enable = %u\n", rgblight_config.enable);
    rgblight_mode_noeeprom(rgblight_config.mode);
}

void rgblight_disable(void) {
  rgblight_config.enable = 0;
  eeconfig_update_rgblight(rgblight_config.raw);
  xprintf("rgblight disable [EEPROM]: rgblight_config.enable = %u\n", rgblight_config.enable);
  #ifdef RGBLIGHT_ANIMATIONS
    //rgblight_timer_disable();
  #endif
  _delay_ms(50);
  rgblight_set();
    rgblight_config.enable = 0;
    eeconfig_update_rgblight(rgblight_config.raw);
    dprintf("rgblight disable [EEPROM]: rgblight_config.enable = %u\n", rgblight_config.enable);
#ifdef RGBLIGHT_USE_TIMER
    rgblight_timer_disable();
#endif
    RGBLIGHT_SPLIT_SET_CHANGE_MODE;
    wait_ms(50);
    rgblight_set();
}

void rgblight_disable_noeeprom(void) {
  rgblight_config.enable = 0;
  xprintf("rgblight disable [noEEPROM]: rgblight_config.enable = %u\n", rgblight_config.enable);
  #ifdef RGBLIGHT_ANIMATIONS
    rgblight_config.enable = 0;
    dprintf("rgblight disable [NOEEPROM]: rgblight_config.enable = %u\n", rgblight_config.enable);
#ifdef RGBLIGHT_USE_TIMER
    rgblight_timer_disable();
  #endif
  _delay_ms(50);
  rgblight_set();
#endif
    RGBLIGHT_SPLIT_SET_CHANGE_MODE;
    wait_ms(50);
    rgblight_set();
}


// Deals with the messy details of incrementing an integer
uint8_t increment( uint8_t value, uint8_t step, uint8_t min, uint8_t max ) {
    int16_t new_value = value;
    new_value += step;
    return MIN( MAX( new_value, min ), max );
void rgblight_increase_hue_helper(bool write_to_eeprom) {
    uint8_t hue = rgblight_config.hue + RGBLIGHT_HUE_STEP;
    rgblight_sethsv_eeprom_helper(hue, rgblight_config.sat, rgblight_config.val, write_to_eeprom);
}

uint8_t decrement( uint8_t value, uint8_t step, uint8_t min, uint8_t max ) {
    int16_t new_value = value;
    new_value -= step;
    return MIN( MAX( new_value, min ), max );
void rgblight_increase_hue_noeeprom(void) { rgblight_increase_hue_helper(false); }
void rgblight_increase_hue(void) { rgblight_increase_hue_helper(true); }
void rgblight_decrease_hue_helper(bool write_to_eeprom) {
    uint8_t hue = rgblight_config.hue - RGBLIGHT_HUE_STEP;
    rgblight_sethsv_eeprom_helper(hue, rgblight_config.sat, rgblight_config.val, write_to_eeprom);
}

void rgblight_increase_hue(void) {
  uint16_t hue;
  hue = (rgblight_config.hue+RGBLIGHT_HUE_STEP) % 360;
  rgblight_sethsv(hue, rgblight_config.sat, rgblight_config.val);
void rgblight_decrease_hue_noeeprom(void) { rgblight_decrease_hue_helper(false); }
void rgblight_decrease_hue(void) { rgblight_decrease_hue_helper(true); }
void rgblight_increase_sat_helper(bool write_to_eeprom) {
    uint8_t sat = qadd8(rgblight_config.sat, RGBLIGHT_SAT_STEP);
    rgblight_sethsv_eeprom_helper(rgblight_config.hue, sat, rgblight_config.val, write_to_eeprom);
}
void rgblight_decrease_hue(void) {
  uint16_t hue;
  if (rgblight_config.hue-RGBLIGHT_HUE_STEP < 0) {
    hue = (rgblight_config.hue + 360 - RGBLIGHT_HUE_STEP) % 360;
  } else {
    hue = (rgblight_config.hue - RGBLIGHT_HUE_STEP) % 360;
  }
  rgblight_sethsv(hue, rgblight_config.sat, rgblight_config.val);
}
void rgblight_increase_sat(void) {
  uint8_t sat;
  if (rgblight_config.sat + RGBLIGHT_SAT_STEP > 255) {
    sat = 255;
  } else {
    sat = rgblight_config.sat + RGBLIGHT_SAT_STEP;
  }
  rgblight_sethsv(rgblight_config.hue, sat, rgblight_config.val);
}
void rgblight_decrease_sat(void) {
  uint8_t sat;
  if (rgblight_config.sat - RGBLIGHT_SAT_STEP < 0) {
    sat = 0;
  } else {
    sat = rgblight_config.sat - RGBLIGHT_SAT_STEP;
  }
  rgblight_sethsv(rgblight_config.hue, sat, rgblight_config.val);
}
void rgblight_increase_val(void) {
  uint8_t val;
  if (rgblight_config.val + RGBLIGHT_VAL_STEP > RGBLIGHT_LIMIT_VAL) {
    val = RGBLIGHT_LIMIT_VAL;
  } else {
    val = rgblight_config.val + RGBLIGHT_VAL_STEP;
  }
  rgblight_sethsv(rgblight_config.hue, rgblight_config.sat, val);
}
void rgblight_decrease_val(void) {
  uint8_t val;
  if (rgblight_config.val - RGBLIGHT_VAL_STEP < 0) {
    val = 0;
  } else {
    val = rgblight_config.val - RGBLIGHT_VAL_STEP;
  }
  rgblight_sethsv(rgblight_config.hue, rgblight_config.sat, val);
void rgblight_increase_sat_noeeprom(void) { rgblight_increase_sat_helper(false); }
void rgblight_increase_sat(void) { rgblight_increase_sat_helper(true); }
void rgblight_decrease_sat_helper(bool write_to_eeprom) {
    uint8_t sat = qsub8(rgblight_config.sat, RGBLIGHT_SAT_STEP);
    rgblight_sethsv_eeprom_helper(rgblight_config.hue, sat, rgblight_config.val, write_to_eeprom);
}
void rgblight_decrease_sat_noeeprom(void) { rgblight_decrease_sat_helper(false); }
void rgblight_decrease_sat(void) { rgblight_decrease_sat_helper(true); }
void rgblight_increase_val_helper(bool write_to_eeprom) {
    uint8_t val = qadd8(rgblight_config.val, RGBLIGHT_VAL_STEP);
    rgblight_sethsv_eeprom_helper(rgblight_config.hue, rgblight_config.sat, val, write_to_eeprom);
}
void rgblight_increase_val_noeeprom(void) { rgblight_increase_val_helper(false); }
void rgblight_increase_val(void) { rgblight_increase_val_helper(true); }
void rgblight_decrease_val_helper(bool write_to_eeprom) {
    uint8_t val = qsub8(rgblight_config.val, RGBLIGHT_VAL_STEP);
    rgblight_sethsv_eeprom_helper(rgblight_config.hue, rgblight_config.sat, val, write_to_eeprom);
}
void rgblight_decrease_val_noeeprom(void) { rgblight_decrease_val_helper(false); }
void rgblight_decrease_val(void) { rgblight_decrease_val_helper(true); }
void rgblight_increase_speed(void) {
    rgblight_config.speed = increment( rgblight_config.speed, 1, 0, 3 );
    eeconfig_update_rgblight(rgblight_config.raw);//EECONFIG needs to be increased to support this
    if (rgblight_config.speed < 3) rgblight_config.speed++;
    // RGBLIGHT_SPLIT_SET_CHANGE_HSVS; // NEED?
    eeconfig_update_rgblight(rgblight_config.raw);  // EECONFIG needs to be increased to support this
}

void rgblight_decrease_speed(void) {
    rgblight_config.speed = decrement( rgblight_config.speed, 1, 0, 3 );
    eeconfig_update_rgblight(rgblight_config.raw);//EECONFIG needs to be increased to support this
    if (rgblight_config.speed > 0) rgblight_config.speed--;
    // RGBLIGHT_SPLIT_SET_CHANGE_HSVS; // NEED??
    eeconfig_update_rgblight(rgblight_config.raw);  // EECONFIG needs to be increased to support this
}

void rgblight_sethsv_noeeprom_old(uint16_t hue, uint8_t sat, uint8_t val) {
  if (rgblight_config.enable) {
    LED_TYPE tmp_led;
    sethsv(hue, sat, val, &tmp_led);
    fled_hs[0].hue = fled_hs[1].hue = hue;
    fled_hs[0].sat = fled_hs[1].sat = sat;
    // dprintf("rgblight set hue [MEMORY]: %u,%u,%u\n", inmem_config.hue, inmem_config.sat, inmem_config.val);
    rgblight_setrgb(tmp_led.r, tmp_led.g, tmp_led.b);
  }
void rgblight_sethsv_noeeprom_old(uint8_t hue, uint8_t sat, uint8_t val) {
    if (rgblight_config.enable) {
        // MxSS custom code
        fled_hs[0].hue = fled_hs[1].hue = hue;
        fled_hs[0].sat = fled_hs[1].sat = sat;

        LED_TYPE tmp_led;
        sethsv(hue, sat, val, &tmp_led);
        rgblight_setrgb(tmp_led.r, tmp_led.g, tmp_led.b);
    }
}

void rgblight_sethsv_eeprom_helper(uint16_t hue, uint8_t sat, uint8_t val, bool write_to_eeprom) {
  if (rgblight_config.enable) {
    if (rgblight_config.mode == 1) {
      // same static color
      LED_TYPE tmp_led;
      sethsv(hue, sat, val, &tmp_led);
      
      fled_hs[0].hue = fled_hs[1].hue = hue;
      fled_hs[0].sat = fled_hs[1].sat = sat;
      
      rgblight_setrgb(tmp_led.r, tmp_led.g, tmp_led.b);
    } else {
      // all LEDs in same color
      if (rgblight_config.mode >= 2 && rgblight_config.mode <= 5) {
        // breathing mode, ignore the change of val, use in memory value instead
        val = rgblight_config.val;
      } else if (rgblight_config.mode >= 6 && rgblight_config.mode <= 14) {
        // rainbow mood and rainbow swirl, ignore the change of hue
        hue = rgblight_config.hue;
      } else if (rgblight_config.mode >= 25 && rgblight_config.mode <= 34) {
        // static gradient
        uint16_t _hue;
        int8_t direction = ((rgblight_config.mode - 25) % 2) ? -1 : 1;
        uint16_t range = pgm_read_word(&RGBLED_GRADIENT_RANGES[(rgblight_config.mode - 25) / 2]);
        for (uint8_t i = 0; i < RGBLED_NUM; i++) {
          _hue = (range / RGBLED_NUM * i * direction + hue + 360) % 360;
          dprintf("rgblight rainbow set hsv: %u,%u,%d,%u\n", i, _hue, direction, range);
          sethsv(_hue, sat, val, (LED_TYPE *)&led[i]);
void rgblight_sethsv_eeprom_helper(uint8_t hue, uint8_t sat, uint8_t val, bool write_to_eeprom) {
    if (rgblight_config.enable) {
        rgblight_status.base_mode = mode_base_table[rgblight_config.mode];
        if (rgblight_config.mode == RGBLIGHT_MODE_STATIC_LIGHT) {
            // same static color
            LED_TYPE tmp_led;
            sethsv(hue, sat, val, &tmp_led);

            // MxSS custom
            // Cache hue/sat for rgb
            fled_hs[0].hue = fled_hs[1].hue = hue;
            fled_hs[0].sat = fled_hs[1].sat = sat;

            rgblight_setrgb(tmp_led.r, tmp_led.g, tmp_led.b);
        } else {
            // all LEDs in same color
            if (1 == 0) {  // dummy
            }
#ifdef RGBLIGHT_EFFECT_BREATHING
            else if (rgblight_status.base_mode == RGBLIGHT_MODE_BREATHING) {
                // breathing mode, ignore the change of val, use in memory value instead
                val = rgblight_config.val;
            }
#endif
#ifdef RGBLIGHT_EFFECT_RAINBOW_MOOD
            else if (rgblight_status.base_mode == RGBLIGHT_MODE_RAINBOW_MOOD) {
                // rainbow mood, ignore the change of hue
                hue = rgblight_config.hue;
            }
#endif
#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL
            else if (rgblight_status.base_mode == RGBLIGHT_MODE_RAINBOW_SWIRL) {
                // rainbow swirl, ignore the change of hue
                hue = rgblight_config.hue;
            }
#endif
#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT
            else if (rgblight_status.base_mode == RGBLIGHT_MODE_STATIC_GRADIENT) {
                // static gradient
                uint8_t delta     = rgblight_config.mode - rgblight_status.base_mode;
                bool    direction = (delta % 2) == 0;
#    ifdef __AVR__
                // probably due to how pgm_read_word is defined for ARM, but the ARM compiler really hates this line
                uint8_t range = pgm_read_word(&RGBLED_GRADIENT_RANGES[delta / 2]);
#    else
                uint8_t range = RGBLED_GRADIENT_RANGES[delta / 2];
#    endif
                for (uint8_t i = 0; i < effect_num_leds; i++) {
                    uint8_t _hue = ((uint16_t)i * (uint16_t)range) / effect_num_leds;
                    if (direction) {
                        _hue = hue + _hue;
                    } else {
                        _hue = hue - _hue;
                    }
                    dprintf("rgblight rainbow set hsv: %d,%d,%d,%u\n", i, _hue, direction, range);
                    sethsv(_hue, sat, val, (LED_TYPE *)&led[i + effect_start_pos]);
                }
                rgblight_set();
            }
#endif
        }
#ifdef RGBLIGHT_SPLIT
        if (rgblight_config.hue != hue || rgblight_config.sat != sat || rgblight_config.val != val) {
            RGBLIGHT_SPLIT_SET_CHANGE_HSVS;
        }
#endif
        rgblight_config.hue = hue;
        rgblight_config.sat = sat;
        rgblight_config.val = val;
        if (write_to_eeprom) {
            eeconfig_update_rgblight(rgblight_config.raw);
            dprintf("rgblight set hsv [EEPROM]: %u,%u,%u\n", rgblight_config.hue, rgblight_config.sat, rgblight_config.val);
        } else {
            dprintf("rgblight set hsv [NOEEPROM]: %u,%u,%u\n", rgblight_config.hue, rgblight_config.sat, rgblight_config.val);
        }
        rgblight_set();
      }
    }
    rgblight_config.hue = hue;
    rgblight_config.sat = sat;
    rgblight_config.val = val;
    if (write_to_eeprom) {
      eeconfig_update_rgblight(rgblight_config.raw);
      xprintf("rgblight set hsv [EEPROM]: %u,%u,%u\n", rgblight_config.hue, rgblight_config.sat, rgblight_config.val);
    } else {
      xprintf("rgblight set hsv [NOEEPROM]: %u,%u,%u\n", rgblight_config.hue, rgblight_config.sat, rgblight_config.val);
    }
  }
}

void rgblight_sethsv(uint16_t hue, uint8_t sat, uint8_t val) {
  rgblight_sethsv_eeprom_helper(hue, sat, val, true);
}
void rgblight_sethsv(uint8_t hue, uint8_t sat, uint8_t val) { rgblight_sethsv_eeprom_helper(hue, sat, val, true); }

void rgblight_sethsv_noeeprom(uint16_t hue, uint8_t sat, uint8_t val) {
  rgblight_sethsv_eeprom_helper(hue, sat, val, false);
}
void rgblight_sethsv_noeeprom(uint8_t hue, uint8_t sat, uint8_t val) { rgblight_sethsv_eeprom_helper(hue, sat, val, false); }

uint16_t rgblight_get_hue(void) {
  return rgblight_config.hue;
uint8_t rgblight_get_hue(void) { return rgblight_config.hue; }

uint8_t rgblight_get_sat(void) { return rgblight_config.sat; }

uint8_t rgblight_get_val(void) { return rgblight_config.val; }

void rgblight_setrgb(uint8_t r, uint8_t g, uint8_t b) {
    if (!rgblight_config.enable) {
        return;
    }

    for (uint8_t i = effect_start_pos; i < effect_end_pos; i++) {
        led[i].r = r;
        led[i].g = g;
        led[i].b = b;
#ifdef RGBW
        led[i].w = 0;
#endif
    }
    rgblight_set();
}

uint8_t rgblight_get_sat(void) {
  return rgblight_config.sat;
void rgblight_setrgb_at(uint8_t r, uint8_t g, uint8_t b, uint8_t index) {
    if (!rgblight_config.enable || index >= RGBLED_NUM) {
        return;
    }

    led[index].r = r;
    led[index].g = g;
    led[index].b = b;
#ifdef RGBW
    led[index].w = 0;
#endif
    rgblight_set();
}

uint8_t rgblight_get_val(void) {
  return rgblight_config.val;
void rgblight_sethsv_at(uint8_t hue, uint8_t sat, uint8_t val, uint8_t index) {
    if (!rgblight_config.enable) {
        return;
    }

    LED_TYPE tmp_led;
    sethsv(hue, sat, val, &tmp_led);
    rgblight_setrgb_at(tmp_led.r, tmp_led.g, tmp_led.b, index);
}

void rgblight_setrgb(uint8_t r, uint8_t g, uint8_t b) {
  if (!rgblight_config.enable) { return; }
#if defined(RGBLIGHT_EFFECT_BREATHING) || defined(RGBLIGHT_EFFECT_RAINBOW_MOOD) || defined(RGBLIGHT_EFFECT_RAINBOW_SWIRL) || defined(RGBLIGHT_EFFECT_SNAKE) || defined(RGBLIGHT_EFFECT_KNIGHT)

  for (uint8_t i = 0; i < RGBLED_NUM; i++) {
    led[i].r = r;
    led[i].g = g;
    led[i].b = b;
  }
  rgblight_set();
static uint8_t get_interval_time(const uint8_t *default_interval_address, uint8_t velocikey_min, uint8_t velocikey_max) {
    return
#    ifdef VELOCIKEY_ENABLE
        velocikey_enabled() ? velocikey_match_speed(velocikey_min, velocikey_max) :
#    endif
                            pgm_read_byte(default_interval_address);
}

void rgblight_setrgb_at(uint8_t r, uint8_t g, uint8_t b, uint8_t index) {
  if (!rgblight_config.enable || index >= RGBLED_NUM) { return; }
#endif

void rgblight_setrgb_range(uint8_t r, uint8_t g, uint8_t b, uint8_t start, uint8_t end) {
    if (!rgblight_config.enable || start < 0 || start >= end || end > RGBLED_NUM) {
        return;
    }

  led[index].r = r;
  led[index].g = g;
  led[index].b = b;
  rgblight_set();
    for (uint8_t i = start; i < end; i++) {
        led[i].r = r;
        led[i].g = g;
        led[i].b = b;
#ifdef RGBW
        led[i].w = 0;
#endif
    }
    rgblight_set();
    wait_ms(1);
}

void rgblight_sethsv_at(uint16_t hue, uint8_t sat, uint8_t val, uint8_t index) {
  if (!rgblight_config.enable) { return; }
void rgblight_sethsv_range(uint8_t hue, uint8_t sat, uint8_t val, uint8_t start, uint8_t end) {
    if (!rgblight_config.enable) {
        return;
    }

  LED_TYPE tmp_led;
  sethsv(hue, sat, val, &tmp_led);
  rgblight_setrgb_at(tmp_led.r, tmp_led.g, tmp_led.b, index);
    LED_TYPE tmp_led;
    sethsv(hue, sat, val, &tmp_led);
    rgblight_setrgb_range(tmp_led.r, tmp_led.g, tmp_led.b, start, end);
}

#ifndef RGBLIGHT_SPLIT
void rgblight_setrgb_master(uint8_t r, uint8_t g, uint8_t b) { rgblight_setrgb_range(r, g, b, 0, (uint8_t)RGBLED_NUM / 2); }

void rgblight_setrgb_slave(uint8_t r, uint8_t g, uint8_t b) { rgblight_setrgb_range(r, g, b, (uint8_t)RGBLED_NUM / 2, (uint8_t)RGBLED_NUM); }

void rgblight_sethsv_master(uint8_t hue, uint8_t sat, uint8_t val) { rgblight_sethsv_range(hue, sat, val, 0, (uint8_t)RGBLED_NUM / 2); }

void rgblight_sethsv_slave(uint8_t hue, uint8_t sat, uint8_t val) { rgblight_sethsv_range(hue, sat, val, (uint8_t)RGBLED_NUM / 2, (uint8_t)RGBLED_NUM); }
#endif  // ifndef RGBLIGHT_SPLIT

#ifndef RGBLIGHT_CUSTOM_DRIVER
void rgblight_set(void) {
  if (!rgblight_config.enable) {
    LED_TYPE *start_led;
    uint16_t  num_leds = clipping_num_leds;

    if (!rgblight_config.enable) {
        for (uint8_t i = effect_start_pos; i < effect_end_pos; i++) {
            if (i == RGBLIGHT_FLED1 && i == RGBLIGHT_FLED2)
                continue;

            led[i].r = 0;
            led[i].g = 0;
            led[i].b = 0;
#    ifdef RGBW
            led[i].w = 0;
#    endif
        }
    }

#    ifdef RGBLIGHT_LED_MAP
    LED_TYPE led0[RGBLED_NUM];
    for (uint8_t i = 0; i < RGBLED_NUM; i++) {
      if (i == RGBLIGHT_FLED1 && i == RGBLIGHT_FLED2)
          continue;
      
      led[i].r = 0;
      led[i].g = 0;
      led[i].b = 0;
        led0[i] = led[pgm_read_byte(&led_map[i])];
    }
  }
  
  switch (fled_mode) {
    start_led = led0 + clipping_start_pos;
#    else
    start_led = led + clipping_start_pos;
#    endif

#ifdef RGBW
    for (uint8_t i = 0; i < num_leds; i++) {
        convert_rgb_to_rgbw(&start_led[i]);
    }
#endif
    // MxSS custom
    switch (fled_mode) {
      case FLED_OFF:
      setrgb(0, 0, 0, &led[RGBLIGHT_FLED1]);
      setrgb(0, 0, 0, &led[RGBLIGHT_FLED2]);
        setrgb(0, 0, 0, &led[RGBLIGHT_FLED1]);
        setrgb(0, 0, 0, &led[RGBLIGHT_FLED2]);
      break;
      

      case FLED_INDI:
      copyrgb(&fleds[0], &led[RGBLIGHT_FLED1]);
      copyrgb(&fleds[1], &led[RGBLIGHT_FLED2]);
        copyrgb(&fleds[0], &led[RGBLIGHT_FLED1]);
        copyrgb(&fleds[1], &led[RGBLIGHT_FLED2]);
      break;
      

      case FLED_RGB:
      if (fled_hs[0].hue == 0 && fled_hs[0].hue == 0 && (rgblight_config.mode >= 15 && rgblight_config.mode <= 23))
          setrgb(0, 0, 0, &led[RGBLIGHT_FLED1]);
      else
        sethsv(fled_hs[0].hue, fled_hs[0].sat, fled_val, &led[RGBLIGHT_FLED1]);
      
      if (fled_hs[1].hue == 0 && fled_hs[1].hue == 0 && (rgblight_config.mode >= 15 && rgblight_config.mode <= 23))
          setrgb(0, 0, 0, &led[RGBLIGHT_FLED2]);
      else
          sethsv(fled_hs[1].hue, fled_hs[1].sat, fled_val, &led[RGBLIGHT_FLED2]);
        if (fled_hs[0].hue == 0 && fled_hs[0].hue == 0 &&
                (rgblight_status.base_mode == RGBLIGHT_MODE_SNAKE ||
                rgblight_status.base_mode == RGBLIGHT_MODE_KNIGHT))
            setrgb(0, 0, 0, &led[RGBLIGHT_FLED1]);
        else
            sethsv(fled_hs[0].hue, fled_hs[0].sat, fled_val, &led[RGBLIGHT_FLED1]);

        if (fled_hs[1].hue == 0 && fled_hs[1].hue == 0 &&
                (rgblight_status.base_mode == RGBLIGHT_MODE_SNAKE ||
                rgblight_status.base_mode == RGBLIGHT_MODE_KNIGHT))
            setrgb(0, 0, 0, &led[RGBLIGHT_FLED2]);
        else
            sethsv(fled_hs[1].hue, fled_hs[1].sat, fled_val, &led[RGBLIGHT_FLED2]);
      break;
      

      default:
      break;
  }

   ws2812_setleds(led, RGBLED_NUM);
   ws2812_setleds(start_led, num_leds);
}
#endif

#ifdef RGBLIGHT_SPLIT
/* for split keyboard master side */
uint8_t rgblight_get_change_flags(void) { return rgblight_status.change_flags; }

void rgblight_clear_change_flags(void) { rgblight_status.change_flags = 0; }

void rgblight_get_syncinfo(rgblight_syncinfo_t *syncinfo) {
    syncinfo->config = rgblight_config;
    syncinfo->status = rgblight_status;
}

/* for split keyboard slave side */
void rgblight_update_sync(rgblight_syncinfo_t *syncinfo, bool write_to_eeprom) {
    if (syncinfo->status.change_flags & RGBLIGHT_STATUS_CHANGE_MODE) {
        if (syncinfo->config.enable) {
            rgblight_config.enable = 1;  // == rgblight_enable_noeeprom();
            rgblight_mode_eeprom_helper(syncinfo->config.mode, write_to_eeprom);
        } else {
            rgblight_disable_noeeprom();
        }
    }
    if (syncinfo->status.change_flags & RGBLIGHT_STATUS_CHANGE_HSVS) {
        rgblight_sethsv_eeprom_helper(syncinfo->config.hue, syncinfo->config.sat, syncinfo->config.val, write_to_eeprom);
        // rgblight_config.speed = config->speed; // NEED???
    }
#    ifdef RGBLIGHT_USE_TIMER
    if (syncinfo->status.change_flags & RGBLIGHT_STATUS_CHANGE_TIMER) {
        if (syncinfo->status.timer_enabled) {
            rgblight_timer_enable();
        } else {
            rgblight_timer_disable();
        }
    }
#        ifndef RGBLIGHT_SPLIT_NO_ANIMATION_SYNC
    if (syncinfo->status.change_flags & RGBLIGHT_STATUS_ANIMATION_TICK) {
        animation_status.restart = true;
    }
#        endif /* RGBLIGHT_SPLIT_NO_ANIMATION_SYNC */
#    endif     /* RGBLIGHT_USE_TIMER */
}
#endif /* RGBLIGHT_SPLIT */

#ifdef RGBLIGHT_ANIMATIONS
#ifdef RGBLIGHT_USE_TIMER

// Animation timer -- AVR Timer3
typedef void (*effect_func_t)(animation_status_t *anim);

// Animation timer -- use system timer (AVR Timer0)
void rgblight_timer_init(void) {
  // static uint8_t rgblight_timer_is_init = 0;
  // if (rgblight_timer_is_init) {
  //   return;
  // }
  // rgblight_timer_is_init = 1;
  // /* Timer 3 setup */
  // TCCR3B = _BV(WGM32) // CTC mode OCR3A as TOP
  //       | _BV(CS30); // Clock selelct: clk/1
  // /* Set TOP value */
  // uint8_t sreg = SREG;
  // cli();
  // OCR3AH = (RGBLED_TIMER_TOP >> 8) & 0xff;
  // OCR3AL = RGBLED_TIMER_TOP & 0xff;
  // SREG = sreg;

  rgblight_timer_enabled = true;
    // OLD!!!! Animation timer -- AVR Timer3
    // static uint8_t rgblight_timer_is_init = 0;
    // if (rgblight_timer_is_init) {
    //   return;
    // }
    // rgblight_timer_is_init = 1;
    // /* Timer 3 setup */
    // TCCR3B = _BV(WGM32) // CTC mode OCR3A as TOP
    //       | _BV(CS30); // Clock selelct: clk/1
    // /* Set TOP value */
    // uint8_t sreg = SREG;
    // cli();
    // OCR3AH = (RGBLED_TIMER_TOP >> 8) & 0xff;
    // OCR3AL = RGBLED_TIMER_TOP & 0xff;
    // SREG = sreg;

    rgblight_status.timer_enabled = false;
    RGBLIGHT_SPLIT_SET_CHANGE_TIMER_ENABLE;
}
void rgblight_timer_enable(void) {
  rgblight_timer_enabled = true;
  dprintf("TIMER3 enabled.\n");
    if (!is_static_effect(rgblight_config.mode)) {
        rgblight_status.timer_enabled = true;
    }
    animation_status.last_timer = timer_read();
    RGBLIGHT_SPLIT_SET_CHANGE_TIMER_ENABLE;
    dprintf("rgblight timer enabled.\n");
}
void rgblight_timer_disable(void) {
  rgblight_timer_enabled = false;
  dprintf("TIMER3 disabled.\n");
    // MxSS custom code
    if (fled_mode != FLED_RGB) {
        rgblight_status.timer_enabled = false;
        RGBLIGHT_SPLIT_SET_CHANGE_TIMER_ENABLE;
        dprintf("rgblight timer disable.\n");
    }
}
void rgblight_timer_toggle(void) {
  rgblight_timer_enabled ^= rgblight_timer_enabled;
  dprintf("TIMER3 toggled.\n");
    dprintf("rgblight timer toggle.\n");
    if (rgblight_status.timer_enabled) {
        rgblight_timer_disable();
    } else {
        rgblight_timer_enable();
    }
}

void rgblight_show_solid_color(uint8_t r, uint8_t g, uint8_t b) {
  rgblight_enable();
  rgblight_mode(1);
  rgblight_setrgb(r, g, b);
    rgblight_enable();
    rgblight_mode(RGBLIGHT_MODE_STATIC_LIGHT);
    rgblight_setrgb(r, g, b);
}

static void rgblight_effect_dummy(animation_status_t *anim) {
    // do nothing
    /********
    dprintf("rgblight_task() what happened?\n");
    dprintf("is_static_effect %d\n", is_static_effect(rgblight_config.mode));
    dprintf("mode = %d, base_mode = %d, timer_enabled %d, ",
            rgblight_config.mode, rgblight_status.base_mode,
            rgblight_status.timer_enabled);
    dprintf("last_timer = %d\n",anim->last_timer);
    **/
}

void rgblight_task(void) {
  if (rgblight_timer_enabled) {
    // mode = 1, static light, do nothing here
    if (rgblight_config.mode >= 2 && rgblight_config.mode <= 5) {
      // mode = 2 to 5, breathing mode
      rgblight_effect_breathing(rgblight_config.mode - 2);
    } else if (rgblight_config.mode >= 6 && rgblight_config.mode <= 8) {
      // mode = 6 to 8, rainbow mood mod
      rgblight_effect_rainbow_mood(rgblight_config.mode - 6);
    } else if (rgblight_config.mode >= 9 && rgblight_config.mode <= 14) {
      // mode = 9 to 14, rainbow swirl mode
      rgblight_effect_rainbow_swirl(rgblight_config.mode - 9);
    } else if (rgblight_config.mode >= 15 && rgblight_config.mode <= 20) {
      // mode = 15 to 20, snake mode
      rgblight_effect_snake(rgblight_config.mode - 15);
    } else if (rgblight_config.mode >= 21 && rgblight_config.mode <= 23) {
      // mode = 21 to 23, knight mode
      rgblight_effect_knight(rgblight_config.mode - 21);
    } else if (rgblight_config.mode == 24) {
      // mode = 24, christmas mode
      rgblight_effect_christmas();
    } else if (rgblight_config.mode == 35) {
      // mode = 35, RGB test
      rgblight_effect_rgbtest();
    if (rgblight_status.timer_enabled) {
        effect_func_t effect_func   = rgblight_effect_dummy;
        uint16_t      interval_time = 2000;  // dummy interval
        uint8_t       delta         = rgblight_config.mode - rgblight_status.base_mode;
        animation_status.delta      = delta;

        // static light mode, do nothing here
        if (1 == 0) {  // dummy
        }
#    ifdef RGBLIGHT_EFFECT_BREATHING
        else if (rgblight_status.base_mode == RGBLIGHT_MODE_BREATHING) {
            // breathing mode
            interval_time = get_interval_time(&RGBLED_BREATHING_INTERVALS[delta], 1, 100);
            effect_func   = rgblight_effect_breathing;
        }
#    endif
#    ifdef RGBLIGHT_EFFECT_RAINBOW_MOOD
        else if (rgblight_status.base_mode == RGBLIGHT_MODE_RAINBOW_MOOD) {
            // rainbow mood mode
            interval_time = get_interval_time(&RGBLED_RAINBOW_MOOD_INTERVALS[delta], 5, 100);
            effect_func   = rgblight_effect_rainbow_mood;
        }
#    endif
#    ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL
        else if (rgblight_status.base_mode == RGBLIGHT_MODE_RAINBOW_SWIRL) {
            // rainbow swirl mode
            interval_time = get_interval_time(&RGBLED_RAINBOW_SWIRL_INTERVALS[delta / 2], 1, 100);
            effect_func   = rgblight_effect_rainbow_swirl;
        }
#    endif
#    ifdef RGBLIGHT_EFFECT_SNAKE
        else if (rgblight_status.base_mode == RGBLIGHT_MODE_SNAKE) {
            // snake mode
            interval_time = get_interval_time(&RGBLED_SNAKE_INTERVALS[delta / 2], 1, 200);
            effect_func   = rgblight_effect_snake;
        }
#    endif
#    ifdef RGBLIGHT_EFFECT_KNIGHT
        else if (rgblight_status.base_mode == RGBLIGHT_MODE_KNIGHT) {
            // knight mode
            interval_time = get_interval_time(&RGBLED_KNIGHT_INTERVALS[delta], 5, 100);
            effect_func   = rgblight_effect_knight;
        }
#    endif
#    ifdef RGBLIGHT_EFFECT_CHRISTMAS
        else if (rgblight_status.base_mode == RGBLIGHT_MODE_CHRISTMAS) {
            // christmas mode
            interval_time = RGBLIGHT_EFFECT_CHRISTMAS_INTERVAL;
            effect_func   = (effect_func_t)rgblight_effect_christmas;
        }
#    endif
#    ifdef RGBLIGHT_EFFECT_RGB_TEST
        else if (rgblight_status.base_mode == RGBLIGHT_MODE_RGB_TEST) {
            // RGB test mode
            interval_time = pgm_read_word(&RGBLED_RGBTEST_INTERVALS[0]);
            effect_func   = (effect_func_t)rgblight_effect_rgbtest;
        }
#    endif
#    ifdef RGBLIGHT_EFFECT_ALTERNATING
        else if (rgblight_status.base_mode == RGBLIGHT_MODE_ALTERNATING) {
            interval_time = 500;
            effect_func   = (effect_func_t)rgblight_effect_alternating;
        }
#    endif
        if (animation_status.restart) {
            animation_status.restart    = false;
            animation_status.last_timer = timer_read() - interval_time - 1;
            animation_status.pos16      = 0;  // restart signal to local each effect
        }
        if (timer_elapsed(animation_status.last_timer) >= interval_time) {
#    if defined(RGBLIGHT_SPLIT) && !defined(RGBLIGHT_SPLIT_NO_ANIMATION_SYNC)
            static uint16_t report_last_timer = 0;
            static bool     tick_flag         = false;
            uint16_t        oldpos16;
            if (tick_flag) {
                tick_flag = false;
                if (timer_elapsed(report_last_timer) >= 30000) {
                    report_last_timer = timer_read();
                    dprintf("rgblight animation tick report to slave\n");
                    RGBLIGHT_SPLIT_ANIMATION_TICK;
                }
            }
            oldpos16 = animation_status.pos16;
#    endif
            animation_status.last_timer += interval_time;
            effect_func(&animation_status);
#    if defined(RGBLIGHT_SPLIT) && !defined(RGBLIGHT_SPLIT_NO_ANIMATION_SYNC)
            if (animation_status.pos16 == 0 && oldpos16 != 0) {
                tick_flag = true;
            }
#    endif
        }
    }
  }
}

// Effects
void rgblight_effect_breathing(uint8_t interval) {
  static uint8_t pos = 0;
  static uint16_t last_timer = 0;
  float val;
#endif /* RGBLIGHT_USE_TIMER */

  if (timer_elapsed(last_timer) < pgm_read_byte(&RGBLED_BREATHING_INTERVALS[interval])) {
    return;
  }
  last_timer = timer_read();
// Effects
#ifdef RGBLIGHT_EFFECT_BREATHING

#    ifndef RGBLIGHT_EFFECT_BREATHE_CENTER
#        ifndef RGBLIGHT_BREATHE_TABLE_SIZE
#            define RGBLIGHT_BREATHE_TABLE_SIZE 256  // 256 or 128 or 64
#        endif
#        include <rgblight_breathe_table.h>
#    endif

__attribute__((weak)) const uint8_t RGBLED_BREATHING_INTERVALS[] PROGMEM = {30, 20, 10, 5};

void rgblight_effect_breathing(animation_status_t *anim) {
    float val;

    // http://sean.voisen.org/blog/2011/10/breathing-led-with-arduino/
#    ifdef RGBLIGHT_EFFECT_BREATHE_TABLE
    val = pgm_read_byte(&rgblight_effect_breathe_table[anim->pos / table_scale]);
#    else
    val = (exp(sin((anim->pos / 255.0) * M_PI)) - RGBLIGHT_EFFECT_BREATHE_CENTER / M_E) * (RGBLIGHT_EFFECT_BREATHE_MAX / (M_E - 1 / M_E));
#    endif
    rgblight_sethsv_noeeprom_old(rgblight_config.hue, rgblight_config.sat, val);
    anim->pos = (anim->pos + 1);
}
#endif

#ifdef RGBLIGHT_EFFECT_RAINBOW_MOOD
__attribute__((weak)) const uint8_t RGBLED_RAINBOW_MOOD_INTERVALS[] PROGMEM = {120, 60, 30};

  // http://sean.voisen.org/blog/2011/10/breathing-led-with-arduino/
  val = (exp(sin((pos/255.0)*M_PI)) - RGBLIGHT_EFFECT_BREATHE_CENTER/M_E)*(RGBLIGHT_EFFECT_BREATHE_MAX/(M_E-1/M_E));
  rgblight_sethsv_noeeprom_old(rgblight_config.hue, rgblight_config.sat, val);
  pos = (pos + 1) % 256;
void rgblight_effect_rainbow_mood(animation_status_t *anim) {
    rgblight_sethsv_noeeprom_old(anim->current_hue, rgblight_config.sat, rgblight_config.val);
    anim->current_hue++;
}
void rgblight_effect_rainbow_mood(uint8_t interval) {
  static uint16_t current_hue = 0;
  static uint16_t last_timer = 0;
#endif

  if (timer_elapsed(last_timer) < pgm_read_byte(&RGBLED_RAINBOW_MOOD_INTERVALS[interval])) {
    return;
  }
  last_timer = timer_read();
  rgblight_sethsv_noeeprom_old(current_hue, rgblight_config.sat, rgblight_config.val);
  current_hue = (current_hue + 1) % 360;
}
void rgblight_effect_rainbow_swirl(uint8_t interval) {
  static uint16_t current_hue = 0;
  static uint16_t last_timer = 0;
  uint16_t hue;
  uint8_t i;
  if (timer_elapsed(last_timer) < pgm_read_byte(&RGBLED_RAINBOW_SWIRL_INTERVALS[interval / 2])) {
    return;
  }
  last_timer = timer_read();
  for (i = 0; i < RGBLED_NUM; i++) {
    hue = (360 / RGBLED_NUM * i + current_hue) % 360;
    sethsv(hue, rgblight_config.sat, rgblight_config.val, (LED_TYPE *)&led[i]);
  }
  rgblight_set();
#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL
#    ifndef RGBLIGHT_RAINBOW_SWIRL_RANGE
#        define RGBLIGHT_RAINBOW_SWIRL_RANGE 255
#    endif

__attribute__((weak)) const uint8_t RGBLED_RAINBOW_SWIRL_INTERVALS[] PROGMEM = {100, 50, 20};

void rgblight_effect_rainbow_swirl(animation_status_t *anim) {
    uint8_t hue;
    uint8_t i;

    for (i = 0; i < effect_num_leds; i++) {
        hue = (RGBLIGHT_RAINBOW_SWIRL_RANGE / effect_num_leds * i + anim->current_hue);
        sethsv(hue, rgblight_config.sat, rgblight_config.val, (LED_TYPE *)&led[i + effect_start_pos]);
    }
    rgblight_set();

  if (interval % 2) {
    current_hue = (current_hue + 1) % 360;
  } else {
    if (current_hue - 1 < 0) {
      current_hue = 359;
    if (anim->delta % 2) {
        anim->current_hue++;
    } else {
      current_hue = current_hue - 1;
        anim->current_hue--;
    }
  }
}
void rgblight_effect_snake(uint8_t interval) {
  static uint8_t pos = 0;
  static uint16_t last_timer = 0;
  uint8_t i, j;
  int8_t k;
  int8_t increment = 1;
  if (interval % 2) {
    increment = -1;
  }
  if (timer_elapsed(last_timer) < pgm_read_byte(&RGBLED_SNAKE_INTERVALS[interval / 2])) {
    return;
  }
  last_timer = timer_read();
  
  fled_hs[0].hue = fled_hs[1].hue = 0;
  fled_hs[0].sat = fled_hs[1].sat = 0;
  
  for (i = 0; i < RGBLED_NUM; i++) {
    led[i].r = 0;
    led[i].g = 0;
    led[i].b = 0;

    for (j = 0; j < RGBLIGHT_EFFECT_SNAKE_LENGTH; j++) {
      k = pos + j * increment;
      if (k < 0) {
        k = k + RGBLED_NUM;
      }
      if (i == k) {
        sethsv(rgblight_config.hue, rgblight_config.sat, (uint8_t)(rgblight_config.val*(RGBLIGHT_EFFECT_SNAKE_LENGTH-j)/RGBLIGHT_EFFECT_SNAKE_LENGTH), (LED_TYPE *)&led[i]);
      }
#endif

#ifdef RGBLIGHT_EFFECT_SNAKE
__attribute__((weak)) const uint8_t RGBLED_SNAKE_INTERVALS[] PROGMEM = {100, 50, 20};

void rgblight_effect_snake(animation_status_t *anim) {
    static uint8_t pos = 0;
    uint8_t        i, j;
    int8_t         k;
    int8_t         increment = 1;

    if (anim->delta % 2) {
        increment = -1;
    }
  }
  rgblight_set();
  if (increment == 1) {
    if (pos - 1 < 0) {
      pos = RGBLED_NUM - 1;

#    if defined(RGBLIGHT_SPLIT) && !defined(RGBLIGHT_SPLIT_NO_ANIMATION_SYNC)
    if (anim->pos == 0) {  // restart signal
        if (increment == 1) {
            pos = effect_num_leds - 1;
        } else {
            pos = 0;
        }
        anim->pos = 1;
    }
#    endif

    // MxSS custom
    fled_hs[0].hue = fled_hs[1].hue = 0;
    fled_hs[0].sat = fled_hs[1].sat = 0;

    for (i = 0; i < effect_num_leds; i++) {
        LED_TYPE *ledp = led + i + effect_start_pos;
        ledp->r        = 0;
        ledp->g        = 0;
        ledp->b        = 0;
#    ifdef RGBW
        ledp->w = 0;
#    endif
        for (j = 0; j < RGBLIGHT_EFFECT_SNAKE_LENGTH; j++) {
            k = pos + j * increment;
            if (k > RGBLED_NUM) {
                k = k % RGBLED_NUM;
            }
            if (k < 0) {
                k = k + effect_num_leds;
            }
            if (i == k) {
                sethsv(rgblight_config.hue, rgblight_config.sat, (uint8_t)(rgblight_config.val * (RGBLIGHT_EFFECT_SNAKE_LENGTH - j) / RGBLIGHT_EFFECT_SNAKE_LENGTH), ledp);
            }
        }
    }
    rgblight_set();
    if (increment == 1) {
        if (pos - 1 < 0) {
            pos = effect_num_leds - 1;
#    if defined(RGBLIGHT_SPLIT) && !defined(RGBLIGHT_SPLIT_NO_ANIMATION_SYNC)
            anim->pos = 0;
#    endif
        } else {
            pos -= 1;
#    if defined(RGBLIGHT_SPLIT) && !defined(RGBLIGHT_SPLIT_NO_ANIMATION_SYNC)
            anim->pos = 1;
#    endif
        }
    } else {
      pos -= 1;
        pos = (pos + 1) % effect_num_leds;
#    if defined(RGBLIGHT_SPLIT) && !defined(RGBLIGHT_SPLIT_NO_ANIMATION_SYNC)
        anim->pos = pos;
#    endif
    }
  } else {
    pos = (pos + 1) % RGBLED_NUM;
  }
}
void rgblight_effect_knight(uint8_t interval) {
  static uint16_t last_timer = 0;
  if (timer_elapsed(last_timer) < pgm_read_byte(&RGBLED_KNIGHT_INTERVALS[interval])) {
    return;
  }
  last_timer = timer_read();

  static int8_t low_bound = 0;
  static int8_t high_bound = RGBLIGHT_EFFECT_KNIGHT_LENGTH - 1;
  static int8_t increment = 1;
  uint8_t i, cur;

  // Set all the LEDs to 0
  for (i = 0; i < RGBLED_NUM; i++) {
    led[i].r = 0;
    led[i].g = 0;
    led[i].b = 0;
  }
  // Determine which LEDs should be lit up
  for (i = 0; i < RGBLIGHT_EFFECT_KNIGHT_LED_NUM; i++) {
    cur = (i + RGBLIGHT_EFFECT_KNIGHT_OFFSET) % RGBLED_NUM;
#endif

    if (i >= low_bound && i <= high_bound) {
      sethsv(rgblight_config.hue, rgblight_config.sat, rgblight_config.val, (LED_TYPE *)&led[cur]);
    } else {
      if (i == RGBLIGHT_FLED1 || i == RGBLIGHT_FLED2) {
          fled_hs[0].hue = fled_hs[1].hue = 0;
          fled_hs[0].sat = fled_hs[1].sat = 0;
      }
        
      led[cur].r = 0;
      led[cur].g = 0;
      led[cur].b = 0;
#ifdef RGBLIGHT_EFFECT_KNIGHT
__attribute__((weak)) const uint8_t RGBLED_KNIGHT_INTERVALS[] PROGMEM = {127, 63, 31};

void rgblight_effect_knight(animation_status_t *anim) {
    static int8_t low_bound  = 0;
    static int8_t high_bound = RGBLIGHT_EFFECT_KNIGHT_LENGTH - 1;
    static int8_t increment  = 1;
    uint8_t       i, cur;

#    if defined(RGBLIGHT_SPLIT) && !defined(RGBLIGHT_SPLIT_NO_ANIMATION_SYNC)
    if (anim->pos == 0) {  // restart signal
        anim->pos  = 1;
        low_bound  = 0;
        high_bound = RGBLIGHT_EFFECT_KNIGHT_LENGTH - 1;
        increment  = 1;
    }
#    endif
    // Set all the LEDs to 0
    for (i = effect_start_pos; i < effect_end_pos; i++) {
        led[i].r = 0;
        led[i].g = 0;
        led[i].b = 0;
#    ifdef RGBW
        led[i].w = 0;
#    endif
    }
    // Determine which LEDs should be lit up
    for (i = 0; i < RGBLIGHT_EFFECT_KNIGHT_LED_NUM; i++) {
        cur = (i + RGBLIGHT_EFFECT_KNIGHT_OFFSET) % effect_num_leds + effect_start_pos;

        if (i >= low_bound && i <= high_bound) {
            sethsv(rgblight_config.hue, rgblight_config.sat, rgblight_config.val, (LED_TYPE *)&led[cur]);
        } else {
            // MxSS custom code
            if (cur == RGBLIGHT_FLED1) {
                fled_hs[0].hue = fled_hs[0].sat = 0;
            } else if (cur == RGBLIGHT_FLED2) {
                fled_hs[1].hue = fled_hs[1].sat = 0;
            }

            led[cur].r = 0;
            led[cur].g = 0;
            led[cur].b = 0;
#    ifdef RGBW
            led[cur].w = 0;
#    endif
        }
    }
    rgblight_set();

    // Move from low_bound to high_bound changing the direction we increment each
    // time a boundary is hit.
    low_bound += increment;
    high_bound += increment;

    if (high_bound <= 0 || low_bound >= RGBLIGHT_EFFECT_KNIGHT_LED_NUM - 1) {
        increment = -increment;
#    if defined(RGBLIGHT_SPLIT) && !defined(RGBLIGHT_SPLIT_NO_ANIMATION_SYNC)
        if (increment == 1) {
            anim->pos = 0;
        }
#    endif
    }
  }
  rgblight_set();

  // Move from low_bound to high_bound changing the direction we increment each
  // time a boundary is hit.
  low_bound += increment;
  high_bound += increment;

  if (high_bound <= 0 || low_bound >= RGBLIGHT_EFFECT_KNIGHT_LED_NUM - 1) {
    increment = -increment;
  }
}
#endif

#ifdef RGBLIGHT_EFFECT_CHRISTMAS
void rgblight_effect_christmas(animation_status_t *anim) {
    uint8_t hue;
    uint8_t i;

void rgblight_effect_christmas(void) {
  static uint16_t current_offset = 0;
  static uint16_t last_timer = 0;
  uint16_t hue;
  uint8_t i;
  if (timer_elapsed(last_timer) < RGBLIGHT_EFFECT_CHRISTMAS_INTERVAL) {
    return;
  }
  last_timer = timer_read();
  current_offset = (current_offset + 1) % 2;
  for (i = 0; i < RGBLED_NUM; i++) {
    hue = 0 + ((i/RGBLIGHT_EFFECT_CHRISTMAS_STEP + current_offset) % 2) * 120;
    sethsv(hue, rgblight_config.sat, rgblight_config.val, (LED_TYPE *)&led[i]);
  }
  rgblight_set();
    anim->current_offset = (anim->current_offset + 1) % 2;
    for (i = 0; i < effect_num_leds; i++) {
        hue = 0 + ((i / RGBLIGHT_EFFECT_CHRISTMAS_STEP + anim->current_offset) % 2) * 85;
        sethsv(hue, rgblight_config.sat, rgblight_config.val, (LED_TYPE *)&led[i + effect_start_pos]);
    }
    rgblight_set();
}
#endif

void rgblight_effect_rgbtest(void) {
  static uint8_t pos = 0;
  static uint16_t last_timer = 0;
  static uint8_t maxval = 0;
  uint8_t g; uint8_t r; uint8_t b;
#ifdef RGBLIGHT_EFFECT_RGB_TEST
__attribute__((weak)) const uint16_t RGBLED_RGBTEST_INTERVALS[] PROGMEM = {1024};

  if (timer_elapsed(last_timer) < pgm_read_word(&RGBLED_RGBTEST_INTERVALS[0])) {
    return;
  }
void rgblight_effect_rgbtest(animation_status_t *anim) {
    static uint8_t maxval = 0;
    uint8_t        g;
    uint8_t        r;
    uint8_t        b;

  if( maxval == 0 ) {
      LED_TYPE tmp_led;
      sethsv(0, 255, RGBLIGHT_LIMIT_VAL, &tmp_led);
      maxval = tmp_led.r;
  }
  last_timer = timer_read();
  g = r = b = 0;
  switch( pos ) {
    case 0: r = maxval; break;
    case 1: g = maxval; break;
    case 2: b = maxval; break;
  }
  rgblight_setrgb(r, g, b);
  pos = (pos + 1) % 3;
    if (maxval == 0) {
        LED_TYPE tmp_led;
        sethsv(0, 255, RGBLIGHT_LIMIT_VAL, &tmp_led);
        maxval = tmp_led.r;
    }
    g = r = b = 0;
    switch (anim->pos) {
        // MxSS custom code
        case 0:
            r = maxval;
            fled_hs[0].hue = 0;
            fled_hs[0].sat = 255;
            fled_hs[1].hue = 0;
            fled_hs[1].sat = 255;
            break;
        case 1:
            g = maxval;
            fled_hs[0].hue = 85;
            fled_hs[0].sat = 255;
            fled_hs[1].hue = 85;
            fled_hs[1].sat = 255;
            break;
        case 2:
            b = maxval;
            fled_hs[0].hue = 170;
            fled_hs[0].sat = 255;
            fled_hs[1].hue = 170;
            fled_hs[1].sat = 255;
            break;
    }
    rgblight_setrgb(r, g, b);
    anim->pos = (anim->pos + 1) % 3;
}
#endif

#endif /* RGBLIGHT_ANIMATIONS */
#ifdef RGBLIGHT_EFFECT_ALTERNATING
void rgblight_effect_alternating(animation_status_t *anim) {
    for (int i = 0; i < effect_num_leds; i++) {
        LED_TYPE *ledp = led + i + effect_start_pos;
        if (i < effect_num_leds / 2 && anim->pos) {
            sethsv(rgblight_config.hue, rgblight_config.sat, rgblight_config.val, ledp);
        } else if (i >= effect_num_leds / 2 && !anim->pos) {
            sethsv(rgblight_config.hue, rgblight_config.sat, rgblight_config.val, ledp);
        } else {
            sethsv(rgblight_config.hue, rgblight_config.sat, 0, ledp);
        }
    }
    rgblight_set();
    anim->pos = (anim->pos + 1) % 2;
}
#endif

M keyboards/mxss/rgblight.h => keyboards/mxss/rgblight.h +255 -107
@@ 16,137 16,245 @@
#ifndef RGBLIGHT_H
#define RGBLIGHT_H

#ifdef RGBLIGHT_ANIMATIONS
	#define RGBLIGHT_MODES 35
#else
	#define RGBLIGHT_MODES 1
#endif

#ifndef RGBLIGHT_EFFECT_BREATHE_CENTER
#define RGBLIGHT_EFFECT_BREATHE_CENTER 1.85  // 1-2.7
#endif

#ifndef RGBLIGHT_EFFECT_BREATHE_MAX
#define RGBLIGHT_EFFECT_BREATHE_MAX 255   // 0-255
#endif

#ifndef RGBLIGHT_EFFECT_SNAKE_LENGTH
#define RGBLIGHT_EFFECT_SNAKE_LENGTH 4
#endif

#ifndef RGBLIGHT_EFFECT_KNIGHT_LENGTH
#define RGBLIGHT_EFFECT_KNIGHT_LENGTH 3
#endif

#ifndef RGBLIGHT_EFFECT_KNIGHT_OFFSET
#define RGBLIGHT_EFFECT_KNIGHT_OFFSET 0
#endif

#ifndef RGBLIGHT_EFFECT_KNIGHT_LED_NUM
#define RGBLIGHT_EFFECT_KNIGHT_LED_NUM RGBLED_NUM
#endif

#ifndef RGBLIGHT_EFFECT_CHRISTMAS_INTERVAL
#define RGBLIGHT_EFFECT_CHRISTMAS_INTERVAL 1000
#endif

#ifndef RGBLIGHT_EFFECT_CHRISTMAS_STEP
#define RGBLIGHT_EFFECT_CHRISTMAS_STEP 2
#endif

#ifndef RGBLIGHT_HUE_STEP
#define RGBLIGHT_HUE_STEP 10
#endif
#ifndef RGBLIGHT_SAT_STEP
#define RGBLIGHT_SAT_STEP 17
#endif
#ifndef RGBLIGHT_VAL_STEP
#define RGBLIGHT_VAL_STEP 17
#endif

#define RGBLED_TIMER_TOP F_CPU/(256*64)
#include "rgblight_reconfig.h"

/***** rgblight_mode(mode)/rgblight_mode_noeeprom(mode) ****

 old mode number (before 0.6.117) to new mode name table

|-----------------|-----------------------------------|
| old mode number | new mode name                     |
|-----------------|-----------------------------------|
|        1        | RGBLIGHT_MODE_STATIC_LIGHT        |
|        2        | RGBLIGHT_MODE_BREATHING           |
|        3        | RGBLIGHT_MODE_BREATHING + 1       |
|        4        | RGBLIGHT_MODE_BREATHING + 2       |
|        5        | RGBLIGHT_MODE_BREATHING + 3       |
|        6        | RGBLIGHT_MODE_RAINBOW_MOOD        |
|        7        | RGBLIGHT_MODE_RAINBOW_MOOD + 1    |
|        8        | RGBLIGHT_MODE_RAINBOW_MOOD + 2    |
|        9        | RGBLIGHT_MODE_RAINBOW_SWIRL       |
|       10        | RGBLIGHT_MODE_RAINBOW_SWIRL + 1   |
|       11        | RGBLIGHT_MODE_RAINBOW_SWIRL + 2   |
|       12        | RGBLIGHT_MODE_RAINBOW_SWIRL + 3   |
|       13        | RGBLIGHT_MODE_RAINBOW_SWIRL + 4   |
|       14        | RGBLIGHT_MODE_RAINBOW_SWIRL + 5   |
|       15        | RGBLIGHT_MODE_SNAKE               |
|       16        | RGBLIGHT_MODE_SNAKE + 1           |
|       17        | RGBLIGHT_MODE_SNAKE + 2           |
|       18        | RGBLIGHT_MODE_SNAKE + 3           |
|       19        | RGBLIGHT_MODE_SNAKE + 4           |
|       20        | RGBLIGHT_MODE_SNAKE + 5           |
|       21        | RGBLIGHT_MODE_KNIGHT              |
|       22        | RGBLIGHT_MODE_KNIGHT + 1          |
|       23        | RGBLIGHT_MODE_KNIGHT + 2          |
|       24        | RGBLIGHT_MODE_CHRISTMAS           |
|       25        | RGBLIGHT_MODE_STATIC_GRADIENT     |
|       26        | RGBLIGHT_MODE_STATIC_GRADIENT + 1 |
|       27        | RGBLIGHT_MODE_STATIC_GRADIENT + 2 |
|       28        | RGBLIGHT_MODE_STATIC_GRADIENT + 3 |
|       29        | RGBLIGHT_MODE_STATIC_GRADIENT + 4 |
|       30        | RGBLIGHT_MODE_STATIC_GRADIENT + 5 |
|       31        | RGBLIGHT_MODE_STATIC_GRADIENT + 6 |
|       32        | RGBLIGHT_MODE_STATIC_GRADIENT + 7 |
|       33        | RGBLIGHT_MODE_STATIC_GRADIENT + 8 |
|       34        | RGBLIGHT_MODE_STATIC_GRADIENT + 9 |
|       35        | RGBLIGHT_MODE_RGB_TEST            |
|       36        | RGBLIGHT_MODE_ALTERNATING         |
|-----------------|-----------------------------------|
 *****/

#define _RGBM_SINGLE_STATIC(sym) RGBLIGHT_MODE_##sym,
#define _RGBM_SINGLE_DYNAMIC(sym) RGBLIGHT_MODE_##sym,
#define _RGBM_MULTI_STATIC(sym) RGBLIGHT_MODE_##sym,
#define _RGBM_MULTI_DYNAMIC(sym) RGBLIGHT_MODE_##sym,
#define _RGBM_TMP_STATIC(sym, msym) RGBLIGHT_MODE_##sym,
#define _RGBM_TMP_DYNAMIC(sym, msym) RGBLIGHT_MODE_##sym,
enum RGBLIGHT_EFFECT_MODE {
    RGBLIGHT_MODE_zero = 0,
#include "rgblight_modes.h"
    RGBLIGHT_MODE_last
};

#ifndef RGBLIGHT_H_DUMMY_DEFINE

#    define RGBLIGHT_MODES (RGBLIGHT_MODE_last - 1)

// sample: #define RGBLIGHT_EFFECT_BREATHE_CENTER   1.85

#    ifndef RGBLIGHT_EFFECT_BREATHE_MAX
#        define RGBLIGHT_EFFECT_BREATHE_MAX 255  // 0-255
#    endif

#    ifndef RGBLIGHT_EFFECT_SNAKE_LENGTH
#        define RGBLIGHT_EFFECT_SNAKE_LENGTH 4
#    endif

#    ifndef RGBLIGHT_EFFECT_KNIGHT_LENGTH
#        define RGBLIGHT_EFFECT_KNIGHT_LENGTH 3
#    endif

#    ifndef RGBLIGHT_EFFECT_KNIGHT_OFFSET
#        define RGBLIGHT_EFFECT_KNIGHT_OFFSET 0
#    endif

#    ifndef RGBLIGHT_EFFECT_KNIGHT_LED_NUM
#        define RGBLIGHT_EFFECT_KNIGHT_LED_NUM (effect_num_leds)
#    endif

#    ifndef RGBLIGHT_EFFECT_CHRISTMAS_INTERVAL
#        define RGBLIGHT_EFFECT_CHRISTMAS_INTERVAL 1000
#    endif

#    ifndef RGBLIGHT_EFFECT_CHRISTMAS_STEP
#        define RGBLIGHT_EFFECT_CHRISTMAS_STEP 2
#    endif

#    ifndef RGBLIGHT_HUE_STEP
#        define RGBLIGHT_HUE_STEP 8
#    endif
#    ifndef RGBLIGHT_SAT_STEP
#        define RGBLIGHT_SAT_STEP 17
#    endif
#    ifndef RGBLIGHT_VAL_STEP
#        define RGBLIGHT_VAL_STEP 17
#    endif
#    ifndef RGBLIGHT_LIMIT_VAL
#        define RGBLIGHT_LIMIT_VAL 255
#    endif

#    define RGBLED_TIMER_TOP F_CPU / (256 * 64)
// #define RGBLED_TIMER_TOP 0xFF10

#include <stdint.h>
#include <stdbool.h>
#include "eeconfig.h"
#ifndef RGBLIGHT_CUSTOM_DRIVER
#include "ws2812.h"
#endif
#include "color.h"
#include "rgblight_list.h"
#    include <stdint.h>
#    include <stdbool.h>
#    include "eeconfig.h"
#    ifndef RGBLIGHT_CUSTOM_DRIVER
#        include "ws2812.h"
#    endif
#    include "color.h"
#    include "rgblight_list.h"

#    if defined(__AVR__)
#        include <avr/pgmspace.h>
#    endif

extern LED_TYPE led[RGBLED_NUM];

extern const uint8_t RGBLED_BREATHING_INTERVALS[4] PROGMEM;
extern const uint8_t RGBLED_RAINBOW_MOOD_INTERVALS[3] PROGMEM;
extern const uint8_t RGBLED_RAINBOW_SWIRL_INTERVALS[3] PROGMEM;
extern const uint8_t RGBLED_SNAKE_INTERVALS[3] PROGMEM;
extern const uint8_t RGBLED_KNIGHT_INTERVALS[3] PROGMEM;
extern const uint8_t  RGBLED_BREATHING_INTERVALS[4] PROGMEM;
extern const uint8_t  RGBLED_RAINBOW_MOOD_INTERVALS[3] PROGMEM;
extern const uint8_t  RGBLED_RAINBOW_SWIRL_INTERVALS[3] PROGMEM;
extern const uint8_t  RGBLED_SNAKE_INTERVALS[3] PROGMEM;
extern const uint8_t  RGBLED_KNIGHT_INTERVALS[3] PROGMEM;
extern const uint16_t RGBLED_RGBTEST_INTERVALS[1] PROGMEM;
extern bool           is_rgblight_initialized;

// Should stay in sycn with rgb matrix config as we reuse eeprom storage for both (for now)
typedef union {
  uint32_t raw;
  struct {
    bool     enable  :1;
    uint8_t  mode    :6;
    uint16_t hue     :9;
    uint8_t  sat     :8;
    uint8_t  val     :8;
    uint8_t  speed   :8;//EECONFIG needs to be increased to support this
  };
    uint32_t raw;
    struct {
        bool    enable : 1;
        uint8_t mode : 7;
        uint8_t hue : 8;
        uint8_t sat : 8;
        uint8_t val : 8;
        uint8_t speed : 8;  // EECONFIG needs to be increased to support this
    };
} rgblight_config_t;

void rgblight_init(void);
typedef struct _rgblight_status_t {
    uint8_t base_mode;
    bool    timer_enabled;
#    ifdef RGBLIGHT_SPLIT
    uint8_t change_flags;
#    endif
} rgblight_status_t;

/* === Utility Functions ===*/
void sethsv(uint8_t hue, uint8_t sat, uint8_t val, LED_TYPE *led1);
void sethsv_raw(uint8_t hue, uint8_t sat, uint8_t val, LED_TYPE *led1);  // without RGBLIGHT_LIMIT_VAL check
void setrgb(uint8_t r, uint8_t g, uint8_t b, LED_TYPE *led1);

/* === Low level Functions === */
void rgblight_set(void);
void rgblight_set_clipping_range(uint8_t start_pos, uint8_t num_leds);

/* === Effects and Animations Functions === */
/*   effect range setting */
void rgblight_set_effect_range(uint8_t start_pos, uint8_t num_leds);

/*   direct operation */
void rgblight_setrgb_at(uint8_t r, uint8_t g, uint8_t b, uint8_t index);
void rgblight_sethsv_at(uint8_t hue, uint8_t sat, uint8_t val, uint8_t index);
void rgblight_setrgb_range(uint8_t r, uint8_t g, uint8_t b, uint8_t start, uint8_t end);
void rgblight_sethsv_range(uint8_t hue, uint8_t sat, uint8_t val, uint8_t start, uint8_t end);
void rgblight_setrgb(uint8_t r, uint8_t g, uint8_t b);

#    ifndef RGBLIGHT_SPLIT
void rgblight_setrgb_master(uint8_t r, uint8_t g, uint8_t b);
void rgblight_setrgb_slave(uint8_t r, uint8_t g, uint8_t b);
void rgblight_sethsv_master(uint8_t hue, uint8_t sat, uint8_t val);
void rgblight_sethsv_slave(uint8_t hue, uint8_t sat, uint8_t val);
#    endif

/*   effect mode change */
void rgblight_mode(uint8_t mode);
void rgblight_mode_noeeprom(uint8_t mode);
void rgblight_increase(void);
void rgblight_decrease(void);
void rgblight_step(void);
void rgblight_step_noeeprom(void);
void rgblight_step_reverse(void);
void rgblight_step_reverse_noeeprom(void);

/*   effects mode disable/enable */
void rgblight_toggle(void);
void rgblight_toggle_noeeprom(void);
void rgblight_enable(void);
void rgblight_enable_noeeprom(void);
void rgblight_disable(void);
void rgblight_step(void);
void rgblight_step_reverse(void);
uint8_t rgblight_get_mode(void);
void rgblight_mode(uint8_t mode);
void rgblight_set(void);
void rgblight_update_dword(uint32_t dword);
void rgblight_disable_noeeprom(void);

/*   hue, sat, val change */
void rgblight_increase_hue(void);
void rgblight_increase_hue_noeeprom(void);
void rgblight_decrease_hue(void);
void rgblight_decrease_hue_noeeprom(void);
void rgblight_increase_sat(void);
void rgblight_increase_sat_noeeprom(void);
void rgblight_decrease_sat(void);
void rgblight_decrease_sat_noeeprom(void);
void rgblight_increase_val(void);
void rgblight_increase_val_noeeprom(void);
void rgblight_decrease_val(void);
void rgblight_decrease_val_noeeprom(void);
void rgblight_increase_speed(void);
void rgblight_decrease_speed(void);
void rgblight_sethsv(uint16_t hue, uint8_t sat, uint8_t val);
uint16_t rgblight_get_hue(void);
void rgblight_sethsv(uint8_t hue, uint8_t sat, uint8_t val);
void rgblight_sethsv_noeeprom(uint8_t hue, uint8_t sat, uint8_t val);

/*       query */
uint8_t rgblight_get_mode(void);
uint8_t rgblight_get_hue(void);
uint8_t rgblight_get_sat(void);
uint8_t rgblight_get_val(void);
void rgblight_setrgb(uint8_t r, uint8_t g, uint8_t b);
void rgblight_setrgb_at(uint8_t r, uint8_t g, uint8_t b, uint8_t index);
void rgblight_sethsv_at(uint16_t hue, uint8_t sat, uint8_t val, uint8_t index);

/* === qmk_firmware (core)internal Functions === */
void     rgblight_init(void);
uint32_t rgblight_read_dword(void);
void     rgblight_update_dword(uint32_t dword);
uint32_t eeconfig_read_rgblight(void);
void eeconfig_update_rgblight(uint32_t val);
void eeconfig_update_rgblight_default(void);
void eeconfig_debug_rgblight(void);
void     eeconfig_update_rgblight(uint32_t val);
void     eeconfig_update_rgblight_default(void);
void     eeconfig_debug_rgblight(void);

void rgb_matrix_increase(void);
void rgb_matrix_decrease(void);

void sethsv(uint16_t hue, uint8_t sat, uint8_t val, LED_TYPE *led1);
void setrgb(uint8_t r, uint8_t g, uint8_t b, LED_TYPE *led1);

void rgblight_sethsv_noeeprom(uint16_t hue, uint8_t sat, uint8_t val);
void rgblight_mode_noeeprom(uint8_t mode);
void rgblight_toggle_noeeprom(void);
void rgblight_enable_noeeprom(void);
void rgblight_disable_noeeprom(void);

void rgblight_sethsv_eeprom_helper(uint16_t hue, uint8_t sat, uint8_t val, bool write_to_eeprom);
void rgblight_sethsv_eeprom_helper(uint8_t hue, uint8_t sat, uint8_t val, bool write_to_eeprom);
void rgblight_mode_eeprom_helper(uint8_t mode, bool write_to_eeprom);


#define EZ_RGB(val) rgblight_show_solid_color((val >> 16) & 0xFF, (val >> 8) & 0xFF, val & 0xFF)
#    define EZ_RGB(val) rgblight_show_solid_color((val >> 16) & 0xFF, (val >> 8) & 0xFF, val & 0xFF)
void rgblight_show_solid_color(uint8_t r, uint8_t g, uint8_t b);

void rgblight_task(void);


@@ 155,12 263,52 @@ void rgblight_timer_init(void);
void rgblight_timer_enable(void);
void rgblight_timer_disable(void);
void rgblight_timer_toggle(void);
void rgblight_effect_breathing(uint8_t interval);
void rgblight_effect_rainbow_mood(uint8_t interval);
void rgblight_effect_rainbow_swirl(uint8_t interval);
void rgblight_effect_snake(uint8_t interval);
void rgblight_effect_knight(uint8_t interval);
void rgblight_effect_christmas(void);
void rgblight_effect_rgbtest(void);

#endif

#    ifdef RGBLIGHT_SPLIT
#        define RGBLIGHT_STATUS_CHANGE_MODE (1 << 0)
#        define RGBLIGHT_STATUS_CHANGE_HSVS (1 << 1)
#        define RGBLIGHT_STATUS_CHANGE_TIMER (1 << 2)
#        define RGBLIGHT_STATUS_ANIMATION_TICK (1 << 3)

typedef struct _rgblight_syncinfo_t {
    rgblight_config_t config;
    rgblight_status_t status;
} rgblight_syncinfo_t;

/* for split keyboard master side */
uint8_t rgblight_get_change_flags(void);
void    rgblight_clear_change_flags(void);
void    rgblight_get_syncinfo(rgblight_syncinfo_t *syncinfo);
/* for split keyboard slave side */
void rgblight_update_sync(rgblight_syncinfo_t *syncinfo, bool write_to_eeprom);
#    endif

#    ifdef RGBLIGHT_USE_TIMER

typedef struct _animation_status_t {
    uint16_t last_timer;
    uint8_t  delta; /* mode - base_mode */
    bool     restart;
    union {
        uint16_t pos16;
        uint8_t  pos;
        int8_t   current_hue;
        uint16_t current_offset;
    };
} animation_status_t;

extern animation_status_t animation_status;

void rgblight_effect_breathing(animation_status_t *anim);
void rgblight_effect_rainbow_mood(animation_status_t *anim);
void rgblight_effect_rainbow_swirl(animation_status_t *anim);
void rgblight_effect_snake(animation_status_t *anim);
void rgblight_effect_knight(animation_status_t *anim);
void rgblight_effect_christmas(animation_status_t *anim);
void rgblight_effect_rgbtest(animation_status_t *anim);
void rgblight_effect_alternating(animation_status_t *anim);

#    endif

#endif  // #ifndef RGBLIGHT_H_DUMMY_DEFINE
#endif  // RGBLIGHT_H

M keyboards/mxss/rules.mk => keyboards/mxss/rules.mk +4 -2
@@ 14,7 14,7 @@ BOOTLOADER = atmel-dfu
# Build Options
#   change yes to no to disable
#
BOOTMAGIC_ENABLE = no      # Virtual DIP switch configuration(+1000)
BOOTMAGIC_ENABLE = lite      # Virtual DIP switch configuration(+1000)
MOUSEKEY_ENABLE = yes       # Mouse keys(+4700)
EXTRAKEY_ENABLE = yes       # Audio control and System control(+450)
CONSOLE_ENABLE = no        # Console for debug(+400)


@@ 30,10 30,12 @@ BLUETOOTH_ENABLE = no       # Enable Bluetooth with the Adafruit EZ-Key HID
AUDIO_ENABLE = no           # Audio output on port C6
FAUXCLICKY_ENABLE = no      # Use buzzer to emulate clicky switches

SRC += mxss_frontled.c

# Remove the common RGB light code and use my iteration instead
OPT_DEFS += -DRGBLIGHT_ENABLE
SRC += $(QUANTUM_DIR)/process_keycode/process_rgb.c
SRC += rgblight.c
SRC += color.c
SRC += ws2812.c
CIE1931_CURVE = yes
LED_BREATHING_TABLE = yes

M keyboards/mxss/templates/keymap.c => keyboards/mxss/templates/keymap.c +10 -9
@@ 14,23 14,24 @@
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include QMK_KEYBOARD_H
#include "mxss_frontled.h"

// Predefined colors for layers
hs_set caps_color = { .hue = 0, .sat = 255 };

// Colors for layers
// Format: {hue, saturation}
// {0, 0} to turn off the LED
// Add additional rows to handle more layers
hs_set layer_colors[] = {
    [0] = {0,     0},  // Color for Layer 0
    [1] = {86,    255},  // Color for Layer 1
    [2] = {36,    255},  // Color for Layer 2
    [3] = {185,   255},  // Color for Layer 3
hs_set layer_colors[4] = {
    [0] = {.hue = 0,     .sat = 0},  // Color for Layer 0
    [1] = {.hue = 86,    .sat = 255},  // Color for Layer 1
    [2] = {.hue = 36,    .sat = 255},  // Color for Layer 2
    [3] = {.hue = 185,   .sat = 255},  // Color for Layer 3
};
const size_t lc_size = sizeof(layer_colors) / sizeof(uint16_t);
size_t lc_size = sizeof(layer_colors) / sizeof(uint16_t);

// Use NEW_SAFE_RANGE to define new custom keycodes in order to not overwrite the ones used for front LED control
enum custom_keycodes {
  MY_KEYCODE = NEW_SAFE_RANGE, 
  MY_KEYCODE = NEW_SAFE_RANGE,
};