From 4fd7f6b7cbc3db57c6466fe7768b0175f9279e00 Mon Sep 17 00:00:00 2001 From: Florian Fleissner Date: Wed, 6 Nov 2019 16:46:38 +0100 Subject: [PATCH] An intrusive boilerplate-free Focus/EEPROM storage class This is an example of how Focus/EEPROM handling of individual parameters could be implemented in a non-boilerplate fashion. A new header focus_eeprom.h contains two template classes that allow to add Focus/EEPROM handling both to members of base classes (ReferenceFocusEEPROMWrapper) and to define new members in derived classes (ValueFocusEEPROMWrapper). Using the wrapper classes is intrusive in a sense that it requires classes (plugins) to be modified to add Focus/EEPROM capability. Warning: The code compiles but was not thoroughly tested. It is just meant as an example how things could be done and a basis for discussion. Signed-off-by: Florian Fleissner --- src/kaleidoscope/focus_eeprom.h | 134 +++++++++++++++++++++++++++ src/kaleidoscope/plugin/IdleLEDs.cpp | 30 ++++++ src/kaleidoscope/plugin/IdleLEDs.h | 23 ++++- 3 files changed, 186 insertions(+), 1 deletion(-) create mode 100644 src/kaleidoscope/focus_eeprom.h diff --git a/src/kaleidoscope/focus_eeprom.h b/src/kaleidoscope/focus_eeprom.h new file mode 100644 index 0000000000..d5a4198a40 --- /dev/null +++ b/src/kaleidoscope/focus_eeprom.h @@ -0,0 +1,134 @@ +/* -*- mode: c++ -*- + * Kaleidoscope-Idle-LEDs -- Turn off the LEDs when the keyboard's idle + * Copyright (C) 2018 Keyboard.io, Inc + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#pragma once + + +// TODO: Including EEPROM-Settings should be eliminated by +// adding additional methods to the storage controller API +// +#include + +#include + +namespace kaleidoscope { + +// TODO: Give the FocusEEPROMWrapper(s) an appropriate names. +// +template +class BaseFocusEEPROMWrapper { + public: + + typedef BaseFocusEEPROMWrapper<_DatumType, _StoredType, _FocusType> ThisType; + + typedef _DatumType DatumType; + typedef _StoredType StoredType; + typedef _FocusType FocusType; + + BaseFocusEEPROMWrapper(const StoredType datum, + const char *focus_string) + : focus_string_{focus_string}, + datum_{datum} + {} + + void setup() { + + // TODO: Shouldn't EEPROMSettings be wrapped by + // KeyboardHardware.storage()? + focus_address_ = ::EEPROMSettings.requestSlice(sizeof(FocusType)); + + // Check if the value is initialized and initialize it if necessary + // TODO: Have a storage command that enables this + // KeyboardHardware.storage().isInitialized(focus_address_); + // (checks for uninitialized EEPROM, e.g. FFFF...) + // + //if(!KeyboardHardware.storage().isInitialized(focus_address_)) { + // KeyboardHardware.storage().put(focus_address_, datum_); + //} + + KeyboardHardware.storage().get(focus_address_, datum_); + } + + bool focusRead(const char *command) { + if (::Focus.handleHelp(command, focus_string_)) + return false; + + if (strcmp_P(command, focus_string_) != 0) + return false; + + if (::Focus.isEOL()) { + ::Focus.send((FocusType)datum_); + } else { + FocusType tmpDatum; + ::Focus.read(tmpDatum); + datum_ = tmpDatum; + } + + return true; + } + + ThisType &operator=(DatumType datum) { + datum_ = datum; + this->store(); + return *this; + } + + void store() { + KeyboardHardware.storage().put(focus_address_, datum_); + } + + operator DatumType() { + return datum_; + } + + private: + + StoredType datum_; + uint16_t focus_address_; + const char *focus_string_; +}; + +// Use this template if you intent to add Focus/EEPROM capability +// to a member of a base class. In this case the wrapper stores a +// reference to the member. +// +template +using ReferenceFocusEEPROMWrapper + = BaseFocusEEPROMWrapper<_DatumType, _DatumType&, _FocusType>; + +// Use this template if you want to have a variable that is directly +// wrapped (stored inside the wrapper). In this case the wrapper +// stores the datum (by value). +// +template +using ValueFocusEEPROMWrapper + = BaseFocusEEPROMWrapper<_DatumType, _DatumType, _FocusType>; + +} // namespace kaleidoscope + +// Due to the way the PSTR macro is designed, it does not allow +// for being used at global scope. That's why we have to +// add a temporary lambda function to define a macro that can be evaluated +// anywhere. +// +#define PSTR_ANYWHERE(S) \ + []() -> const char * { \ + static const char tmp[] PROGMEM = (S); \ + return &tmp[0]; \ + }() diff --git a/src/kaleidoscope/plugin/IdleLEDs.cpp b/src/kaleidoscope/plugin/IdleLEDs.cpp index 0489bd30bb..c47dce72cb 100644 --- a/src/kaleidoscope/plugin/IdleLEDs.cpp +++ b/src/kaleidoscope/plugin/IdleLEDs.cpp @@ -56,7 +56,37 @@ EventHandlerResult IdleLEDs::onKeyswitchEvent(Key &mapped_key, KeyAddr key_addr, return EventHandlerResult::OK; } +ReferenceFocusEEPROMWrapper +PersistentIdleLEDs::start_time_focus_{ + // The parent class member variable that is + // controlled by the wrapper + // + PersistentIdleLEDs::start_time_, + + // Make sure to store the focus string only once + // in PROGMEM. + // + PSTR_ANYWHERE("idleLeds.idleTimeLimit")}; + +EventHandlerResult PersistentIdleLEDs::onSetup() { + start_time_focus_.setup(); + return EventHandlerResult::OK; +} +EventHandlerResult PersistentIdleLEDs::onFocusEvent(const char *command) { + if (start_time_focus_.focusRead(command)) { + return EventHandlerResult::EVENT_CONSUMED; + } + + return EventHandlerResult::OK; +} + +void PersistentIdleLEDs::setIdleTimeoutSeconds(uint32_t new_limit) { + IdleLEDs::setIdleTimeoutSeconds(new_limit); + start_time_focus_.store(); +} + } } kaleidoscope::plugin::IdleLEDs IdleLEDs; +kaleidoscope::plugin::PersistentIdleLEDs PersistentIdleLEDs; diff --git a/src/kaleidoscope/plugin/IdleLEDs.h b/src/kaleidoscope/plugin/IdleLEDs.h index 8722cf8398..ba9c0f91e0 100644 --- a/src/kaleidoscope/plugin/IdleLEDs.h +++ b/src/kaleidoscope/plugin/IdleLEDs.h @@ -18,6 +18,7 @@ #pragma once #include +#include "kaleidoscope/focus_eeprom.h" namespace kaleidoscope { namespace plugin { @@ -34,10 +35,30 @@ class IdleLEDs: public kaleidoscope::Plugin { EventHandlerResult beforeEachCycle(); EventHandlerResult onKeyswitchEvent(Key &mapped_key, KeyAddr key_addr, uint8_t key_state); - private: + protected: static uint32_t start_time_; }; + +class PersistentIdleLEDs : public IdleLEDs { + public: + + EventHandlerResult onSetup(); + EventHandlerResult onFocusEvent(const char *command); + + static void setIdleTimeoutSeconds(uint32_t new_limit); + + private: + static ReferenceFocusEEPROMWrapper start_time_focus_; + + // Note: If members are added that should themselves be Focus controlled + // and support EEPROM storage without being associated with + // a variable of a parent class, use the ValueFocusEEPROMWrapper + // instead. + //ValueFocusEEPROMWrapper start_time_focus_; +}; + } } extern kaleidoscope::plugin::IdleLEDs IdleLEDs; +extern kaleidoscope::plugin::PersistentIdleLEDs PersistentIdleLEDs;