Merge pull request #617 from CapeLeidokos/ng_dynamic_led_modes
Introduced dynamic LED modespull/632/head
commit
5acb97940b
@ -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
|
@ -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…
Reference in new issue