summaryrefslogtreecommitdiff
path: root/quantum
diff options
context:
space:
mode:
authorNathan Gray <n8gray@n8gray.org>2020-03-10 12:50:01 -0700
committerFlorian Didron <fdidron@users.noreply.github.com>2020-06-12 17:00:27 +0900
commit0471dc3d8871348055e4fae4b74fa3cf47baca17 (patch)
tree5e1ecfbd3625e77b12da8e361f28b3cf886a6c89 /quantum
parent4434339a9d3bfdbf2cc563524f19f065a79951cc (diff)
Feature: RGBLight layers (#7768)
* New feature: RGBLIGHT_LAYERS This feature allows users to define multiple independent layers of lighting that can be toggled on and off individually, making it easy to use your RGB lighting to indicate things like active keyboard layer & modifier state. * Demonstrate built in functions for layer state checking Also link the video in the docs. * Follow existing pattern for setting rgblight_status flags * Eliminate rgblight_is_static_mode since it's not needed Just check to see if the timer is enabled directly.
Diffstat (limited to 'quantum')
-rw-r--r--quantum/rgblight.c71
-rw-r--r--quantum/rgblight.h46
2 files changed, 109 insertions, 8 deletions
diff --git a/quantum/rgblight.c b/quantum/rgblight.c
index acebde876d..6ae185fea8 100644
--- a/quantum/rgblight.c
+++ b/quantum/rgblight.c
@@ -38,17 +38,23 @@
# include "velocikey.h"
#endif
+#ifndef MIN
+# define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+
#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_LAYERS rgblight_status.change_flags |= RGBLIGHT_STATUS_CHANGE_LAYERS
# 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_LAYERS
# define RGBLIGHT_SPLIT_SET_CHANGE_TIMER_ENABLE
# define RGBLIGHT_SPLIT_ANIMATION_TICK
#endif
@@ -97,6 +103,10 @@ LED_TYPE led[RGBLED_NUM];
# define LED_ARRAY led
#endif
+#ifdef RGBLIGHT_LAYERS
+rgblight_segment_t const *const *rgblight_layers = NULL;
+#endif
+
static uint8_t clipping_start_pos = 0;
static uint8_t clipping_num_leds = RGBLED_NUM;
static uint8_t effect_start_pos = 0;
@@ -604,11 +614,67 @@ void rgblight_sethsv_master(uint8_t hue, uint8_t sat, uint8_t val) { rgblight_se
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
+#ifdef RGBLIGHT_LAYERS
+void rgblight_set_layer_state(uint8_t layer, bool enabled) {
+ uint8_t mask = 1 << layer;
+ if (enabled) {
+ rgblight_status.enabled_layer_mask |= mask;
+ } else {
+ rgblight_status.enabled_layer_mask &= ~mask;
+ }
+ RGBLIGHT_SPLIT_SET_CHANGE_LAYERS;
+ // Static modes don't have a ticker running to update the LEDs
+ if (rgblight_status.timer_enabled == false) {
+ rgblight_mode_noeeprom(rgblight_config.mode);
+ }
+}
+
+bool rgblight_get_layer_state(uint8_t layer) {
+ uint8_t mask = 1 << layer;
+ return (rgblight_status.enabled_layer_mask & mask) != 0;
+}
+
+// Write any enabled LED layers into the buffer
+static void rgblight_layers_write(void) {
+ uint8_t i = 0;
+ // For each layer
+ for (const rgblight_segment_t *const *layer_ptr = rgblight_layers; i < RGBLIGHT_MAX_LAYERS; layer_ptr++, i++) {
+ if (!rgblight_get_layer_state(i)) {
+ continue; // Layer is disabled
+ }
+ const rgblight_segment_t *segment_ptr = pgm_read_ptr(layer_ptr);
+ if (segment_ptr == NULL) {
+ break; // No more layers
+ }
+ // For each segment
+ while (1) {
+ rgblight_segment_t segment;
+ memcpy_P(&segment, segment_ptr, sizeof(rgblight_segment_t));
+ if (segment.index == RGBLIGHT_END_SEGMENT_INDEX) {
+ break; // No more segments
+ }
+ // Write segment.count LEDs
+ LED_TYPE *const limit = &led[MIN(segment.index + segment.count, RGBLED_NUM)];
+ for (LED_TYPE *led_ptr = &led[segment.index]; led_ptr < limit; led_ptr++) {
+ sethsv(segment.hue, segment.sat, segment.val, led_ptr);
+ }
+ segment_ptr++;
+ }
+ }
+}
+#endif
+
#ifndef RGBLIGHT_CUSTOM_DRIVER
void rgblight_set(void) {
LED_TYPE *start_led;
uint16_t num_leds = clipping_num_leds;
+# ifdef RGBLIGHT_LAYERS
+ if (rgblight_layers != NULL) {
+ rgblight_layers_write();
+ }
+# endif
+
if (!rgblight_config.enable) {
for (uint8_t i = effect_start_pos; i < effect_end_pos; i++) {
led[i].r = 0;
@@ -651,6 +717,11 @@ 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) {
+# ifdef RGBLIGHT_LAYERS
+ if (syncinfo->status.change_flags & RGBLIGHT_STATUS_CHANGE_LAYERS) {
+ rgblight_status.enabled_layer_mask = syncinfo->status.enabled_layer_mask;
+ }
+# endif
if (syncinfo->status.change_flags & RGBLIGHT_STATUS_CHANGE_MODE) {
if (syncinfo->config.enable) {
rgblight_config.enable = 1; // == rgblight_enable_noeeprom();
diff --git a/quantum/rgblight.h b/quantum/rgblight.h
index 39c4c2784f..97882c5b29 100644
--- a/quantum/rgblight.h
+++ b/quantum/rgblight.h
@@ -170,6 +170,32 @@ enum RGBLIGHT_EFFECT_MODE {
# include <avr/pgmspace.h>
# endif
+# ifdef RGBLIGHT_LAYERS
+typedef struct {
+ uint8_t index; // The first LED to light
+ uint8_t count; // The number of LEDs to light
+ uint8_t hue;
+ uint8_t sat;
+ uint8_t val;
+} rgblight_segment_t;
+
+# define RGBLIGHT_END_SEGMENT_INDEX (255)
+# define RGBLIGHT_END_SEGMENTS \
+ { RGBLIGHT_END_SEGMENT_INDEX, 0, 0, 0 }
+# define RGBLIGHT_MAX_LAYERS 8
+# define RGBLIGHT_LAYER_SEGMENTS(...) \
+ { __VA_ARGS__, RGBLIGHT_END_SEGMENTS }
+# define RGBLIGHT_LAYERS_LIST(...) \
+ { __VA_ARGS__, NULL }
+
+// Get/set enabled rgblight layers
+void rgblight_set_layer_state(uint8_t layer, bool enabled);
+bool rgblight_get_layer_state(uint8_t layer);
+
+// Point this to an array of rgblight_segment_t arrays in keyboard_post_init_user to use rgblight layers
+extern const rgblight_segment_t *const *rgblight_layers;
+# endif
+
extern LED_TYPE led[RGBLED_NUM];
extern const uint8_t RGBLED_BREATHING_INTERVALS[4] PROGMEM;
@@ -199,6 +225,9 @@ typedef struct _rgblight_status_t {
# ifdef RGBLIGHT_SPLIT
uint8_t change_flags;
# endif
+# ifdef RGBLIGHT_LAYERS
+ uint8_t enabled_layer_mask;
+# endif
} rgblight_status_t;
/* === Utility Functions ===*/
@@ -294,25 +323,26 @@ 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)
void rgblight_show_solid_color(uint8_t r, uint8_t g, uint8_t b);
-#ifdef RGBLIGHT_USE_TIMER
+# ifdef RGBLIGHT_USE_TIMER
void rgblight_task(void);
void rgblight_timer_init(void);
void rgblight_timer_enable(void);
void rgblight_timer_disable(void);
void rgblight_timer_toggle(void);
-#else
-#define rgblight_task()
-#define rgblight_timer_init()
-#define rgblight_timer_enable()
-#define rgblight_timer_disable()
-#define rgblight_timer_toggle()
-#endif
+# else
+# define rgblight_task()
+# define rgblight_timer_init()
+# define rgblight_timer_enable()
+# define rgblight_timer_disable()
+# define rgblight_timer_toggle()
+# 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)
+# define RGBLIGHT_STATUS_CHANGE_LAYERS (1 << 4)
typedef struct _rgblight_syncinfo_t {
rgblight_config_t config;