This PR introduces the concept of dynamic LED modes. Those are LED modes whose class instances have a restricted lifetime that lasts only as long as a LED mode is active. By this means it is possible to support a greater amount of LED modes - especially RAM-hungry ones - in the same firmware build. The amount of RAM used to store dynamic LED modes is now bounded by the maximum size (`sizeof(...)`) of the largest dynamic LED mode. Old-style LED modes are furtheron called _static_ in the terminology of this PR. They are still supported and blend in nicely with the newly introduced dynamic LED modes. All changes are entirely backward compatible. No user sketches or existing user plugins require changes. The greatest benefit of this change is that it drastically reduces the consumption of RAM when multiple complex LED modes are used. Currently the most complex stock LED mode is the wavepool effect. Its plugin requires around 140 bytes of RAM that are statically allocated and cannot be shared with any other features. With this change it becomes possible to have a large number of such resource-hungry LED modes in parallel without a significant gain in RAM consumption. For the stock firmware this change means a small (~30 byte) growth in terms of PROGMEM. On the other hand it reduces the amount of statically consumed RAM by ~90 bytes. As the current atmel architectures come with around ten times as much PROGMEM as RAM, this means a great improvement as RAM is the more critical resource. If the wavepool effect, a especially RAM-hungry LED mode is added to the stock firmware, the saving of RAM increases to 160 bytes which is almost 8% of RAM of the Keyboardio Model01. A new interface class `LEDModeInterface` was introduced that those plugins that export dynamic LED modes inherit from. To remain backward compatible, the `LEDMode` class that all pre-existing LED mode plugins inherited from is also derived from `LEDModeInterface`. The new interface class currently lives in header `LEDMode.h` (see information about this new header below). This is because `LEDMode` and `LEDModeInterface` will always be used together by dynamic LED modes. Thus, an extra header for `LEDModeInterface` would only mean extra include work for users writing plugins. Those plugins that export dynamic LED modes must furtheron provide a exported type `DynamicLEDMode`. This can either be done by defining a nested class of that name or by typedef-ing a class that is defined at global scope to `DynamicLEDMode`. See the modified stock LED modes for examples. Some of those plugins that export dynamic led modes require access to their particular dynamic LED mode. By adding the macro `ACCESS_THIS_LED_MODE` to the plugin class definition, additional data and methods (an integer `led_mode_id_) are synthesized, that enable the plugin class to gain access to their particular dynamic LED mode instance (as long as it is active). The synthesized integer member `led_mode_id_` can be used to query if the currently active LED mode is the oned handled by the plugin class instance (note that there might be more than one plugin instance of the same class and thus also several dynamic LED modes, see e.g. the solid color LED mode). A query in the plugin's event handler e.g. looks as follows. ```cpp if (::LEDControl.get_mode_index() != led_mode_id_) return EventHandlerResult::OK; ``` All stock LED modes have been adapted to export dynamic LED modes (if possible). This does not apply to all of them as for some the transition would have provided no gain. It would even have meant a deterioration of resource consumption for those few pre-existing stock LED mode plugins that hardly have no (static) data-members at all (like e.g. `LEDOff`). To reduce the amount of compile unit and header interdependencies, the class `LEDMode` has been moved to a header/implementation file of its own. The `LEDControl` class now does not have a static array anymore to store LED mode pointers. Instead, it delegates the core LED mode handling to a newly introduced `LEDModeManager` class that lives in internal namespace. The `LEDModeManager` class is there to restrict access to LED modes but also to wrap up core LED mode handling. If this functionality would have been added to class `LEDControl`, far too much of the internals of LED mode handling would have been exposed to users through header `LEDControl.h`. The new internal header `array_like_storage.h` contains a template class that is used to generate array-like storages. Here array-like means that the contained pieces of information are stored contiguously in memory in the same way as they would be when defining language intrinsic (C-style) arrays. This type of storage is especially useful to generate array-like data struktures in PROGMEM at compile time based on a list of global objects or POD data. By casting the array-like storage's address to the content's pointer type, an array-like indexed access is possible. In this PR an array-like data structure is used to generate a PROGMEM array of LED mode factories. Array-like data structures could also become useful in other places and for future applications. The most complex part of the implementation of the new LED mode handling is wrapped up in `LEDModeManager.h` and `LEDModeManager.cpp` to hide it from users' site. There, recursive template classes are used to setup an array-like data structure of `LEDModeFactory` instances in PROGMEM. Each of the stored `LEDModeFactory`s are associated with one LED mode-plugin as specified in the sketch. The template mechanism filters out any other plugins unrelated to LED modes. `LEDModeFactory`s thereby handle both static and dynamic LED modes. Class `LEDModeManager` provides access to the LED mode factories and LED modes in general. It exports methods to query the number of LED modes and to activate a LED mode by its mode-ID. Most of this is only available to `LEDControl` that represents the actual user interface. When a dynamic LED mode is activated, a dedicated `LEDModeFactory` generates an instance of the dynamic LED mode class in the LED mode buffer. This buffer is shared by all dynamic LED modes. Its size has been determined at compile time by examining all exported dynamic LED mode types and determining the maximum necessary amount of RAM to store any of those. All LED mode handling related data structures are generated at compile time, based on the list of plugins that are passed to `KALEIDOSCOPE_INIT_PLUGINS(...)`. This function macro invokes a new function macro `_INIT_LED_MODE_MANAGER` from `LEDModeManager.h` that handles the LED mode related stuff. Signed-off-by: Florian Fleissner <florian.fleissner@inpartik.de>pull/617/head
parent
af1c2ba659
commit
f54e11a9b2
@ -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
|
@ -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
|
Loading…
Reference in new issue