Merge pull request #617 from CapeLeidokos/ng_dynamic_led_modes

Introduced dynamic LED modes
pull/632/head
Jesse Vincent 6 years ago committed by GitHub
commit 5acb97940b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -16,6 +16,7 @@
#pragma once
#include <kaleidoscope/plugin/LEDMode.h>
#include <kaleidoscope/plugin/LEDControl.h>
#include <kaleidoscope/plugin/LEDControl/LEDUtils.h>
#include <kaleidoscope/plugin/LEDControl/LED-Off.h>

@ -49,6 +49,7 @@ extern HARDWARE_IMPLEMENTATION KeyboardHardware;
#include "kaleidoscope/layers.h"
#include "kaleidoscope/macro_map.h"
#include "kaleidoscope_internal/event_dispatch.h"
#include "kaleidoscope_internal/LEDModeManager.h"
#include "kaleidoscope/macro_helpers.h"
#include "kaleidoscope/plugin.h"

@ -38,23 +38,23 @@ void ColormapEffect::max_layers(uint8_t max_) {
map_base_ = ::LEDPaletteTheme.reserveThemes(max_layers_);
}
void ColormapEffect::onActivate(void) {
void ColormapEffect::TransientLEDMode::onActivate(void) {
if (!Kaleidoscope.has_leds)
return;
top_layer_ = Layer.top();
if (top_layer_ <= max_layers_)
::LEDPaletteTheme.updateHandler(map_base_, top_layer_);
parent_->top_layer_ = Layer.top();
if (parent_->top_layer_ <= parent_->max_layers_)
::LEDPaletteTheme.updateHandler(parent_->map_base_, parent_->top_layer_);
}
void ColormapEffect::refreshAt(byte row, byte col) {
if (top_layer_ <= max_layers_)
::LEDPaletteTheme.refreshAt(map_base_, top_layer_, row, col);
void ColormapEffect::TransientLEDMode::refreshAt(byte row, byte col) {
if (parent_->top_layer_ <= parent_->max_layers_)
::LEDPaletteTheme.refreshAt(parent_->map_base_, parent_->top_layer_, row, col);
}
EventHandlerResult ColormapEffect::onLayerChange() {
if (::LEDControl.get_mode() == this)
onActivate();
if (::LEDControl.get_mode_index() == led_mode_id_)
::LEDControl.get_mode<TransientLEDMode>()->onActivate();
return EventHandlerResult::OK;
}

@ -22,7 +22,9 @@
namespace kaleidoscope {
namespace plugin {
class ColormapEffect : public LEDMode {
class ColormapEffect : public Plugin,
public LEDModeInterface,
public AccessTransientLEDMode {
public:
ColormapEffect(void) {}
@ -31,9 +33,28 @@ class ColormapEffect : public LEDMode {
EventHandlerResult onLayerChange();
EventHandlerResult onFocusEvent(const char *command);
protected:
void onActivate(void) final;
void refreshAt(byte row, byte col) final;
// This class' instance has dynamic lifetime
//
class TransientLEDMode : public LEDMode {
public:
// Please note that storing the parent ptr is only required
// for those LED modes that require access to
// members of their parent class. Most LED modes can do without.
//
TransientLEDMode(const ColormapEffect *parent) : parent_(parent) {}
protected:
friend class ColormapEffect;
virtual void onActivate(void) final;
virtual void refreshAt(byte row, byte col) final;
private:
const ColormapEffect *parent_;
};
private:
static uint8_t top_layer_;

@ -30,7 +30,6 @@ bool FingerPainter::edit_mode_;
EventHandlerResult FingerPainter::onSetup() {
color_base_ = ::LEDPaletteTheme.reserveThemes(1);
::LEDControl.mode_add(this);
return EventHandlerResult::OK;
}

@ -21,6 +21,11 @@
namespace kaleidoscope {
namespace plugin {
// This class is implemented as a persistent LED mode
// as there is no benefit of transforming it into a dynamic
// LED mode in terms of PROGMEM or RAM.
//
class FingerPainter : public LEDMode {
public:
FingerPainter(void) {}

@ -56,10 +56,18 @@ void HardwareTestMode::testLeds(void) {
setLeds(blue);
setLeds(green);
setLeds(red);
// This works under the assumption that LEDRainbowEffect
// has been registered with KALEIDOSCOPE_INIT_PLUGINS in
// the sketch. Otherwise LEDRainbowEffect would not be
// known to LEDControl.
//
::LEDControl.activate(&::LEDRainbowEffect);
// rainbow for 10 seconds
::LEDRainbowEffect.update_delay(5);
for (auto i = 0; i < 300; i++) {
::LEDRainbowEffect.update();
::LEDControl.update();
::LEDControl.syncLeds();
}
}

@ -17,16 +17,11 @@
#include <Kaleidoscope.h>
#include <Kaleidoscope-Heatmap.h>
#include <Kaleidoscope-LEDControl.h>
namespace kaleidoscope {
namespace plugin {
// store the number of times each key has been strock
uint16_t Heatmap::heatmap_[ROWS][COLS];
// max of heatmap_ (we divide by it so we start at 1)
uint16_t Heatmap::highest_ = 1;
// next heatmap computation time
uint32_t Heatmap::next_heatmap_comp_time_ = 0;
// in the cRGB struct the order is blue, green, red (It should be called cBGR…)
// default heat_colors black green yellow red
static const cRGB heat_colors_default_[] PROGMEM = {{0, 0, 0}, {25, 255, 25}, {25, 255, 255}, {25, 25, 255}};
@ -37,7 +32,17 @@ uint8_t Heatmap::heat_colors_length = 4;
// number of millisecond to wait between each heatmap computation
uint16_t Heatmap::update_delay = 1000;
cRGB Heatmap::computeColor(float v) {
Heatmap::TransientLEDMode::TransientLEDMode(const Heatmap *parent)
: parent_(parent),
// store the number of times each key has been strock
heatmap_{},
// max of heatmap_ (we divide by it so we start at 1)
highest_(1),
// next heatmap computation time
next_heatmap_comp_time_(0)
{}
cRGB Heatmap::TransientLEDMode::computeColor(float v) {
// compute the color corresponding to a value between 0 and 1
/*
@ -95,7 +100,7 @@ cRGB Heatmap::computeColor(float v) {
return {b, g, r};
}
void Heatmap::shiftStats(void) {
void Heatmap::TransientLEDMode::shiftStats(void) {
// this method is called when:
// 1. a value in heatmap_ reach INT8_MAX
// 2. highest_ reach heat_colors_length*512 (see Heatmap::loopHook)
@ -112,6 +117,15 @@ void Heatmap::shiftStats(void) {
}
void Heatmap::resetMap(void) {
if (::LEDControl.get_mode_index() != led_mode_id_)
return;
::LEDControl.get_mode<TransientLEDMode>()->resetMap();
}
void Heatmap::TransientLEDMode::resetMap() {
// this method can be used as a way to work around an existing bug with a single key
// getting special attention or if the user just wants a button to reset the map
for (uint8_t r = 0; r < ROWS; r++) {
@ -138,6 +152,15 @@ EventHandlerResult Heatmap::onKeyswitchEvent(Key &mapped_key, byte row, byte col
if (!keyToggledOn(key_state))
return EventHandlerResult::OK;
// if the LED mode is not current, skip it
if (::LEDControl.get_mode_index() != led_mode_id_)
return EventHandlerResult::OK;
return ::LEDControl.get_mode<TransientLEDMode>()
->onKeyswitchEvent(mapped_key, row, col, key_state);
}
EventHandlerResult Heatmap::TransientLEDMode::onKeyswitchEvent(Key &mapped_key, byte row, byte col, uint8_t key_state) {
// increment the heatmap_ value related to the key
heatmap_[row][col]++;
@ -160,6 +183,13 @@ EventHandlerResult Heatmap::beforeEachCycle() {
if (!Kaleidoscope.has_leds)
return EventHandlerResult::OK;
if (::LEDControl.get_mode_index() != led_mode_id_)
return EventHandlerResult::OK;
return ::LEDControl.get_mode<TransientLEDMode>()->beforeEachCycle();
}
EventHandlerResult Heatmap::TransientLEDMode::beforeEachCycle() {
// this methode is called frequently by Kaleidoscope
// even if the module isn't activated
@ -175,7 +205,7 @@ EventHandlerResult Heatmap::beforeEachCycle() {
return EventHandlerResult::OK;
}
void Heatmap::update(void) {
void Heatmap::TransientLEDMode::update(void) {
if (!Kaleidoscope.has_leds)
return;

@ -22,7 +22,9 @@
namespace kaleidoscope {
namespace plugin {
class Heatmap : public LEDMode {
class Heatmap : public Plugin,
public LEDModeInterface,
public AccessTransientLEDMode {
public:
Heatmap(void) {}
@ -34,16 +36,38 @@ class Heatmap : public LEDMode {
EventHandlerResult onKeyswitchEvent(Key &mapped_key, byte row, byte col, uint8_t key_state);
EventHandlerResult beforeEachCycle();
protected:
void update(void) final;
// This class' instance has dynamic lifetime
//
class TransientLEDMode : public LEDMode {
public:
private:
static uint16_t heatmap_[ROWS][COLS];
static uint16_t highest_;
static uint32_t next_heatmap_comp_time_;
// Please note that storing the parent ptr is only required
// for those LED modes that require access to
// members of their parent class. Most LED modes can do without.
//
TransientLEDMode(const Heatmap *parent);
static void shiftStats(void);
static cRGB computeColor(float v);
void resetMap();
EventHandlerResult onKeyswitchEvent(Key &mapped_key, byte row, byte col, uint8_t key_state);
EventHandlerResult beforeEachCycle();
protected:
virtual void update() final;
private:
const Heatmap *parent_;
uint16_t heatmap_[ROWS][COLS];
uint16_t highest_;
uint32_t next_heatmap_comp_time_;
void shiftStats(void);
cRGB computeColor(float v);
friend class Heatmap;
};
};
}
}

@ -20,26 +20,31 @@
namespace kaleidoscope {
namespace plugin {
cRGB LEDActiveLayerColorEffect::active_color_;
const cRGB *LEDActiveLayerColorEffect::colormap_;
LEDActiveLayerColorEffect::TransientLEDMode::TransientLEDMode(
const LEDActiveLayerColorEffect *parent)
: parent_(parent),
active_color_{0, 0, 0}
{}
void LEDActiveLayerColorEffect::setColormap(const cRGB colormap[]) {
colormap_ = colormap;
}
cRGB LEDActiveLayerColorEffect::getActiveColor() {
cRGB LEDActiveLayerColorEffect::TransientLEDMode::getActiveColor() {
cRGB color;
uint8_t top_layer = ::Layer.top();
color.r = pgm_read_byte(&(colormap_[top_layer].r));
color.g = pgm_read_byte(&(colormap_[top_layer].g));
color.b = pgm_read_byte(&(colormap_[top_layer].b));
color.r = pgm_read_byte(&(parent_->colormap_[top_layer].r));
color.g = pgm_read_byte(&(parent_->colormap_[top_layer].g));
color.b = pgm_read_byte(&(parent_->colormap_[top_layer].b));
return color;
}
void LEDActiveLayerColorEffect::onActivate(void) {
void LEDActiveLayerColorEffect::TransientLEDMode::onActivate(void) {
if (!Kaleidoscope.has_leds)
return;
@ -47,13 +52,14 @@ void LEDActiveLayerColorEffect::onActivate(void) {
::LEDControl.set_all_leds_to(active_color_);
}
void LEDActiveLayerColorEffect::refreshAt(byte row, byte col) {
void LEDActiveLayerColorEffect::TransientLEDMode::refreshAt(byte row, byte col) {
::LEDControl.setCrgbAt(row, col, active_color_);
}
EventHandlerResult LEDActiveLayerColorEffect::onLayerChange() {
if (::LEDControl.get_mode() == this)
onActivate();
if (::LEDControl.get_mode_index() == led_mode_id_)
::LEDControl.get_mode<TransientLEDMode>()->onActivate();
return EventHandlerResult::OK;
}

@ -21,22 +21,45 @@
namespace kaleidoscope {
namespace plugin {
class LEDActiveLayerColorEffect : public LEDMode {
class LEDActiveLayerColorEffect : public Plugin,
public LEDModeInterface,
public AccessTransientLEDMode {
public:
LEDActiveLayerColorEffect(void) {}
EventHandlerResult onLayerChange();
void setColormap(const cRGB colormap[]);
protected:
void onActivate(void) final;
void refreshAt(byte row, byte col) final;
// This class' instance has dynamic lifetime
//
class TransientLEDMode : public LEDMode {
public:
// Please note that storing the parent ptr is only required
// for those LED modes that require access to
// members of their parent class. Most LED modes can do without.
//
TransientLEDMode(const LEDActiveLayerColorEffect *parent);
protected:
virtual void onActivate(void) final;
virtual void refreshAt(byte row, byte col) final;
private:
const LEDActiveLayerColorEffect *parent_;
cRGB active_color_;
cRGB getActiveColor();
friend class LEDActiveLayerColorEffect;
};
private:
static const cRGB *colormap_;
static cRGB active_color_;
static cRGB getActiveColor();
static const cRGB *colormap_;
};
}
}

@ -21,10 +21,15 @@ namespace kaleidoscope {
namespace plugin {
uint16_t AlphaSquareEffect::length = 1000;
uint32_t AlphaSquareEffect::end_time_left_, AlphaSquareEffect::end_time_right_;
Key AlphaSquareEffect::last_key_left_, AlphaSquareEffect::last_key_right_;
void AlphaSquareEffect::update(void) {
AlphaSquareEffect::TransientLEDMode::TransientLEDMode(AlphaSquareEffect */*parent*/)
: end_time_left_(0),
end_time_right_(0),
last_key_left_(Key{}),
last_key_right_(Key{})
{}
void AlphaSquareEffect::TransientLEDMode::update(void) {
if (!Kaleidoscope.has_leds)
return;
@ -38,7 +43,7 @@ void AlphaSquareEffect::update(void) {
}
}
void AlphaSquareEffect::refreshAt(byte row, byte col) {
void AlphaSquareEffect::TransientLEDMode::refreshAt(byte row, byte col) {
bool timed_out;
uint8_t display_col = 2;
Key key = last_key_left_;
@ -59,7 +64,7 @@ EventHandlerResult AlphaSquareEffect::onKeyswitchEvent(Key &mappedKey, byte row,
if (!Kaleidoscope.has_leds)
return EventHandlerResult::OK;
if (::LEDControl.get_mode() != &::AlphaSquareEffect)
if (::LEDControl.get_mode_index() != led_mode_id_)
return EventHandlerResult::OK;
if (keyState & INJECTED)
@ -72,15 +77,17 @@ EventHandlerResult AlphaSquareEffect::onKeyswitchEvent(Key &mappedKey, byte row,
return EventHandlerResult::OK;
uint8_t display_col = 2;
Key prev_key = last_key_left_;
auto this_led_mode = ::LEDControl.get_mode<TransientLEDMode>();
Key prev_key = this_led_mode->last_key_left_;
if (col < COLS / 2) {
last_key_left_ = mappedKey;
end_time_left_ = millis() + length;
this_led_mode->last_key_left_ = mappedKey;
this_led_mode->end_time_left_ = millis() + length;
} else {
prev_key = last_key_right_;
last_key_right_ = mappedKey;
end_time_right_ = millis() + length;
prev_key = this_led_mode->last_key_right_;
this_led_mode->last_key_right_ = mappedKey;
this_led_mode->end_time_right_ = millis() + length;
display_col = 10;
}

@ -22,7 +22,9 @@
namespace kaleidoscope {
namespace plugin {
class AlphaSquareEffect : public LEDMode {
class AlphaSquareEffect : public Plugin,
public LEDModeInterface,
public AccessTransientLEDMode {
public:
AlphaSquareEffect(void) {}
@ -30,13 +32,22 @@ class AlphaSquareEffect : public LEDMode {
EventHandlerResult onKeyswitchEvent(Key &mappedKey, byte row, byte col, uint8_t keyState);
protected:
void update(void) final;
void refreshAt(byte row, byte col) final;
// This class' instance has dynamic lifetime
//
class TransientLEDMode : public LEDMode {
public:
TransientLEDMode(AlphaSquareEffect *parent);
private:
static uint32_t end_time_left_, end_time_right_;
static Key last_key_left_, last_key_right_;
protected:
void update(void) final;
void refreshAt(byte row, byte col) final;
private:
uint32_t end_time_left_, end_time_right_;
Key last_key_left_, last_key_right_;
friend class AlphaSquareEffect;
};
};
}
}

@ -16,21 +16,22 @@
*/
#include <Kaleidoscope-LED-Stalker.h>
#include <Kaleidoscope-LEDControl.h>
namespace kaleidoscope {
namespace plugin {
uint8_t StalkerEffect::map_[ROWS][COLS];
StalkerEffect::ColorComputer *StalkerEffect::variant;
uint16_t StalkerEffect::step_length = 50;
uint16_t StalkerEffect::step_start_time_;
cRGB StalkerEffect::inactive_color = (cRGB) {
0, 0, 0
};
void StalkerEffect::onActivate(void) {
memset(map_, 0, sizeof(map_));
}
StalkerEffect::TransientLEDMode::TransientLEDMode(const StalkerEffect *parent)
: parent_(parent),
step_start_time_(0),
map_{}
{}
EventHandlerResult StalkerEffect::onKeyswitchEvent(Key &mapped_key, byte row, byte col, uint8_t keyState) {
if (!Kaleidoscope.has_leds)
@ -39,35 +40,38 @@ EventHandlerResult StalkerEffect::onKeyswitchEvent(Key &mapped_key, byte row, by
if (row >= ROWS || col >= COLS)
return EventHandlerResult::OK;
if (::LEDControl.get_mode_index() != led_mode_id_)
return EventHandlerResult::OK;
if (keyIsPressed(keyState)) {
map_[row][col] = 0xff;
::LEDControl.get_mode<TransientLEDMode>()->map_[row][col] = 0xff;
}
return EventHandlerResult::OK;
}
void StalkerEffect::update(void) {
void StalkerEffect::TransientLEDMode::update(void) {
if (!Kaleidoscope.has_leds)
return;
if (!variant)
if (!parent_->variant)
return;
uint16_t elapsed = Kaleidoscope.millisAtCycleStart() - step_start_time_;
if (elapsed < step_length)
if (elapsed < parent_->step_length)
return;
for (byte r = 0; r < ROWS; r++) {
for (byte c = 0; c < COLS; c++) {
uint8_t step = map_[r][c];
if (step) {
::LEDControl.setCrgbAt(r, c, variant->compute(&step));
::LEDControl.setCrgbAt(r, c, parent_->variant->compute(&step));
}
map_[r][c] = step;
if (!map_[r][c])
::LEDControl.setCrgbAt(r, c, inactive_color);
::LEDControl.setCrgbAt(r, c, parent_->inactive_color);
}
}

@ -24,7 +24,9 @@
namespace kaleidoscope {
namespace plugin {
class StalkerEffect : public LEDMode {
class StalkerEffect : public Plugin,
public LEDModeInterface,
public AccessTransientLEDMode {
public:
class ColorComputer {
public:
@ -39,13 +41,30 @@ class StalkerEffect : public LEDMode {
EventHandlerResult onKeyswitchEvent(Key &mapped_key, byte row, byte col, uint8_t keyState);
protected:
void onActivate(void) final;
void update(void) final;
// This class' instance has dynamic lifetime
//
class TransientLEDMode : public LEDMode {
public:
private:
static uint16_t step_start_time_;
static uint8_t map_[ROWS][COLS];
// Please note that storing the parent ptr is only required
// for those LED modes that require access to
// members of their parent class. Most LED modes can do without.
//
TransientLEDMode(const StalkerEffect *parent);
protected:
virtual void update() final;
private:
const StalkerEffect *parent_;
uint16_t step_start_time_;
uint8_t map_[ROWS][COLS];
friend class StalkerEffect;
};
};
namespace stalker {

@ -27,52 +27,64 @@ namespace plugin {
#define MS_PER_FRAME 40 // 40 = 25 fps
#define FRAMES_PER_DROP 120 // max time between raindrops during idle animation
int8_t WavepoolEffect::surface[2][WP_WID * WP_HGT];
uint8_t WavepoolEffect::page = 0;
uint8_t WavepoolEffect::frames_since_event = 0;
uint16_t WavepoolEffect::idle_timeout = 5000; // 5 seconds
int16_t WavepoolEffect::ripple_hue = WavepoolEffect::rainbow_hue; // automatic hue
// map native keyboard coordinates (16x4) into geometric space (14x5)
PROGMEM const uint8_t WavepoolEffect::rc2pos[ROWS * COLS] = {
PROGMEM const uint8_t WavepoolEffect::TransientLEDMode::rc2pos[ROWS * COLS] = {
0, 1, 2, 3, 4, 5, 6, 59, 66, 7, 8, 9, 10, 11, 12, 13,
14, 15, 16, 17, 18, 19, 34, 60, 65, 35, 22, 23, 24, 25, 26, 27,
28, 29, 30, 31, 32, 33, 48, 61, 64, 49, 36, 37, 38, 39, 40, 41,
42, 43, 44, 45, 46, 47, 58, 62, 63, 67, 50, 51, 52, 53, 54, 55,
};
WavepoolEffect::TransientLEDMode::TransientLEDMode(const WavepoolEffect *parent)
: parent_(parent),
frames_since_event_(0),
surface_{},
page_(0)
{}
EventHandlerResult WavepoolEffect::onKeyswitchEvent(Key &mapped_key, byte row, byte col, uint8_t key_state) {
if (row >= ROWS || col >= COLS)
return EventHandlerResult::OK;
if (::LEDControl.get_mode_index() != led_mode_id_)
return EventHandlerResult::OK;
return ::LEDControl.get_mode<TransientLEDMode>()
->onKeyswitchEvent(mapped_key, row, col, key_state);
}
EventHandlerResult WavepoolEffect::TransientLEDMode::onKeyswitchEvent(Key &mapped_key, byte row, byte col, uint8_t key_state) {
if (keyIsPressed(key_state)) {
uint8_t offset = (row * COLS) + col;
surface[page][pgm_read_byte(rc2pos + offset)] = 0x7f;
frames_since_event = 0;
surface_[page_][pgm_read_byte(rc2pos + offset)] = 0x7f;
frames_since_event_ = 0;
}
return EventHandlerResult::OK;
}
void WavepoolEffect::raindrop(uint8_t x, uint8_t y, int8_t *page) {
void WavepoolEffect::TransientLEDMode::raindrop(uint8_t x, uint8_t y, int8_t *page_) {
uint8_t rainspot = (y * WP_WID) + x;
page[rainspot] = 0x7f;
if (y > 0) page[rainspot - WP_WID] = 0x60;
if (y < (WP_HGT - 1)) page[rainspot + WP_WID] = 0x60;
if (x > 0) page[rainspot - 1] = 0x60;
if (x < (WP_WID - 1)) page[rainspot + 1] = 0x60;
page_[rainspot] = 0x7f;
if (y > 0) page_[rainspot - WP_WID] = 0x60;
if (y < (WP_HGT - 1)) page_[rainspot + WP_WID] = 0x60;
if (x > 0) page_[rainspot - 1] = 0x60;
if (x < (WP_WID - 1)) page_[rainspot + 1] = 0x60;
}
// this is a lot smaller than the standard library's rand(),
// and still looks random-ish
uint8_t WavepoolEffect::wp_rand() {
uint8_t WavepoolEffect::TransientLEDMode::wp_rand() {
static uint16_t offset = 0x400;
offset = ((offset + 1) & 0x4fff) | 0x400;
return (millis() / MS_PER_FRAME) + pgm_read_byte(offset);
}
void WavepoolEffect::update(void) {
void WavepoolEffect::TransientLEDMode::update(void) {
// limit the frame rate; one frame every 64 ms
static uint8_t prev_time = 0;
@ -89,11 +101,11 @@ void WavepoolEffect::update(void) {
static uint8_t current_hue = 0;
current_hue ++;
frames_since_event ++;
frames_since_event_ ++;
// needs two pages of height map to do the calculations
int8_t *newpg = &surface[page ^ 1][0];
int8_t *oldpg = &surface[page][0];
int8_t *newpg = &surface_[page_ ^ 1][0];
int8_t *oldpg = &surface_[page_][0];
// rain a bit while idle
static uint8_t frames_till_next_drop = 0;
@ -112,11 +124,11 @@ void WavepoolEffect::update(void) {
raindrop(prev_x, prev_y, oldpg);
prev_x = prev_y = -1;
}
if (frames_since_event
if (frames_since_event_
>= (frames_till_next_drop
+ (idle_timeout / MS_PER_FRAME))) {
frames_till_next_drop = 4 + (wp_rand() % FRAMES_PER_DROP);
frames_since_event = idle_timeout / MS_PER_FRAME;
frames_since_event_ = idle_timeout / MS_PER_FRAME;
uint8_t x = wp_rand() % WP_WID;
uint8_t y = wp_rand() % WP_HGT;
@ -195,7 +207,7 @@ void WavepoolEffect::update(void) {
uint8_t value = (intensity >= 128) ? 255 : intensity << 1;
int16_t hue = ripple_hue;
if (ripple_hue == rainbow_hue) {
if (ripple_hue == WavepoolEffect::rainbow_hue) {
// color starts white but gets dimmer and more saturated as it fades,
// with hue wobbling according to height map
hue = (current_hue + height + (height >> 1)) & 0xff;
@ -209,10 +221,10 @@ void WavepoolEffect::update(void) {
#ifdef INTERPOLATE
// swap pages every other frame
if (!(now & 1)) page ^= 1;
if (!(now & 1)) page_ ^= 1;
#else
// swap pages every frame
page ^= 1;
page_ ^= 1;
#endif
}

@ -28,7 +28,9 @@
namespace kaleidoscope {
namespace plugin {
class WavepoolEffect : public LEDMode {
class WavepoolEffect : public Plugin,
public LEDModeInterface,
public AccessTransientLEDMode {
public:
WavepoolEffect(void) {}
@ -39,17 +41,38 @@ class WavepoolEffect : public LEDMode {
static int16_t ripple_hue;
static constexpr int16_t rainbow_hue = INT16_MAX;
protected:
void update(void) final;
private:
static uint8_t frames_since_event;
static int8_t surface[2][WP_WID * WP_HGT];
static uint8_t page;
static PROGMEM const uint8_t rc2pos[ROWS * COLS];
// This class' instance has dynamic lifetime
//
class TransientLEDMode : public LEDMode {
public:
static void raindrop(uint8_t x, uint8_t y, int8_t *page);
static uint8_t wp_rand();
// Please note that storing the parent ptr is only required
// for those LED modes that require access to
// members of their parent class. Most LED modes can do without.
//
TransientLEDMode(const WavepoolEffect *parent);
EventHandlerResult onKeyswitchEvent(Key &mapped_key, byte row, byte col, uint8_t key_state);
protected:
virtual void update() final;
private:
const WavepoolEffect *parent_;
uint8_t frames_since_event_;
int8_t surface_[2][WP_WID * WP_HGT];
uint8_t page_;
static PROGMEM const uint8_t rc2pos[ROWS * COLS];
void raindrop(uint8_t x, uint8_t y, int8_t *page);
uint8_t wp_rand();
friend class WavepoolEffect;
};
};
}

@ -16,88 +16,72 @@
#include "Kaleidoscope-LEDControl.h"
#include "Kaleidoscope-FocusSerial.h"
#include "kaleidoscope_internal/LEDModeManager.h"
using namespace kaleidoscope::internal;
namespace kaleidoscope {
namespace plugin {
LEDMode *LEDControl::modes[LED_MAX_MODES];
uint8_t LEDControl::mode;
uint16_t LEDControl::syncDelay = 32; // 32ms interval => 30Hz refresh rate
static constexpr uint8_t uninitialized_mode_id = 255;
uint8_t LEDControl::mode_id = uninitialized_mode_id;
uint8_t LEDControl::num_led_modes_ = LEDModeManager::numLEDModes();
LEDMode *LEDControl::cur_led_mode_;
uint16_t LEDControl::syncDelay = 32;
uint16_t LEDControl::syncTimer;
bool LEDControl::paused = false;
void LEDMode::activate(void) {
::LEDControl.activate(this);
}
kaleidoscope::EventHandlerResult LEDMode::onSetup() {
::LEDControl.mode_add(this);
setup();
return EventHandlerResult::OK;
}
LEDControl::LEDControl(void) {
mode = 0;
memset(modes, 0, LED_MAX_MODES * sizeof(modes[0]));
}
void LEDControl::next_mode(void) {
mode++;
mode_id++;
if (mode >= LED_MAX_MODES || !modes[mode]) {
if (mode_id >= num_led_modes_) {
return set_mode(0);
}
return set_mode(mode);
return set_mode(mode_id);
}
void LEDControl::prev_mode(void) {
if (mode == 0) {
if (mode_id == 0) {
// wrap around
mode = LED_MAX_MODES - 1;
// then count down until reaching a valid mode
while (mode > 0 && !modes[mode]) mode--;
mode_id = num_led_modes_ - 1;
} else {
mode--;
mode_id--;
}
return set_mode(mode);
return set_mode(mode_id);
}
void
LEDControl::set_mode(uint8_t mode_) {
if (mode_ >= LED_MAX_MODES)
if (mode_ >= num_led_modes_)
return;
mode = mode_;
refreshAll();
}
mode_id = mode_;
uint8_t LEDControl::get_mode_index(void) {
return mode;
}
// Cache the LED mode
//
cur_led_mode_ = LEDModeManager::getLEDMode(mode_id);
LEDMode *LEDControl::get_mode(void) {
return modes[mode];
refreshAll();
}
void LEDControl::activate(LEDMode *mode) {
for (uint8_t i = 0; i < LED_MAX_MODES; i++) {
if (modes[i] == mode)
return set_mode(i);
}
}
void LEDControl::activate(LEDModeInterface *plugin) {
for (uint8_t i = 0; i < num_led_modes_; i++) {
led_mode_management::LEDModeFactory fac;
int8_t LEDControl::mode_add(LEDMode *mode) {
for (int i = 0; i < LED_MAX_MODES; i++) {
if (modes[i])
continue;
LEDModeManager::retreiveLEDModeFactoryFromPROGMEM(i, fac);
modes[i] = mode;
return i;
if (fac.isAssociatedWithPlugin(plugin)) {
set_mode(i);
return;
}
}
return -1;
}
void LEDControl::set_all_leds_to(uint8_t r, uint8_t g, uint8_t b) {
@ -139,13 +123,14 @@ void LEDControl::syncLeds(void) {
kaleidoscope::EventHandlerResult LEDControl::onSetup() {
set_all_leds_to({0, 0, 0});
for (uint8_t i = 0; i < LED_MAX_MODES; i++) {
if (modes[i])
(modes[i]->setup)();
}
LEDModeManager::setupPersistentLEDModes();
syncTimer = millis() + syncDelay;
if (mode_id == uninitialized_mode_id) {
set_mode(0);
}
return EventHandlerResult::OK;
}
@ -249,10 +234,10 @@ EventHandlerResult FocusLEDCommand::onFocusEvent(const char *command) {
} else if (peek == 'p') {
::LEDControl.prev_mode();
} else {
uint8_t mode;
uint8_t mode_id;
::Focus.read(mode);
::LEDControl.set_mode(mode);
::Focus.read(mode_id);
::LEDControl.set_mode(mode_id);
}
break;
}

@ -18,88 +18,20 @@
#include <Kaleidoscope.h>
#define LED_MAX_MODES 24
#define LED_TOGGLE B00000001 // Synthetic, internal
#define Key_LEDEffectNext Key(0, KEY_FLAGS | SYNTHETIC | IS_INTERNAL | LED_TOGGLE)
#define Key_LEDEffectPrevious Key(1, KEY_FLAGS | SYNTHETIC | IS_INTERNAL | LED_TOGGLE)
#define _DEPRECATED_MESSAGE_LED_CONTROL_MODE_ADD \
"LEDControl::mode_add(LEDMode *mode) is deprecated. LEDModes are now \n" \
"automatically registered. You can safely remove any calls to \n" \
"LEDControl::mode_add from your code."
namespace kaleidoscope {
namespace plugin {
/** Base class for LED modes.
*
* LED modes are a special kind of plugin, they are in charge of updating LED
* colors, setting a theme. While it is possible to have other plugins
* override the mode's colors, the LED mode is the baseline.
*
* Most of its functionality is called via @ref LEDControl, with only a few
* public methods.
*
* A LED mode **must** implement at least one of @ref onActivate or @ref
* update, and possibly @ref refreshAt too.
*/
class LEDMode : public kaleidoscope::Plugin {
friend class LEDControl;
protected:
// These methods should only be called by LEDControl.
/** One-time setup, called at keyboard boot.
*
* Any hooks that need registering, any one-time setup that needs to be
* performed, shall be done here. This is purely for preparation purposes, the
* LEDs should not be touched yet at this time.
*/
virtual void setup(void) {}
/** Function to call whenever the mode is activated.
*
* Like @ref setup, this method need not touch LEDs, @ref update will be
* called right after it. The purpose of this callback is to allow a plugin to
* do some preparation whenever it is activated, instead of only on boot, or
* always at each cycle.
*
* However, unlike @ref setup, this method can change LED colors, if so
* desired. Either to provide an initial state, or a static color set. In the
* latter case, consider implementing @ref refreshAt too, because other
* plugins may override some of the colors set at activation time, and @ref
* refreshAt can be used to restore them when needed.
*
* Before the callback runs, LEDs will be blanked.
*/
virtual void onActivate(void) {}
/** Update the LEDs once per cycle.
*
* Usually the brains of the plugin, which updates the LEDs each cycle. It is
* called after the matrix has been scanned, once per cycle.
*/
virtual void update(void) {}
/** Refresh the color of a given key.
*
* If we have another plugin that overrides colors set by the active LED mode
* (either at @onActivate time, or via @ref update), if that plugin wants to
* restore whatever color the mode would set the key color to, this is the
* method it will call.
*
* @param row is the row coordinate of the key to refresh the color of.
* @param col is the column coordinate of the key to refresh the color of.
*/
virtual void refreshAt(byte row, byte col) {}
public:
/** Activate the current object as the LED mode.
*/
void activate(void);
/** Plugin initialization.
*
* Called via `Kaleidoscope.use()`, registers the LED mode, and does the
* necessary initialization steps. Calls @ref setup at the end.
*/
kaleidoscope::EventHandlerResult onSetup();
};
class LEDMode;
class LEDControl : public kaleidoscope::Plugin {
public:
@ -112,19 +44,26 @@ class LEDControl : public kaleidoscope::Plugin {
if (!Kaleidoscope.has_leds)
return;
if (modes[mode])
modes[mode]->update();
cur_led_mode_->update();
}
static void refreshAt(byte row, byte col) {
if (!Kaleidoscope.has_leds)
return;
if (modes[mode])
modes[mode]->refreshAt(row, col);
cur_led_mode_->refreshAt(row, col);
}
static void set_mode(uint8_t mode_id);
static uint8_t get_mode_index() {
return mode_id;
}
static LEDMode *get_mode() {
return cur_led_mode_;
}
template<typename LEDMode__>
static LEDMode__ *get_mode() {
return static_cast<LEDMode__*>(cur_led_mode_);
}
static void set_mode(uint8_t mode);
static uint8_t get_mode_index();
static LEDMode *get_mode();
static void refreshAll() {
if (!Kaleidoscope.has_leds)
return;
@ -133,11 +72,14 @@ class LEDControl : public kaleidoscope::Plugin {
return;
set_all_leds_to({0, 0, 0});
if (modes[mode])
modes[mode]->onActivate();
cur_led_mode_->onActivate();
}
static int8_t mode_add(LEDMode *mode);
DEPRECATED(LED_CONTROL_MODE_ADD)
static int8_t mode_add(LEDMode *mode) {
return 0;
}
static void setCrgbAt(int8_t i, cRGB crgb);
static void setCrgbAt(byte row, byte col, cRGB color);
@ -147,7 +89,11 @@ class LEDControl : public kaleidoscope::Plugin {
static void set_all_leds_to(uint8_t r, uint8_t g, uint8_t b);
static void set_all_leds_to(cRGB color);
static void activate(LEDMode *mode);
// We restict activate to LEDModeInterface to make sure that
// a compiler error is thrown when activate() is accidentally
// applied to a non-LED mode plugin.
//
static void activate(LEDModeInterface *plugin);
static uint16_t syncDelay;
static bool paused;
@ -158,8 +104,9 @@ class LEDControl : public kaleidoscope::Plugin {
private:
static uint16_t syncTimer;
static LEDMode *modes[LED_MAX_MODES];
static uint8_t mode;
static uint8_t mode_id;
static uint8_t num_led_modes_;
static LEDMode *cur_led_mode_;
};
class FocusLEDCommand : public Plugin {

@ -20,6 +20,10 @@
namespace kaleidoscope {
namespace plugin {
// This is still an old style persistent LEDMode as it does not have
// any members and thus there would not be any gain from making it dynamic.
//
class LEDOff : public LEDMode {
public:
LEDOff(void) { }

@ -20,7 +20,7 @@
namespace kaleidoscope {
namespace plugin {
void LEDBreatheEffect::update(void) {
void LEDBreatheEffect::TransientLEDMode::update(void) {
if (!Kaleidoscope.has_leds)
return;
@ -29,7 +29,7 @@ void LEDBreatheEffect::update(void) {
return;
last_update_ = now;
cRGB color = breath_compute(hue, saturation);
cRGB color = breath_compute(parent_->hue, parent_->saturation);
::LEDControl.set_all_leds_to(color);
}
}

@ -20,18 +20,34 @@
namespace kaleidoscope {
namespace plugin {
class LEDBreatheEffect : public LEDMode {
class LEDBreatheEffect : public Plugin,
public LEDModeInterface {
public:
LEDBreatheEffect(void) {}
uint8_t hue = 170;
uint8_t saturation = 255;
protected:
void update(void) final;
// This class' instance has dynamic lifetime
//
class TransientLEDMode : public LEDMode {
public:
private:
uint16_t last_update_ = 0;
// Please note that storing the parent ptr is only required
// for those LED modes that require access to
// members of their parent class. Most LED modes can do without.
//
TransientLEDMode(const LEDBreatheEffect *parent)
: parent_(parent) {}
protected:
virtual void update(void) final;
private:
const LEDBreatheEffect *parent_;
uint16_t last_update_ = 0;
};
};
}
}

@ -19,19 +19,19 @@
namespace kaleidoscope {
namespace plugin {
void LEDChaseEffect::update(void) {
void LEDChaseEffect::TransientLEDMode::update(void) {
if (!Kaleidoscope.has_leds)
return;
uint16_t now = Kaleidoscope.millisAtCycleStart();
if ((now - last_update_) < update_delay_) {
if ((now - last_update_) < parent_->update_delay_) {
return;
}
last_update_ = now;
// The red LED is at `pos_`; the blue one follows behind. `direction_` is
// either +1 or -1; `distance_` is the gap between them.
int8_t pos2 = pos_ - (direction_ * distance_);
int8_t pos2 = pos_ - (direction_ * parent_->distance_);
// First, we turn off the LEDs that were turned on in the previous update.
// `pos_` is always in the valid range (0 <= pos_ < LED_COUNT), but after it

@ -20,7 +20,8 @@
namespace kaleidoscope {
namespace plugin {
class LEDChaseEffect : public LEDMode {
class LEDChaseEffect : public Plugin,
public LEDModeInterface {
public:
LEDChaseEffect(void) {}
@ -37,15 +38,34 @@ class LEDChaseEffect : public LEDMode {
distance_ = value;
}
protected:
void update(void) final;
// This class' instance has dynamic lifetime
//
class TransientLEDMode : public LEDMode {
public:
// Please note that storing the parent ptr is only required
// for those LED modes that require access to
// members of their parent class. Most LED modes can do without.
//
TransientLEDMode(const LEDChaseEffect *parent)
: parent_(parent) {}
protected:
virtual void update() final;
private:
const LEDChaseEffect *parent_;
int8_t pos_ = 0;
int8_t direction_ = 1;
uint16_t last_update_ = 0;
};
private:
int8_t pos_ = 0;
int8_t direction_ = 1;
uint8_t distance_ = 5;
uint16_t update_delay_ = 150;
uint16_t last_update_ = 0;
};
}
}

@ -19,18 +19,18 @@
namespace kaleidoscope {
namespace plugin {
void LEDRainbowEffect::update(void) {
void LEDRainbowEffect::TransientLEDMode::update(void) {
if (!Kaleidoscope.has_leds)
return;
uint16_t now = millis();
if ((now - rainbow_last_update) < rainbow_update_delay) {
if ((now - rainbow_last_update) < parent_->rainbow_update_delay) {
return;
} else {
rainbow_last_update = now;
}
cRGB rainbow = hsvToRgb(rainbow_hue, rainbow_saturation, rainbow_value);
cRGB rainbow = hsvToRgb(rainbow_hue, rainbow_saturation, parent_->rainbow_value);
rainbow_hue += rainbow_steps;
if (rainbow_hue >= 255) {
@ -50,12 +50,12 @@ void LEDRainbowEffect::update_delay(byte delay) {
// ---------
void LEDRainbowWaveEffect::update(void) {
void LEDRainbowWaveEffect::TransientLEDMode::update(void) {
if (!Kaleidoscope.has_leds)
return;
uint16_t now = millis();
if ((now - rainbow_last_update) < rainbow_update_delay) {
if ((now - rainbow_last_update) < parent_->rainbow_update_delay) {
return;
} else {
rainbow_last_update = now;
@ -66,7 +66,7 @@ void LEDRainbowWaveEffect::update(void) {
if (key_hue >= 255) {
key_hue -= 255;
}
cRGB rainbow = hsvToRgb(key_hue, rainbow_saturation, rainbow_value);
cRGB rainbow = hsvToRgb(key_hue, rainbow_saturation, parent_->rainbow_value);
::LEDControl.setCrgbAt(i, rainbow);
}
rainbow_hue += rainbow_wave_steps;

@ -20,7 +20,8 @@
namespace kaleidoscope {
namespace plugin {
class LEDRainbowEffect : public LEDMode {
class LEDRainbowEffect : public Plugin,
public LEDModeInterface {
public:
LEDRainbowEffect(void) {}
@ -32,21 +33,40 @@ class LEDRainbowEffect : public LEDMode {
byte update_delay(void) {
return rainbow_update_delay;
}
void update(void) final;
private:
uint16_t rainbow_hue = 0; // stores 0 to 614
// This class' instance has dynamic lifetime
//
class TransientLEDMode : public LEDMode {
public:
uint8_t rainbow_steps = 1; // number of hues we skip in a 360 range per update
uint16_t rainbow_last_update = 0;
uint16_t rainbow_update_delay = 40; // delay between updates (ms)
// Please note that storing the parent ptr is only required
// for those LED modes that require access to
// members of their parent class. Most LED modes can do without.
//
TransientLEDMode(const LEDRainbowEffect *parent)
: parent_(parent) {}
virtual void update() final;
private:
const LEDRainbowEffect *parent_;
byte rainbow_saturation = 255;
uint16_t rainbow_hue = 0; // stores 0 to 614
uint8_t rainbow_steps = 1; // number of hues we skip in a 360 range per update
uint16_t rainbow_last_update = 0;
byte rainbow_saturation = 255;
};
private:
uint16_t rainbow_update_delay = 40; // delay between updates (ms)
byte rainbow_value = 50;
};
class LEDRainbowWaveEffect : public LEDMode {
class LEDRainbowWaveEffect : public Plugin, public LEDModeInterface {
public:
LEDRainbowWaveEffect(void) {}
@ -58,16 +78,34 @@ class LEDRainbowWaveEffect : public LEDMode {
byte update_delay(void) {
return rainbow_update_delay;
}
void update(void) final;
private:
uint16_t rainbow_hue = 0; // stores 0 to 614
// This class' instance has dynamic lifetime
//
class TransientLEDMode : public LEDMode {
public:
uint8_t rainbow_wave_steps = 1; // number of hues we skip in a 360 range per update
uint16_t rainbow_last_update = 0;
uint16_t rainbow_update_delay = 40; // delay between updates (ms)
// Please note that storing the parent ptr is only required
// for those LED modes that require access to
// members of their parent class. Most LED modes can do without.
//
TransientLEDMode(const LEDRainbowWaveEffect *parent)
: parent_(parent) {}
virtual void update() final;
private:
const LEDRainbowWaveEffect *parent_;
byte rainbow_saturation = 255;
uint16_t rainbow_hue = 0; // stores 0 to 614
uint8_t rainbow_wave_steps = 1; // number of hues we skip in a 360 range per update
uint16_t rainbow_last_update = 0;
byte rainbow_saturation = 255;
};
uint16_t rainbow_update_delay = 40; // delay between updates (ms)
byte rainbow_value = 50;
};
}

@ -18,18 +18,18 @@
namespace kaleidoscope {
namespace plugin {
LEDSolidColor::LEDSolidColor(uint8_t r, uint8_t g, uint8_t b) {
this->r = r;
this->g = g;
this->b = b;
}
void LEDSolidColor::onActivate(void) {
::LEDControl.set_all_leds_to(r, g, b);
void LEDSolidColor::TransientLEDMode::onActivate(void) {
::LEDControl.set_all_leds_to(parent_->r_,
parent_->g_,
parent_->b_);
}
void LEDSolidColor::refreshAt(byte row, byte col) {
::LEDControl.setCrgbAt(row, col, CRGB(r, g, b));
void LEDSolidColor::TransientLEDMode::refreshAt(byte row, byte col) {
::LEDControl.setCrgbAt(row, col,
CRGB(parent_->r_,
parent_->g_,
parent_->b_));
}
}

@ -20,16 +20,38 @@
namespace kaleidoscope {
namespace plugin {
class LEDSolidColor : public LEDMode {
class LEDSolidColor : public Plugin,
public LEDModeInterface {
public:
LEDSolidColor(uint8_t r, uint8_t g, uint8_t b);
protected:
void onActivate(void) final;
void refreshAt(byte row, byte col) final;
LEDSolidColor(uint8_t r, uint8_t g, uint8_t b)
: r_(r), g_(g), b_(b)
{}
// This class' instance has dynamic lifetime
//
class TransientLEDMode : public LEDMode {
public:
// Please note that storing the parent ptr is only required
// for those LED modes that require access to
// members of their parent class. Most LED modes can do without.
//
TransientLEDMode(const LEDSolidColor *parent)
: parent_(parent) {}
protected:
virtual void onActivate(void) final;
virtual void refreshAt(byte row, byte col) final;
private:
const LEDSolidColor *parent_;
};
private:
uint8_t r, g, b;
uint8_t r_, g_, b_;
};
}
}

@ -0,0 +1,30 @@
/* Kaleidoscope-LEDControl - LED control plugin for Kaleidoscope
* Copyright (C) 2017-2018 Keyboard.io, Inc.
*
* 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, version 3.
*
* 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 <kaleidoscope/plugin/LEDMode.h>
#include <kaleidoscope/plugin/LEDControl.h>
#include <kaleidoscope/event_handler_result.h>
namespace kaleidoscope {
namespace plugin {
void LEDModeInterface::activate() {
LEDControl::activate(this);
}
} // end namespace plugin
} // end namespace kaleidoscope

@ -0,0 +1,144 @@
/* Kaleidoscope-LEDControl - LED control plugin for Kaleidoscope
* Copyright (C) 2017-2018 Keyboard.io, Inc.
*
* 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, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "kaleidoscope/plugin.h"
namespace kaleidoscope {
namespace internal {
// Forward declaration
class LEDModeManager;
} // end namespace internal
namespace plugin {
class LEDModeInterface {
public:
void activate();
// This auxiliary class helps to generate a verbose error message
// in case that there is no TransientLEDMode typedef or nested
// class present in a derived class of LEDModeInterface.
//
struct _____LEDModeInterface_derived_class_is_missing_a_TransientLEDMode_typedef_or_nested_class_____ {};
typedef _____LEDModeInterface_derived_class_is_missing_a_TransientLEDMode_typedef_or_nested_class_____ NoLEDMode;
// By redefining TransientLEDMode, derived plugins export
// a LED mode that becomes part of the set of LED modes whose
// lifetime is handled dynamically.
//
typedef NoLEDMode DynamicLEDMode;
};
class AccessTransientLEDMode {
public:
// This method is called when a plugin's LED mode is activated.
// Derived plugins may reimplement it to store the id of their
// exported LED mode. A plugin can thus check
// whether their LED mode is currently active.
//
void registerLEDModeActivated(uint8_t led_mode_id) {
led_mode_id_ = led_mode_id;
}
protected:
uint8_t led_mode_id_ = 255; /* 255 means uninitialized */
};
/** Base class for LED modes.
*
* LED modes are a special kind of plugin, they are in charge of updating LED
* colors, setting a theme. While it is possible to have other plugins
* override the mode's colors, the LED mode is the baseline.
*
* Most of its functionality is called via @ref LEDControl, with only a few
* public methods.
*
* A LED mode **must** implement at least one of @ref onActivate or @ref
* update, and possibly @ref refreshAt too.
*/
class LEDMode : public kaleidoscope::Plugin,
public LEDModeInterface {
friend class LEDControl;
friend class kaleidoscope::internal::LEDModeManager;
protected:
// These methods should only be called by LEDControl.
/** One-time setup, called at keyboard boot.
*
* Any hooks that need registering, any one-time setup that needs to be
* performed, shall be done here. This is purely for preparation purposes, the
* LEDs should not be touched yet at this time.
*/
virtual void setup(void) {}
/** Function to call whenever the mode is activated.
*
* Like @ref setup, this method need not touch LEDs, @ref update will be
* called right after it. The purpose of this callback is to allow a plugin to
* do some preparation whenever it is activated, instead of only on boot, or
* always at each cycle.
*
* However, unlike @ref setup, this method can change LED colors, if so
* desired. Either to provide an initial state, or a static color set. In the
* latter case, consider implementing @ref refreshAt too, because other
* plugins may override some of the colors set at activation time, and @ref
* refreshAt can be used to restore them when needed.
*
* Before the callback runs, LEDs will be blanked.
*/
virtual void onActivate(void) {}
/** Update the LEDs once per cycle.
*
* Usually the brains of the plugin, which updates the LEDs each cycle. It is
* called after the matrix has been scanned, once per cycle.
*/
virtual void update(void) {}
/** Refresh the color of a given key.
*
* If we have another plugin that overrides colors set by the active LED mode
* (either at @onActivate time, or via @ref update), if that plugin wants to
* restore whatever color the mode would set the key color to, this is the
* method it will call.
*
* @param row is the row coordinate of the key to refresh the color of.
* @param col is the column coordinate of the key to refresh the color of.
*/
virtual void refreshAt(byte row, byte col) {}
public:
/** Plugin initialization.
*
* Called via `Kaleidoscope.use()`, registers the LED mode, and does the
* necessary initialization steps. Calls @ref setup at the end.
*/
kaleidoscope::EventHandlerResult onSetup() {
setup();
return EventHandlerResult::OK;
}
};
} // end namespace plugin
} // end namespace kaleidoscope

@ -19,6 +19,7 @@
#include "Kaleidoscope.h"
#include "Kaleidoscope-Model01-TestMode.h"
#include "Kaleidoscope-LEDEffect-Rainbow.h"
#include "kaleidoscope_internal/LEDModeManager.h"
namespace kaleidoscope {
namespace plugin {
@ -73,9 +74,17 @@ void TestMode::test_leds(void) {
set_leds(blue);
// make all the LEDs bright white (1.6A)
set_leds(brightWhite);
// This works under the assumption that LEDRainbowEffect
// has been registered with KALEIDOSCOPE_INIT_PLUGINS in
// the sketch. Otherwise LEDRainbowEffect would not be
// known to LEDControl.
//
::LEDControl.activate(&::LEDRainbowEffect);
// rainbow for 10 seconds
for (auto i = 0; i < 1000; i++) {
::LEDRainbowEffect.update();
::LEDControl.update();
::LEDControl.syncLeds();
}
waitForKeypress();

@ -26,18 +26,18 @@ TriColor::TriColor(cRGB base_color, cRGB mod_color, cRGB esc_color) {
esc_color_ = esc_color;
}
void TriColor::update(void) {
void TriColor::TransientLEDMode::update(void) {
for (uint8_t r = 0; r < ROWS; r++) {
for (uint8_t c = 0; c < COLS; c++) {
Key k = Layer.lookup(r, c);
// Special keys are always mod_color
if (k.flags != 0) {
::LEDControl.setCrgbAt(r, c, mod_color_);
::LEDControl.setCrgbAt(r, c, parent_->mod_color_);
continue;
}
cRGB color = mod_color_;
cRGB color = parent_->mod_color_;
switch (k.keyCode) {
case Key_A.keyCode ... Key_0.keyCode:
@ -46,10 +46,10 @@ void TriColor::update(void) {
case Key_Keypad1.keyCode ... Key_KeypadDot.keyCode:
case Key_F1.keyCode ... Key_F4.keyCode:
case Key_F9.keyCode ... Key_F12.keyCode:
color = base_color_;
color = parent_->base_color_;
break;
case Key_Escape.keyCode:
color = esc_color_;
color = parent_->esc_color_;
break;
}

@ -22,13 +22,32 @@
namespace kaleidoscope {
namespace plugin {
class TriColor : public LEDMode {
class TriColor : public Plugin,
public LEDModeInterface {
public:
TriColor(cRGB base_color, cRGB mod_color, cRGB esc_color);
TriColor(cRGB base_color, cRGB mod_color) : TriColor(base_color, mod_color, mod_color) {}
protected:
void update(void) final;
// This class' instance has dynamic lifetime
//
class TransientLEDMode : public LEDMode {
public:
// Please note that storing the parent ptr is only required
// for those LED modes that require access to
// members of their parent class. Most LED modes can do without.
//
TransientLEDMode(const TriColor *parent)
: parent_(parent) {}
protected:
virtual void update(void) final;
private:
const TriColor *parent_;
};
private:
cRGB base_color_;

@ -0,0 +1,112 @@
/* Kaleidoscope - Firmware for computer input devices
* Copyright (C) 2013-2018 Keyboard.io, Inc.
*
* 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, version 3.
*
* 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 "kaleidoscope_internal/LEDModeManager.h"
#include "kaleidoscope/plugin/LEDMode.h"
namespace kaleidoscope {
namespace internal {
namespace {
// We want to export as little symbols as possible. That's why the
// internal state of the LED mode management lives in
// an anonymous namespace.
uint8_t cur_mode_id = 255; // We use 255 as a flag value that signals
// uninitialized. That's why we can only have
// LED mode ids in the range [0..254].
// A pointer to cache the current transient LED mode.
//
kaleidoscope::plugin::LEDMode *cur_led_mode = nullptr;
bool current_led_mode_dynamic = false;
}
kaleidoscope::plugin::LEDMode *LEDModeManager::getLEDMode(uint8_t mode_id) {
// If we end up here, the requested LED mode is a dynamic one.
// Check if the requested LED mode is already active
//
if (cur_mode_id == mode_id) {
return cur_led_mode;
}
// If there is already an active LED mode, its obviously the wrong one
// (see test above). To generate a new transient LED mode, we need
// to destroy the current one. To achieve this, we call its
// (possibly - see explanation below) virtual destuctor and let
// it take care of the cleanup.
//
// Please note that due to the fact that transient LED modes are
// allocated using placement new within a pre-existing static buffer,
// there is no need to free any memory after the destructor of the
// current LED mode was called. We can just reuse the buffer
// for the next LED mode instance.
//
// Please note: Currently, LEDMode has no virtual destructor.
// That's why the explicit destructor call below is a noop
// that is optimized out by the compiler. It is there
// to enable the possible future introduction of a virtual
// destructor for class LEDMode.
//
// A virtual destructor would enable LEDModes to do any types of
// cleanup on destruction. But as it's a virtual method,
// introducing such a destructor would create additional
// entries in vtables and mean higher PROGMEM consumption.
// If it will be introduced in future versions of Kaleidoscope
// uncomment the commented lines below.
//
if (current_led_mode_dynamic) {
cur_led_mode->~LEDMode();
}
// Store the current mode id to enable cache access (see above).
//
cur_mode_id = mode_id;
// Get a factory struct for the creation of the new LED mode.
//
led_mode_management::LEDModeFactory fac;
retreiveLEDModeFactoryFromPROGMEM(mode_id, fac);
// The factories for persistent LED modes serve to pass through the LED
// mode plugin's pointer in parent_plugin_.
//
if (fac.isPersistentLEDMode()) {
current_led_mode_dynamic = false;
cur_led_mode = fac.getPersistentLEDMode();
} else {
current_led_mode_dynamic = true;
// Generate a new led mode by calling the factory function
// (fac.generate_led_mode_).
//
// We store a pointer to the newly created LED mode to enable
// cached access (see check above).
//
cur_led_mode = fac.generateTransientLEDMode(led_mode_buffer_, mode_id);
}
return cur_led_mode;
}
} // end namespace internal
} // end namespace kaleidoscope

@ -0,0 +1,474 @@
/* Kaleidoscope - Firmware for computer input devices
* Copyright (C) 2013-2018 Keyboard.io, Inc.
*
* 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, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "kaleidoscope_internal/array_like_storage.h"
#include "kaleidoscope/plugin.h"
#include "kaleidoscope/plugin/LEDMode.h"
#include "kaleidoscope_internal/type_traits/has_method.h"
#include <stddef.h>
// To enable placement new, we need to supply a global operator
// function.
//
inline void* operator new (size_t, void* __p) throw() {
return __p;
}
namespace kaleidoscope {
namespace plugin {
// Forward declarations to avoid header inclusions
class LEDControl;
class LEDModeInterface;
} // end namespace plugin
namespace internal {
namespace led_mode_management {
template<bool T> struct Bool2Type {};
// Functions of this type allocate a new LED mode within a global
// buffer and give it access to it's parent plugin.
// This allows a transient LED mode to access member data of its parent
// plugin, which can help to reduce its RAM footprint.
// Some transient LED modes might ignore the parent pointer entirely, e.g. if
// they do not have a configurable internal state.
//
// We use a factory function instead of a polymorphic
// class to generate LED modes. By this means we can avoid the additional
// cost of vtables which safes some program memory.
// Conceptually, the factory function approach is similar to having
// a polymorphic class with only one method, that is accessed directly
// instead of the detour via the vtable.
//
typedef kaleidoscope::plugin::LEDMode*
(*LEDModeFactoryFunc)(void *raw_buffer,
kaleidoscope::plugin::LEDModeInterface *parent_plugin,
uint8_t mode_id
);
template<typename ParentPlugin__>
inline void registerLEDModeActivated(Bool2Type<true>,
ParentPlugin__ *parent_plugin,
uint8_t mode_id) {
parent_plugin->registerLEDModeActivated(mode_id);
}
template<typename ParentPlugin__>
inline void registerLEDModeActivated(Bool2Type<false>,
ParentPlugin__ *parent_plugin,
uint8_t mode_id) {
// Noop
}
DEFINE_HAS_METHOD_TRAITS(Plugin, registerLEDModeActivated, void, (uint8_t led_mode_id))
// A templated implementation of LEDModeFactoryFunc.
//
// Please note that the actual type of ParentPlugin__ is only
// known to the context where the function is invoked or in our case
// the function pointer to generateLEDMode is generated (at compile time).
//
// The caller can be completely agnostic of ParentPlugin__ an just pass
// a pointer to kaleidoscope::Plugin for argument parent_plugin.
//
// The function generateLEDMode knows the actual type of parent_plugin.
// Or, more precise, we can rely on the fact that we store a pointer to a plugin
// of appropriate type together with the generateLEDMode-pointer in
// an LEDModeFactory instance in PROGMEM. Thus, generateLEDMode will only
// be called with a parent_plugin of appropriate type.
//
template<typename ParentPlugin__>
static kaleidoscope::plugin::LEDMode *
generateLEDMode(void *raw_buffer,
kaleidoscope::plugin::LEDModeInterface *parent_plugin,
uint8_t mode_id
) {
// We know the type of the parent plugin via the template parameter
// ParentPlugin__, thus it is safe to cast to the actual type.
//
auto parent_plugin_actual = static_cast<ParentPlugin__*>(parent_plugin);
// Types defined by template parameters like ParentPlugin__ must
// be declared explicitly using the "typename" keyword.
// Because of this lengthy syntax, we use a typedef to
// shorten the constructor call below.
//
typedef typename ParentPlugin__::TransientLEDMode TLM;
// Generate a transient LED mode within the LED mode buffer.
//
auto led_mode_ptr
= new (raw_buffer) TLM{parent_plugin_actual};
constexpr bool accesses_transient_led_mode
= Plugin_HasMethod_registerLEDModeActivated<ParentPlugin__>::value;
// Register the newly created LED mode with its parent plugin.
// Please note that this call is optimized away by the compiler
// for all those plugins that do not reimplement registerLEDModeActivated.
//
registerLEDModeActivated(Bool2Type<accesses_transient_led_mode>(),
parent_plugin_actual, mode_id);
return led_mode_ptr;
}
// A data structure that contains all the information that is required
// to generate a transient LED mode. Instances of this class are stored
// in an array-like data structure in PROGMEM.
//
struct LEDModeFactory {
// Don't try to make this a class with a constructor. This
// will turn it into a non POD which cannot be used as
// return type of a constexpr.
bool isPersistentLEDMode() const {
return !generate_led_mode_;
}
kaleidoscope::plugin::LEDMode *getPersistentLEDMode() const {
return static_cast<kaleidoscope::plugin::LEDMode*>(parent_plugin_);
}
kaleidoscope::plugin::LEDMode *generateTransientLEDMode(
uint8_t *buffer, uint8_t mode_id) const {
return (*generate_led_mode_)(buffer, parent_plugin_, mode_id);
}
bool isAssociatedWithPlugin(kaleidoscope::plugin::LEDModeInterface *plugin) const {
return parent_plugin_ == plugin;
}
kaleidoscope::plugin::LEDModeInterface *parent_plugin_;
LEDModeFactoryFunc generate_led_mode_;
};
// The traits class remove_pointer is part of the C++ standard library
// but not present on Arduino.
//
template< class T > struct remove_pointer {
typedef T type;
};
template< class T > struct remove_pointer<T*> {
typedef T type;
};
enum PluginType {
PluginType_NoLEDMode,
PluginType_PersistentLEDMode,
PluginType_TransientLEDMode
};
// The following three functions enable determining the PluginType
// at compile time by examining a constexpr pointer to one of the
// global plugin instances (pointers to global variables/objects are
// constexpr).
//
static constexpr int ledModePluginType(kaleidoscope::plugin::LEDMode *) {
return PluginType_PersistentLEDMode;
}
static constexpr int ledModePluginType(kaleidoscope::plugin::LEDModeInterface *) {
return PluginType_TransientLEDMode;
}
// Invoked for all plugins that inherit neither from LEDMode
// nor from LEDModeInterface.
//
static constexpr bool ledModePluginType(void *) {
return PluginType_NoLEDMode;
}
// The following traits classes are used to distinguish between three cases
//
// 1) A plugin is unrelated to LEDModes (i.e. non of the other two applies).
//
// 2) A plugin is an old syle persistent LEDMode (plugin is derived from
// LED mode = persistent LED mode)
//
// 3) A plugin exports a LED mode with dynamic lifetime (= transient LED mode)
// Checks if a plugin is related to LED modes in any kind of ways.
// This traits check if for a plugin
// an entry in the plugin factories array needs to be reserved.
//
constexpr bool pluginControlsLEDMode(int led_mode_plugin_type) {
return led_mode_plugin_type != PluginType_NoLEDMode;
}
// The template GenerateLEDModeFactory generates LED mode factories
// in three different ways, depending on the type of a plugin (PluginType)
// Generates a dummy factory (discarded at compile time) for
// those plugins that are unrelated to LED modes. This template
// is selected for LEDModePluginType__ == NoLEDMode.
//
template<int LEDModePluginType__>
struct GenerateLEDModeFactory {
static constexpr LEDModeFactory apply(kaleidoscope::Plugin */* non LED mode plugin*/) {
return LEDModeFactory{nullptr, nullptr};
}
};
template<>
struct GenerateLEDModeFactory<PluginType_TransientLEDMode> {
// This version of apply must be templated as we must know the actual
// type of plugin as the instanciatioin of template function
// generateLEDMode depends upon it.
//
template<typename Plugin__>
static constexpr LEDModeFactory apply(Plugin__ *plugin) {
return LEDModeFactory{
plugin,
generateLEDMode<Plugin__> // pointer to template instantiation of
// generateLEDMode<...>
};
}
};
template<>
struct GenerateLEDModeFactory<PluginType_PersistentLEDMode> {
static constexpr LEDModeFactory apply(kaleidoscope::plugin::LEDModeInterface *plugin) {
// For persistent LED modes, we use the LED factory to simply store
// the plugin pointer (a persistent LED mode is itself the LEDMode).
// Thus, no factory function is required.
//
return LEDModeFactory{plugin, nullptr};
}
};
// The following template TransientLEDModeSize is used to determine
// the size of a transient LED mode. It is only active for dynamic
// LED modes. For all other plugins, it simply returns zero.
//
template<typename PluginPtr__, int LEDModePluginType__>
struct TransientLEDModeSize {
static constexpr size_t value = 0;
};
template<typename PluginPtr__>
struct TransientLEDModeSize<PluginPtr__, PluginType_TransientLEDMode> {
typedef typename remove_pointer<PluginPtr__>::type Plugin__;
static constexpr size_t value = sizeof(typename Plugin__::TransientLEDMode);
};
// This helper class serves to determine the maximum memory footprint
// of any transient LED mode in the sketch.
// It implements a template type recursion
// that examins all specified LED modes. For this calculation
// it is no problem that also the type NoLEDMode is considered
// in this check as it is an empty class (size == 1 byte) and
// thus does not affect the maximum size computation.
//
template<typename PluginPtr__, typename...MorePluginPtrs__>
struct TransientLEDModeMaxSize {
static constexpr size_t this_size
= TransientLEDModeSize<PluginPtr__, ledModePluginType(PluginPtr__())>::value;
static constexpr size_t nested_size = TransientLEDModeMaxSize<MorePluginPtrs__...>::value;
static constexpr size_t value = (this_size > nested_size) ? this_size : nested_size;
};
// Specialization to end recursion
//
template<typename PluginPtr__>
struct TransientLEDModeMaxSize<PluginPtr__> {
static constexpr size_t value = TransientLEDModeSize<PluginPtr__, ledModePluginType(PluginPtr__())>::value;
};
} // end namespace led_mode_management
class LEDModeManager {
public:
// Everything in this class private on purpose.
//
// Only the class LEDControl is supposed to gain
// access to any inventory of this class to ensure that
// the handling of transient LED modes is safely handled
// by a well defined central instance.
//
friend class kaleidoscope::plugin::LEDControl;
static uint8_t numLEDModes();
#if 0
// This method could be used in rare cases
// where a stack allocation of a LED mode may not be avoided.
//
template<typename ParentPlugin__>
static auto
generateLEDModeTemporary(void *raw_buffer,
ParentPlugin__ *parent_plugin
) -> typename ParentPlugin__::TransientLEDMode * {
auto led_mode = static_cast<typename ParentPlugin__::TransientLEDMode *>(
led_mode_management::generateLEDMode<ParentPlugin__>(
raw_buffer,
parent_plugin,
0 /*dummy*/
)
);
parent_plugin->registerLEDModeActivated(255 /* dymmy to disable access
to transient LED modes through LEDControl */
);
return led_mode;
}
#endif
private:
// For the sake of convenience make type LEDModeFactory
// available in class namespace
//
typedef led_mode_management::LEDModeFactory LEDModeFactory;
static kaleidoscope::plugin::LEDMode *getLEDMode(uint8_t mode_id);
static void retreiveLEDModeFactoryFromPROGMEM(uint8_t mode_id,
LEDModeFactory &fac);
static void setupPersistentLEDModes();
// Persistent LED mode plugins are derived from kaleidoscope::plugin::LEDMode.
// The standard dictates that for them this more specialized overload
// of setupLEDMode is supposed to be called instead of the more
// general one below.
//
static void setupLEDMode(kaleidoscope::plugin::LEDMode *persistent_led_mode) {
persistent_led_mode->setup();
}
static void setupLEDMode(kaleidoscope::Plugin */*not_a_persistent_led_mode*/) {}
static uint8_t led_mode_buffer_[];
};
} // end namespace internal
} // end namespace kaleidoscope
// Some auxiliary macros that are mapped to the list of
// plugins defined via KALEIDOSCOPE_INIT_PLUGINS follow.
// Evaluates to a boolean value that signals whether
// a plugin is related to LED modes and thus needs to be considered
// during setup of the LED mode factory array.
//
#define _LED_MODE_MANAGEMENT__PLUGIN_CONTROLS_LED_MODE(PLUGIN) \
pluginControlsLEDMode(ledModePluginType(&::PLUGIN))
// Generates a LEDModeFactory for each plugin. For all those
// plugins that do not export transient LED modes, a nullptr-initialized
// factory is created. This does not mean a performance penalty, as
// those empty factories are then directly discarded, still at compile time.
//
#define _LED_MODE_MANAGEMENT__GENERATE_LED_MODE_FACTORY(PLUGIN) \
kaleidoscope::internal::led_mode_management __NL__ \
::GenerateLEDModeFactory<ledModePluginType(&::PLUGIN)>::apply(&::PLUGIN)
// Retreive the pointer type of the exported transient LED mode of a plugin.
//
#define _LED_MODE_MANAGEMENT__PLUGIN_PTR_TYPE(PLUGIN) \
decltype(&::PLUGIN)
#define _LED_MODE_MANAGEMENT__SETUP_STATIC_LED_MODE(PLUGIN) \
LEDModeManager::setupLEDMode(&::PLUGIN);
#define _INIT_LED_MODE_MANAGER(...) \
namespace kaleidoscope { __NL__ \
namespace internal { __NL__ \
namespace led_mode_management { __NL__ \
__NL__ \
/* Setup the array-like data structure that stores __NL__ \
* LEDModeFactories */ __NL__ \
typedef kaleidoscope::internal::ArrayLikeStorage< __NL__ \
LEDModeFactory, __NL__ \
/* Map the list of global plugin __NL__ \
* objects to a list of led mode __NL__ \
* factories */ __NL__ \
MAP_LIST( __NL__ \
/* Generate a series of boolean values. True for __NL__ \
* each plugin that is related to LED modes, __NL__ \
* false otherwise. */ __NL__ \
_LED_MODE_MANAGEMENT__PLUGIN_CONTROLS_LED_MODE, __NL__ \
__VA_ARGS__ __NL__ \
) __NL__ \
> ArrayType; __NL__ \
__NL__ \
/* Generate the actual instance of template class __NL__ \
* TypedPluginArray and initialize it with a list of __NL__ \
* LEDModeFactories. __NL__ \
*/ __NL__ \
const PROGMEM ArrayType led_mode_factories( __NL__ \
MAP_LIST( __NL__ \
_LED_MODE_MANAGEMENT__GENERATE_LED_MODE_FACTORY, __NL__ \
__VA_ARGS__ __NL__ \
) __NL__ \
); __NL__ \
} /* end namespace led_mode_management */ __NL__ \
__NL__ \
/* Store the number of LEDModeFactories. __NL__ \
*/ __NL__ \
const PROGMEM uint8_t num_led_modes __NL__ \
= led_mode_management::ArrayType::n_entries; __NL__ \
__NL__ \
uint8_t LEDModeManager::numLEDModes() { __NL__ \
return pgm_read_byte(&num_led_modes); __NL__ \
} __NL__ \
__NL__ \
void LEDModeManager::retreiveLEDModeFactoryFromPROGMEM( __NL__ \
uint8_t mode_id, __NL__ \
LEDModeFactory &fac) { __NL__ \
memcpy_P(&fac, __NL__ \
&reinterpret_cast<const LEDModeFactory*>( __NL__ \
&led_mode_management::led_mode_factories __NL__ \
)[mode_id], __NL__ \
sizeof(LEDModeFactory) __NL__ \
); __NL__ \
} __NL__ \
__NL__ \
static constexpr size_t max_led_mode_size __NL__ \
= led_mode_management::TransientLEDModeMaxSize< __NL__ \
MAP_LIST( __NL__ \
_LED_MODE_MANAGEMENT__PLUGIN_PTR_TYPE, __NL__ \
__VA_ARGS__ __NL__ \
) __NL__ \
>::value; __NL__ \
__NL__ \
/* This buffer is dimensioned in a way that it can hold __NL__ \
* the largest of all transient LED modes defined in the sketch. __NL__ \
*/ __NL__ \
uint8_t LEDModeManager::led_mode_buffer_[max_led_mode_size]; __NL__ \
__NL__ \
void LEDModeManager::setupPersistentLEDModes() { __NL__ \
MAP( __NL__ \
_LED_MODE_MANAGEMENT__SETUP_STATIC_LED_MODE, __NL__ \
__VA_ARGS__ __NL__ \
) __NL__ \
} __NL__ \
} /* end namespace internal */ __NL__ \
} /* end namespace kaleidoscope */

@ -0,0 +1,114 @@
/* Kaleidoscope - Firmware for computer input devices
* Copyright (C) 2013-2018 Keyboard.io, Inc.
*
* 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, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
namespace kaleidoscope {
namespace internal {
// The ArrayLikeStorage class stores data of a specific
// type in a recursive fashion. It is used to generate array like
// data structures at compile time that are initialized from a
// list of entities only a subset of which is supposed to be
// stored in the container.
//
template<typename StoredType__,
bool IsAppropriateType__,
bool...MoreTypeInfo__>
class ArrayLikeStorage {
public:
typedef ArrayLikeStorage<StoredType__, MoreTypeInfo__...> NestedArray;
template<typename...MoreEntities__>
constexpr ArrayLikeStorage(StoredType__ entry, MoreEntities__...more_entities)
: entry_(entry),
nested_array_(more_entities...) {}
static constexpr uint8_t n_entries
= NestedArray::n_entries + 1;
typedef StoredType__ ContentType;
private:
StoredType__ entry_;
NestedArray nested_array_;
} __attribute__((packed)); // Make sure that there are no padding
// bytes added by the compiler.
// This is important to let the class
// have the same layout as a POD array.
template<typename StoredType__,
bool...MoreTypeInfo__>
class ArrayLikeStorage<StoredType__,
false /* Not of appropriate type */,
MoreTypeInfo__...> {
public:
typedef ArrayLikeStorage<StoredType__, MoreTypeInfo__...> NestedArray;
template<typename AnyType__, typename...MoreEntities__>
constexpr ArrayLikeStorage(AnyType__/* non-matching entity */,
MoreEntities__...more_entities)
: nested_array_(more_entities...) {}
static constexpr uint8_t n_entries
= NestedArray::n_entries;
typedef StoredType__ ContentType;
private:
NestedArray nested_array_;
} __attribute__((packed));
template<typename StoredType__>
struct ArrayLikeStorage<StoredType__, true /* is of appropriate type */> {
public:
constexpr ArrayLikeStorage(StoredType__ entry)
: entry_(entry)
{}
static constexpr uint8_t n_entries = 1;
typedef StoredType__ ContentType;
private:
StoredType__ entry_;
} __attribute__((packed));
template<typename StoredType__>
struct ArrayLikeStorage<StoredType__, false /* not of appropriate type */> {
public:
template<typename AnyType__>
constexpr ArrayLikeStorage(AnyType__/* non-matching entity */) {}
static constexpr uint8_t n_entries = 0;
typedef StoredType__ ContentType;
} __attribute__((packed));
} // end namespace internal
} // end namespace kaleidoscope

@ -198,4 +198,8 @@
__NL__ \
_PREPARE_EVENT_HANDLER_SIGNATURE_CHECK __NL__ \
__NL__ \
_FOR_EACH_EVENT_HANDLER(_REGISTER_EVENT_HANDLER)
_FOR_EACH_EVENT_HANDLER(_REGISTER_EVENT_HANDLER) __NL__ \
__NL__ \
/* This generates a PROGMEM array-kind-of data structure that contains */ __NL__ \
/* LEDModeFactory entries */ __NL__ \
_INIT_LED_MODE_MANAGER(__VA_ARGS__)

@ -19,6 +19,7 @@
#include "kaleidoscope/macro_helpers.h"
#include "kaleidoscope/plugin.h"
#include "kaleidoscope_internal/type_traits/has_member.h"
#include "kaleidoscope_internal/type_traits/has_method.h"
// *************************************************************************
// *************************************************************************
@ -92,59 +93,22 @@ template<typename Plugin__> struct
#define _DEFINE_IMPLEMENTATION_CHECK_CLASS_SPECIALIZATION( \
HOOK_NAME, HOOK_VERSION, DEPRECATION_TAG, \
SHOULD_ABORT_ON_CONSUMED_EVENT, SIGNATURE, ARGS_LIST) \
\
/* We use the generalized traits class found in header has_method.h __NL__ \
* to do check if a plugin defines a hook method with a specific __NL__ \
* signature. __NL__ \
*/ __NL__ \
DEFINE_HAS_METHOD_TRAITS(Plugin, HOOK_NAME, __NL__ \
kaleidoscope::EventHandlerResult, __NL__ \
SIGNATURE) __NL__ \
__NL__ \
/* This specialization checks if a plugin of type Plugin__ __NL__ \
* implements a handler with given signature SIGNATURE. __NL__ \
*/ __NL__ \
template<typename Plugin__> __NL__ \
struct HookVersionImplemented_##HOOK_NAME<Plugin__, HOOK_VERSION> __NL__ \
{ __NL__ \
/* Define a pointer to member function with the correct __NL__ \
* argument signature. The newly defined type is named __NL__ \
* HookType__. __NL__ \
*/ __NL__ \
typedef kaleidoscope::EventHandlerResult __NL__ \
(Plugin__::*HookType__)SIGNATURE; __NL__ \
__NL__ \
/* See the definition of HookIimplemented_##HOOK_NAME above __NL__ \
* for an explanation of the SFINAE concept that is applied here. __NL__ \
* The difference to the afforementioned class is that __NL__ \
* here we check if a specific version of a handler was __NL__ \
* implemented. This is done by forcing the compiler __NL__ \
* through a static cast to select the respective method __NL__ \
* if possible. If the method signature cannot be found, __NL__ \
* the substitution fails and the first version of method "test" __NL__ \
* will not be defined. __NL__ \
*/ __NL__ \
template <typename PluginAux__> __NL__ \
static constexpr __NL__ \
/* The following decltype-clause defines the function return type __NL__ \
*/ __NL__ \
decltype( __NL__ \
/* If &PluginAux__::HOOK_NAME exists and is of type __NL__ \
* HookType__, the list below evaluates to bool{} whose __NL__ \
* type can be determined. Otherwise the comma expression __NL__ \
* cannot be evaluated and the content __NL__ \
* of decltype is undefined and this function overload __NL__ \
* is ignored by the compiler __NL__ \
* (SFINAE = substitution failure is not an error) __NL__ \
* and the test(...) overload is used instead. __NL__ \
*/ __NL__ \
static_cast<HookType__>(&PluginAux__::HOOK_NAME), bool{} __NL__ \
) __NL__ \
test(int /* unused */) __NL__ \
{ __NL__ \
return true; __NL__ \
} __NL__ \
__NL__ \
template <typename PluginAux__> __NL__ \
static constexpr bool test(...) __NL__ \
{ __NL__ \
return false; __NL__ \
} __NL__ \
__NL__ \
static constexpr bool value = test<Plugin__>(int{}); __NL__ \
};
: public Plugin_HasMethod_##HOOK_NAME<Plugin__> __NL__ \
{};
#define _PREPARE_EVENT_HANDLER_SIGNATURE_CHECK_START(HOOK_NAME, ...) \
__NL__ \

@ -0,0 +1,73 @@
/* Kaleidoscope - Firmware for computer input devices
* Copyright (C) 2013-2018 Keyboard.io, Inc.
*
* 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, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "kaleidoscope/macro_helpers.h"
#define DEFINE_HAS_METHOD_TRAITS(PREFIX, METHOD_NAME, \
RETURN_TYPE, ARGUMENTS) \
__NL__ \
/* This traits checks if a class of type Class__ __NL__ \
* implements a method with given signature. __NL__ \
*/ __NL__ \
template<typename Class__> __NL__ \
struct PREFIX##_HasMethod_##METHOD_NAME __NL__ \
{ __NL__ \
/* Define a pointer to member function with the correct __NL__ \
* argument signature. The newly defined type is named __NL__ \
* MethodType__. __NL__ \
*/ __NL__ \
typedef RETURN_TYPE (Class__::*MethodType__)ARGUMENTS; __NL__ \
__NL__ \
/* This is an application of the SFINAE concept. __NL__ \
* We check if Class__ defines a method with given name and __NL__ \
* signature. This is done by forcing the compiler __NL__ \
* through a static cast to select the respective method __NL__ \
* if possible. If the method signature cannot be found, __NL__ \
* the substitution fails and the first version of method "test" __NL__ \
* will not be defined. __NL__ \
*/ __NL__ \
template <typename ClassAux__> __NL__ \
static constexpr __NL__ \
/* The following decltype-clause defines the function return type __NL__ \
*/ __NL__ \
decltype( __NL__ \
/* If &ClassAux__::METHOD_NAME exists and is of type __NL__ \
* MethodType__, the list below evaluates to bool{} whose __NL__ \
* type can be determined. Otherwise the comma expression __NL__ \
* cannot be evaluated and the content __NL__ \
* of decltype is undefined and this function overload __NL__ \
* is ignored by the compiler __NL__ \
* (SFINAE = substitution failure is not an error) __NL__ \
* and the test(...) overload is used instead. __NL__ \
*/ __NL__ \
static_cast<MethodType__>(&ClassAux__::METHOD_NAME), bool{} __NL__ \
) __NL__ \
test(int /* unused */) __NL__ \
{ __NL__ \
return true; __NL__ \
} __NL__ \
__NL__ \
template <typename ClassAux__> __NL__ \
static constexpr bool test(...) __NL__ \
{ __NL__ \
return false; __NL__ \
} __NL__ \
__NL__ \
static constexpr bool value = test<Class__>(int{}); __NL__ \
};
Loading…
Cancel
Save