payload: {"action":"created","issue":{"url":"https://api.github.com/repos/keyboardio/Kaleidoscope/issues/617","repository_url":"https://api.github.com/repos/keyboardio/Kaleidoscope","labels_url":"https://api.github.com/repos/keyboardio/Kaleidoscope/issues/617/labels{/name}","comments_url":"https://api.github.com/repos/keyboardio/Kaleidoscope/issues/617/comments","events_url":"https://api.github.com/repos/keyboardio/Kaleidoscope/issues/617/events","html_url":"https://github.com/keyboardio/Kaleidoscope/pull/617","id":425359841,"node_id":"MDExOlB1bGxSZXF1ZXN0MjY0NDc1ODE1","number":617,"title":"Introduced dynamic LED modes","user":{"login":"noseglasses","id":29701465,"node_id":"MDQ6VXNlcjI5NzAxNDY1","avatar_url":"https://avatars3.githubusercontent.com/u/29701465?v=4","gravatar_id":"","url":"https://api.github.com/users/noseglasses","html_url":"https://github.com/noseglasses","followers_url":"https://api.github.com/users/noseglasses/followers","following_url":"https://api.github.com/users/noseglasses/following{/other_user}","gists_url":"https://api.github.com/users/noseglasses/gists{/gist_id}","starred_url":"https://api.github.com/users/noseglasses/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/noseglasses/subscriptions","organizations_url":"https://api.github.com/users/noseglasses/orgs","repos_url":"https://api.github.com/users/noseglasses/repos","events_url":"https://api.github.com/users/noseglasses/events{/privacy}","received_events_url":"https://api.github.com/users/noseglasses/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":29,"created_at":"2019-03-26T11:20:11Z","updated_at":"2019-04-01T16:17:36Z","closed_at":null,"author_association":"CONTRIBUTOR","pull_request":{"url":"https://api.github.com/repos/keyboardio/Kaleidoscope/pulls/617","html_url":"https://github.com/keyboardio/Kaleidoscope/pull/617","diff_url":"https://github.com/keyboardio/Kaleidoscope/pull/617.diff","patch_url":"https://github.com/keyboardio/Kaleidoscope/pull/617.patch"},"body":"This PR extends the capability of https://github.com/keyboardio/Kaleidoscope/pull/612. I opened a separate PR as the code largely differs. Explanations of some of the concepts used in this PR can be found in #612 and https://github.com/keyboardio/Kaleidoscope/issues/613.\r\n\r\n# Dynamic LED Modes with Kaleidoscope\r\n\r\n## Purpose of this Change\r\n\r\nThis PR introduces the concept of dynamic LED modes. Those are LED modes whose class instances\r\nhave a restricted lifetime that lasts only as long as a LED mode is active. By this means\r\nit 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\r\nby the maximum size (`sizeof(...)`) of the largest dynamic LED mode.\r\n\r\nOld-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.\r\n\r\nAll changes are entirely backward compatible. No user sketches or existing user plugins require changes.\r\n\r\n## Benefits\r\n\r\nThe greatest benefit of this change is that it drastically reduces the consumption of RAM\r\nwhen multiple complex LED modes are used. Currently the most complex stock LED mode is\r\nthe wavepool effect. Its plugin requires around 140 bytes of RAM that are statically allocated and cannot be shared with any other features.\r\nWith 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.\r\n\r\n## Resource Consumption\r\n\r\nFor 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.\r\n\r\nIf the wavepool effect, a especially RAM-hungry LED mode is added to the stock firmware,\r\nthe saving of RAM increases to 160 bytes which is almost 8% of RAM of the Keyboardio Model01.\r\n\r\n## Changes\r\n\r\n### `LEDModeInterface`\r\n\r\nA new interface class `LEDModeInterface` was introduced that those plugins\r\nthat 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`.\r\n\r\nThe new interface class currently lives in header `LEDMode.h` (see information about this new header below). This is because `LEDMode` and `LEDModeInterface` will\r\nalways be used together by dynamic LED modes. Thus, an extra header for `LEDModeInterface` would only mean extra include work for users writing plugins.\r\n\r\n### Dynamic LED Modes\r\n\r\nThose plugins that export dynamic LED modes must furtheron provide a exported type `DynamicLEDMode`.\r\nThis 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.\r\n\r\n### `ACCESS_THIS_LED_MODE`\r\n\r\nSome of those plugins that export dynamic led modes require access to their particular\r\ndynamic LED mode. By adding the macro `ACCESS_THIS_LED_MODE` to the plugin class definition,\r\nadditional 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).\r\n\r\nThe 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).\r\n\r\nA query in the plugin's event handler e.g. looks as follows.\r\n```cpp\r\n  if (::LEDControl.get_mode_index() != led_mode_id_)\r\n    return EventHandlerResult::OK;\r\n```\r\n\r\n### Stock LED Modes\r\n\r\nAll stock LED modes have been adapted to export dynamic LED modes (if possible).\r\nThis does not apply to all of them as for some the transition would have provided no gain.\r\nIt 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`).\r\n\r\n### `LEDMode`\r\n\r\nTo reduce the amount of compile unit and header interdependencies, the class `LEDMode` has been moved to a header/implementation file of its own.\r\n\r\n### `LEDControl`\r\n\r\nThe `LEDControl` class now does not have a static array anymore to store LED mode pointers.\r\nInstead, it delegates the core LED mode handling to a newly introduced `LEDModeManager` class\r\nthat lives in internal namespace. The `LEDModeManager` class is there to restrict access\r\nto LED modes but also to wrap up core LED mode handling. If this functionality would\r\nhave 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`.\r\n\r\n### `array_like_storage.h`\r\n\r\nThe new internal header `array_like_storage.h` contains a template class that is used to generate\r\narray-like storages. Here array-like means that the contained pieces of information\r\nare stored contiguously in memory in the same way as they would be when defining\r\nlanguage intrinsic (C-style) arrays. This type of storage is especially useful to generate array-like data struktures\r\nin PROGMEM at compile time based on a list of global objects or POD data. By casting the array-like storage's address\r\nto the content's pointer type, an array-like indexed access is possible.\r\n\r\nIn this PR an array-like data structure is used to generate a PROGMEM\r\narray of LED mode factories. Array-like data structures could also become useful in other places and for future applications.\r\n\r\n## Implementation\r\n\r\nThe most complex part of the implementation of the new LED mode handling is wrapped up in\r\n`LEDModeManager.h` and `LEDModeManager.cpp` to hide it from users' site.\r\n\r\nThere, 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.\r\n\r\nClass `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.\r\n\r\nWhen a dynamic LED mode is activated, a dedicated `LEDModeFactory` generates an instance of the dynamic LED mode class in the\r\nLED 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.\r\n\r\nAll LED mode handling related data structures are generated at compile time, based on\r\nthe 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.\r\n\r\n## Change Report\r\n\r\nConsult the following change report for information about how this PR affects RAM and PROGMEM consumption.\r\n\r\n[kaleidoscope_change_report_dynamic_LED_modes.pdf](https://github.com/keyboardio/Kaleidoscope/files/3008128/kaleidoscope_change_report_dynamic_LED_modes.pdf)\r\n\r\n"},"comment":{"url":"https://api.github.com/repos/keyboardio/Kaleidoscope/issues/comments/478646020","html_url":"https://github.com/keyboardio/Kaleidoscope/pull/617#issuecomment-478646020","issue_url":"https://api.github.com/repos/keyboardio/Kaleidoscope/issues/617","id":478646020,"node_id":"MDEyOklzc3VlQ29tbWVudDQ3ODY0NjAyMA==","user":{"login":"noseglasses","id":29701465,"node_id":"MDQ6VXNlcjI5NzAxNDY1","avatar_url":"https://avatars3.githubusercontent.com/u/29701465?v=4","gravatar_id":"","url":"https://api.github.com/users/noseglasses","html_url":"https://github.com/noseglasses","followers_url":"https://api.github.com/users/noseglasses/followers","following_url":"https://api.github.com/users/noseglasses/following{/other_user}","gists_url":"https://api.github.com/users/noseglasses/gists{/gist_id}","starred_url":"https://api.github.com/users/noseglasses/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/noseglasses/subscriptions","organizations_url":"https://api.github.com/users/noseglasses/orgs","repos_url":"https://api.github.com/users/noseglasses/repos","events_url":"https://api.github.com/users/noseglasses/events{/privacy}","received_events_url":"https://api.github.com/users/noseglasses/received_events","type":"User","site_admin":false},"created_at":"2019-04-01T16:17:36Z","updated_at":"2019-04-01T16:17:36Z","author_association":"CONTRIBUTOR","body":"@obra, could you please comment on the current set of name suggestions. Up to now we have\r\n\r\n|static|dynamic|\r\n|---|---|\r\n|short-lived|long-lived|\r\n|permanent|transient|\r\n|persistent|replaceable|\r\n||deciduous|\r\n||conjured|\r\n||buffer sharing|\r\n\r\nAny other suggestions?\r\n\r\nAs soon as we agree on a set of terms, I can update this PR."}}
other: {"actor":{"display_login":"noseglasses"}}