renderbug-cpp/lib/Figments/Input.h

221 lines
4.7 KiB
C
Raw Normal View History

2019-05-10 05:17:29 +00:00
#pragma once
2021-03-29 08:10:55 +00:00
#include <Arduino.h>
2019-05-10 05:17:29 +00:00
#include "./Geometry.h"
#include "./Figment.h"
2021-03-28 21:50:31 +00:00
#include "./Ringbuf.h"
2021-03-29 08:10:55 +00:00
#include <FastLED.h>
2019-05-10 05:17:29 +00:00
typedef Vector3d<int> MotionVec;
struct Variant {
enum Type {
Null,
Integer,
String,
Color,
Pointer,
2019-05-10 05:17:29 +00:00
};
Variant(int v)
: type(Integer), m_value{.asInt=v} {}
Variant(const char* v)
: type(String), m_value{.asString=v} {}
2021-03-29 08:10:55 +00:00
Variant(const CRGB &v)
2019-05-10 05:17:29 +00:00
: type(Color), m_value{.asRGB={v.r, v.g, v.b}} {}
Variant(void* p)
: type(Pointer), m_value{.asPointer=p} {}
2019-05-10 05:17:29 +00:00
Variant()
: type(Null) {}
Type type;
const char* asString() const;
2021-03-29 08:10:55 +00:00
CRGB asRGB() const;
2019-05-10 05:17:29 +00:00
int asInt() const;
bool asBool() const;
template<typename T> T* as() const {
return (T*)m_value.asPointer;
}
2019-05-10 05:17:29 +00:00
private:
union {
int asInt;
const char* asString;
uint8_t asRGB[3];
void* asPointer;
2019-05-10 05:17:29 +00:00
} m_value;
};
struct InputEvent: public Variant {
enum Intent {
2021-03-28 01:19:55 +00:00
// An empty non-event
2019-05-10 05:17:29 +00:00
None,
2021-03-28 01:19:55 +00:00
// 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
2019-05-10 05:17:29 +00:00
PowerToggle,
SetPower,
SetBrightness,
2021-03-28 01:19:55 +00:00
// Animation sequencing
2019-05-10 05:17:29 +00:00
PreviousPattern,
NextPattern,
SetPattern,
2021-03-28 01:19:55 +00:00
PreviousScene,
NextScene,
SetScene,
// Timekeeping
ScheduleChange,
Beat,
BeatDetect,
2021-03-28 01:19:55 +00:00
// Task management
2019-05-10 05:17:29 +00:00
StartThing,
StopThing,
2021-03-28 01:19:55 +00:00
// Configuration
SetDisplayOffset,
SetDisplayLength,
LoadConfigurationByName,
2021-03-28 01:19:55 +00:00
SetColor,
SaveConfigurationRequest,
ConfigurationChanged,
2021-03-28 01:19:55 +00:00
// Firmware events
FirmwareUpdate,
ReadyToRoll,
2019-05-10 05:17:29 +00:00
};
template<typename Value>
InputEvent(Intent s, Value v)
: Variant(v), intent(s) {}
InputEvent(Intent s)
: Variant(), intent(s) {}
InputEvent()
: Variant(), intent(None) {}
Intent intent;
};
struct MainLoop;
2019-05-10 05:17:29 +00:00
class InputSource: public Task {
public:
InputSource() : Task() {init();}
explicit InputSource(const char* name) : Task(name) {init();}
2019-05-10 05:17:29 +00:00
void loop() override;
void onStart() override;
2019-05-10 05:17:29 +00:00
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
2019-05-10 05:17:29 +00:00
};
class InputFunc : public InputSource {
public:
InputFunc(std::function<InputEvent(void)> f) : InputSource(), m_func(f) {}
InputFunc(std::function<InputEvent(void)> f, const char* name) : InputSource(name), m_func(f) {}
InputEvent read() override {
return m_func();
}
private:
std::function<InputEvent(void)> m_func;
};
2019-05-10 05:17:29 +00:00
class BufferedInputSource: public InputSource {
public:
BufferedInputSource(const char* name) : InputSource(name) {}
InputEvent read() override;
protected:
void setEvent(InputEvent &&evt);
2021-03-28 01:19:55 +00:00
void setEvent(InputEvent::Intent intent, Variant &&v);
2019-05-10 05:17:29 +00:00
private:
2021-04-10 18:10:25 +00:00
Ringbuf<InputEvent, 12> m_eventQueue;
2019-05-10 05:17:29 +00:00
};
2021-03-28 01:19:55 +00:00
class InputMapper: public BufferedInputSource {
public:
2021-03-29 08:10:55 +00:00
InputMapper(std::function<InputEvent(const InputEvent)> f, const char* name) : BufferedInputSource(name), m_func(f) {}
2021-03-28 01:19:55 +00:00
void handleEvent(const InputEvent& evt) override {
setEvent(m_func(evt));
}
private:
std::function<InputEvent(const InputEvent)> m_func;
};
2021-03-29 08:10:55 +00:00
class ConfigTaskMixin : public virtual Loopable {
public:
void handleEvent(const InputEvent &evt) override {
if (evt.intent == InputEvent::ConfigurationChanged) {
handleConfigChange(evt);
}
}
virtual void handleConfigChange(const InputEvent& evt) {}
};
2021-03-29 08:10:55 +00:00
class OnlineTaskMixin : public virtual Loopable {
public:
void handleEvent(const InputEvent &evt) override {
if (evt.intent == InputEvent::NetworkStatus) {
m_online = evt.asInt();
2021-04-10 18:10:25 +00:00
if (m_online) {
onOnline();
} else {
onOffline();
}
2021-03-29 08:10:55 +00:00
}
if (m_online) {
handleEventOnline(evt);
}
}
2021-04-10 18:10:25 +00:00
virtual void onOnline() {}
virtual void onOffline() {}
virtual void handleEventOnline(const InputEvent &evt) {}
2021-03-29 08:10:55 +00:00
void loop() override {
if (m_online) {
loopOnline();
}
}
virtual void loopOnline() {}
2021-03-29 08:10:55 +00:00
private:
bool m_online = false;
};