#pragma once #include #include "./Geometry.h" #include "./Figment.h" #include "./Ringbuf.h" #include typedef Vector3d MotionVec; struct Variant { enum Type { Null, Integer, String, Color, }; Variant(int v) : type(Integer), m_value{.asInt=v} {} Variant(const char* v) : type(String), m_value{.asString=v} {} Variant(const CRGB &v) : type(Color), m_value{.asRGB={v.r, v.g, v.b}} {} Variant() : type(Null) {} Type type; const char* asString() const; CRGB asRGB() const; int asInt() const; bool asBool() const; private: union { int asInt; const char* asString; uint8_t asRGB[3]; } m_value; }; struct InputEvent: public Variant { enum Intent { // An empty non-event None, // An input from the user, for other tasks to translate into canonical // types. Makes for easy button re-mapping on the fly. UserInput, // // The canonical types // // Hardware inputs ButtonPress, Acceleration, NetworkStatus, NetworkActivity, // Power management PowerToggle, SetPower, SetBrightness, // Animation sequencing PreviousPattern, NextPattern, SetPattern, PreviousScene, NextScene, SetScene, // Timekeeping ScheduleChange, Beat, BeatDetect, // Task management StartThing, StopThing, // Configuration SetDisplayOffset, SetDisplayLength, SetColor, SaveConfigurationRequest, // Firmware events FirmwareUpdate, ReadyToRoll, }; template InputEvent(Intent s, Value v) : Variant(v), intent(s) {} InputEvent(Intent s) : Variant(), intent(s) {} InputEvent() : Variant(), intent(None) {} Intent intent; }; struct MainLoop; class InputSource: public Task { public: InputSource() : Task() {init();} explicit InputSource(const char* name) : Task(name) {init();} explicit InputSource(Task::State initialState) : Task(initialState) {init();} InputSource(const char* name, Task::State initialState) : Task(name, initialState) {init();} void loop() override; void onStart() override; virtual InputEvent read() = 0; private: void init(); #ifdef CONFIG_THREADED_INPUTS static void readThread(void* data); MainLoop* m_threadLoop; static uint8_t m_threadBuf[sizeof(InputEvent) * 32]; QueueHandle_t m_queue; StaticQueue_t m_threadQueue; #endif }; class InputFunc : public InputSource { public: InputFunc(std::function f) : InputSource(), m_func(f) {} InputFunc(std::function f, const char* name) : InputSource(name), m_func(f) {} InputFunc(std::function f, const char* name, Task::State initialState) : InputSource(name, initialState), m_func(f) {} InputEvent read() override { return m_func(); } private: std::function m_func; }; class BufferedInputSource: public InputSource { public: BufferedInputSource(const char* name) : InputSource(name) {} InputEvent read() override; protected: void setEvent(InputEvent &&evt); void setEvent(InputEvent::Intent intent, Variant &&v); private: Ringbuf m_eventQueue; }; class InputMapper: public BufferedInputSource { public: InputMapper(std::function f, const char* name) : BufferedInputSource(name), m_func(f) {} void handleEvent(const InputEvent& evt) override { setEvent(m_func(evt)); } private: std::function m_func; }; class OnlineTaskMixin : public virtual Loopable { public: void handleEvent(const InputEvent &evt) override { if (evt.intent == InputEvent::NetworkStatus) { m_online = evt.asInt(); if (m_online) { onOnline(); } else { onOffline(); } } if (m_online) { handleEventOnline(evt); } } virtual void onOnline() {} virtual void onOffline() {} virtual void handleEventOnline(const InputEvent &evt) {} void loop() override { if (m_online) { loopOnline(); } } virtual void loopOnline() {} private: bool m_online = false; };