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.
Kaleidoscope/plugins/Kaleidoscope-LED-Wavepool/src/kaleidoscope/plugin/LED-Wavepool.cpp

247 lines
8.1 KiB

/* -*- mode: c++ -*-
* Kaleidoscope-LED-Wavepool
* Copyright (C) 2017 Selene Scriven
*
* 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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/>.
*/
#if defined(ARDUINO_AVR_MODEL01) || defined(ARDUINO_keyboardio_model_100)
#include "kaleidoscope/plugin/LED-Wavepool.h"
#include <Arduino.h> // for pgm_read_byte, PROGMEM, abs
#include <stdint.h> // for int8_t, uint8_t, int16_t, intptr_t
#include "kaleidoscope/KeyAddr.h" // for MatrixAddr, KeyAddr, MatrixAddr<>::...
#include "kaleidoscope/KeyEvent.h" // for KeyEvent
#include "kaleidoscope/Runtime.h" // for Runtime, Runtime_
#include "kaleidoscope/device/device.h" // for Device, cRGB
#include "kaleidoscope/event_handler_result.h" // for EventHandlerResult, EventHandlerRes...
#include "kaleidoscope/keyswitch_state.h" // for keyIsPressed
#include "kaleidoscope/plugin/LEDControl.h" // for LEDControl
#include "kaleidoscope/plugin/LEDControl/LEDUtils.h" // for hsvToRgb
namespace kaleidoscope {
namespace plugin {
#define INTERPOLATE 1 // smoother, slower animation
#define MS_PER_FRAME 40 // 40 = 25 fps
#define FRAMES_PER_DROP 120 // max time between raindrops during idle animation
uint16_t WavepoolEffect::idle_timeout = 5000; // 5 seconds
int16_t WavepoolEffect::ripple_hue = WavepoolEffect::rainbow_hue; // automatic hue
// map native keyboard coordinates (16x4) into geometric space (14x5)
PROGMEM const uint8_t WavepoolEffect::TransientLEDMode::rc2pos[Runtime.device().numKeys()] = {
// clang-format off
0, 1, 2, 3, 4, 5, 6, 59, 66, 7, 8, 9, 10, 11, 12, 13,
14, 15, 16, 17, 18, 19, 34, 60, 65, 35, 22, 23, 24, 25, 26, 27,
28, 29, 30, 31, 32, 33, 48, 61, 64, 49, 36, 37, 38, 39, 40, 41,
42, 43, 44, 45, 46, 47, 58, 62, 63, 67, 50, 51, 52, 53, 54, 55,
// clang-format on
};
Introduced dynamic LED modes 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>
6 years ago
WavepoolEffect::TransientLEDMode::TransientLEDMode(const WavepoolEffect *parent)
: frames_since_event_(0),
Introduced dynamic LED modes 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>
6 years ago
surface_{},
page_(0) {}
Introduced dynamic LED modes 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>
6 years ago
EventHandlerResult WavepoolEffect::onKeyEvent(KeyEvent &event) {
if (!event.addr.isValid())
return EventHandlerResult::OK;
Introduced dynamic LED modes 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>
6 years ago
if (::LEDControl.get_mode_index() != led_mode_id_)
return EventHandlerResult::OK;
return ::LEDControl.get_mode<TransientLEDMode>()->onKeyEvent(event);
Introduced dynamic LED modes 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>
6 years ago
}
EventHandlerResult WavepoolEffect::TransientLEDMode::onKeyEvent(KeyEvent &event) {
// It might be better to trigger on both toggle-on and toggle-off, but maybe
// just the former.
if (keyIsPressed(event.state)) {
surface_[page_][pgm_read_byte(rc2pos + event.addr.toInt())] = 0x7f;
frames_since_event_ = 0;
}
return EventHandlerResult::OK;
}
Introduced dynamic LED modes 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>
6 years ago
void WavepoolEffect::TransientLEDMode::raindrop(uint8_t x, uint8_t y, int8_t *page_) {
uint8_t rainspot = (y * WP_WID) + x;
Introduced dynamic LED modes 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>
6 years ago
page_[rainspot] = 0x7f;
if (y > 0) page_[rainspot - WP_WID] = 0x60;
if (y < (WP_HGT - 1)) page_[rainspot + WP_WID] = 0x60;
if (x > 0) page_[rainspot - 1] = 0x60;
if (x < (WP_WID - 1)) page_[rainspot + 1] = 0x60;
}
// this is a lot smaller than the standard library's rand(),
// and still looks random-ish
Introduced dynamic LED modes 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>
6 years ago
uint8_t WavepoolEffect::TransientLEDMode::wp_rand() {
static intptr_t offset = 0x400;
offset = ((offset + 1) & 0x4fff) | 0x400;
return (Runtime.millisAtCycleStart() / MS_PER_FRAME) + pgm_read_byte((const uint8_t *)offset);
}
void WavepoolEffect::TransientLEDMode::update() {
// limit the frame rate; one frame every 64 ms
static uint8_t prev_time = 0;
uint8_t now = Runtime.millisAtCycleStart() / MS_PER_FRAME;
if (now != prev_time) {
prev_time = now;
} else {
return;
}
// rotate the colors over time
// (side note: it's weird that this is a 16-bit int instead of 8-bit,
// but that's what the library function wants)
static uint8_t current_hue = 0;
current_hue++;
frames_since_event_++;
// needs two pages of height map to do the calculations
Introduced dynamic LED modes 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>
6 years ago
int8_t *newpg = &surface_[page_ ^ 1][0];
int8_t *oldpg = &surface_[page_][0];
// rain a bit while idle
static uint8_t frames_till_next_drop = 0;
static int8_t prev_x = -1;
static int8_t prev_y = -1;
#ifdef INTERPOLATE
// even frames: water movement and page flipping
// odd frames: raindrops and tweening
// (this arrangement seems to look best overall)
if (((now & 1)) && (idle_timeout > 0)) {
#else
if (idle_timeout > 0) {
#endif
// repeat previous raindrop to give it a slightly better effect
if (prev_x >= 0) {
raindrop(prev_x, prev_y, oldpg);
prev_x = prev_y = -1;
}
if (frames_since_event_ >= (frames_till_next_drop + (idle_timeout / MS_PER_FRAME))) {
frames_till_next_drop = 4 + (wp_rand() % FRAMES_PER_DROP);
frames_since_event_ = idle_timeout / MS_PER_FRAME;
uint8_t x = wp_rand() % WP_WID;
uint8_t y = wp_rand() % WP_HGT;
raindrop(x, y, oldpg);
prev_x = x;
prev_y = y;
}
}
// calculate water movement
// (originally skipped edges, but this keyboard is too small for that)
//for (uint8_t y = 1; y < WP_HGT-1; y++) {
// for (uint8_t x = 1; x < WP_WID-1; x++) {
#ifdef INTERPOLATE
if (!(now & 1)) { // even frames only
#endif
for (uint8_t y = 0; y < WP_HGT; y++) {
for (uint8_t x = 0; x < WP_WID; x++) {
uint8_t offset = (y * WP_WID) + x;
int16_t value;
int8_t offsets[] = {
// clang-format off
-WP_WID, WP_WID,
-1, 1,
-WP_WID - 1, -WP_WID + 1,
WP_WID - 1, WP_WID + 1
// clang-format on
};
// don't wrap around edges or go out of bounds
if (y == 0) {
offsets[0] = 0;
offsets[4] += WP_WID;
offsets[5] += WP_WID;
} else if (y == WP_HGT - 1) {
offsets[1] = 0;
offsets[6] -= WP_WID;
offsets[7] -= WP_WID;
}
if (x == 0) {
offsets[2] = 0;
offsets[4] += 1;
offsets[6] += 1;
} else if (x == WP_WID - 1) {
offsets[3] = 0;
offsets[5] -= 1;
offsets[7] -= 1;
}
// add up all samples, divide, subtract prev frame's center
int8_t *p;
for (p = offsets, value = 0; p < offsets + 8; p++)
value += oldpg[offset + (*p)];
value = (value >> 2) - newpg[offset];
// reduce intensity gradually over time
newpg[offset] = value - (value >> 3);
}
}
#ifdef INTERPOLATE
}
#endif
// draw the water on the keys
for (auto key_addr : KeyAddr::all()) {
int8_t height = oldpg[pgm_read_byte(rc2pos + key_addr.toInt())];
#ifdef INTERPOLATE
if (now & 1) { // odd frames only
// average height with other frame
height = ((int16_t)height + newpg[pgm_read_byte(rc2pos + key_addr.toInt())]) >> 1;
}
#endif
uint8_t intensity = abs(height) * 2;
uint8_t saturation = 0xff - intensity;
uint8_t value = (intensity >= 128) ? 255 : intensity << 1;
int16_t hue = ripple_hue;
if (ripple_hue == WavepoolEffect::rainbow_hue) {
// color starts white but gets dimmer and more saturated as it fades,
// with hue wobbling according to height map
hue = (current_hue + height + (height >> 1)) & 0xff;
}
cRGB color = hsvToRgb(hue, saturation, value);
::LEDControl.setCrgbAt(key_addr, color);
}
#ifdef INTERPOLATE
// swap pages every other frame
Introduced dynamic LED modes 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>
6 years ago
if (!(now & 1)) page_ ^= 1;
#else
// swap pages every frame
Introduced dynamic LED modes 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>
6 years ago
page_ ^= 1;
#endif
}
} // namespace plugin
} // namespace kaleidoscope
kaleidoscope::plugin::WavepoolEffect WavepoolEffect;
#endif