renderbug/lib/Figments/Input.h

204 lines
4.5 KiB
C++

#pragma once
#include <Arduino.h>
#include "./Geometry.h"
#include "./Figment.h"
#include "./Ringbuf.h"
#include <FastLED.h>
typedef Vector3d<int> 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,
LoadConfigurationByName,
SetColor,
SaveConfigurationRequest,
// Firmware events
FirmwareUpdate,
ReadyToRoll,
};
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;
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<InputEvent(void)> f) : InputSource(), m_func(f) {}
InputFunc(std::function<InputEvent(void)> f, const char* name) : InputSource(name), m_func(f) {}
InputFunc(std::function<InputEvent(void)> f, const char* name, Task::State initialState) : InputSource(name, initialState), m_func(f) {}
InputEvent read() override {
return m_func();
}
private:
std::function<InputEvent(void)> 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<InputEvent, 12> m_eventQueue;
};
class InputMapper: public BufferedInputSource {
public:
InputMapper(std::function<InputEvent(const InputEvent)> f, const char* name) : BufferedInputSource(name), m_func(f) {}
void handleEvent(const InputEvent& evt) override {
setEvent(m_func(evt));
}
private:
std::function<InputEvent(const InputEvent)> 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;
};