/* 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 . */ #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 #ifdef KALEIDOSCOPE_VIRTUAL_BUILD #include #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 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 inline void registerLEDModeActivated(Bool2Type, ParentPlugin__ *parent_plugin, uint8_t mode_id) { parent_plugin->registerLEDModeActivated(mode_id); } template inline void registerLEDModeActivated(Bool2Type, 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 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(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::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(), 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(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 { 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 struct GenerateLEDModeFactory { static constexpr LEDModeFactory apply(kaleidoscope::Plugin */* non LED mode plugin*/) { return LEDModeFactory{nullptr, nullptr}; } }; template<> struct GenerateLEDModeFactory { // 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 static constexpr LEDModeFactory apply(Plugin__ *plugin) { return LEDModeFactory{ plugin, generateLEDMode // pointer to template instantiation of // generateLEDMode<...> }; } }; template<> struct GenerateLEDModeFactory { 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 struct TransientLEDModeSize { static constexpr size_t value = 0; }; template struct TransientLEDModeSize { typedef typename remove_pointer::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 struct TransientLEDModeMaxSize { static constexpr size_t this_size = TransientLEDModeSize::value; static constexpr size_t nested_size = TransientLEDModeMaxSize::value; static constexpr size_t value = (this_size > nested_size) ? this_size : nested_size; }; // Specialization to end recursion // template struct TransientLEDModeMaxSize { static constexpr size_t value = TransientLEDModeSize::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 static auto generateLEDModeTemporary(void *raw_buffer, ParentPlugin__ *parent_plugin ) -> typename ParentPlugin__::TransientLEDMode * { auto led_mode = static_cast( led_mode_management::generateLEDMode( 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::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( __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 */