You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
482 lines
20 KiB
482 lines
20 KiB
/* 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>
|
|
|
|
#ifdef KALEIDOSCOPE_VIRTUAL_BUILD
|
|
#include <new>
|
|
#else
|
|
|
|
// To enable placement new, we need to supply a global operator
|
|
// function.
|
|
//
|
|
inline void* operator new (size_t, void* __p) throw() {
|
|
return __p;
|
|
}
|
|
#endif
|
|
|
|
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 not templated */ (), (),
|
|
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 */
|