diff --git a/VortexEngine/VortexCLI/VortexCLI.cpp b/VortexEngine/VortexCLI/VortexCLI.cpp index 89ca527e04..307a79979d 100644 --- a/VortexEngine/VortexCLI/VortexCLI.cpp +++ b/VortexEngine/VortexCLI/VortexCLI.cpp @@ -516,21 +516,23 @@ bool VortexCLI::init(int argc, char *argv[]) // load a .vortex or .vtxmode savefile if (m_loadSaveFile.size() > 0) { - ByteStream stream(4096); + ByteStream stream; + ByteStream_make(&stream); + ByteStream_init(&stream, 4096); // Open the file in read mode FILE *inputFile = fopen(m_loadSaveFile.c_str(), "r"); if (!inputFile) { printf("Failed to open: [%s] (%s)\n", m_loadSaveFile.c_str(), strerror(errno)); exit(2); } - if (!fread((void *)stream.rawData(), 1, stream.capacity(), inputFile)) { + if (!fread((void *)ByteStream_rawData(&stream), 1, ByteStream_capacity(&stream), inputFile)) { // error printf("Failed to read [%s]\n", m_loadSaveFile.c_str()); exit(2); } fclose(inputFile); // clear existing modes - Modes::clearModes(); + Modes_clearModes(); // check if the load from savefile was provided, this is kinda ugly but whatever if (m_loadSaveFile.size() >= 8 && m_loadSaveFile.rfind(".vtxmode") == m_loadSaveFile.size() - 8) { // ends with .vtxmode, load just a single mode @@ -560,13 +562,14 @@ bool VortexCLI::init(int argc, char *argv[]) stringstream ss(m_colorsetStr); string color; Colorset set; + Colorset_make(&set); while (getline(ss, color, ',')) { // iterate letters and lowercase them transform(color.begin(), color.end(), color.begin(), [](unsigned char c){ return tolower(c); }); if (color_map.count(color) > 0) { - set.addColor(color_map[color]); + Colorset_addColor(&set, RGBColor_fromU32(color_map[color])); } else { - set.addColor(strtoul(color.c_str(), nullptr, 16)); + Colorset_addColor(&set, RGBColor_fromU32(strtoul(color.c_str(), nullptr, 16))); } } // TODO: add arg for the led position @@ -640,6 +643,7 @@ void VortexCLI::cleanup() } if (m_writeSaveFile.length() > 0) { ByteStream stream; + ByteStream_make(&stream); Vortex::getModes(stream); FILE *outputFile = fopen(m_writeSaveFile.c_str(), "w"); if (!outputFile) { @@ -647,13 +651,14 @@ void VortexCLI::cleanup() exit(2); } // Print the recorded input to the file - fwrite(stream.rawData(), 1, stream.rawSize(), outputFile); + fwrite(ByteStream_rawData(&stream), 1, ByteStream_rawSize(&stream), outputFile); // Close the output file fclose(outputFile); printf("Wrote vortex save to [%s]\n", m_writeSaveFile.c_str()); } if (m_writeModeFile.length() > 0) { ByteStream stream; + ByteStream_make(&stream); Vortex::getCurMode(stream); FILE *outputFile = fopen(m_writeModeFile.c_str(), "w"); if (!outputFile) { @@ -661,7 +666,7 @@ void VortexCLI::cleanup() exit(2); } // Print the recorded input to the file - fwrite(stream.rawData(), 1, stream.rawSize(), outputFile); + fwrite(ByteStream_rawData(&stream), 1, ByteStream_rawSize(&stream), outputFile); // Close the output file fclose(outputFile); printf("Wrote vtxmode save to [%s]\n", m_writeModeFile.c_str()); @@ -718,7 +723,7 @@ void VortexCLI::show() // otherwise this just prints out the raw hex code if not in color mode for (uint32_t i = 0; i < m_numLeds; ++i) { char buf[128] = { 0 }; - snprintf(buf, sizeof(buf), "%06X", m_ledList[i].raw()); + snprintf(buf, sizeof(buf), "%06X", RGBColor_raw(&m_ledList[i])); out += buf; } } diff --git a/VortexEngine/VortexLib/VortexLib.cpp b/VortexEngine/VortexLib/VortexLib.cpp index 2dadb22a50..5b316da014 100644 --- a/VortexEngine/VortexLib/VortexLib.cpp +++ b/VortexEngine/VortexLib/VortexLib.cpp @@ -79,8 +79,8 @@ val tick_vortex() { emscripten::val getDataArray(const ByteStream &byteStream) { - const uint8_t *dataPtr = byteStream.data(); - uint32_t size = byteStream.size(); + const uint8_t *dataPtr = ByteStream_data(&byteStream); + uint32_t size = ByteStream_size(&byteStream); emscripten::val dataArray = emscripten::val::array(); @@ -93,8 +93,8 @@ emscripten::val getDataArray(const ByteStream &byteStream) emscripten::val getRawDataArray(const ByteStream &byteStream) { - const uint8_t *rawDataPtr = reinterpret_cast(byteStream.rawData()); - uint32_t rawSize = byteStream.rawSize(); + const uint8_t *rawDataPtr = reinterpret_cast(ByteStream_rawData(&byteStream)); + uint32_t rawSize = ByteStream_rawSize(&byteStream); emscripten::val rawDataArray = emscripten::val::array(); @@ -112,7 +112,7 @@ bool createByteStreamFromData(val bytesArray, ByteStream &stream) // Assuming size is part of the data or passed separately size_t size = data.size(); - if (!stream.rawInit(data.data(), size)) { + if (!ByteStream_rawInit(&stream, data.data(), size)) { return true; } return false; @@ -124,6 +124,100 @@ PatternID intToPatternID(int val) return (PatternID)val; } +// embind deletes the objects it owns, ByteStreams must release their arena +// chunk first and are heap allocated so the arena owner never moves +namespace emscripten { +namespace internal { +template<> +void raw_destructor(ByteStream *ptr) +{ + ByteStream_destroy(ptr); + delete ptr; +} +} +} + +// constructor wrappers for the C style init functions +static ByteStream *wasm_ByteStream_new() +{ + ByteStream *stream = new ByteStream; + ByteStream_make(stream); + return stream; +} + +static ByteStream *wasm_ByteStream_newBuf(uint32_t size, const uint8_t *buf) +{ + ByteStream *stream = new ByteStream; + ByteStream_makeBuf(stream, size, buf); + return stream; +} + +static Colorset wasm_Colorset_new() +{ + Colorset set; + Colorset_make(&set); + return set; +} + +static Colorset wasm_Colorset_newRGB(RGBColor c1, RGBColor c2, RGBColor c3, RGBColor c4, + RGBColor c5, RGBColor c6, RGBColor c7, RGBColor c8) +{ + Colorset set; + Colorset_makeRGB(&set, c1, c2, c3, c4, c5, c6, c7, c8); + return set; +} + +static Colorset wasm_Colorset_newCols(uint8_t numCols, const uint32_t *cols) +{ + Colorset set; + Colorset_makeCols(&set, numCols, cols); + return set; +} + +#define WASM_ARGS_P0 +#define WASM_ARGS_P1 uint8_t a1 +#define WASM_ARGS_P2 WASM_ARGS_P1, uint8_t a2 +#define WASM_ARGS_P3 WASM_ARGS_P2, uint8_t a3 +#define WASM_ARGS_P4 WASM_ARGS_P3, uint8_t a4 +#define WASM_ARGS_P5 WASM_ARGS_P4, uint8_t a5 +#define WASM_ARGS_P6 WASM_ARGS_P5, uint8_t a6 +#define WASM_ARGS_P7 WASM_ARGS_P6, uint8_t a7 +#define WASM_ARGS_P8 WASM_ARGS_P7, uint8_t a8 +#define WASM_ARGS_A0 +#define WASM_ARGS_A1 , a1 +#define WASM_ARGS_A2 WASM_ARGS_A1, a2 +#define WASM_ARGS_A3 WASM_ARGS_A2, a3 +#define WASM_ARGS_A4 WASM_ARGS_A3, a4 +#define WASM_ARGS_A5 WASM_ARGS_A4, a5 +#define WASM_ARGS_A6 WASM_ARGS_A5, a6 +#define WASM_ARGS_A7 WASM_ARGS_A6, a7 +#define WASM_ARGS_A8 WASM_ARGS_A7, a8 +#define WASM_PATTERN_ARGS_NEW(n) \ + static PatternArgs wasm_PatternArgs_new##n(WASM_ARGS_P##n) \ + { \ + PatternArgs args; \ + PatternArgs_make##n(&args WASM_ARGS_A##n); \ + return args; \ + } +WASM_PATTERN_ARGS_NEW(0) +WASM_PATTERN_ARGS_NEW(1) +WASM_PATTERN_ARGS_NEW(2) +WASM_PATTERN_ARGS_NEW(3) +WASM_PATTERN_ARGS_NEW(4) +WASM_PATTERN_ARGS_NEW(5) +WASM_PATTERN_ARGS_NEW(6) +WASM_PATTERN_ARGS_NEW(7) +WASM_PATTERN_ARGS_NEW(8) + +// the anonymous enums that hold the LedPos and PatternID values +typedef decltype(LED_FIRST) LedPosEnum; +typedef decltype(PATTERN_NONE) PatternIDEnum; + +// placeholder types to group the static module functions in js +struct PatternBuilderBinding {}; +struct MenusBinding {}; +struct ModesBinding {}; + EMSCRIPTEN_BINDINGS(Vortex) { // vector register_vector("VectorString"); @@ -135,163 +229,164 @@ EMSCRIPTEN_BINDINGS(Vortex) { // Bind the HSVColor class class_("HSVColor") - .constructor<>() - .constructor() - .constructor() - .function("empty", &HSVColor::empty) - .function("clear", &HSVColor::clear) - .function("raw", &HSVColor::raw) + .constructor(&HSVColor_make) + .constructor(&HSVColor_makeHSV) + .constructor(&HSVColor_fromU32) + .function("empty", &HSVColor_empty, allow_raw_pointers()) + .function("clear", &HSVColor_clear, allow_raw_pointers()) + .function("raw", &HSVColor_raw, allow_raw_pointers()) .property("hue", &HSVColor::hue) .property("sat", &HSVColor::sat) .property("val", &HSVColor::val); // Bind the RGBColor class class_("RGBColor") - .constructor<>() - .constructor() - .constructor() - .function("empty", &RGBColor::empty) - .function("clear", &RGBColor::clear) - .function("adjustBrightness", &RGBColor::adjustBrightness) - .function("serialize", &RGBColor::serialize) - .function("unserialize", &RGBColor::unserialize) - .function("raw", &RGBColor::raw) + .constructor(&RGBColor_make) + .constructor(&RGBColor_makeRGB) + .constructor(&RGBColor_fromU32) + .function("empty", &RGBColor_empty, allow_raw_pointers()) + .function("clear", &RGBColor_clear, allow_raw_pointers()) + .function("adjustBrightness", &RGBColor_adjustBrightness, allow_raw_pointers()) + .function("serialize", &RGBColor_serialize, allow_raw_pointers()) + .function("unserialize", &RGBColor_unserialize, allow_raw_pointers()) + .function("raw", &RGBColor_raw, allow_raw_pointers()) .property("red", &RGBColor::red) .property("green", &RGBColor::green) .property("blue", &RGBColor::blue); // Bind the utility conversion functions - function("hsv_to_rgb_rainbow", &hsv_to_rgb_rainbow); - function("hsv_to_rgb_raw_C", &hsv_to_rgb_raw_C); - function("hsv_to_rgb_generic", &hsv_to_rgb_generic); - function("rgb_to_hsv_approx", &rgb_to_hsv_approx); - function("rgb_to_hsv_generic", &rgb_to_hsv_generic); + function("hsv_to_rgb_rainbow", &hsv_to_rgb_rainbow, allow_raw_pointers()); + function("hsv_to_rgb_raw_C", &hsv_to_rgb_raw_C, allow_raw_pointers()); + function("hsv_to_rgb_generic", &hsv_to_rgb_generic, allow_raw_pointers()); + function("rgb_to_hsv_approx", &rgb_to_hsv_approx, allow_raw_pointers()); + function("rgb_to_hsv_generic", &rgb_to_hsv_generic, allow_raw_pointers()); class_("ByteStream") - .constructor<>() - .constructor() + .constructor(&wasm_ByteStream_new, allow_raw_pointers()) + .constructor(&wasm_ByteStream_newBuf, allow_raw_pointers()) // member functions - .function("rawInit", &ByteStream::rawInit, allow_raw_pointer()) - .function("init", &ByteStream::init, allow_raw_pointer()) - .function("clear", &ByteStream::clear) - .function("shrink", &ByteStream::shrink) - .function("append", &ByteStream::append) - .function("extend", &ByteStream::extend) - .function("trim", &ByteStream::trim) - .function("compress", &ByteStream::compress) - .function("decompress", &ByteStream::decompress) - .function("recalcCRC", &ByteStream::recalcCRC) - .function("sanity", &ByteStream::sanity) - .function("checkCRC", &ByteStream::checkCRC) - .function("isCRCDirty", &ByteStream::isCRCDirty) - .function("serialize8", &ByteStream::serialize8) - .function("serialize16", &ByteStream::serialize16) - .function("serialize32", &ByteStream::serialize32) - .function("resetUnserializer", &ByteStream::resetUnserializer) - .function("moveUnserializer", &ByteStream::moveUnserializer) - .function("unserializerAtEnd", &ByteStream::unserializerAtEnd) + .function("rawInit", &ByteStream_rawInit, allow_raw_pointers()) + .function("init", &ByteStream_initBuf, allow_raw_pointers()) + .function("clear", &ByteStream_clear, allow_raw_pointers()) + .function("shrink", &ByteStream_shrink, allow_raw_pointers()) + .function("append", &ByteStream_append, allow_raw_pointers()) + .function("extend", &ByteStream_extend, allow_raw_pointers()) + .function("trim", &ByteStream_trim, allow_raw_pointers()) + .function("compress", &ByteStream_compress, allow_raw_pointers()) + .function("decompress", &ByteStream_decompress, allow_raw_pointers()) + .function("recalcCRC", &ByteStream_recalcCRC, allow_raw_pointers()) + .function("sanity", &ByteStream_sanity, allow_raw_pointers()) + .function("checkCRC", &ByteStream_checkCRC, allow_raw_pointers()) + .function("isCRCDirty", &ByteStream_isCRCDirty, allow_raw_pointers()) + .function("serialize8", &ByteStream_serialize8, allow_raw_pointers()) + .function("serialize16", &ByteStream_serialize16, allow_raw_pointers()) + .function("serialize32", &ByteStream_serialize32, allow_raw_pointers()) + .function("resetUnserializer", &ByteStream_resetUnserializer, allow_raw_pointers()) + .function("moveUnserializer", &ByteStream_moveUnserializer, allow_raw_pointers()) + .function("unserializerAtEnd", &ByteStream_unserializerAtEnd, allow_raw_pointers()) // TODO: provide better apis here - //.function("unserialize8", &ByteStream::unserialize8) - //.function("unserialize16", &ByteStream::unserialize16) - //.function("unserialize32", &ByteStream::unserialize32) - .function("peek8", &ByteStream::peek8) - .function("peek16", &ByteStream::peek16) - .function("peek32", &ByteStream::peek32) - .function("data", &ByteStream::data, allow_raw_pointer()) - .function("rawData", &ByteStream::rawData, allow_raw_pointer()) - .function("rawSize", &ByteStream::rawSize) - .function("size", &ByteStream::size) - .function("capacity", &ByteStream::capacity) - .function("is_compressed", &ByteStream::is_compressed) - .function("CRC", &ByteStream::CRC); + //.function("unserialize8", &ByteStream_unserialize8) + //.function("unserialize16", &ByteStream_unserialize16) + //.function("unserialize32", &ByteStream_unserialize32) + .function("peek8", &ByteStream_peek8, allow_raw_pointers()) + .function("peek16", &ByteStream_peek16, allow_raw_pointers()) + .function("peek32", &ByteStream_peek32, allow_raw_pointers()) + .function("data", &ByteStream_data, allow_raw_pointers()) + .function("rawData", &ByteStream_rawData, allow_raw_pointers()) + .function("rawSize", &ByteStream_rawSize, allow_raw_pointers()) + .function("size", &ByteStream_size, allow_raw_pointers()) + .function("capacity", &ByteStream_capacity, allow_raw_pointers()) + .function("is_compressed", &ByteStream_is_compressed, allow_raw_pointers()) + .function("CRC", &ByteStream_CRC, allow_raw_pointers()); // Binding static enum values - enum_("LedPos") - .value("LED_FIRST", LedPos::LED_FIRST) - .value("LED_0", LedPos::LED_0) - .value("LED_1", LedPos::LED_1) - .value("LED_COUNT", LedPos::LED_COUNT) - .value("LED_LAST", LedPos::LED_LAST) - .value("LED_ALL", LedPos::LED_ALL) - .value("LED_MULTI", LedPos::LED_MULTI) - .value("LED_ALL_SINGLE", LedPos::LED_ALL_SINGLE) - .value("LED_ANY", LedPos::LED_ANY); + // LedPos and PatternID are plain integers now so they are exposed to js as numbers + enum_("LedPos", enum_value_type::number) + .value("LED_FIRST", (LedPosEnum)LED_FIRST) + .value("LED_0", (LedPosEnum)LED_0) + .value("LED_1", (LedPosEnum)LED_1) + .value("LED_COUNT", (LedPosEnum)LED_COUNT) + .value("LED_LAST", (LedPosEnum)LED_LAST) + .value("LED_ALL", (LedPosEnum)LED_ALL) + .value("LED_MULTI", (LedPosEnum)LED_MULTI) + .value("LED_ALL_SINGLE", (LedPosEnum)LED_ALL_SINGLE) + .value("LED_ANY", (LedPosEnum)LED_ANY); function("intToPatternID", &intToPatternID); function("isMultiLedPatternID", &isMultiLedPatternID); function("isSingleLedPatternID", &isSingleLedPatternID); - enum_("PatternID") + enum_("PatternID", enum_value_type::number) // Meta Constants - .value("PATTERN_NONE", PatternID::PATTERN_NONE) + .value("PATTERN_NONE", (PatternIDEnum)PATTERN_NONE) // single led patterns - .value("PATTERN_STROBE", PatternID::PATTERN_STROBE) - .value("PATTERN_HYPERSTROBE", PatternID::PATTERN_HYPERSTROBE) - .value("PATTERN_PICOSTROBE", PatternID::PATTERN_PICOSTROBE) - .value("PATTERN_STROBIE", PatternID::PATTERN_STROBIE) - .value("PATTERN_DOPS", PatternID::PATTERN_DOPS) - .value("PATTERN_ULTRADOPS", PatternID::PATTERN_ULTRADOPS) - .value("PATTERN_STROBEGAP", PatternID::PATTERN_STROBEGAP) - .value("PATTERN_HYPERGAP", PatternID::PATTERN_HYPERGAP) - .value("PATTERN_PICOGAP", PatternID::PATTERN_PICOGAP) - .value("PATTERN_STROBIEGAP", PatternID::PATTERN_STROBIEGAP) - .value("PATTERN_DOPSGAP", PatternID::PATTERN_DOPSGAP) - .value("PATTERN_ULTRAGAP", PatternID::PATTERN_ULTRAGAP) - .value("PATTERN_BLINKIE", PatternID::PATTERN_BLINKIE) - .value("PATTERN_GHOSTCRUSH", PatternID::PATTERN_GHOSTCRUSH) - .value("PATTERN_DOUBLEDOPS", PatternID::PATTERN_DOUBLEDOPS) - .value("PATTERN_CHOPPER", PatternID::PATTERN_CHOPPER) - .value("PATTERN_DASHGAP", PatternID::PATTERN_DASHGAP) - .value("PATTERN_DASHDOPS", PatternID::PATTERN_DASHDOPS) - .value("PATTERN_DASHCRUSH", PatternID::PATTERN_DASHCRUSH) - .value("PATTERN_ULTRADASH", PatternID::PATTERN_ULTRADASH) - .value("PATTERN_GAPCYCLE", PatternID::PATTERN_GAPCYCLE) - .value("PATTERN_DASHCYCLE", PatternID::PATTERN_DASHCYCLE) - .value("PATTERN_TRACER", PatternID::PATTERN_TRACER) - .value("PATTERN_RIBBON", PatternID::PATTERN_RIBBON) - .value("PATTERN_MINIRIBBON", PatternID::PATTERN_MINIRIBBON) - .value("PATTERN_BLEND", PatternID::PATTERN_BLEND) - .value("PATTERN_BLENDSTROBE", PatternID::PATTERN_BLENDSTROBE) - .value("PATTERN_BLENDSTROBEGAP", PatternID::PATTERN_BLENDSTROBEGAP) - .value("PATTERN_COMPLEMENTARY_BLEND", PatternID::PATTERN_COMPLEMENTARY_BLEND) - .value("PATTERN_COMPLEMENTARY_BLENDSTROBE", PatternID::PATTERN_COMPLEMENTARY_BLENDSTROBE) - .value("PATTERN_COMPLEMENTARY_BLENDSTROBEGAP", PatternID::PATTERN_COMPLEMENTARY_BLENDSTROBEGAP) - .value("PATTERN_SOLID", PatternID::PATTERN_SOLID) + .value("PATTERN_STROBE", (PatternIDEnum)PATTERN_STROBE) + .value("PATTERN_HYPERSTROBE", (PatternIDEnum)PATTERN_HYPERSTROBE) + .value("PATTERN_PICOSTROBE", (PatternIDEnum)PATTERN_PICOSTROBE) + .value("PATTERN_STROBIE", (PatternIDEnum)PATTERN_STROBIE) + .value("PATTERN_DOPS", (PatternIDEnum)PATTERN_DOPS) + .value("PATTERN_ULTRADOPS", (PatternIDEnum)PATTERN_ULTRADOPS) + .value("PATTERN_STROBEGAP", (PatternIDEnum)PATTERN_STROBEGAP) + .value("PATTERN_HYPERGAP", (PatternIDEnum)PATTERN_HYPERGAP) + .value("PATTERN_PICOGAP", (PatternIDEnum)PATTERN_PICOGAP) + .value("PATTERN_STROBIEGAP", (PatternIDEnum)PATTERN_STROBIEGAP) + .value("PATTERN_DOPSGAP", (PatternIDEnum)PATTERN_DOPSGAP) + .value("PATTERN_ULTRAGAP", (PatternIDEnum)PATTERN_ULTRAGAP) + .value("PATTERN_BLINKIE", (PatternIDEnum)PATTERN_BLINKIE) + .value("PATTERN_GHOSTCRUSH", (PatternIDEnum)PATTERN_GHOSTCRUSH) + .value("PATTERN_DOUBLEDOPS", (PatternIDEnum)PATTERN_DOUBLEDOPS) + .value("PATTERN_CHOPPER", (PatternIDEnum)PATTERN_CHOPPER) + .value("PATTERN_DASHGAP", (PatternIDEnum)PATTERN_DASHGAP) + .value("PATTERN_DASHDOPS", (PatternIDEnum)PATTERN_DASHDOPS) + .value("PATTERN_DASHCRUSH", (PatternIDEnum)PATTERN_DASHCRUSH) + .value("PATTERN_ULTRADASH", (PatternIDEnum)PATTERN_ULTRADASH) + .value("PATTERN_GAPCYCLE", (PatternIDEnum)PATTERN_GAPCYCLE) + .value("PATTERN_DASHCYCLE", (PatternIDEnum)PATTERN_DASHCYCLE) + .value("PATTERN_TRACER", (PatternIDEnum)PATTERN_TRACER) + .value("PATTERN_RIBBON", (PatternIDEnum)PATTERN_RIBBON) + .value("PATTERN_MINIRIBBON", (PatternIDEnum)PATTERN_MINIRIBBON) + .value("PATTERN_BLEND", (PatternIDEnum)PATTERN_BLEND) + .value("PATTERN_BLENDSTROBE", (PatternIDEnum)PATTERN_BLENDSTROBE) + .value("PATTERN_BLENDSTROBEGAP", (PatternIDEnum)PATTERN_BLENDSTROBEGAP) + .value("PATTERN_COMPLEMENTARY_BLEND", (PatternIDEnum)PATTERN_COMPLEMENTARY_BLEND) + .value("PATTERN_COMPLEMENTARY_BLENDSTROBE", (PatternIDEnum)PATTERN_COMPLEMENTARY_BLENDSTROBE) + .value("PATTERN_COMPLEMENTARY_BLENDSTROBEGAP", (PatternIDEnum)PATTERN_COMPLEMENTARY_BLENDSTROBEGAP) + .value("PATTERN_SOLID", (PatternIDEnum)PATTERN_SOLID) // multi led patterns - .value("PATTERN_HUE_SCROLL", PatternID::PATTERN_HUE_SCROLL) - .value("PATTERN_THEATER_CHASE", PatternID::PATTERN_THEATER_CHASE) - .value("PATTERN_CHASER", PatternID::PATTERN_CHASER) - .value("PATTERN_ZIGZAG", PatternID::PATTERN_ZIGZAG) - .value("PATTERN_ZIPFADE", PatternID::PATTERN_ZIPFADE) - .value("PATTERN_DRIP", PatternID::PATTERN_DRIP) - .value("PATTERN_DRIPMORPH", PatternID::PATTERN_DRIPMORPH) - .value("PATTERN_CROSSDOPS", PatternID::PATTERN_CROSSDOPS) - .value("PATTERN_DOUBLESTROBE", PatternID::PATTERN_DOUBLESTROBE) - .value("PATTERN_METEOR", PatternID::PATTERN_METEOR) - .value("PATTERN_SPARKLETRACE", PatternID::PATTERN_SPARKLETRACE) - .value("PATTERN_VORTEXWIPE", PatternID::PATTERN_VORTEXWIPE) - .value("PATTERN_WARP", PatternID::PATTERN_WARP) - .value("PATTERN_WARPWORM", PatternID::PATTERN_WARPWORM) - .value("PATTERN_SNOWBALL", PatternID::PATTERN_SNOWBALL) - .value("PATTERN_LIGHTHOUSE", PatternID::PATTERN_LIGHTHOUSE) - .value("PATTERN_PULSISH", PatternID::PATTERN_PULSISH) - .value("PATTERN_FILL", PatternID::PATTERN_FILL) - .value("PATTERN_BOUNCE", PatternID::PATTERN_BOUNCE) - .value("PATTERN_SPLITSTROBIE", PatternID::PATTERN_SPLITSTROBIE) - .value("PATTERN_BACKSTROBE", PatternID::PATTERN_BACKSTROBE) - .value("PATTERN_VORTEX", PatternID::PATTERN_VORTEX) - .value("PATTERN_COUNT", PatternID::PATTERN_COUNT); + .value("PATTERN_HUE_SCROLL", (PatternIDEnum)PATTERN_HUE_SCROLL) + .value("PATTERN_THEATER_CHASE", (PatternIDEnum)PATTERN_THEATER_CHASE) + .value("PATTERN_CHASER", (PatternIDEnum)PATTERN_CHASER) + .value("PATTERN_ZIGZAG", (PatternIDEnum)PATTERN_ZIGZAG) + .value("PATTERN_ZIPFADE", (PatternIDEnum)PATTERN_ZIPFADE) + .value("PATTERN_DRIP", (PatternIDEnum)PATTERN_DRIP) + .value("PATTERN_DRIPMORPH", (PatternIDEnum)PATTERN_DRIPMORPH) + .value("PATTERN_CROSSDOPS", (PatternIDEnum)PATTERN_CROSSDOPS) + .value("PATTERN_DOUBLESTROBE", (PatternIDEnum)PATTERN_DOUBLESTROBE) + .value("PATTERN_METEOR", (PatternIDEnum)PATTERN_METEOR) + .value("PATTERN_SPARKLETRACE", (PatternIDEnum)PATTERN_SPARKLETRACE) + .value("PATTERN_VORTEXWIPE", (PatternIDEnum)PATTERN_VORTEXWIPE) + .value("PATTERN_WARP", (PatternIDEnum)PATTERN_WARP) + .value("PATTERN_WARPWORM", (PatternIDEnum)PATTERN_WARPWORM) + .value("PATTERN_SNOWBALL", (PatternIDEnum)PATTERN_SNOWBALL) + .value("PATTERN_LIGHTHOUSE", (PatternIDEnum)PATTERN_LIGHTHOUSE) + .value("PATTERN_PULSISH", (PatternIDEnum)PATTERN_PULSISH) + .value("PATTERN_FILL", (PatternIDEnum)PATTERN_FILL) + .value("PATTERN_BOUNCE", (PatternIDEnum)PATTERN_BOUNCE) + .value("PATTERN_SPLITSTROBIE", (PatternIDEnum)PATTERN_SPLITSTROBIE) + .value("PATTERN_BACKSTROBE", (PatternIDEnum)PATTERN_BACKSTROBE) + .value("PATTERN_VORTEX", (PatternIDEnum)PATTERN_VORTEX) + .value("PATTERN_COUNT", (PatternIDEnum)PATTERN_COUNT); // meta constants - //.value("PATTERN_FIRST", PatternID::PATTERN_FIRST) - //.value("PATTERN_SINGLE_FIRST", PatternID::PATTERN_SINGLE_FIRST) - //.value("PATTERN_MULTI_FIRST", PatternID::PATTERN_MULTI_FIRST) - //.value("PATTERN_SINGLE_LAST", PatternID::PATTERN_SINGLE_LAST) - //.value("PATTERN_SINGLE_COUNT", PatternID::PATTERN_SINGLE_COUNT) - //.value("PATTERN_MULTI_LAST", PatternID::PATTERN_MULTI_LAST) - //.value("PATTERN_MULTI_COUNT", PatternID::PATTERN_MULTI_COUNT) - //.value("PATTERN_LAST", PatternID::PATTERN_LAST) + //.value("PATTERN_FIRST", (PatternIDEnum)PATTERN_FIRST) + //.value("PATTERN_SINGLE_FIRST", (PatternIDEnum)PATTERN_SINGLE_FIRST) + //.value("PATTERN_MULTI_FIRST", (PatternIDEnum)PATTERN_MULTI_FIRST) + //.value("PATTERN_SINGLE_LAST", (PatternIDEnum)PATTERN_SINGLE_LAST) + //.value("PATTERN_SINGLE_COUNT", (PatternIDEnum)PATTERN_SINGLE_COUNT) + //.value("PATTERN_MULTI_LAST", (PatternIDEnum)PATTERN_MULTI_LAST) + //.value("PATTERN_MULTI_COUNT", (PatternIDEnum)PATTERN_MULTI_COUNT) + //.value("PATTERN_LAST", (PatternIDEnum)PATTERN_LAST) enum_("MenuEntryID") .value("MENU_NONE", MenuEntryID::MENU_NONE) @@ -307,69 +402,69 @@ EMSCRIPTEN_BINDINGS(Vortex) { .value("MENU_FACTORY_RESET", MenuEntryID::MENU_FACTORY_RESET); class_("Colorset") - .constructor<>() - .constructor() - .constructor() - .function("init", &Colorset::init) - .function("clear", &Colorset::clear) - .function("equals", select_overload(&Colorset::equals)) - .function("get", &Colorset::get) - .function("set", &Colorset::set) - .function("skip", &Colorset::skip) - .function("cur", &Colorset::cur) - .function("setCurIndex", &Colorset::setCurIndex) - .function("resetIndex", &Colorset::resetIndex) - .function("curIndex", &Colorset::curIndex) - .function("getPrev", &Colorset::getPrev) - .function("getNext", &Colorset::getNext) - .function("peek", &Colorset::peek) - .function("peekNext", &Colorset::peekNext) - .function("numColors", &Colorset::numColors) - .function("onStart", &Colorset::onStart) - .function("onEnd", &Colorset::onEnd) - .function("serialize", &Colorset::serialize) - .function("unserialize", &Colorset::unserialize) - .function("addColor", select_overload(&Colorset::addColor)) - .function("addColorHSV", &Colorset::addColorHSV) - .function("randomize", &Colorset::randomize) - .function("adjustBrightness", &Colorset::adjustBrightness) - .function("removeColor", &Colorset::removeColor) - .function("operator[]", &Colorset::operator[]) + .constructor(&wasm_Colorset_new) + .constructor(&wasm_Colorset_newRGB) + .constructor(&wasm_Colorset_newCols, allow_raw_pointers()) + .function("init", &Colorset_init, allow_raw_pointers()) + .function("clear", &Colorset_clear, allow_raw_pointers()) + .function("equals", &Colorset_equals, allow_raw_pointers()) + .function("get", &Colorset_get, allow_raw_pointers()) + .function("set", &Colorset_setColor, allow_raw_pointers()) + .function("skip", &Colorset_skip, allow_raw_pointers()) + .function("cur", &Colorset_cur, allow_raw_pointers()) + .function("setCurIndex", &Colorset_setCurIndex, allow_raw_pointers()) + .function("resetIndex", &Colorset_resetIndex, allow_raw_pointers()) + .function("curIndex", &Colorset_curIndex, allow_raw_pointers()) + .function("getPrev", &Colorset_getPrev, allow_raw_pointers()) + .function("getNext", &Colorset_getNext, allow_raw_pointers()) + .function("peek", &Colorset_peek, allow_raw_pointers()) + .function("peekNext", &Colorset_peekNext, allow_raw_pointers()) + .function("numColors", &Colorset_numColors, allow_raw_pointers()) + .function("onStart", &Colorset_onStart, allow_raw_pointers()) + .function("onEnd", &Colorset_onEnd, allow_raw_pointers()) + .function("serialize", &Colorset_serialize, allow_raw_pointers()) + .function("unserialize", &Colorset_unserialize, allow_raw_pointers()) + .function("addColor", &Colorset_addColor, allow_raw_pointers()) + .function("addColorHSV", &Colorset_addColorHSV, allow_raw_pointers()) + .function("randomize", &Colorset_randomize, allow_raw_pointers()) + .function("adjustBrightness", &Colorset_adjustBrightness, allow_raw_pointers()) + .function("removeColor", &Colorset_removeColor, allow_raw_pointers()) + .function("operator[]", &Colorset_at, allow_raw_pointers()) // If more specific or additional randomize functions are needed, they can be added here as well - .function("randomizeSolid", &Colorset::randomizeSolid) - .function("randomizeComplimentary", &Colorset::randomizeComplimentary) - .function("randomizeTriadic", &Colorset::randomizeTriadic) - .function("randomizeSquare", &Colorset::randomizeSquare) - .function("randomizePentadic", &Colorset::randomizePentadic) - .function("randomizeRainbow", &Colorset::randomizeRainbow); + .function("randomizeSolid", &Colorset_randomizeSolid, allow_raw_pointers()) + .function("randomizeComplimentary", &Colorset_randomizeComplimentary, allow_raw_pointers()) + .function("randomizeTriadic", &Colorset_randomizeTriadic, allow_raw_pointers()) + .function("randomizeSquare", &Colorset_randomizeSquare, allow_raw_pointers()) + .function("randomizePentadic", &Colorset_randomizePentadic, allow_raw_pointers()) + .function("randomizeRainbow", &Colorset_randomizeRainbow, allow_raw_pointers()); class_("PatternArgs") - .constructor<>() - .constructor() - .constructor() - .constructor() - .constructor() - .constructor() - .constructor() - .constructor() - .constructor() - .function("init", select_overload(&PatternArgs::init)) - .function("init", select_overload(&PatternArgs::init)) - .function("init", select_overload(&PatternArgs::init)) - .function("init", select_overload(&PatternArgs::init)) - .function("init", select_overload(&PatternArgs::init)) - .function("init", select_overload(&PatternArgs::init)) - .function("init", select_overload(&PatternArgs::init)) - .function("init", select_overload(&PatternArgs::init)) - .function("init", select_overload(&PatternArgs::init)) - .function("addArgs", select_overload(&PatternArgs::addArgs)) - .function("addArgs", select_overload(&PatternArgs::addArgs)) - .function("addArgs", select_overload(&PatternArgs::addArgs)) - .function("addArgs", select_overload(&PatternArgs::addArgs)) - .function("addArgs", select_overload(&PatternArgs::addArgs)) - .function("addArgs", select_overload(&PatternArgs::addArgs)) - .function("addArgs", select_overload(&PatternArgs::addArgs)) - .function("addArgs", select_overload(&PatternArgs::addArgs)) + .constructor(&wasm_PatternArgs_new0) + .constructor(&wasm_PatternArgs_new1) + .constructor(&wasm_PatternArgs_new2) + .constructor(&wasm_PatternArgs_new3) + .constructor(&wasm_PatternArgs_new4) + .constructor(&wasm_PatternArgs_new5) + .constructor(&wasm_PatternArgs_new6) + .constructor(&wasm_PatternArgs_new7) + .constructor(&wasm_PatternArgs_new8) + .function("init", &PatternArgs_init0, allow_raw_pointers()) + .function("init", &PatternArgs_init1, allow_raw_pointers()) + .function("init", &PatternArgs_init2, allow_raw_pointers()) + .function("init", &PatternArgs_init3, allow_raw_pointers()) + .function("init", &PatternArgs_init4, allow_raw_pointers()) + .function("init", &PatternArgs_init5, allow_raw_pointers()) + .function("init", &PatternArgs_init6, allow_raw_pointers()) + .function("init", &PatternArgs_init7, allow_raw_pointers()) + .function("init", &PatternArgs_init8, allow_raw_pointers()) + .function("addArgs", &PatternArgs_addArgs1, allow_raw_pointers()) + .function("addArgs", &PatternArgs_addArgs2, allow_raw_pointers()) + .function("addArgs", &PatternArgs_addArgs3, allow_raw_pointers()) + .function("addArgs", &PatternArgs_addArgs4, allow_raw_pointers()) + .function("addArgs", &PatternArgs_addArgs5, allow_raw_pointers()) + .function("addArgs", &PatternArgs_addArgs6, allow_raw_pointers()) + .function("addArgs", &PatternArgs_addArgs7, allow_raw_pointers()) + .function("addArgs", &PatternArgs_addArgs8, allow_raw_pointers()) .property("arg1", &PatternArgs::arg1) .property("arg2", &PatternArgs::arg2) .property("arg3", &PatternArgs::arg3) @@ -380,33 +475,33 @@ EMSCRIPTEN_BINDINGS(Vortex) { .property("arg8", &PatternArgs::arg8); class_("Pattern") - .function("bind", &Pattern::bind) - .function("init", &Pattern::init) - .function("serialize", &Pattern::serialize) - .function("unserialize", &Pattern::unserialize) - .function("setArg", &Pattern::setArg) - .function("getArg", &Pattern::getArg) - .function("setArgs", &Pattern::setArgs) - .function("getArgs", &Pattern::getArgs) - .function("getNumArgs", &Pattern::getNumArgs) - .function("equals", &Pattern::equals, allow_raw_pointer()) - .function("getColorset", select_overload(&Pattern::getColorset)) - .function("setColorset", &Pattern::setColorset) - .function("clearColorset", &Pattern::clearColorset) - .function("setLedPos", &Pattern::setLedPos) - .function("getPatternID", &Pattern::getPatternID) - .function("getLedPos", &Pattern::getLedPos) - .function("getFlags", &Pattern::getFlags) - .function("hasFlags", &Pattern::hasFlags); - - class_("PatternBuilder") - .class_function("make", &PatternBuilder::make, allow_raw_pointers()) - .class_function("dupe", &PatternBuilder::dupe, allow_raw_pointers()) - .class_function("makeSingle", &PatternBuilder::makeSingle, allow_raw_pointers()) - .class_function("makeMulti", &PatternBuilder::makeMulti, allow_raw_pointers()) - //.function("unserialize", &PatternBuilder::unserialize) - .class_function("getDefaultArgs", &PatternBuilder::getDefaultArgs) - .class_function("numDefaultArgs", &PatternBuilder::numDefaultArgs); + .function("bind", &Pattern_bind, allow_raw_pointers()) + .function("init", &Pattern_init, allow_raw_pointers()) + .function("serialize", &Pattern_serialize, allow_raw_pointers()) + .function("unserialize", &Pattern_unserialize, allow_raw_pointers()) + .function("setArg", &Pattern_setArg, allow_raw_pointers()) + .function("getArg", &Pattern_getArg, allow_raw_pointers()) + .function("setArgs", &Pattern_setArgs, allow_raw_pointers()) + .function("getArgs", &Pattern_getArgs, allow_raw_pointers()) + .function("getNumArgs", &Pattern_getNumArgs, allow_raw_pointers()) + .function("equals", &Pattern_equals, allow_raw_pointers()) + .function("getColorset", &Pattern_getColorset, allow_raw_pointers()) + .function("setColorset", &Pattern_setColorset, allow_raw_pointers()) + .function("clearColorset", &Pattern_clearColorset, allow_raw_pointers()) + .function("setLedPos", &Pattern_setLedPos, allow_raw_pointers()) + .function("getPatternID", &Pattern_getPatternID, allow_raw_pointers()) + .function("getLedPos", &Pattern_getLedPos, allow_raw_pointers()) + .function("getFlags", &Pattern_getFlags, allow_raw_pointers()) + .function("hasFlags", &Pattern_hasFlags, allow_raw_pointers()); + + class_("PatternBuilder") + .class_function("make", &PatternBuilder_make, allow_raw_pointers()) + .class_function("dupe", &PatternBuilder_dupe, allow_raw_pointers()) + .class_function("makeSingle", &PatternBuilder_makeSingle, allow_raw_pointers()) + .class_function("makeMulti", &PatternBuilder_makeMulti, allow_raw_pointers()) + //.function("unserialize", &PatternBuilder_unserialize) + .class_function("getDefaultArgs", &PatternBuilder_getDefaultArgs, allow_raw_pointers()) + .class_function("numDefaultArgs", &PatternBuilder_numDefaultArgs); class_("Mode") //.constructor<>() @@ -415,96 +510,96 @@ EMSCRIPTEN_BINDINGS(Vortex) { //.constructor() //.constructor() //.constructor() - .function("copyFrom", select_overload(&Mode::operator=)) - .function("equals", &Mode::operator==) - .function("notEquals", &Mode::operator!=) - .function("init", &Mode::init) - .function("play", &Mode::play) - .function("saveToBuffer", &Mode::saveToBuffer) - .function("loadFromBuffer", &Mode::loadFromBuffer) - .function("serialize", &Mode::serialize) - .function("unserialize", &Mode::unserialize) - .function("equalsMode", &Mode::equals, allow_raw_pointer()) - //.function("setLedCount", &Mode::setLedCount) - .function("getLedCount", &Mode::getLedCount) - .function("getColorset", select_overload(&Mode::getColorset)) - .function("getPatternID", &Mode::getPatternID) - .function("getPattern", select_overload(&Mode::getPattern), emscripten::allow_raw_pointers()) - .function("getConstPattern", select_overload(&Mode::getPattern), emscripten::allow_raw_pointers()) - .function("setPattern", static_cast(&Mode::setPattern), emscripten::allow_raw_pointers()) - .function("setPattern2", &Mode::setPattern, allow_raw_pointers()) - .function("setColorset", select_overload(&Mode::setColorset)) - .function("clearPattern", &Mode::clearPattern) - .function("clearColorset", &Mode::clearColorset) - .function("setArg", &Mode::setArg) - .function("getArg", &Mode::getArg) - .function("hasMultiLed", &Mode::hasMultiLed) - .function("hasSingleLed", &Mode::hasSingleLed) - .function("hasSameSingleLed", &Mode::hasSameSingleLed) - .function("hasSparseSingleLed", &Mode::hasSparseSingleLed) - .function("isEmpty", &Mode::isEmpty) - //.function("getSingleLedMap", &Mode::getSingleLedMap) - .function("isMultiLed", &Mode::isMultiLed); - - class_("Menus") - .class_function("init", &Menus::init) - .class_function("cleanup", &Menus::cleanup) - .class_function("run", &Menus::run) - .class_function("openMenuSelection", &Menus::openMenuSelection) - .class_function("openMenu", &Menus::openMenu) - .class_function("showSelection", &Menus::showSelection) - .class_function("checkOpen", &Menus::checkOpen) - .class_function("checkInMenu", &Menus::checkInMenu) - .class_function("curMenu", &Menus::curMenu, allow_raw_pointers()) - .class_function("curMenuID", &Menus::curMenuID); + .function("copyFrom", &Mode_assign, allow_raw_pointers()) + .function("equals", &Mode_opEquals, allow_raw_pointers()) + .function("notEquals", &Mode_opNotEquals, allow_raw_pointers()) + .function("init", &Mode_initPatterns, allow_raw_pointers()) + .function("play", &Mode_play, allow_raw_pointers()) + .function("saveToBuffer", &Mode_saveToBuffer, allow_raw_pointers()) + .function("loadFromBuffer", &Mode_loadFromBuffer, allow_raw_pointers()) + .function("serialize", &Mode_serialize, allow_raw_pointers()) + .function("unserialize", &Mode_unserialize, allow_raw_pointers()) + .function("equalsMode", &Mode_equals, allow_raw_pointers()) + //.function("setLedCount", &Mode_setLedCount) + .function("getLedCount", &Mode_getLedCount, allow_raw_pointers()) + .function("getColorset", &Mode_getColorset, allow_raw_pointers()) + .function("getPatternID", &Mode_getPatternID, allow_raw_pointers()) + .function("getPattern", &Mode_getPatternMutable, allow_raw_pointers()) + .function("getConstPattern", &Mode_getPattern, allow_raw_pointers()) + .function("setPattern", &Mode_setPattern, allow_raw_pointers()) + .function("setPattern2", &Mode_setPattern, allow_raw_pointers()) + .function("setColorset", &Mode_setColorset, allow_raw_pointers()) + .function("clearPattern", &Mode_clearPattern, allow_raw_pointers()) + .function("clearColorset", &Mode_clearColorset, allow_raw_pointers()) + .function("setArg", &Mode_setArg, allow_raw_pointers()) + .function("getArg", &Mode_getArg, allow_raw_pointers()) + .function("hasMultiLed", &Mode_hasMultiLed, allow_raw_pointers()) + .function("hasSingleLed", &Mode_hasSingleLed, allow_raw_pointers()) + .function("hasSameSingleLed", &Mode_hasSameSingleLed, allow_raw_pointers()) + .function("hasSparseSingleLed", &Mode_hasSparseSingleLed, allow_raw_pointers()) + .function("isEmpty", &Mode_isEmpty, allow_raw_pointers()) + //.function("getSingleLedMap", &Mode_getSingleLedMap) + .function("isMultiLed", &Mode_isMultiLed, allow_raw_pointers()); + + class_("Menus") + .class_function("init", &Menus_init) + .class_function("cleanup", &Menus_cleanup) + .class_function("run", &Menus_run) + .class_function("openMenuSelection", &Menus_openMenuSelection) + .class_function("openMenu", &Menus_openMenu) + .class_function("showSelection", &Menus_showSelection) + .class_function("checkOpen", &Menus_checkOpen) + .class_function("checkInMenu", &Menus_checkInMenu) + .class_function("curMenu", &Menus_curMenu, allow_raw_pointers()) + .class_function("curMenuID", &Menus_curMenuID); class_("Menu") - .function("init", &Menu::init) - .function("run", &Menu::run) - .function("onLedSelected", &Menu::onLedSelected) - .function("onShortClick", &Menu::onShortClick) - .function("onLongClick", &Menu::onLongClick) - .function("leaveMenu", &Menu::leaveMenu); - //.function("setTargetLeds", &Menu::setTargetLeds); - - class_("Modes") - .class_function("init", &Modes::init) - .class_function("cleanup", &Modes::cleanup) - .class_function("play", &Modes::play) - .class_function("saveToBuffer", select_overload(&Modes::saveToBuffer)) - .class_function("loadFromBuffer", select_overload(&Modes::loadFromBuffer)) - .class_function("loadStorage", &Modes::loadStorage) - .class_function("saveStorage", &Modes::saveStorage) - .class_function("serialize", &Modes::serialize) - .class_function("unserialize", &Modes::unserialize) - .class_function("setDefaults", &Modes::setDefaults) - .class_function("shiftCurMode", &Modes::shiftCurMode) - .class_function("updateCurMode", &Modes::updateCurMode, allow_raw_pointers()) - .class_function("setCurMode", &Modes::setCurMode, allow_raw_pointers()) - .class_function("curMode", &Modes::curMode, allow_raw_pointers()) - .class_function("nextMode", &Modes::nextMode, allow_raw_pointers()) - .class_function("previousMode", &Modes::previousMode, allow_raw_pointers()) - .class_function("nextModeSkipEmpty", &Modes::nextModeSkipEmpty, allow_raw_pointers()) - .class_function("numModes", &Modes::numModes) - .class_function("curModeIndex", &Modes::curModeIndex) - .class_function("lastSwitchTime", &Modes::lastSwitchTime) - .class_function("deleteCurMode", &Modes::deleteCurMode) - .class_function("clearModes", &Modes::clearModes) - .class_function("setStartupMode", &Modes::setStartupMode) - .class_function("startupMode", &Modes::startupMode) - //.class_function("initCurMode", &Modes::initCurMode, allow_raw_pointers()) - //.class_function("saveCurMode", &Modes::saveCurMode) - .class_function("setFlag", &Modes::setFlag) - .class_function("getFlag", &Modes::getFlag) - .class_function("resetFlags", &Modes::resetFlags) - .class_function("setOneClickMode", &Modes::setOneClickMode) - .class_function("oneClickModeEnabled", &Modes::oneClickModeEnabled) - .class_function("setLocked", &Modes::setLocked) - .class_function("locked", &Modes::locked) - .class_function("setAdvancedMenus", &Modes::setAdvancedMenus) - .class_function("advancedMenusEnabled", &Modes::advancedMenusEnabled) - .class_function("setKeychainMode", &Modes::setKeychainMode) - .class_function("keychainModeEnabled", &Modes::keychainModeEnabled); + .function("init", &Menu_init, allow_raw_pointers()) + .function("run", &Menu_run, allow_raw_pointers()) + .function("onLedSelected", &Menu_onLedSelected, allow_raw_pointers()) + .function("onShortClick", &Menu_onShortClick, allow_raw_pointers()) + .function("onLongClick", &Menu_onLongClick, allow_raw_pointers()) + .function("leaveMenu", &Menu_leaveMenu, allow_raw_pointers()); + //.function("setTargetLeds", &Menu_setTargetLeds); + + class_("Modes") + .class_function("init", &Modes_init) + .class_function("cleanup", &Modes_cleanup) + .class_function("play", &Modes_play) + .class_function("saveToBuffer", &Modes_saveToBuffer, allow_raw_pointers()) + .class_function("loadFromBuffer", &Modes_loadFromBuffer, allow_raw_pointers()) + .class_function("loadStorage", &Modes_loadStorage) + .class_function("saveStorage", &Modes_saveStorage) + .class_function("serialize", &Modes_serialize, allow_raw_pointers()) + .class_function("unserialize", &Modes_unserialize, allow_raw_pointers()) + .class_function("setDefaults", &Modes_setDefaults) + .class_function("shiftCurMode", &Modes_shiftCurMode) + .class_function("updateCurMode", &Modes_updateCurMode, allow_raw_pointers()) + .class_function("setCurMode", &Modes_setCurMode, allow_raw_pointers()) + .class_function("curMode", &Modes_curMode, allow_raw_pointers()) + .class_function("nextMode", &Modes_nextMode, allow_raw_pointers()) + .class_function("previousMode", &Modes_previousMode, allow_raw_pointers()) + .class_function("nextModeSkipEmpty", &Modes_nextModeSkipEmpty, allow_raw_pointers()) + .class_function("numModes", &Modes_numModes) + .class_function("curModeIndex", &Modes_curModeIndex) + .class_function("lastSwitchTime", &Modes_lastSwitchTime) + .class_function("deleteCurMode", &Modes_deleteCurMode) + .class_function("clearModes", &Modes_clearModes) + .class_function("setStartupMode", &Modes_setStartupMode) + .class_function("startupMode", &Modes_startupMode) + //.class_function("initCurMode", &Modes_initCurMode, allow_raw_pointers()) + //.class_function("saveCurMode", &Modes_saveCurMode) + .class_function("setFlag", &Modes_setFlag) + .class_function("getFlag", &Modes_getFlag) + .class_function("resetFlags", &Modes_resetFlags) + .class_function("setOneClickMode", &Modes_setOneClickMode) + .class_function("oneClickModeEnabled", &Modes_oneClickModeEnabled) + .class_function("setLocked", &Modes_setLocked) + .class_function("locked", &Modes_locked) + .class_function("setAdvancedMenus", &Modes_setAdvancedMenus) + .class_function("advancedMenusEnabled", &Modes_advancedMenusEnabled) + .class_function("setKeychainMode", &Modes_setKeychainMode) + .class_function("keychainModeEnabled", &Modes_keychainModeEnabled); class_("Vortex") .class_function("init", select_overload(&Vortex::init)) @@ -521,8 +616,8 @@ EMSCRIPTEN_BINDINGS(Vortex) { .class_function("isButtonPressed", &Vortex::isButtonPressed) .class_function("sendWait", &Vortex::sendWait) .class_function("rapidClick", &Vortex::rapidClick) - .class_function("getMenuDemoMode", &Vortex::getMenuDemoMode, allow_raw_pointer>()) - .class_function("setMenuDemoMode", &Vortex::setMenuDemoMode, allow_raw_pointer>()) + .class_function("getMenuDemoMode", &Vortex::getMenuDemoMode, allow_raw_pointers()) + .class_function("setMenuDemoMode", &Vortex::setMenuDemoMode, allow_raw_pointers()) .class_function("quitClick", &Vortex::quitClick) .class_function("IRDeliver", &Vortex::IRDeliver) .class_function("VLDeliver", &Vortex::VLDeliver) @@ -680,14 +775,14 @@ Vortex::Vortex() // error! out of memory } #ifdef _WIN32 - m_randCtx.seed((uint32_t)(time(NULL) ^ GetTickCount64())); + Random_seed(&m_randCtx, (uint32_t)(time(NULL) ^ GetTickCount64())); #else struct timespec ts; unsigned long theTick = 0U; clock_gettime(CLOCK_MONOTONIC, &ts); theTick = ts.tv_nsec / 1000000; theTick += ts.tv_sec * 1000; - m_randCtx.seed(time(NULL) ^ theTick); + Random_seed(&m_randCtx, time(NULL) ^ theTick); #endif } @@ -732,12 +827,12 @@ bool Vortex::init(VortexCallbacks *callbacks) #endif // init the engine - VortexEngine::init(); + VortexEngine_init(); // load the modes // TODO: don't load modes here? separate api? - Modes::load(); + Modes_load(); // clear the modes - //Modes::clearModes(); + //Modes_clearModes(); // save and set undo buffer doSave(); @@ -748,7 +843,7 @@ bool Vortex::init(VortexCallbacks *callbacks) void Vortex::cleanup() { - VortexEngine::cleanup(); + VortexEngine_cleanup(); if (m_storedCallbacks) { delete m_storedCallbacks; m_storedCallbacks = nullptr; @@ -913,12 +1008,12 @@ bool Vortex::sleepEnabled() // whether the engine is sleeping, and/or to enter sleep void Vortex::enterSleep(bool save) { - VortexEngine::enterSleep(save); + VortexEngine_enterSleep(save); } bool Vortex::isSleeping() { - return VortexEngine::isSleeping(); + return VortexEngine_isSleeping(); } bool Vortex::tick() @@ -942,7 +1037,7 @@ bool Vortex::tick() doCommand(getchar()); } // tick the vortex engine forward - VortexEngine::tick(); + VortexEngine_tick(); return true; } @@ -954,13 +1049,13 @@ void Vortex::installCallbacks(VortexCallbacks *callbacks) void Vortex::setInstantTimestep(bool timestep) { - Time::setInstantTimestep(timestep); + Time_setInstantTimestep(timestep); } // select the button to send clicks to (0 = first button) void Vortex::selectButton(uint8_t buttonIndex) { - if (buttonIndex >= Buttons::numButtons()) { + if (buttonIndex >= Buttons_numButtons()) { return; } m_selectedButton = buttonIndex; @@ -1061,7 +1156,7 @@ void Vortex::rapidClick(uint32_t amount) Mode *Vortex::getMenuDemoMode() { - Menu *pMenu = Menus::curMenu(); + Menu *pMenu = Menus_curMenu(); if (!pMenu) { return nullptr; } @@ -1073,15 +1168,15 @@ bool Vortex::setMenuDemoMode(const Mode *mode) if (!mode) { return false; } - Menu *pMenu = Menus::curMenu(); + Menu *pMenu = Menus_curMenu(); if (!pMenu) { return false; } - if (!mode->getLedCount() || pMenu->m_previewMode.equals(mode)) { + if (!Mode_getLedCount(mode) || Mode_equals(&pMenu->m_previewMode, mode)) { return false; } - pMenu->m_previewMode = *mode; - pMenu->m_previewMode.init(); + Mode_assign(&pMenu->m_previewMode, mode); + Mode_initPatterns(&pMenu->m_previewMode); return true; } @@ -1093,72 +1188,72 @@ void Vortex::quitClick() void Vortex::IRDeliver(uint32_t timing) { #if IR_ENABLE_RECEIVER == 1 - IRReceiver::handleIRTiming(timing); + IRReceiver_handleIRTiming(timing); #endif } void Vortex::VLDeliver(uint32_t timing) { #if VL_ENABLE_RECEIVER == 1 - VLReceiver::handleVLTiming(timing); + VLReceiver_handleVLTiming(timing); #endif } void Vortex::getStorageStats(uint32_t *outTotal, uint32_t *outUsed) { if (outTotal) { - *outTotal = VortexEngine::totalStorageSpace(); + *outTotal = VortexEngine_totalStorageSpace(); } if (outUsed) { - *outUsed = VortexEngine::savefileSize(); + *outUsed = VortexEngine_savefileSize(); } } void Vortex::loadStorage() { - Modes::loadStorage(); + Modes_loadStorage(); } void Vortex::openRandomizer(bool advanced) { - Menus::openMenu(MENU_RANDOMIZER, advanced); + Menus_openMenu(MENU_RANDOMIZER, advanced); } void Vortex::openColorSelect(bool advanced) { - Menus::openMenu(MENU_COLOR_SELECT, advanced); + Menus_openMenu(MENU_COLOR_SELECT, advanced); } void Vortex::openPatternSelect(bool advanced) { - Menus::openMenu(MENU_PATTERN_SELECT, advanced); + Menus_openMenu(MENU_PATTERN_SELECT, advanced); } void Vortex::openGlobalBrightness(bool advanced) { - Menus::openMenu(MENU_GLOBAL_BRIGHTNESS, advanced); + Menus_openMenu(MENU_GLOBAL_BRIGHTNESS, advanced); } void Vortex::openFactoryReset(bool advanced) { - Menus::openMenu(MENU_FACTORY_RESET, advanced); + Menus_openMenu(MENU_FACTORY_RESET, advanced); } void Vortex::openModeSharing(bool advanced) { - Menus::openMenu(MENU_MODE_SHARING, advanced); + Menus_openMenu(MENU_MODE_SHARING, advanced); } void Vortex::openEditorConnection(bool advanced) { #if ENABLE_EDITOR_CONNECTION == 1 - Menus::openMenu(MENU_EDITOR_CONNECTION, advanced); + Menus_openMenu(MENU_EDITOR_CONNECTION, advanced); #endif } //void Vortex::clearMenuTargetLeds() //{ -// Menu *cur = Menus::curMenu(); +// Menu *cur = Menus_curMenu(); // if (!cur) { // return; // } @@ -1167,7 +1262,7 @@ void Vortex::openEditorConnection(bool advanced) // //void Vortex::setMenuTargetLeds(LedMap targetLeds) //{ -// Menu *cur = Menus::curMenu(); +// Menu *cur = Menus_curMenu(); // if (!cur) { // return; // } @@ -1176,7 +1271,7 @@ void Vortex::openEditorConnection(bool advanced) // //void Vortex::addMenuTargetLeds(LedPos pos) //{ -// Menu *cur = Menus::curMenu(); +// Menu *cur = Menus_curMenu(); // if (!cur) { // return; // } @@ -1187,15 +1282,15 @@ void Vortex::openEditorConnection(bool advanced) bool Vortex::getModes(ByteStream &outStream) { // now serialize all the modes - Modes::saveToBuffer(outStream); + Modes_saveToBuffer(&outStream); return true; } bool Vortex::setModes(ByteStream &stream, bool save) { - Modes::clearModes(); + Modes_clearModes(); // now unserialize the stream of data that was read - if (!Modes::loadFromBuffer(stream)) { + if (!Modes_loadFromBuffer(&stream)) { return false; } return !save || doSave(); @@ -1203,41 +1298,41 @@ bool Vortex::setModes(ByteStream &stream, bool save) bool Vortex::matchLedCount(ByteStream &stream, bool vtxMode) { - if (!stream.decompress()) { + if (!ByteStream_decompress(&stream)) { return false; } // reset the unserializer index before unserializing anything - stream.resetUnserializer(); + ByteStream_resetUnserializer(&stream); uint8_t major = 0; uint8_t minor = 0; // unserialize the vortex version - stream.unserialize8(&major); - stream.unserialize8(&minor); + ByteStream_unserialize8(&stream, &major); + ByteStream_unserialize8(&stream, &minor); // unserialize the global brightness if (!vtxMode) { uint8_t flags; - stream.unserialize8(&flags); + ByteStream_unserialize8(&stream, &flags); uint8_t brightness = 0; - stream.unserialize8(&brightness); + ByteStream_unserialize8(&stream, &brightness); uint8_t numModes = 0; - stream.unserialize8(&numModes); + ByteStream_unserialize8(&stream, &numModes); } uint8_t ledCount = 0; - stream.unserialize8(&ledCount); + ByteStream_unserialize8(&stream, &ledCount); // put the unserializer back where it was for the next thing - stream.resetUnserializer(); + ByteStream_resetUnserializer(&stream); return setLedCount(ledCount); } bool Vortex::checkLedCount() { - Mode *mode = Modes::curMode(); + Mode *mode = Modes_curMode(); if (!mode) { return false; } - uint8_t numLeds = mode->getLedCount(); + uint8_t numLeds = Mode_getLedCount(mode); if (numLeds != LED_COUNT) { - //Leds::setLedCount(numLeds); + //Leds_setLedCount(numLeds); return false; } return true; @@ -1255,40 +1350,40 @@ uint8_t Vortex::getLedCount() bool Vortex::getCurMode(ByteStream &outStream) { - Mode *pMode = Modes::curMode(); + Mode *pMode = Modes_curMode(); if (!pMode) { return false; } // save to ensure we get the correct mode, not using doSave() because it causes // an undo buffer entry to be added - if (!Modes::saveStorage()) { + if (!Modes_saveStorage()) { return false; } - return Modes::curMode()->saveToBuffer(outStream); + return Mode_saveToBuffer(Modes_curMode(), &outStream, LED_COUNT); } uint32_t Vortex::curModeIndex() { - return Modes::curModeIndex(); + return Modes_curModeIndex(); } uint32_t Vortex::numModes() { - return Modes::numModes(); + return Modes_numModes(); } uint32_t Vortex::numLedsInMode() { - Mode *pMode = Modes::curMode(); + Mode *pMode = Modes_curMode(); if (!pMode) { return false; } - return pMode->getLedCount(); + return Mode_getLedCount(pMode); } bool Vortex::addMode(const Mode *mode, bool save) { - if (!Modes::addMode(mode)) { + if (!Modes_addMode(mode)) { return false; } return !save || doSave(); @@ -1297,14 +1392,15 @@ bool Vortex::addMode(const Mode *mode, bool save) bool Vortex::addNewMode(bool save) { Colorset set; - set.randomize(m_randCtx); + Colorset_make(&set); + Colorset_randomize(&set, &m_randCtx, 0); // create a random pattern ID from all patterns PatternID randomPattern; do { // continuously re-randomize the pattern so we don't get solids - randomPattern = (PatternID)m_randCtx.next16(PATTERN_FIRST, PATTERN_SINGLE_LAST); + randomPattern = (PatternID)Random_next16(&m_randCtx, PATTERN_FIRST, PATTERN_SINGLE_LAST); } while (randomPattern == PATTERN_SOLID); - if (!Modes::addMode(randomPattern, nullptr, &set)) { + if (!Modes_addMode(randomPattern, nullptr, &set)) { return false; } return !save || doSave(); @@ -1312,7 +1408,7 @@ bool Vortex::addNewMode(bool save) bool Vortex::addNewMode(ByteStream &stream, bool save) { - if (!Modes::addModeFromBuffer(stream)) { + if (!Modes_addModeFromBuffer(&stream)) { return false; } return !save || doSave(); @@ -1320,10 +1416,10 @@ bool Vortex::addNewMode(ByteStream &stream, bool save) bool Vortex::setCurMode(uint32_t index, bool save) { - if (index >= Modes::numModes()) { + if (index >= Modes_numModes()) { return true; } - if (!Modes::setCurMode(index)) { + if (!Modes_setCurMode(index)) { return false; } return !save || doSave(); @@ -1331,54 +1427,54 @@ bool Vortex::setCurMode(uint32_t index, bool save) bool Vortex::nextMode(bool save) { - if (!Modes::numModes()) { + if (!Modes_numModes()) { return true; } - Modes::nextMode(); + Modes_nextMode(); return !save || doSave(); } bool Vortex::delCurMode(bool save) { - if (!Modes::numModes()) { + if (!Modes_numModes()) { return true; } - Modes::deleteCurMode(); + Modes_deleteCurMode(); return !save || doSave(); } bool Vortex::shiftCurMode(int8_t offset, bool save) { - if (!Modes::numModes()) { + if (!Modes_numModes()) { return true; } if (offset == 0) { return true; } - Modes::shiftCurMode(offset); + Modes_shiftCurMode(offset); return !save || doSave(); } bool Vortex::setPattern(PatternID id, const PatternArgs *args, const Colorset *set, bool save) { - Mode *pMode = Modes::curMode(); + Mode *pMode = Modes_curMode(); if (!pMode) { return false; } - if (!pMode->setPattern(id, LED_ANY, args, set)) { + if (!Mode_setPattern(pMode, id, LED_ANY, args, set)) { return false; } - pMode->init(); + Mode_initPatterns(pMode); return !save || doSave(); } PatternID Vortex::getPatternID(LedPos pos) { - Mode *pMode = Modes::curMode(); + Mode *pMode = Modes_curMode(); if (!pMode) { return PATTERN_NONE; } - return pMode->getPatternID(pos); + return Mode_getPatternID(pMode, pos); } string Vortex::getPatternName(LedPos pos) @@ -1388,21 +1484,21 @@ string Vortex::getPatternName(LedPos pos) string Vortex::getModeName() { - Mode *pMode = Modes::curMode(); + Mode *pMode = Modes_curMode(); if (!pMode) { return patternToString(PATTERN_NONE); } - if (pMode->isMultiLed()) { - return patternToString(pMode->getPatternID(LED_MULTI)); + if (Mode_isMultiLed(pMode)) { + return patternToString(Mode_getPatternID(pMode, LED_MULTI)); } // can't use isSampleSingleLed because that will compare the entire // pattern for differences in any single led pattern, we only care // about the pattern id being different bool all_same_id = true; - PatternID first = pMode->getPatternID(LED_FIRST); + PatternID first = Mode_getPatternID(pMode, LED_FIRST); for (uint32_t i = LED_FIRST + 1; i < numLedsInMode(); ++i) { // if any don't match 0 then no good - if (pMode->getPatternID((LedPos)i) != first) { + if (Mode_getPatternID(pMode, (LedPos)i) != first) { all_same_id = false; break; } @@ -1418,57 +1514,57 @@ string Vortex::getModeName() bool Vortex::setPatternAt(LedPos pos, PatternID id, const PatternArgs *args, const Colorset *set, bool save) { - Mode *pMode = Modes::curMode(); + Mode *pMode = Modes_curMode(); if (!pMode) { return false; } - if (!pMode->setPattern(id, pos, args, set)) { + if (!Mode_setPattern(pMode, id, pos, args, set)) { return false; } - pMode->init(); + Mode_initPatterns(pMode); return !save || doSave(); } bool Vortex::getColorset(LedPos pos, Colorset &set) { - Mode *pMode = Modes::curMode(); + Mode *pMode = Modes_curMode(); if (!pMode) { return false; } - set = pMode->getColorset(pos); + set = Mode_getColorset(pMode, pos); return true; } bool Vortex::setColorset(LedPos pos, const Colorset &set, bool save) { - Mode *pMode = Modes::curMode(); + Mode *pMode = Modes_curMode(); if (!pMode) { return false; } - if (!pMode->setColorset(set, pos)) { + if (!Mode_setColorset(pMode, &set, pos)) { return false; } - pMode->init(); + Mode_initPatterns(pMode); return !save || doSave(); } bool Vortex::getPatternArgs(LedPos pos, PatternArgs &args) { - Mode *pMode = Modes::curMode(); + Mode *pMode = Modes_curMode(); if (!pMode) { return false; } - Pattern *pat = pMode->getPattern(pos); + Pattern *pat = Mode_getPatternMutable(pMode, pos); if (!pat) { return false; } - pat->getArgs(args); + Pattern_getArgs(pat, &args); return true; } bool Vortex::setPatternArgs(LedPos pos, PatternArgs &args, bool save) { - Mode *pMode = Modes::curMode(); + Mode *pMode = Modes_curMode(); if (!pMode) { return false; } @@ -1476,25 +1572,25 @@ bool Vortex::setPatternArgs(LedPos pos, PatternArgs &args, bool save) // Equivalent to cases LED_ANY, LED_ALL and LED_MULTI if (pos == LED_ANY || pos == LED_ALL || pos == LED_MULTI) { #if VORTEX_SLIM == 0 - pat = pMode->getPattern(LED_MULTI); + pat = Mode_getPatternMutable(pMode, LED_MULTI); if (pat) { - pat->setArgs(args); + Pattern_setArgs(pat, &args); } if (pos == LED_MULTI) { // don't fallthrough if actually multi, it's possible // we got here by falling through from LED_ALL - pMode->init(); + Mode_initPatterns(pMode); return !save || doSave(); } // fall through if LED_ALL and change the single leds else if (pos == LED_ANY || pos == LED_ALL) { for (LedPos pos = LED_FIRST; pos < LED_COUNT; ++pos) { - pat = pMode->getPattern(pos); + pat = Mode_getPatternMutable(pMode, pos); if (pat) { - pat->setArgs(args); + Pattern_setArgs(pat, &args); } } - pMode->init(); + Mode_initPatterns(pMode); // actually break here return !save || doSave(); } @@ -1504,12 +1600,12 @@ bool Vortex::setPatternArgs(LedPos pos, PatternArgs &args, bool save) // equivalent to case LED_ALL_SINGLE else if (pos == LED_ALL_SINGLE) { for (LedPos pos = LED_FIRST; pos < LED_COUNT; ++pos) { - pat = pMode->getPattern(pos); + pat = Mode_getPatternMutable(pMode, pos); if (pat) { - pat->setArgs(args); + Pattern_setArgs(pat, &args); } } - pMode->init(); + Mode_initPatterns(pMode); // actually break here return !save || doSave(); } @@ -1519,12 +1615,12 @@ bool Vortex::setPatternArgs(LedPos pos, PatternArgs &args, bool save) if (pos >= LED_COUNT) { return false; } - pat = pMode->getPattern(pos); + pat = Mode_getPatternMutable(pMode, pos); if (!pat) { return false; } - pat->setArgs(args); - pMode->init(); + Pattern_setArgs(pat, &args); + Mode_initPatterns(pMode); return !save || doSave(); } @@ -1533,11 +1629,11 @@ bool Vortex::setPatternArgs(LedPos pos, PatternArgs &args, bool save) bool Vortex::isCurModeMulti() { - Mode *pMode = Modes::curMode(); + Mode *pMode = Modes_curMode(); if (!pMode) { return false; } - return pMode->isMultiLed(); + return Mode_isMultiLed(pMode); } string Vortex::patternToString(PatternID id) @@ -1581,20 +1677,21 @@ string Vortex::ledToString(LedPos pos) // the number of custom parameters for any given pattern id uint32_t Vortex::numCustomParams(PatternID id) { - return PatternBuilder::numDefaultArgs(id); + return PatternBuilder_numDefaultArgs(id); } vector Vortex::getCustomParams(PatternID id) { - Pattern *pat = PatternBuilder::make(id); + Pattern *pat = PatternBuilder_make(id, nullptr); vector params; if (!pat) { return params; } - for (uint32_t i = 0; i < pat->getNumArgs(); ++i) { - params.push_back(pat->getArgName(i)); + for (uint32_t i = 0; i < Pattern_getNumArgs(pat); ++i) { + params.push_back(Pattern_getArgName(pat, i)); } - delete pat; + Pattern_destroy(pat); + PatternBuilder_freePattern(pat); return params; } @@ -1609,31 +1706,40 @@ bool Vortex::addUndoBuffer() return true; } ByteStream modes; + ByteStream_make(&modes); getModes(modes); - if (!modes.size()) { + if (!ByteStream_size(&modes)) { + ByteStream_destroy(&modes); return false; } // only save an undo if the buffer is different - if (m_undoBuffer.size() > 0 && modes.CRC() == m_undoBuffer.back().CRC()) { + if (m_undoBuffer.size() > 0 && ByteStream_CRC(&modes) == ByteStream_CRC(&m_undoBuffer.back())) { //printf("Not saving duplicate undo buffer\n"); + ByteStream_destroy(&modes); return false; } // must rewind to that step before pushing next step while (m_undoIndex > 0) { + ByteStream_destroy(&m_undoBuffer.back()); m_undoBuffer.pop_back(); m_undoIndex--; } - m_undoBuffer.push_back(modes); + // the arena tracks each buffer's owner so move the data into + // the deque element rather than copying the struct + m_undoBuffer.emplace_back(); + ByteStream_make(&m_undoBuffer.back()); + ByteStream_move(&modes, &m_undoBuffer.back()); //printf("Pushing undo buffer (pos: %u)\n", m_undoIndex); // make sure list doesn't grow too big if (m_undoLimit && m_undoBuffer.size() > m_undoLimit) { //printf("Popping front of undo buffer\n"); + ByteStream_destroy(&m_undoBuffer.front()); m_undoBuffer.pop_front(); } #if 0 printf("Buffer:\n"); for (uint32_t i = 0; i < m_undoBuffer.size(); ++i) { - printf("\t%u: %x", i, m_undoBuffer[i].CRC()); + printf("\t%u: %x", i, ByteStream_CRC(&m_undoBuffer[i])); if ((m_undoBuffer.size() - 1) - m_undoIndex == i) { printf(" <--\n"); } else { @@ -1657,7 +1763,7 @@ bool Vortex::applyUndo() //printf("Buffer:\n"); #if 0 for (uint32_t i = 0; i < m_undoBuffer.size(); ++i) { - printf("\t%u: %x", i, m_undoBuffer[i].CRC()); + printf("\t%u: %x", i, ByteStream_CRC(&m_undoBuffer[i])); if ((m_undoBuffer.size() - 1) - m_undoIndex == i) { printf(" <--\n"); } else { @@ -1667,7 +1773,7 @@ bool Vortex::applyUndo() #endif // index from the back instead of the front uint32_t backIndex = highestIndex - m_undoIndex; - m_undoBuffer[backIndex].resetUnserializer(); + ByteStream_resetUnserializer(&m_undoBuffer[backIndex]); Vortex::setModes(m_undoBuffer[backIndex], false); return true; } @@ -1701,17 +1807,17 @@ bool Vortex::redo() void Vortex::setTickrate(uint32_t tickrate) { - Time::setTickrate(tickrate); + Time_setTickrate(tickrate); } uint32_t Vortex::getTickrate() { - return Time::getTickrate(); + return Time_getTickrate(); } bool Vortex::doSave() { - return Modes::saveStorage() && addUndoBuffer(); + return Modes_saveStorage() && addUndoBuffer(); } void Vortex::handleInputQueue(Button *buttons, uint32_t numButtons) @@ -1741,14 +1847,14 @@ void Vortex::handleInputQueue(Button *buttons, uint32_t numButtons) case EVENT_SHORT_CLICK: pButton->m_newRelease = true; pButton->m_shortClick = true; - pButton->m_pressTime = Time::getCurtime(); + pButton->m_pressTime = Time_getCurtime(); pButton->m_holdDuration = SHORT_CLICK_THRESHOLD_TICKS - 1; DEBUG_LOG("Injecting short click"); break; case EVENT_LONG_CLICK: pButton->m_newRelease = true; pButton->m_longClick = true; - pButton->m_pressTime = Time::getCurtime(); + pButton->m_pressTime = Time_getCurtime(); pButton->m_holdDuration = SHORT_CLICK_THRESHOLD_TICKS + 1; DEBUG_LOG("Injecting long click"); break; @@ -1757,7 +1863,7 @@ void Vortex::handleInputQueue(Button *buttons, uint32_t numButtons) // to something more than the menu trigger threshold that will make // us immediately enter the menus. But we need to unset the pressed // button right after so we push a reset click event to reset the button - pButton->m_pressTime = Time::getCurtime(); + pButton->m_pressTime = Time_getCurtime(); #if defined(SLEEP_ENTER_THRESHOLD_TICKS) && defined(SLEEP_WINDOW_THRESHOLD_TICKS) // microlight must hold longer (past sleep time) pButton->m_holdDuration = MENU_TRIGGER_THRESHOLD_TICKS + SLEEP_ENTER_THRESHOLD_TICKS + SLEEP_WINDOW_THRESHOLD_TICKS + 1; @@ -1773,7 +1879,7 @@ void Vortex::handleInputQueue(Button *buttons, uint32_t numButtons) // to something more than the menu trigger threshold that will make // us immediately enter the menus. But we need to unset the pressed // button right after so we push a reset click event to reset the button - pButton->m_pressTime = Time::getCurtime(); + pButton->m_pressTime = Time_getCurtime(); pButton->m_holdDuration = ADV_MENU_DURATION_TICKS + 1; pButton->m_longClick = true; pButton->m_newRelease = true; @@ -1784,7 +1890,7 @@ void Vortex::handleInputQueue(Button *buttons, uint32_t numButtons) // to something more than the menu trigger threshold that will make // us immediately enter the menus. But we need to unset the pressed // button right after so we push a reset click event to reset the button - pButton->m_pressTime = Time::getCurtime(); + pButton->m_pressTime = Time_getCurtime(); pButton->m_holdDuration = DELETE_THRESHOLD_TICKS + DELETE_CYCLE_TICKS + 1; pButton->m_longClick = true; pButton->m_newRelease = true; @@ -1796,7 +1902,7 @@ void Vortex::handleInputQueue(Button *buttons, uint32_t numButtons) // us immediately enter the menus. But we need to unset the pressed // button right after so we push a reset click event to reset the button #ifdef SLEEP_ENTER_THRESHOLD_TICKS - pButton->m_pressTime = Time::getCurtime(); + pButton->m_pressTime = Time_getCurtime(); pButton->m_holdDuration = SLEEP_ENTER_THRESHOLD_TICKS + 1; pButton->m_longClick = true; pButton->m_newRelease = true; @@ -1809,7 +1915,7 @@ void Vortex::handleInputQueue(Button *buttons, uint32_t numButtons) // us immediately enter the menus. But we need to unset the pressed // button right after so we push a reset click event to reset the button #ifdef FORCE_SLEEP_THRESHOLD_TICKS - pButton->m_pressTime = Time::getCurtime(); + pButton->m_pressTime = Time_getCurtime(); pButton->m_holdDuration = FORCE_SLEEP_THRESHOLD_TICKS + 1; pButton->m_longClick = true; pButton->m_newRelease = true; @@ -1824,20 +1930,20 @@ void Vortex::handleInputQueue(Button *buttons, uint32_t numButtons) // ticks the engine forward some number of ticks, the event queue is empty // so the engine won't process any input events while doing this for (uint32_t i = 0; i < buttonEvent.target; ++i) { - VortexEngine::tick(); + VortexEngine_tick(); } // then restore the event queue so that events are processed like normal swap(backup, m_buttonEventQueue); } break; case EVENT_TOGGLE_CLICK: - if (pButton->isPressed()) { + if (Button_isPressed(pButton)) { // re-calc all this stuff because there's no api in Button class to do it // I don't want to add the api there because it's useless besides for this pButton->m_buttonState = false; pButton->m_isPressed = false; - pButton->m_holdDuration = (uint32_t)(Time::getCurtime() - pButton->m_pressTime); - pButton->m_releaseTime = Time::getCurtime(); + pButton->m_holdDuration = (uint32_t)(Time_getCurtime() - pButton->m_pressTime); + pButton->m_releaseTime = Time_getCurtime(); pButton->m_newRelease = true; pButton->m_shortClick = (pButton->m_holdDuration <= SHORT_CLICK_THRESHOLD_TICKS); pButton->m_longClick = !pButton->m_shortClick; @@ -1845,8 +1951,8 @@ void Vortex::handleInputQueue(Button *buttons, uint32_t numButtons) } else { pButton->m_buttonState = true; pButton->m_isPressed = true; - pButton->m_releaseDuration = (uint32_t)(Time::getCurtime() - pButton->m_releaseTime); - pButton->m_pressTime = Time::getCurtime(); + pButton->m_releaseDuration = (uint32_t)(Time_getCurtime() - pButton->m_releaseTime); + pButton->m_pressTime = Time_getCurtime(); pButton->m_newPress = true; DEBUG_LOG("Injecting press"); } @@ -1855,7 +1961,7 @@ void Vortex::handleInputQueue(Button *buttons, uint32_t numButtons) pButton->m_consecutivePresses = buttonEvent.target; pButton->m_newRelease = true; pButton->m_shortClick = true; - pButton->m_pressTime = Time::getCurtime(); + pButton->m_pressTime = Time_getCurtime(); pButton->m_holdDuration = 1; DEBUG_LOGF("Injecting %u x rapid click", buttonEvent.target); break; @@ -1936,12 +2042,12 @@ void Vortex::printlog(const char *file, const char *func, int line, const char * void Vortex::setStorageFilename(const string &name) { - Storage::setStorageFilename(name); + Storage_setStorageFilename(name); } string Vortex::getStorageFilename() { - return Storage::getStorageFilename(); + return Storage_getStorageFilename(); } json Vortex::modeToJson(const Mode *mode) @@ -1951,17 +2057,17 @@ json Vortex::modeToJson(const Mode *mode) } json modeJson; - modeJson["num_leds"] = mode->getLedCount(); - modeJson["flags"] = (uint8_t)mode->getFlags(); + modeJson["num_leds"] = Mode_getLedCount(mode); + modeJson["flags"] = (uint8_t)Mode_getFlags(mode); - const Pattern *multiPattern = mode->getPattern(LED_MULTI); + const Pattern *multiPattern = Mode_getPattern(mode, LED_MULTI); if (multiPattern) { modeJson["multi_pat"] = patternToJson(multiPattern); } json singlePatterns = json::array(); - for (LedPos l = LED_FIRST; l < mode->getLedCount(); ++l) { - const Pattern *pattern = mode->getPattern(l); + for (LedPos l = LED_FIRST; l < Mode_getLedCount(mode); ++l) { + const Pattern *pattern = Mode_getPattern(mode, l); if (pattern) { singlePatterns.push_back(patternToJson(pattern)); } else { @@ -1979,7 +2085,8 @@ Mode *Vortex::modeFromJson(const json &modeJson) return nullptr; } - Mode *mode = new Mode(); + Mode *mode = new Mode; + Mode_init(mode); if (!mode) { return nullptr; } @@ -1996,10 +2103,12 @@ Mode *Vortex::modeFromJson(const json &modeJson) Pattern *multiPattern = patternFromJson(multiPatJson); if (multiPattern) { PatternArgs args; - multiPattern->getArgs(args); - Colorset set = multiPattern->getColorset(); - mode->setPattern(multiPattern->getPatternID(), LED_MULTI, &args, &set); - delete multiPattern; + PatternArgs_make0(&args); + Pattern_getArgs(multiPattern, &args); + Colorset set = Pattern_getColorset(multiPattern); + Mode_setPattern(mode, Pattern_getPatternID(multiPattern), LED_MULTI, &args, &set); + Pattern_destroy(multiPattern); + PatternBuilder_freePattern(multiPattern); } } @@ -2011,10 +2120,12 @@ Mode *Vortex::modeFromJson(const json &modeJson) Pattern *pattern = patternFromJson(patJson); if (pattern) { PatternArgs args; - pattern->getArgs(args); - Colorset set = pattern->getColorset(); - mode->setPattern(pattern->getPatternID(), pos++, &args, &set); - delete pattern; + PatternArgs_make0(&args); + Pattern_getArgs(pattern, &args); + Colorset set = Pattern_getColorset(pattern); + Mode_setPattern(mode, Pattern_getPatternID(pattern), pos++, &args, &set); + Pattern_destroy(pattern); + PatternBuilder_freePattern(pattern); } } } @@ -2027,10 +2138,12 @@ Mode *Vortex::modeFromJson(const json &modeJson) return mode; } PatternArgs args; - pattern->getArgs(args); - Colorset set = pattern->getColorset(); - mode->setPattern(pattern->getPatternID(), LED_FIRST, &args, &set); - delete pattern; + PatternArgs_make0(&args); + Pattern_getArgs(pattern, &args); + Colorset set = Pattern_getColorset(pattern); + Mode_setPattern(mode, Pattern_getPatternID(pattern), LED_FIRST, &args, &set); + Pattern_destroy(pattern); + PatternBuilder_freePattern(pattern); } return mode; @@ -2043,15 +2156,15 @@ json Vortex::patternToJson(const Pattern *pattern) } json patternJson; - patternJson["pattern_id"] = pattern->getPatternID(); - patternJson["flags"] = pattern->getFlags(); + patternJson["pattern_id"] = Pattern_getPatternID(pattern); + patternJson["flags"] = Pattern_getFlags(pattern); - const Colorset &colorset = pattern->getColorset(); - patternJson["numColors"] = colorset.numColors(); + const Colorset &colorset = Pattern_getColorset(pattern); + patternJson["numColors"] = Colorset_numColors(&colorset); json colorsetArray = json::array(); - for (uint8_t c = 0; c < colorset.numColors(); ++c) { - const RGBColor &color = colorset.get(c); + for (uint8_t c = 0; c < Colorset_numColors(&colorset); ++c) { + const RGBColor &color = Colorset_get(&colorset, c); std::stringstream colorString; colorString << "0x" << std::setfill('0') << std::setw(2) << std::hex << (int)color.red @@ -2062,8 +2175,8 @@ json Vortex::patternToJson(const Pattern *pattern) patternJson["colorset"] = colorsetArray; json argsArray = json::array(); - for (uint8_t a = 0; a < pattern->getNumArgs(); ++a) { - argsArray.push_back(pattern->getArg(a)); + for (uint8_t a = 0; a < Pattern_getNumArgs(pattern); ++a) { + argsArray.push_back(Pattern_getArg(pattern, a)); } patternJson["args"] = argsArray; @@ -2089,16 +2202,18 @@ Pattern *Vortex::patternFromJson(const json &patternJson) // Parse out the args PatternArgs args; + PatternArgs_make0(&args); if (patternJson.contains("args") && patternJson["args"].is_array()) { for (const auto &arg : patternJson["args"]) { if (arg.is_number_unsigned()) { - args.addArgs(static_cast(arg.get())); + PatternArgs_addArgs1(&args, static_cast(arg.get())); } } } // Parse out the colorset Colorset set; + Colorset_make(&set); if (patternJson.contains("colorset") && patternJson["colorset"].is_array()) { for (const auto &colorElement : patternJson["colorset"]) { if (colorElement.is_string()) { @@ -2107,20 +2222,20 @@ Pattern *Vortex::patternFromJson(const json &patternJson) strVal = strVal.substr(2); } uint32_t dwCol = strtoul(strVal.c_str(), NULL, 16); - set.addColor(RGBColor(dwCol)); + Colorset_addColor(&set, RGBColor_fromU32(dwCol)); } } } // Build the pattern with ID + args - Pattern *pattern = PatternBuilder::make(id, &args); + Pattern *pattern = PatternBuilder_make(id, &args); if (!pattern) { return nullptr; } // Apply colorset and init - pattern->setColorset(set); - pattern->init(); + Pattern_setColorset(pattern, &set); + Pattern_init(pattern); return pattern; } @@ -2128,7 +2243,7 @@ Pattern *Vortex::patternFromJson(const json &patternJson) json Vortex::saveModeToJson() { json saveJson; - Mode *cur = Modes::curMode(); + Mode *cur = Modes_curMode(); if (!cur) { return nullptr; } @@ -2152,7 +2267,7 @@ bool Vortex::loadModeFromJson(const json &js) // minor = js["version_minor"].get(); //} - //if (!VortexEngine::checkVersion(major, minor)) { + //if (!VortexEngine_checkVersion(major, minor)) { // return false; //} @@ -2160,7 +2275,8 @@ bool Vortex::loadModeFromJson(const json &js) if (!mode) { return false; } - bool rv = Modes::addMode(mode); + bool rv = Modes_addMode(mode); + Mode_destroy(mode); delete mode; return rv; } @@ -2171,17 +2287,17 @@ json Vortex::saveToJson() saveJson["version_major"] = static_cast(VORTEX_VERSION_MAJOR); saveJson["version_minor"] = static_cast(VORTEX_VERSION_MINOR); - saveJson["global_flags"] = Modes::globalFlags(); - saveJson["brightness"] = static_cast(Leds::getBrightness()); + saveJson["global_flags"] = Modes_globalFlags(); + saveJson["brightness"] = static_cast(Leds_getBrightness()); - uint8_t numModes = Modes::numModes(); + uint8_t numModes = Modes_numModes(); saveJson["num_modes"] = numModes; json modesArray = json::array(); - Modes::setCurMode(0); + Modes_setCurMode(0); for (uint8_t i = 0; i < numModes; ++i) { modesArray.push_back(saveModeToJson()); - Modes::nextMode(); + Modes_nextMode(); } saveJson["modes"] = modesArray; @@ -2204,18 +2320,18 @@ bool Vortex::loadFromJson(const json& js) minor = js["version_minor"].get(); } - if (!VortexEngine::checkVersion(major, minor)) { + if (!VortexEngine_checkVersion(major, minor)) { return false; } if (js.contains("brightness") && js["brightness"].is_number_unsigned()) { uint8_t brightness = js["brightness"].get(); - Leds::setBrightness(brightness); + Leds_setBrightness(brightness); } if (js.contains("global_flags") && js["global_flags"].is_number_unsigned()) { uint8_t global_flags = js["global_flags"].get(); - Modes::setFlag(global_flags, true, false); + Modes_setFlag(global_flags, true, false); } uint8_t num_modes = 0; @@ -2227,7 +2343,7 @@ bool Vortex::loadFromJson(const json& js) return false; } - Modes::clearModes(); + Modes_clearModes(); for (const auto &modeValue : js["modes"]) { if (modeValue.is_null() || !modeValue.is_object()) { continue; @@ -2238,14 +2354,14 @@ bool Vortex::loadFromJson(const json& js) } } - return Modes::numModes() == num_modes; + return Modes_numModes() == num_modes; } // print/parse the current mode json std::string Vortex::printModeJson(bool pretty) { std::string jsonStr = "{}"; - Mode *cur = Modes::curMode(); + Mode *cur = Modes_curMode(); if (!cur) { return jsonStr; } @@ -2267,7 +2383,8 @@ bool Vortex::parseModeJson(const std::string &jsonStr) return false; } // update the current mode with the parsed json mode - bool result = Modes::updateCurMode(tempMode); + bool result = Modes_updateCurMode(tempMode); + Mode_destroy(tempMode); delete tempMode; return result; } @@ -2276,11 +2393,11 @@ bool Vortex::parseModeJson(const std::string &jsonStr) std::string Vortex::printPatternJson(LedPos pos, bool pretty) { std::string jsonStr = "{}"; - Mode *cur = Modes::curMode(); + Mode *cur = Modes_curMode(); if (!cur) { return jsonStr; } - Pattern *pat = cur->getPattern(pos); + Pattern *pat = Mode_getPatternMutable(cur, pos); if (!pat) { return jsonStr; } @@ -2290,7 +2407,7 @@ std::string Vortex::printPatternJson(LedPos pos, bool pretty) bool Vortex::parsePatternJson(LedPos pos, const std::string &jsonStr) { - Mode *cur = Modes::curMode(); + Mode *cur = Modes_curMode(); if (!cur) { return false; } @@ -2306,12 +2423,14 @@ bool Vortex::parsePatternJson(LedPos pos, const std::string &jsonStr) return false; } // update the current mode with the parsed json mode - PatternID patID = tempPat->getPatternID(); + PatternID patID = Pattern_getPatternID(tempPat); PatternArgs args; - tempPat->getArgs(args); - Colorset set = tempPat->getColorset(); - delete tempPat; - return cur->setPattern(patID, pos, &args, &set); + PatternArgs_make0(&args); + Pattern_getArgs(tempPat, &args); + Colorset set = Pattern_getColorset(tempPat); + Pattern_destroy(tempPat); + PatternBuilder_freePattern(tempPat); + return Mode_setPattern(cur, patID, pos, &args, &set); } // dump the json to output diff --git a/VortexEngine/VortexLib/VortexLib.h b/VortexEngine/VortexLib/VortexLib.h index dd58ccef30..30bd23d084 100644 --- a/VortexEngine/VortexLib/VortexLib.h +++ b/VortexEngine/VortexLib/VortexLib.h @@ -64,12 +64,12 @@ class VortexCallbacks }; // forward decls for various classes -class PatternArgs; -class ByteStream; -class Colorset; -class Pattern; -class Button; -class Mode; +typedef struct PatternArgs PatternArgs; +typedef struct ByteStream ByteStream; +typedef struct Colorset Colorset; +typedef struct Pattern Pattern; +typedef struct Button Button; +typedef struct Mode Mode; // json type is just nlohmann::json using json = nlohmann::json; @@ -316,8 +316,8 @@ class Vortex // internal function to handle numeric values in commands static void handleNumber(char c); - // so that the buttons class can call handleInputQueue - friend class Buttons; + // so that the buttons can call handleInputQueue + friend void Buttons_update(void); // called by the engine right after all buttons are checked, this will process // the input deque that is fed by the apis like shortClick() above and translate diff --git a/VortexEngine/appmain.cpp b/VortexEngine/appmain.cpp index 342961b72f..24d345cd35 100644 --- a/VortexEngine/appmain.cpp +++ b/VortexEngine/appmain.cpp @@ -2,6 +2,6 @@ int main() { - VortexEngine::init(); + VortexEngine_init(); return 0; } diff --git a/VortexEngine/src/Buttons/Button.cpp b/VortexEngine/src/Buttons/Button.cpp index 2290229db6..59f36d6503 100644 --- a/VortexEngine/src/Buttons/Button.cpp +++ b/VortexEngine/src/Buttons/Button.cpp @@ -38,59 +38,59 @@ ISR(PORT_VECT) // turn off the interrupt BUTTON_PORT.PIN_CTRL &= ~PORT_ISC_gm; // call the wakeup routine in the engine - VortexEngine::wakeup(); + VortexEngine_wakeup(true); } -void Button::enableWake() +void Button_enableWake(Button *self) { // turn on the above interrupt for FALLING edge, maintain the pullup enabled BUTTON_PORT.PIN_CTRL = PORT_PULLUPEN_bm | PORT_ISC_FALLING_gc; } #endif -Button::Button() : - m_pressTime(0), - m_releaseTime(0), - m_holdDuration(0), - m_releaseDuration(0), - m_consecutivePresses(0), - m_releaseCount(0), - m_buttonState(false), - m_newPress(false), - m_newRelease(false), - m_isPressed(false), - m_shortClick(false), - m_longClick(false) +void Button_make(Button *self) { + self->m_pressTime = 0; + self->m_releaseTime = 0; + self->m_holdDuration = 0; + self->m_releaseDuration = 0; + self->m_consecutivePresses = 0; + self->m_releaseCount = 0; + self->m_buttonState = false; + self->m_newPress = false; + self->m_newRelease = false; + self->m_isPressed = false; + self->m_shortClick = false; + self->m_longClick = false; } -Button::~Button() +void Button_destroy(Button *self) { } -bool Button::init(uint8_t pin) +bool Button_init(Button *self, uint8_t pin) { - m_pressTime = 0; - m_releaseTime = 0; - m_holdDuration = 0; - m_releaseDuration = 0; - m_consecutivePresses = 0; - m_newPress = false; - m_newRelease = false; - m_shortClick = false; - m_longClick = false; + self->m_pressTime = 0; + self->m_releaseTime = 0; + self->m_holdDuration = 0; + self->m_releaseDuration = 0; + self->m_consecutivePresses = 0; + self->m_newPress = false; + self->m_newRelease = false; + self->m_shortClick = false; + self->m_longClick = false; // this is weird, when I did m_releaseCount = !m_buttonState the // compiler generated a huge amount of assembly, but not !check() - m_buttonState = check(); - m_releaseCount = !check(); - m_isPressed = m_buttonState; + self->m_buttonState = Button_check(self); + self->m_releaseCount = !Button_check(self); + self->m_isPressed = self->m_buttonState; #ifdef VORTEX_EMBEDDED BUTTON_PORT.PIN_CTRL = PORT_PULLUPEN_bm; #endif return true; } -bool Button::check() +bool Button_check(Button *self) { #ifdef VORTEX_LIB return (Vortex::vcallbacks()->checkPinHook(0) == 0); @@ -99,79 +99,91 @@ bool Button::check() #endif } -void Button::update() +void Button_update(Button *self) { // reset the new press/release members this tick - m_newPress = false; - m_newRelease = false; + self->m_newPress = false; + self->m_newRelease = false; // read the new button state - bool newButtonState = check(); + bool newButtonState = Button_check(self); // did the button change (press/release occurred) - if (newButtonState != m_buttonState) { + if (newButtonState != self->m_buttonState) { // set the new state - m_buttonState = newButtonState; + self->m_buttonState = newButtonState; // update the currently pressed member - m_isPressed = m_buttonState; + self->m_isPressed = self->m_buttonState; // update the press/release times and newpress/newrelease members - if (m_isPressed) { + if (self->m_isPressed) { // the button was just pressed - m_pressTime = Time::getCurtime(); - m_newPress = true; + self->m_pressTime = Time_getCurtime(); + self->m_newPress = true; } else { // simply ignore the first release, always. Because they just turned the device // on and we don't want them to cycle to the next mode or something - if (m_releaseCount > 0) { + if (self->m_releaseCount > 0) { // the button was just released - m_releaseTime = Time::getCurtime(); - m_newRelease = true; + self->m_releaseTime = Time_getCurtime(); + self->m_newRelease = true; } // count releases even inside the ignore window so that // we can tell if the button was released to stop ignoring - m_releaseCount++; + self->m_releaseCount++; } } // calculate new hold/release durations if currently held/released - if (m_isPressed) { + if (self->m_isPressed) { // update the hold duration as long as the button is pressed - if (Time::getCurtime() >= m_pressTime) { - m_holdDuration = (uint32_t)(Time::getCurtime() - m_pressTime); + if (Time_getCurtime() >= self->m_pressTime) { + self->m_holdDuration = (uint32_t)(Time_getCurtime() - self->m_pressTime); } } else { // update the release duration as long as the button is released - if (Time::getCurtime() >= m_releaseTime) { - m_releaseDuration = (uint32_t)(Time::getCurtime() - m_releaseTime); - if (m_releaseDuration > CONSECUTIVE_WINDOW_TICKS) { + if (Time_getCurtime() >= self->m_releaseTime) { + self->m_releaseDuration = (uint32_t)(Time_getCurtime() - self->m_releaseTime); + if (self->m_releaseDuration > CONSECUTIVE_WINDOW_TICKS) { // if the release duration is greater than the threshold, reset the consecutive presses - m_consecutivePresses = 0; + self->m_consecutivePresses = 0; } } } - if (m_newRelease) { - m_consecutivePresses++; + if (self->m_newRelease) { + self->m_consecutivePresses++; } // whether a shortclick or long click just occurred - m_shortClick = (m_newRelease && (m_holdDuration <= SHORT_CLICK_THRESHOLD_TICKS)); - m_longClick = (m_newRelease && (m_holdDuration > SHORT_CLICK_THRESHOLD_TICKS)); + self->m_shortClick = (self->m_newRelease && (self->m_holdDuration <= SHORT_CLICK_THRESHOLD_TICKS)); + self->m_longClick = (self->m_newRelease && (self->m_holdDuration > SHORT_CLICK_THRESHOLD_TICKS)); - if (m_shortClick) { + if (self->m_shortClick) { DEBUG_LOG("Short click"); } - if (m_longClick) { + if (self->m_longClick) { DEBUG_LOG("Long click"); } } -bool Button::onConsecutivePresses(uint8_t numPresses) +bool Button_onConsecutivePresses(Button *self, uint8_t numPresses) { - if (m_consecutivePresses >= numPresses) { - m_consecutivePresses = 0; + if (self->m_consecutivePresses >= numPresses) { + self->m_consecutivePresses = 0; return true; } return false; } + +bool Button_onPress(const Button *self) { return self->m_newPress; } +bool Button_onRelease(const Button *self) { return self->m_newRelease; } +bool Button_isPressed(const Button *self) { return self->m_isPressed; } +bool Button_onShortClick(const Button *self) { return self->m_shortClick; } +bool Button_onLongClick(const Button *self) { return self->m_longClick; } +uint32_t Button_pressTime(const Button *self) { return self->m_pressTime; } +uint32_t Button_releaseTime(const Button *self) { return self->m_releaseTime; } +uint32_t Button_holdDuration(const Button *self) { return self->m_holdDuration; } +uint32_t Button_releaseDuration(const Button *self) { return self->m_releaseDuration; } +uint8_t Button_consecutivePresses(const Button *self) { return self->m_consecutivePresses; } +uint8_t Button_releaseCount(const Button *self) { return self->m_releaseCount; } diff --git a/VortexEngine/src/Buttons/Button.h b/VortexEngine/src/Buttons/Button.h index 8d0d98ab19..9c289938c0 100644 --- a/VortexEngine/src/Buttons/Button.h +++ b/VortexEngine/src/Buttons/Button.h @@ -1,66 +1,17 @@ #ifndef BUTTON_H #define BUTTON_H +#include #include #include "../VortexConfig.h" // although there is only one button on the VortexFramework -// I am still opting for a non-static button class -class Button -{ -private: - // private unimplemented copy and assignment constructors to prevent copies - Button(Button const &); - void operator=(Button const &); - -public: - Button(); - ~Button(); - - // initialize a new button object with a pin number - bool init(uint8_t pin); - // directly poll the pin for whether it's pressed right now - bool check(); - // poll the button pin and update the state of the button object - void update(); - -#ifdef VORTEX_EMBEDDED - // enable the button-wake trigger to wake the device on press - void enableWake(); -#endif - - // whether the button was pressed this tick - bool onPress() const { return m_newPress; } - // whether the button was released this tick - bool onRelease() const { return m_newRelease; } - // whether the button is currently pressed - bool isPressed() const { return m_isPressed; } - - // whether the button was shortclicked this tick - bool onShortClick() const { return m_shortClick; } - // whether the button was long clicked this tick - bool onLongClick() const { return m_longClick; } - // fired when a certain number of presses is reached, the consecutive press - // counter is automatically reset when that happens - bool onConsecutivePresses(uint8_t numPresses); - - // when the button was last pressed - uint32_t pressTime() const { return m_pressTime; } - // when the button was last released - uint32_t releaseTime() const { return m_releaseTime; } - - // how long the button is currently or was last held down (in ticks) - uint32_t holdDuration() const { return m_holdDuration; } - // how long the button is currently or was last released for (in ticks) - uint32_t releaseDuration() const { return m_releaseDuration; } - - // the number of consecutive presses - uint8_t consecutivePresses() const { return m_consecutivePresses; } - // the number of releases - uint8_t releaseCount() const { return m_releaseCount; } - -private: +// I am still opting for a non-static button struct +// +// the members are intentionally left public so the VortexLib side can +// manipulate them directly to inject button events +typedef struct Button { // ======================================== // state data that is populated each check @@ -92,14 +43,53 @@ class Button bool m_shortClick; // whether a long click occurred bool m_longClick; +} Button; -#ifdef VORTEX_LIB - // allow the VortexLib class to manipulate the members of this - // class so that it can inject button events to make apis like - // Vortex::shortClick() and Vortex::longClick() possible - friend class Vortex; +// initialize a new button object, this does not touch the pin +void Button_make(Button *self); +void Button_destroy(Button *self); + +// initialize a new button object with a pin number +bool Button_init(Button *self, uint8_t pin); +// directly poll the pin for whether it's pressed right now +bool Button_check(Button *self); +// poll the button pin and update the state of the button object +void Button_update(Button *self); + +#ifdef VORTEX_EMBEDDED +// enable the button-wake trigger to wake the device on press +void Button_enableWake(Button *self); #endif -}; + +// whether the button was pressed this tick +bool Button_onPress(const Button *self); +// whether the button was released this tick +bool Button_onRelease(const Button *self); +// whether the button is currently pressed +bool Button_isPressed(const Button *self); + +// whether the button was shortclicked this tick +bool Button_onShortClick(const Button *self); +// whether the button was long clicked this tick +bool Button_onLongClick(const Button *self); +// fired when a certain number of presses is reached, the consecutive press +// counter is automatically reset when that happens +bool Button_onConsecutivePresses(Button *self, uint8_t numPresses); + +// when the button was last pressed +uint32_t Button_pressTime(const Button *self); +// when the button was last released +uint32_t Button_releaseTime(const Button *self); + +// how long the button is currently or was last held down (in ticks) +uint32_t Button_holdDuration(const Button *self); +// how long the button is currently or was last released for (in ticks) +uint32_t Button_releaseDuration(const Button *self); + +// the number of consecutive presses +uint8_t Button_consecutivePresses(const Button *self); +// the number of releases +uint8_t Button_releaseCount(const Button *self); // See Button.cpp for info about this extern Button *g_pButton; diff --git a/VortexEngine/src/Buttons/Buttons.cpp b/VortexEngine/src/Buttons/Buttons.cpp index 36c4736b43..61ad6f5101 100644 --- a/VortexEngine/src/Buttons/Buttons.cpp +++ b/VortexEngine/src/Buttons/Buttons.cpp @@ -1,5 +1,7 @@ #include "Buttons.h" +#include "../Memory/Memory.h" + #ifdef VORTEX_LIB #include "VortexLib.h" #else @@ -12,27 +14,36 @@ // is added. This makes it easier to access the button from other places while // still allowing for a second instance to be added. I wish there was a more // elegant way to make the button accessible but not global. -// This will simply point at Buttons::m_button. -Button *g_pButton = nullptr; +// This will simply point at the static button below. +Button *g_pButton = NULL; + +// the storage for the button(s), these are static so they never touch the heap +static Button s_buttons[NUM_BUTTONS]; -bool Buttons::init() +bool Buttons_init() { - g_pButton = new Button(); - if (!g_pButton) { - return false; - } + g_pButton = &s_buttons[0]; + Button_make(g_pButton); // init the button on pin 1 - return g_pButton->init(1); + return Button_init(g_pButton, 1); +} + +void Buttons_cleanup() +{ + if (g_pButton) { + Button_destroy(g_pButton); + g_pButton = NULL; + } } -void Buttons::cleanup() +uint8_t Buttons_numButtons() { - delete g_pButton; + return NUM_BUTTONS; } -void Buttons::update() +void Buttons_update() { - g_pButton->update(); + Button_update(g_pButton); #ifdef VORTEX_LIB // read input from the vortex lib interface, for example Vortex::shortClick() Vortex::handleInputQueue(g_pButton, NUM_BUTTONS); diff --git a/VortexEngine/src/Buttons/Buttons.h b/VortexEngine/src/Buttons/Buttons.h index ad9ed95146..f7e8755f70 100644 --- a/VortexEngine/src/Buttons/Buttons.h +++ b/VortexEngine/src/Buttons/Buttons.h @@ -1,6 +1,7 @@ #ifndef BUTTONS_H #define BUTTONS_H +#include #include "Button.h" // the number of buttons, this isn't in VortexConfig.h because @@ -9,23 +10,14 @@ // of a hardcoded constant than a configuration setting #define NUM_BUTTONS 1 -class Buttons -{ - // private unimplemented constructor - Buttons(); - -public: - // initialize all buttons - static bool init(); - static void cleanup(); - - // poll the buttons - static void update(); - - static uint8_t numButtons() { return NUM_BUTTONS; } - -private: -}; +// initialize all buttons +bool Buttons_init(void); +// release the button resources +void Buttons_cleanup(void); +// poll the buttons +void Buttons_update(void); +// the number of buttons +uint8_t Buttons_numButtons(void); // best way I think extern Button *g_pButton; diff --git a/VortexEngine/src/Colors/ColorConstants.h b/VortexEngine/src/Colors/ColorConstants.h index a33f6c3a80..47b10d22f6 100644 --- a/VortexEngine/src/Colors/ColorConstants.h +++ b/VortexEngine/src/Colors/ColorConstants.h @@ -34,107 +34,107 @@ #define HSV_OFF HSV_BIT | (uint32_t)0x000000 // 0 0 0 // Pure RGB colors (brightest) -#define RGB_WHITE (uint32_t)0xFFFFFF // 255 255 255 -#define RGB_BLUE (uint32_t)0x0000FF // 0 0 255 -#define RGB_YELLOW (uint32_t)0xFFFF00 // 255 255 0 -#define RGB_RED (uint32_t)0xFF0000 // 255 0 0 -#define RGB_GREEN (uint32_t)0x00FF00 // 0 255 0 -#define RGB_CYAN (uint32_t)0x00FFFF // 0 255 255 -#define RGB_PURPLE (uint32_t)0x9933FF // 153 51 255 -#define RGB_ORANGE (uint32_t)0xFF8300 // 255 131 0 -#define RGB_PINK (uint32_t)0xFF0099 // 255 0 153 -#define RGB_MAGENTA (uint32_t)0xFF00FF // 255 0 255 -#define RGB_OFF (uint32_t)0x000000 // 0 0 0 +#define RGB_WHITE ((RGBColor){ .red = 0xFF, .green = 0xFF, .blue = 0xFF }) // 255 255 255 +#define RGB_BLUE ((RGBColor){ .red = 0x00, .green = 0x00, .blue = 0xFF }) // 0 0 255 +#define RGB_YELLOW ((RGBColor){ .red = 0xFF, .green = 0xFF, .blue = 0x00 }) // 255 255 0 +#define RGB_RED ((RGBColor){ .red = 0xFF, .green = 0x00, .blue = 0x00 }) // 255 0 0 +#define RGB_GREEN ((RGBColor){ .red = 0x00, .green = 0xFF, .blue = 0x00 }) // 0 255 0 +#define RGB_CYAN ((RGBColor){ .red = 0x00, .green = 0xFF, .blue = 0xFF }) // 0 255 255 +#define RGB_PURPLE ((RGBColor){ .red = 0x99, .green = 0x33, .blue = 0xFF }) // 153 51 255 +#define RGB_ORANGE ((RGBColor){ .red = 0xFF, .green = 0x83, .blue = 0x00 }) // 255 131 0 +#define RGB_PINK ((RGBColor){ .red = 0xFF, .green = 0x00, .blue = 0x99 }) // 255 0 153 +#define RGB_MAGENTA ((RGBColor){ .red = 0xFF, .green = 0x00, .blue = 0xFF }) // 255 0 255 +#define RGB_OFF ((RGBColor){ .red = 0x00, .green = 0x00, .blue = 0x00 }) // 0 0 0 // =========================================================== // Steps of colors in increasing brightness from 0 to 9, // Where O is the lowest brightness and 9 is the highest // brightness before the pure colors above -#define RGB_WHITE0 (uint32_t)0x101010 // 16 16 16 -#define RGB_WHITE1 (uint32_t)0x1C1C1C // 28 28 28 -#define RGB_WHITE2 (uint32_t)0x383838 // 56 56 56 -#define RGB_WHITE3 (uint32_t)0x545454 // 84 84 84 -#define RGB_WHITE4 (uint32_t)0x707070 // 112 112 112 -#define RGB_WHITE5 (uint32_t)0x8C8C8C // 140 140 140 -#define RGB_WHITE6 (uint32_t)0xA8A8A8 // 168 168 168 -#define RGB_WHITE7 (uint32_t)0xC4C4C4 // 196 196 196 -#define RGB_WHITE8 (uint32_t)0xE0E0E0 // 224 224 224 -#define RGB_WHITE9 (uint32_t)0xFCFCFC // 252 252 252 +#define RGB_WHITE0 ((RGBColor){ .red = 0x10, .green = 0x10, .blue = 0x10 }) // 16 16 16 +#define RGB_WHITE1 ((RGBColor){ .red = 0x1C, .green = 0x1C, .blue = 0x1C }) // 28 28 28 +#define RGB_WHITE2 ((RGBColor){ .red = 0x38, .green = 0x38, .blue = 0x38 }) // 56 56 56 +#define RGB_WHITE3 ((RGBColor){ .red = 0x54, .green = 0x54, .blue = 0x54 }) // 84 84 84 +#define RGB_WHITE4 ((RGBColor){ .red = 0x70, .green = 0x70, .blue = 0x70 }) // 112 112 112 +#define RGB_WHITE5 ((RGBColor){ .red = 0x8C, .green = 0x8C, .blue = 0x8C }) // 140 140 140 +#define RGB_WHITE6 ((RGBColor){ .red = 0xA8, .green = 0xA8, .blue = 0xA8 }) // 168 168 168 +#define RGB_WHITE7 ((RGBColor){ .red = 0xC4, .green = 0xC4, .blue = 0xC4 }) // 196 196 196 +#define RGB_WHITE8 ((RGBColor){ .red = 0xE0, .green = 0xE0, .blue = 0xE0 }) // 224 224 224 +#define RGB_WHITE9 ((RGBColor){ .red = 0xFC, .green = 0xFC, .blue = 0xFC }) // 252 252 252 -#define RGB_BLUE0 (uint32_t)0x000010 // 0 0 16 -#define RGB_BLUE1 (uint32_t)0x00001C // 0 0 28 -#define RGB_BLUE2 (uint32_t)0x000038 // 0 0 56 -#define RGB_BLUE3 (uint32_t)0x000054 // 0 0 84 -#define RGB_BLUE4 (uint32_t)0x000070 // 0 0 112 -#define RGB_BLUE5 (uint32_t)0x00008C // 0 0 140 -#define RGB_BLUE6 (uint32_t)0x0000A8 // 0 0 168 -#define RGB_BLUE7 (uint32_t)0x0000C4 // 0 0 196 -#define RGB_BLUE8 (uint32_t)0x0000E0 // 0 0 224 -#define RGB_BLUE9 (uint32_t)0x0000FC // 0 0 252 +#define RGB_BLUE0 ((RGBColor){ .red = 0x00, .green = 0x00, .blue = 0x10 }) // 0 0 16 +#define RGB_BLUE1 ((RGBColor){ .red = 0x00, .green = 0x00, .blue = 0x1C }) // 0 0 28 +#define RGB_BLUE2 ((RGBColor){ .red = 0x00, .green = 0x00, .blue = 0x38 }) // 0 0 56 +#define RGB_BLUE3 ((RGBColor){ .red = 0x00, .green = 0x00, .blue = 0x54 }) // 0 0 84 +#define RGB_BLUE4 ((RGBColor){ .red = 0x00, .green = 0x00, .blue = 0x70 }) // 0 0 112 +#define RGB_BLUE5 ((RGBColor){ .red = 0x00, .green = 0x00, .blue = 0x8C }) // 0 0 140 +#define RGB_BLUE6 ((RGBColor){ .red = 0x00, .green = 0x00, .blue = 0xA8 }) // 0 0 168 +#define RGB_BLUE7 ((RGBColor){ .red = 0x00, .green = 0x00, .blue = 0xC4 }) // 0 0 196 +#define RGB_BLUE8 ((RGBColor){ .red = 0x00, .green = 0x00, .blue = 0xE0 }) // 0 0 224 +#define RGB_BLUE9 ((RGBColor){ .red = 0x00, .green = 0x00, .blue = 0xFC }) // 0 0 252 -#define RGB_YELLOW0 (uint32_t)0x101000 // 16 16 0 -#define RGB_YELLOW1 (uint32_t)0x1C1C00 // 28 28 0 -#define RGB_YELLOW2 (uint32_t)0x383800 // 56 56 0 -#define RGB_YELLOW3 (uint32_t)0x545400 // 84 84 0 -#define RGB_YELLOW4 (uint32_t)0x707000 // 112 112 0 -#define RGB_YELLOW5 (uint32_t)0x8C8C00 // 140 140 0 -#define RGB_YELLOW6 (uint32_t)0xA8A800 // 168 168 0 -#define RGB_YELLOW7 (uint32_t)0xC4C400 // 196 196 0 -#define RGB_YELLOW8 (uint32_t)0xE0E000 // 224 224 0 -#define RGB_YELLOW9 (uint32_t)0xFCFC00 // 252 252 0 +#define RGB_YELLOW0 ((RGBColor){ .red = 0x10, .green = 0x10, .blue = 0x00 }) // 16 16 0 +#define RGB_YELLOW1 ((RGBColor){ .red = 0x1C, .green = 0x1C, .blue = 0x00 }) // 28 28 0 +#define RGB_YELLOW2 ((RGBColor){ .red = 0x38, .green = 0x38, .blue = 0x00 }) // 56 56 0 +#define RGB_YELLOW3 ((RGBColor){ .red = 0x54, .green = 0x54, .blue = 0x00 }) // 84 84 0 +#define RGB_YELLOW4 ((RGBColor){ .red = 0x70, .green = 0x70, .blue = 0x00 }) // 112 112 0 +#define RGB_YELLOW5 ((RGBColor){ .red = 0x8C, .green = 0x8C, .blue = 0x00 }) // 140 140 0 +#define RGB_YELLOW6 ((RGBColor){ .red = 0xA8, .green = 0xA8, .blue = 0x00 }) // 168 168 0 +#define RGB_YELLOW7 ((RGBColor){ .red = 0xC4, .green = 0xC4, .blue = 0x00 }) // 196 196 0 +#define RGB_YELLOW8 ((RGBColor){ .red = 0xE0, .green = 0xE0, .blue = 0x00 }) // 224 224 0 +#define RGB_YELLOW9 ((RGBColor){ .red = 0xFC, .green = 0xFC, .blue = 0x00 }) // 252 252 0 -#define RGB_RED0 (uint32_t)0x100000 // 16 0 0 -#define RGB_RED1 (uint32_t)0x1C0000 // 28 0 0 -#define RGB_RED2 (uint32_t)0x380000 // 56 0 0 -#define RGB_RED3 (uint32_t)0x540000 // 84 0 0 -#define RGB_RED4 (uint32_t)0x700000 // 112 0 0 -#define RGB_RED5 (uint32_t)0x8C0000 // 140 0 0 -#define RGB_RED6 (uint32_t)0xA80000 // 168 0 0 -#define RGB_RED7 (uint32_t)0xC40000 // 196 0 0 -#define RGB_RED8 (uint32_t)0xE00000 // 224 0 0 -#define RGB_RED9 (uint32_t)0xFC0000 // 252 0 0 +#define RGB_RED0 ((RGBColor){ .red = 0x10, .green = 0x00, .blue = 0x00 }) // 16 0 0 +#define RGB_RED1 ((RGBColor){ .red = 0x1C, .green = 0x00, .blue = 0x00 }) // 28 0 0 +#define RGB_RED2 ((RGBColor){ .red = 0x38, .green = 0x00, .blue = 0x00 }) // 56 0 0 +#define RGB_RED3 ((RGBColor){ .red = 0x54, .green = 0x00, .blue = 0x00 }) // 84 0 0 +#define RGB_RED4 ((RGBColor){ .red = 0x70, .green = 0x00, .blue = 0x00 }) // 112 0 0 +#define RGB_RED5 ((RGBColor){ .red = 0x8C, .green = 0x00, .blue = 0x00 }) // 140 0 0 +#define RGB_RED6 ((RGBColor){ .red = 0xA8, .green = 0x00, .blue = 0x00 }) // 168 0 0 +#define RGB_RED7 ((RGBColor){ .red = 0xC4, .green = 0x00, .blue = 0x00 }) // 196 0 0 +#define RGB_RED8 ((RGBColor){ .red = 0xE0, .green = 0x00, .blue = 0x00 }) // 224 0 0 +#define RGB_RED9 ((RGBColor){ .red = 0xFC, .green = 0x00, .blue = 0x00 }) // 252 0 0 -#define RGB_GREEN0 (uint32_t)0x001000 // 0 16 0 -#define RGB_GREEN1 (uint32_t)0x001C00 // 0 28 0 -#define RGB_GREEN2 (uint32_t)0x003800 // 0 56 0 -#define RGB_GREEN3 (uint32_t)0x005400 // 0 84 0 -#define RGB_GREEN4 (uint32_t)0x007000 // 0 112 0 -#define RGB_GREEN5 (uint32_t)0x008C00 // 0 140 0 -#define RGB_GREEN6 (uint32_t)0x00A800 // 0 168 0 -#define RGB_GREEN7 (uint32_t)0x00C400 // 0 196 0 -#define RGB_GREEN8 (uint32_t)0x00E000 // 0 224 0 -#define RGB_GREEN9 (uint32_t)0x00FC00 // 0 252 0 +#define RGB_GREEN0 ((RGBColor){ .red = 0x00, .green = 0x10, .blue = 0x00 }) // 0 16 0 +#define RGB_GREEN1 ((RGBColor){ .red = 0x00, .green = 0x1C, .blue = 0x00 }) // 0 28 0 +#define RGB_GREEN2 ((RGBColor){ .red = 0x00, .green = 0x38, .blue = 0x00 }) // 0 56 0 +#define RGB_GREEN3 ((RGBColor){ .red = 0x00, .green = 0x54, .blue = 0x00 }) // 0 84 0 +#define RGB_GREEN4 ((RGBColor){ .red = 0x00, .green = 0x70, .blue = 0x00 }) // 0 112 0 +#define RGB_GREEN5 ((RGBColor){ .red = 0x00, .green = 0x8C, .blue = 0x00 }) // 0 140 0 +#define RGB_GREEN6 ((RGBColor){ .red = 0x00, .green = 0xA8, .blue = 0x00 }) // 0 168 0 +#define RGB_GREEN7 ((RGBColor){ .red = 0x00, .green = 0xC4, .blue = 0x00 }) // 0 196 0 +#define RGB_GREEN8 ((RGBColor){ .red = 0x00, .green = 0xE0, .blue = 0x00 }) // 0 224 0 +#define RGB_GREEN9 ((RGBColor){ .red = 0x00, .green = 0xFC, .blue = 0x00 }) // 0 252 0 -#define RGB_CYAN0 (uint32_t)0x001010 // 0 16 16 -#define RGB_CYAN1 (uint32_t)0x001C1C // 0 28 28 -#define RGB_CYAN2 (uint32_t)0x003838 // 0 56 56 -#define RGB_CYAN3 (uint32_t)0x005454 // 0 84 84 -#define RGB_CYAN4 (uint32_t)0x007070 // 0 112 112 -#define RGB_CYAN5 (uint32_t)0x008C8C // 0 140 140 -#define RGB_CYAN6 (uint32_t)0x00A8A8 // 0 168 168 -#define RGB_CYAN7 (uint32_t)0x00C4C4 // 0 196 196 -#define RGB_CYAN8 (uint32_t)0x00E0E0 // 0 224 224 -#define RGB_CYAN9 (uint32_t)0x00FCFC // 0 252 252 +#define RGB_CYAN0 ((RGBColor){ .red = 0x00, .green = 0x10, .blue = 0x10 }) // 0 16 16 +#define RGB_CYAN1 ((RGBColor){ .red = 0x00, .green = 0x1C, .blue = 0x1C }) // 0 28 28 +#define RGB_CYAN2 ((RGBColor){ .red = 0x00, .green = 0x38, .blue = 0x38 }) // 0 56 56 +#define RGB_CYAN3 ((RGBColor){ .red = 0x00, .green = 0x54, .blue = 0x54 }) // 0 84 84 +#define RGB_CYAN4 ((RGBColor){ .red = 0x00, .green = 0x70, .blue = 0x70 }) // 0 112 112 +#define RGB_CYAN5 ((RGBColor){ .red = 0x00, .green = 0x8C, .blue = 0x8C }) // 0 140 140 +#define RGB_CYAN6 ((RGBColor){ .red = 0x00, .green = 0xA8, .blue = 0xA8 }) // 0 168 168 +#define RGB_CYAN7 ((RGBColor){ .red = 0x00, .green = 0xC4, .blue = 0xC4 }) // 0 196 196 +#define RGB_CYAN8 ((RGBColor){ .red = 0x00, .green = 0xE0, .blue = 0xE0 }) // 0 224 224 +#define RGB_CYAN9 ((RGBColor){ .red = 0x00, .green = 0xFC, .blue = 0xFC }) // 0 252 252 -#define RGB_MAGENTA0 (uint32_t)0x100010 // 16 0 16 -#define RGB_MAGENTA1 (uint32_t)0x1C001C // 28 0 28 -#define RGB_MAGENTA2 (uint32_t)0x380038 // 56 0 56 -#define RGB_MAGENTA3 (uint32_t)0x540054 // 84 0 84 -#define RGB_MAGENTA4 (uint32_t)0x700070 // 112 0 112 -#define RGB_MAGENTA5 (uint32_t)0x8C008C // 140 0 140 -#define RGB_MAGENTA6 (uint32_t)0xA800A8 // 168 0 168 -#define RGB_MAGENTA7 (uint32_t)0xC400C4 // 196 0 196 -#define RGB_MAGENTA8 (uint32_t)0xE000E0 // 224 0 224 -#define RGB_MAGENTA9 (uint32_t)0xFC00FC // 252 0 252 +#define RGB_MAGENTA0 ((RGBColor){ .red = 0x10, .green = 0x00, .blue = 0x10 }) // 16 0 16 +#define RGB_MAGENTA1 ((RGBColor){ .red = 0x1C, .green = 0x00, .blue = 0x1C }) // 28 0 28 +#define RGB_MAGENTA2 ((RGBColor){ .red = 0x38, .green = 0x00, .blue = 0x38 }) // 56 0 56 +#define RGB_MAGENTA3 ((RGBColor){ .red = 0x54, .green = 0x00, .blue = 0x54 }) // 84 0 84 +#define RGB_MAGENTA4 ((RGBColor){ .red = 0x70, .green = 0x00, .blue = 0x70 }) // 112 0 112 +#define RGB_MAGENTA5 ((RGBColor){ .red = 0x8C, .green = 0x00, .blue = 0x8C }) // 140 0 140 +#define RGB_MAGENTA6 ((RGBColor){ .red = 0xA8, .green = 0x00, .blue = 0xA8 }) // 168 0 168 +#define RGB_MAGENTA7 ((RGBColor){ .red = 0xC4, .green = 0x00, .blue = 0xC4 }) // 196 0 196 +#define RGB_MAGENTA8 ((RGBColor){ .red = 0xE0, .green = 0x00, .blue = 0xE0 }) // 224 0 224 +#define RGB_MAGENTA9 ((RGBColor){ .red = 0xFC, .green = 0x00, .blue = 0xFC }) // 252 0 252 -#define RGB_ORANGE0 (uint32_t)0x100800 // 16 8 0 -#define RGB_ORANGE1 (uint32_t)0x1C0E00 // 28 14 0 -#define RGB_ORANGE2 (uint32_t)0x381C00 // 56 28 0 -#define RGB_ORANGE3 (uint32_t)0x542B00 // 84 43 0 -#define RGB_ORANGE4 (uint32_t)0x703900 // 112 57 0 -#define RGB_ORANGE5 (uint32_t)0x8C4800 // 140 72 0 -#define RGB_ORANGE6 (uint32_t)0xA85600 // 168 86 0 -#define RGB_ORANGE7 (uint32_t)0xC46500 // 196 101 0 -#define RGB_ORANGE8 (uint32_t)0xE07300 // 224 115 0 -#define RGB_ORANGE9 (uint32_t)0xFC8200 // 252 130 0 +#define RGB_ORANGE0 ((RGBColor){ .red = 0x10, .green = 0x08, .blue = 0x00 }) // 16 8 0 +#define RGB_ORANGE1 ((RGBColor){ .red = 0x1C, .green = 0x0E, .blue = 0x00 }) // 28 14 0 +#define RGB_ORANGE2 ((RGBColor){ .red = 0x38, .green = 0x1C, .blue = 0x00 }) // 56 28 0 +#define RGB_ORANGE3 ((RGBColor){ .red = 0x54, .green = 0x2B, .blue = 0x00 }) // 84 43 0 +#define RGB_ORANGE4 ((RGBColor){ .red = 0x70, .green = 0x39, .blue = 0x00 }) // 112 57 0 +#define RGB_ORANGE5 ((RGBColor){ .red = 0x8C, .green = 0x48, .blue = 0x00 }) // 140 72 0 +#define RGB_ORANGE6 ((RGBColor){ .red = 0xA8, .green = 0x56, .blue = 0x00 }) // 168 86 0 +#define RGB_ORANGE7 ((RGBColor){ .red = 0xC4, .green = 0x65, .blue = 0x00 }) // 196 101 0 +#define RGB_ORANGE8 ((RGBColor){ .red = 0xE0, .green = 0x73, .blue = 0x00 }) // 224 115 0 +#define RGB_ORANGE9 ((RGBColor){ .red = 0xFC, .green = 0x82, .blue = 0x00 }) // 252 130 0 diff --git a/VortexEngine/src/Colors/ColorTypes.cpp b/VortexEngine/src/Colors/ColorTypes.cpp index 5dec968e3b..3cd9d03673 100644 --- a/VortexEngine/src/Colors/ColorTypes.cpp +++ b/VortexEngine/src/Colors/ColorTypes.cpp @@ -5,222 +5,206 @@ // global hsv to rgb algorithm selector hsv_to_rgb_algorithm g_hsv_rgb_alg = HSV_TO_RGB_GENERIC; -HSVColor::HSVColor() : - hue(0), - sat(0), - val(0) +// ========== +// HSVColor + +HSVColor HSVColor_make(void) { + HSVColor self; + self.hue = 0; + self.sat = 0; + self.val = 0; + return self; } -HSVColor::HSVColor(uint8_t hue, uint8_t sat, uint8_t val) : - hue(hue), sat(sat), val(val) +HSVColor HSVColor_makeHSV(uint8_t hue, uint8_t sat, uint8_t val) { + HSVColor self; + self.hue = hue; + self.sat = sat; + self.val = val; + return self; } -HSVColor::HSVColor(uint32_t dwVal) : - HSVColor() +HSVColor HSVColor_fromU32(uint32_t dwVal) { - *this = dwVal; + HSVColor self; + HSVColor_setU32(&self, dwVal); + return self; } // assignment from uint32_t -HSVColor &HSVColor::operator=(const uint32_t &rhs) +void HSVColor_setU32(HSVColor *self, uint32_t rhs) { // check for the HSV bit! if (!(rhs & HSV_BIT)) { // IT'S NOT AN HSV DWORD!! - *this = RGBColor(rhs); - return *this; + RGBColor rgb = RGBColor_fromU32(rhs); + HSVColor_setRGB(self, &rgb); + return; } - hue = ((rhs >> 16) & 0xFF); - sat = ((rhs >> 8) & 0xFF); - val = (rhs & 0xFF); - return *this; -} - -// copy construction -HSVColor::HSVColor(const HSVColor &rhs) -{ - hue = rhs.hue; - sat = rhs.sat; - val = rhs.val; + self->hue = ((rhs >> 16) & 0xFF); + self->sat = ((rhs >> 8) & 0xFF); + self->val = (rhs & 0xFF); } // assignment operator -HSVColor &HSVColor::operator=(const HSVColor &rhs) +void HSVColor_setHSV(HSVColor *self, const HSVColor *rhs) { - hue = rhs.hue; - sat = rhs.sat; - val = rhs.val; - return *this; + self->hue = rhs->hue; + self->sat = rhs->sat; + self->val = rhs->val; } // construction/assignment from RGB -HSVColor::HSVColor(const RGBColor &rhs) -{ - *this = rhs; -} - -HSVColor &HSVColor::operator=(const RGBColor &rhs) +void HSVColor_setRGB(HSVColor *self, const RGBColor *rhs) { #if HSV_TO_RGB_ALGORITHM == 1 - *this = rgb_to_hsv_approx(rhs); + *self = rgb_to_hsv_approx(rhs); #else - *this = rgb_to_hsv_generic(rhs); + *self = rgb_to_hsv_generic(rhs); #endif - return *this; } -bool HSVColor::operator==(const HSVColor &other) const +bool HSVColor_equals(const HSVColor *self, const HSVColor *other) { - return (other.raw() == raw()); + return (HSVColor_raw(other) == HSVColor_raw(self)); } -bool HSVColor::operator!=(const HSVColor &other) const +bool HSVColor_empty(const HSVColor *self) { - return (other.raw() != raw()); + return !self->hue && !self->sat && !self->val; } -bool HSVColor::empty() const +void HSVColor_clear(HSVColor *self) { - return !hue && !sat && !val; -} - -void HSVColor::clear() -{ - hue = 0; - sat = 0; - val = 0; + self->hue = 0; + self->sat = 0; + self->val = 0; } // ========== // RGBColor -RGBColor::RGBColor() : - red(0), - green(0), - blue(0) +RGBColor RGBColor_make(void) { + RGBColor self; + self.red = 0; + self.green = 0; + self.blue = 0; + return self; } -RGBColor::RGBColor(uint8_t red, uint8_t green, uint8_t blue) : - red(red), green(green), blue(blue) +RGBColor RGBColor_makeRGB(uint8_t red, uint8_t green, uint8_t blue) { + RGBColor self; + self.red = red; + self.green = green; + self.blue = blue; + return self; } -RGBColor::RGBColor(uint32_t dwVal) : - RGBColor() +RGBColor RGBColor_fromU32(uint32_t dwVal) { - *this = dwVal; + RGBColor self; + RGBColor_setU32(&self, dwVal); + return self; } // assignment from uint32_t -RGBColor &RGBColor::operator=(const uint32_t &rhs) +void RGBColor_setU32(RGBColor *self, uint32_t rhs) { // check for the HSV bit! if ((rhs & HSV_BIT) != 0) { // IT'S NOT AN RGB DWORD!! - *this = HSVColor(rhs); - return *this; + HSVColor hsv = HSVColor_fromU32(rhs); + RGBColor_setHSV(self, &hsv); + return; } - red = ((rhs >> 16) & 0xFF); - green = ((rhs >> 8) & 0xFF); - blue = (rhs & 0xFF); - return *this; -} - -// copy construction -RGBColor::RGBColor(const RGBColor &rhs) -{ - red = rhs.red; - green = rhs.green; - blue = rhs.blue; + self->red = ((rhs >> 16) & 0xFF); + self->green = ((rhs >> 8) & 0xFF); + self->blue = (rhs & 0xFF); } // assignment operator -RGBColor &RGBColor::operator=(const RGBColor &rhs) +void RGBColor_setRGB(RGBColor *self, const RGBColor *rhs) { - red = rhs.red; - green = rhs.green; - blue = rhs.blue; - return *this; + self->red = rhs->red; + self->green = rhs->green; + self->blue = rhs->blue; } -RGBColor::RGBColor(const HSVColor &rhs) +// construction/assignment from HSV +RGBColor RGBColor_fromHSV(const HSVColor *rhs) { - *this = rhs; + RGBColor self; + RGBColor_setHSV(&self, rhs); + return self; } -RGBColor &RGBColor::operator=(const HSVColor &rhs) +void RGBColor_setHSV(RGBColor *self, const HSVColor *rhs) { switch (g_hsv_rgb_alg) { case HSV_TO_RGB_RAINBOW: - *this = hsv_to_rgb_rainbow(rhs); + *self = hsv_to_rgb_rainbow(rhs); break; case HSV_TO_RGB_RAW: - *this = hsv_to_rgb_raw_C(rhs); + *self = hsv_to_rgb_raw_C(rhs); break; case HSV_TO_RGB_GENERIC: - *this = hsv_to_rgb_generic(rhs); + *self = hsv_to_rgb_generic(rhs); break; } - return *this; -} - -bool RGBColor::operator==(const RGBColor &other) const -{ - return (other.raw() == raw()); } -bool RGBColor::operator!=(const RGBColor &other) const +bool RGBColor_equals(const RGBColor *self, const RGBColor *other) { - return (other.raw() != raw()); + return (RGBColor_raw(other) == RGBColor_raw(self)); } -bool RGBColor::empty() const +bool RGBColor_empty(const RGBColor *self) { - return !red && !green && !blue; + return !self->red && !self->green && !self->blue; } -void RGBColor::clear() +void RGBColor_clear(RGBColor *self) { - red = 0; - green = 0; - blue = 0; + self->red = 0; + self->green = 0; + self->blue = 0; } -RGBColor RGBColor::adjustBrightness(uint8_t fadeBy) +void RGBColor_adjustBrightness(RGBColor *self, uint8_t fadeBy) { - red = (((int)red) * (int)(256 - fadeBy)) >> 8; - green = (((int)green) * (int)(256 - fadeBy)) >> 8; - blue = (((int)blue) * (int)(256 - fadeBy)) >> 8; - return *this; + self->red = (((int)self->red) * (int)(256 - fadeBy)) >> 8; + self->green = (((int)self->green) * (int)(256 - fadeBy)) >> 8; + self->blue = (((int)self->blue) * (int)(256 - fadeBy)) >> 8; } -bool RGBColor::serialize(ByteStream &buffer) const +bool RGBColor_serialize(const RGBColor *self, ByteStream *buffer) { - if (!buffer.serialize8(red)) { + if (!ByteStream_serialize8(buffer, self->red)) { return false; } - if (!buffer.serialize8(green)) { + if (!ByteStream_serialize8(buffer, self->green)) { return false; } - if (!buffer.serialize8(blue)) { + if (!ByteStream_serialize8(buffer, self->blue)) { return false; } return true; } -bool RGBColor::unserialize(ByteStream &buffer) +bool RGBColor_unserialize(RGBColor *self, ByteStream *buffer) { - if (!buffer.unserialize8(&red)) { + if (!ByteStream_unserialize8(buffer, &self->red)) { return false; } - if (!buffer.unserialize8(&green)) { + if (!ByteStream_unserialize8(buffer, &self->green)) { return false; } - if (!buffer.unserialize8(&blue)) { + if (!ByteStream_unserialize8(buffer, &self->blue)) { return false; } return true; @@ -235,7 +219,7 @@ bool RGBColor::unserialize(ByteStream &buffer) // Stolen from FastLED hsv to rgb full rainbox where all colours // are given equal weight, this makes for-example yellow larger // best to use this function as it is the legacy choice -RGBColor hsv_to_rgb_rainbow(const HSVColor &rhs) +RGBColor hsv_to_rgb_rainbow(const HSVColor *rhs) { RGBColor col; // Yellow has a higher inherent brightness than @@ -258,9 +242,9 @@ RGBColor hsv_to_rgb_rainbow(const HSVColor &rhs) const uint8_t Gscale = 185; - uint8_t hue = rhs.hue; - uint8_t sat = rhs.sat; - uint8_t val = rhs.val; + uint8_t hue = rhs->hue; + uint8_t sat = rhs->sat; + uint8_t val = rhs->val; uint8_t offset = hue & 0x1F; // 0..31 @@ -410,7 +394,7 @@ RGBColor hsv_to_rgb_rainbow(const HSVColor &rhs) // stolen from FastLED raw hsv to rgb conversion, idk what is special // about this one but I think the hue stops at 190 maybe? -RGBColor hsv_to_rgb_raw_C(const HSVColor &rhs) +RGBColor hsv_to_rgb_raw_C(const HSVColor *rhs) { RGBColor col; // Convert hue, saturation and brightness ( HSV/HSB ) to RGB @@ -418,8 +402,8 @@ RGBColor hsv_to_rgb_raw_C(const HSVColor &rhs) // the output more visually linear. // Apply dimming curves - uint8_t value = rhs.val; - uint8_t saturation = rhs.sat; + uint8_t value = rhs->val; + uint8_t saturation = rhs->sat; // The brightness floor is minimum number that all of // R, G, and B will be set to. @@ -433,8 +417,8 @@ RGBColor hsv_to_rgb_raw_C(const HSVColor &rhs) // Figure out which section of the hue wheel we're in, // and how far offset we are withing that section - uint8_t section = rhs.hue / 0x40; // 0..2 - uint8_t offset = rhs.hue % 0x40; // 0..63 + uint8_t section = rhs->hue / 0x40; // 0..2 + uint8_t offset = rhs->hue % 0x40; // 0..63 uint8_t rampup = offset; // 0..63 uint8_t rampdown = (0x40 - 1) - offset; // 63..0 @@ -498,44 +482,44 @@ RGBColor hsv_to_rgb_raw_C(const HSVColor &rhs) } // generic hsv to rgb conversion nothing special -RGBColor hsv_to_rgb_generic(const HSVColor &rhs) +RGBColor hsv_to_rgb_generic(const HSVColor *rhs) { unsigned char region, remainder, p, q, t; RGBColor col; - if (rhs.sat == 0) { - col.red = rhs.val; - col.green = rhs.val; - col.blue = rhs.val; + if (rhs->sat == 0) { + col.red = rhs->val; + col.green = rhs->val; + col.blue = rhs->val; return col; } - region = rhs.hue / 43; - remainder = ((rhs.hue - (region * 43)) * 6); + region = rhs->hue / 43; + remainder = ((rhs->hue - (region * 43)) * 6); // extraneous casts to uint16_t are to prevent overflow - p = (uint8_t)(((uint16_t)(rhs.val) * (255 - rhs.sat)) >> 8); - q = (uint8_t)(((uint16_t)(rhs.val) * (255 - (((uint16_t)(rhs.sat) * remainder) >> 8))) >> 8); - t = (uint8_t)(((uint16_t)(rhs.val) * (255 - (((uint16_t)(rhs.sat) * (255 - remainder)) >> 8))) >> 8); + p = (uint8_t)(((uint16_t)(rhs->val) * (255 - rhs->sat)) >> 8); + q = (uint8_t)(((uint16_t)(rhs->val) * (255 - (((uint16_t)(rhs->sat) * remainder) >> 8))) >> 8); + t = (uint8_t)(((uint16_t)(rhs->val) * (255 - (((uint16_t)(rhs->sat) * (255 - remainder)) >> 8))) >> 8); switch (region) { case 0: - col.red = rhs.val; col.green = t; col.blue = p; + col.red = rhs->val; col.green = t; col.blue = p; break; case 1: - col.red = q; col.green = rhs.val; col.blue = p; + col.red = q; col.green = rhs->val; col.blue = p; break; case 2: - col.red = p; col.green = rhs.val; col.blue = t; + col.red = p; col.green = rhs->val; col.blue = t; break; case 3: - col.red = p; col.green = q; col.blue = rhs.val; + col.red = p; col.green = q; col.blue = rhs->val; break; case 4: - col.red = t; col.green = p; col.blue = rhs.val; + col.red = t; col.green = p; col.blue = rhs->val; break; default: - col.red = rhs.val; col.green = p; col.blue = q; + col.red = rhs->val; col.green = p; col.blue = q; break; } return col; @@ -587,11 +571,11 @@ enum // Convert rgb to hsv with slower approximation that matches // the fastled hsv to rgb 'rainbow' -HSVColor rgb_to_hsv_approx(const RGBColor &rhs) +HSVColor rgb_to_hsv_approx(const RGBColor *rhs) { - uint8_t r = rhs.red; - uint8_t g = rhs.green; - uint8_t b = rhs.blue; + uint8_t r = rhs->red; + uint8_t g = rhs->green; + uint8_t b = rhs->blue; uint8_t h, s, v; // find desaturation uint8_t desat = 255; @@ -613,7 +597,7 @@ HSVColor rgb_to_hsv_approx(const RGBColor &rhs) // shade of gray. if ((r + g + b) == 0) { // we pick hue zero for no special reason - return HSVColor(0, 0, 255 - s); + return HSVColor_makeHSV(0, 0, 255 - s); } // scale all channels up to compensate for desaturation if (s < 255) { @@ -707,15 +691,15 @@ HSVColor rgb_to_hsv_approx(const RGBColor &rhs) } } h += 1; - return HSVColor(h, s, v); + return HSVColor_makeHSV(h, s, v); } // Convert rgb to hsv with generic fast method -HSVColor rgb_to_hsv_generic(const RGBColor &rhs) +HSVColor rgb_to_hsv_generic(const RGBColor *rhs) { unsigned char rgbMin, rgbMax; - rgbMin = rhs.red < rhs.green ? (rhs.red < rhs.blue ? rhs.red : rhs.blue) : (rhs.green < rhs.blue ? rhs.green : rhs.blue); - rgbMax = rhs.red > rhs.green ? (rhs.red > rhs.blue ? rhs.red : rhs.blue) : (rhs.green > rhs.blue ? rhs.green : rhs.blue); + rgbMin = rhs->red < rhs->green ? (rhs->red < rhs->blue ? rhs->red : rhs->blue) : (rhs->green < rhs->blue ? rhs->green : rhs->blue); + rgbMax = rhs->red > rhs->green ? (rhs->red > rhs->blue ? rhs->red : rhs->blue) : (rhs->green > rhs->blue ? rhs->green : rhs->blue); HSVColor hsv; hsv.val = rgbMax; @@ -731,12 +715,12 @@ HSVColor rgb_to_hsv_generic(const RGBColor &rhs) return hsv; } - if (rgbMax == rhs.red) { - hsv.hue = 0 + 43 * (rhs.green - rhs.blue) / (rgbMax - rgbMin); - } else if (rgbMax == rhs.green) { - hsv.hue = 85 + 43 * (rhs.blue - rhs.red) / (rgbMax - rgbMin); + if (rgbMax == rhs->red) { + hsv.hue = 0 + 43 * (rhs->green - rhs->blue) / (rgbMax - rgbMin); + } else if (rgbMax == rhs->green) { + hsv.hue = 85 + 43 * (rhs->blue - rhs->red) / (rgbMax - rgbMin); } else { - hsv.hue = 171 + 43 * (rhs.red - rhs.green) / (rgbMax - rgbMin); + hsv.hue = 171 + 43 * (rhs->red - rhs->green) / (rgbMax - rgbMin); } return hsv; } diff --git a/VortexEngine/src/Colors/ColorTypes.h b/VortexEngine/src/Colors/ColorTypes.h index c4e92b2936..97a8c41992 100644 --- a/VortexEngine/src/Colors/ColorTypes.h +++ b/VortexEngine/src/Colors/ColorTypes.h @@ -2,12 +2,14 @@ #define COLOR_H #include +#include #include "../VortexConfig.h" #include "ColorConstants.h" -enum hsv_to_rgb_algorithm : uint8_t -{ +// kept as a byte so the global selector costs no extra ram +typedef uint8_t hsv_to_rgb_algorithm; +enum { HSV_TO_RGB_GENERIC, HSV_TO_RGB_RAW, HSV_TO_RGB_RAINBOW @@ -17,93 +19,85 @@ enum hsv_to_rgb_algorithm : uint8_t // all hsv to rgb conversions extern hsv_to_rgb_algorithm g_hsv_rgb_alg; -class ByteStream; -class RGBColor; - -class HSVColor -{ -public: - HSVColor(); - HSVColor(uint8_t hue, uint8_t sat, uint8_t val); - - // assignment from uint32_t - HSVColor(uint32_t dwVal); - HSVColor &operator=(const uint32_t &rhs); - - // copy/assignment construction - HSVColor(const HSVColor &rhs); - HSVColor &operator=(const HSVColor &rhs); - - // construction/assignment from RGB - HSVColor(const RGBColor &rhs); - HSVColor &operator=(const RGBColor &rhs); - - // equality operators - bool operator==(const HSVColor &other) const; - bool operator!=(const HSVColor &other) const; - - bool empty() const; - void clear(); - - uint32_t raw() const { return HSV_BIT | ((uint32_t)hue << 16) | ((uint32_t)sat << 8) | (uint32_t)val; } +typedef struct ByteStream ByteStream; +typedef struct RGBColor RGBColor; +typedef struct HSVColor HSVColor; +typedef struct HSVColor { // public members uint8_t hue; uint8_t sat; uint8_t val; -}; - -class RGBColor +} HSVColor; + +// construction +HSVColor HSVColor_make(void); +HSVColor HSVColor_makeHSV(uint8_t hue, uint8_t sat, uint8_t val); +// assignment from uint32_t +HSVColor HSVColor_fromU32(uint32_t dwVal); +void HSVColor_setU32(HSVColor *self, uint32_t rhs); +// copy/assignment construction +void HSVColor_setHSV(HSVColor *self, const HSVColor *rhs); +// construction/assignment from RGB +void HSVColor_setRGB(HSVColor *self, const RGBColor *rhs); +// equality operators +bool HSVColor_equals(const HSVColor *self, const HSVColor *other); + +bool HSVColor_empty(const HSVColor *self); +void HSVColor_clear(HSVColor *self); + +static inline uint32_t HSVColor_raw(const HSVColor *self) { -public: - RGBColor(); - RGBColor(uint8_t red, uint8_t green, uint8_t blue); - - // assignment from uint32_t - RGBColor(uint32_t dwVal); - RGBColor &operator=(const uint32_t &rhs); - - // copy/assignment construction - RGBColor(const RGBColor &rhs); - RGBColor &operator=(const RGBColor &rhs); - - // construction/assignment from HSV - RGBColor(const HSVColor &rhs); - RGBColor &operator=(const HSVColor &rhs); - - // equality operators - bool operator==(const RGBColor &other) const; - bool operator!=(const RGBColor &other) const; - - bool empty() const; - void clear(); - - RGBColor adjustBrightness(uint8_t fadeBy); - bool serialize(ByteStream &buffer) const; - bool unserialize(ByteStream &buffer); - - uint32_t raw() const { return ((uint32_t)red << 16) | ((uint32_t)green << 8) | (uint32_t)blue; } + return HSV_BIT | ((uint32_t)self->hue << 16) | ((uint32_t)self->sat << 8) | (uint32_t)self->val; +} +typedef struct RGBColor { // public members uint8_t red; uint8_t green; uint8_t blue; -}; +} RGBColor; + +// construction +RGBColor RGBColor_make(void); +RGBColor RGBColor_makeRGB(uint8_t red, uint8_t green, uint8_t blue); +// assignment from uint32_t +RGBColor RGBColor_fromU32(uint32_t dwVal); +void RGBColor_setU32(RGBColor *self, uint32_t rhs); +// copy/assignment construction +void RGBColor_setRGB(RGBColor *self, const RGBColor *rhs); +// construction/assignment from HSV +RGBColor RGBColor_fromHSV(const HSVColor *rhs); +void RGBColor_setHSV(RGBColor *self, const HSVColor *rhs); +// equality operators +bool RGBColor_equals(const RGBColor *self, const RGBColor *other); + +bool RGBColor_empty(const RGBColor *self); +void RGBColor_clear(RGBColor *self); + +void RGBColor_adjustBrightness(RGBColor *self, uint8_t fadeBy); +bool RGBColor_serialize(const RGBColor *self, ByteStream *buffer); +bool RGBColor_unserialize(RGBColor *self, ByteStream *buffer); + +static inline uint32_t RGBColor_raw(const RGBColor *self) +{ + return ((uint32_t)self->red << 16) | ((uint32_t)self->green << 8) | (uint32_t)self->blue; +} // Stolen from FastLED hsv to rgb full rainbox where all colours // are given equal weight, this makes for-example yellow larger // best to use this function as it is the legacy choice -RGBColor hsv_to_rgb_rainbow(const HSVColor &rhs); +RGBColor hsv_to_rgb_rainbow(const HSVColor *rhs); // stolen from FastLED raw hsv to rgb conversion, idk what is special // about this one but I think the hue stops at 190 maybe? -RGBColor hsv_to_rgb_raw_C(const HSVColor &rhs); +RGBColor hsv_to_rgb_raw_C(const HSVColor *rhs); // generic hsv to rgb conversion nothing special -RGBColor hsv_to_rgb_generic(const HSVColor &rhs); +RGBColor hsv_to_rgb_generic(const HSVColor *rhs); // Convert rgb to hsv with slower approximation that matches // the fastled hsv to rgb 'rainbow' -HSVColor rgb_to_hsv_approx(const RGBColor &rhs); +HSVColor rgb_to_hsv_approx(const RGBColor *rhs); // Convert rgb to hsv with generic fast method -HSVColor rgb_to_hsv_generic(const RGBColor &rhs); +HSVColor rgb_to_hsv_generic(const RGBColor *rhs); #endif diff --git a/VortexEngine/src/Colors/Colorset.cpp b/VortexEngine/src/Colors/Colorset.cpp index 8d19353bd8..342b022af5 100644 --- a/VortexEngine/src/Colors/Colorset.cpp +++ b/VortexEngine/src/Colors/Colorset.cpp @@ -13,196 +13,195 @@ // the 0th color in the colorset and after the index will be 0 #define INDEX_NONE UINT8_MAX -Colorset::Colorset() : - m_palette(), - m_curIndex(INDEX_NONE), - m_numColors(0) +void Colorset_make(Colorset *self) { - init(); + memset((void *)self->m_palette, 0, sizeof(self->m_palette)); + self->m_curIndex = INDEX_NONE; + self->m_numColors = 0; + Colorset_init(self, RGBColor_make(), RGBColor_make(), RGBColor_make(), RGBColor_make(), + RGBColor_make(), RGBColor_make(), RGBColor_make(), RGBColor_make()); } -Colorset::Colorset(RGBColor c1, RGBColor c2, RGBColor c3, RGBColor c4, - RGBColor c5, RGBColor c6, RGBColor c7, RGBColor c8) : - Colorset() +// constructor for 1-8 color slots +void Colorset_makeRGB(Colorset *self, RGBColor c1, RGBColor c2, RGBColor c3, RGBColor c4, + RGBColor c5, RGBColor c6, RGBColor c7, RGBColor c8) { - init(c1, c2, c3, c4, c5, c6, c7, c8); + Colorset_make(self); + Colorset_init(self, c1, c2, c3, c4, c5, c6, c7, c8); } -Colorset::Colorset(uint8_t numCols, const uint32_t *cols) : - Colorset() +void Colorset_makeCols(Colorset *self, uint8_t numCols, const uint32_t *cols) { + Colorset_make(self); if (numCols > MAX_COLOR_SLOTS) { numCols = MAX_COLOR_SLOTS; } for (uint8_t i = 0; i < numCols; ++i) { - addColor(RGBColor(cols[i])); + Colorset_addColor(self, RGBColor_fromU32(cols[i])); } } -Colorset::Colorset(const Colorset &other) : - Colorset() -{ - // invoke = operator - *this = other; -} - -Colorset::~Colorset() +void Colorset_destroy(Colorset *self) { - clear(); + Colorset_clear(self); } -Colorset::Colorset(Colorset &&other) noexcept : - m_palette(), - m_curIndex(INDEX_NONE), - m_numColors(other.m_numColors) +// move constructor +void Colorset_moveFrom(Colorset *self, Colorset *other) { - memcpy((void *)m_palette, (void *)other.m_palette, sizeof(m_palette)); - memset((void *)other.m_palette, 0, sizeof(m_palette)); - other.m_numColors = 0; - other.m_curIndex = INDEX_NONE; + memset((void *)self->m_palette, 0, sizeof(self->m_palette)); + self->m_curIndex = INDEX_NONE; + self->m_numColors = other->m_numColors; + memcpy((void *)self->m_palette, (void *)other->m_palette, sizeof(self->m_palette)); + memset((void *)other->m_palette, 0, sizeof(other->m_palette)); + other->m_numColors = 0; + other->m_curIndex = INDEX_NONE; } -void Colorset::operator=(const Colorset &other) +// copy and assignment operators +void Colorset_set(Colorset *self, const Colorset *other) { - initPalette(other.m_numColors); - for (uint8_t i = 0; i < other.m_numColors; ++i) { - m_palette[i] = other.m_palette[i]; + Colorset_initPalette(self, other->m_numColors); + for (uint8_t i = 0; i < other->m_numColors; ++i) { + self->m_palette[i] = other->m_palette[i]; } - resetIndex(); + Colorset_resetIndex(self); } -bool Colorset::operator==(const Colorset &other) const +// equality operators +bool Colorset_equal(const Colorset *self, const Colorset *other) { // only compare the palettes for equality - return (m_numColors == other.m_numColors) && - (memcmp(m_palette, other.m_palette, m_numColors * sizeof(RGBColor)) == 0); + return (self->m_numColors == other->m_numColors) && + (memcmp(self->m_palette, other->m_palette, self->m_numColors * sizeof(RGBColor)) == 0); } -bool Colorset::operator!=(const Colorset &other) const +bool Colorset_notEqual(const Colorset *self, const Colorset *other) { - return !operator==(other); + return !Colorset_equal(self, other); } -void Colorset::init(RGBColor c1, RGBColor c2, RGBColor c3, RGBColor c4, +void Colorset_init(Colorset *self, RGBColor c1, RGBColor c2, RGBColor c3, RGBColor c4, RGBColor c5, RGBColor c6, RGBColor c7, RGBColor c8) { // clear any existing colors - clear(); + Colorset_clear(self); // would be nice if we could do this another way - if (!c1.empty()) addColor(c1); - if (!c2.empty()) addColor(c2); - if (!c3.empty()) addColor(c3); - if (!c4.empty()) addColor(c4); - if (!c5.empty()) addColor(c5); - if (!c6.empty()) addColor(c6); - if (!c7.empty()) addColor(c7); - if (!c8.empty()) addColor(c8); + if (!RGBColor_empty(&c1)) Colorset_addColor(self, c1); + if (!RGBColor_empty(&c2)) Colorset_addColor(self, c2); + if (!RGBColor_empty(&c3)) Colorset_addColor(self, c3); + if (!RGBColor_empty(&c4)) Colorset_addColor(self, c4); + if (!RGBColor_empty(&c5)) Colorset_addColor(self, c5); + if (!RGBColor_empty(&c6)) Colorset_addColor(self, c6); + if (!RGBColor_empty(&c7)) Colorset_addColor(self, c7); + if (!RGBColor_empty(&c8)) Colorset_addColor(self, c8); } -void Colorset::clear() +void Colorset_clear(Colorset *self) { - memset((void *)m_palette, 0, sizeof(m_palette)); - m_numColors = 0; - resetIndex(); + memset((void *)self->m_palette, 0, sizeof(self->m_palette)); + self->m_numColors = 0; + Colorset_resetIndex(self); } -bool Colorset::equals(const Colorset &set) const +bool Colorset_equals(const Colorset *self, const Colorset *set) { - return operator==(set); + return Colorset_equal(self, set); } -bool Colorset::equals(const Colorset *set) const +bool Colorset_equalsPtr(const Colorset *self, const Colorset *set) { if (!set) { return false; } - return operator==(*set); + return Colorset_equal(self, set); } -RGBColor Colorset::operator[](int index) const +RGBColor Colorset_at(const Colorset *self, int index) { - return get(index); + return Colorset_get(self, index); } // add a single color -bool Colorset::addColor(RGBColor col) +bool Colorset_addColor(Colorset *self, RGBColor col) { - if (m_numColors >= MAX_COLOR_SLOTS) { + if (self->m_numColors >= MAX_COLOR_SLOTS) { return false; } // insert new color and increment number of colors - m_palette[m_numColors] = col; - m_numColors++; + self->m_palette[self->m_numColors] = col; + self->m_numColors++; return true; } -bool Colorset::addColorHSV(uint8_t hue, uint8_t sat, uint8_t val) +bool Colorset_addColorHSV(Colorset *self, uint8_t hue, uint8_t sat, uint8_t val) { - return addColor(HSVColor(hue, sat, val)); + HSVColor hsv = HSVColor_makeHSV(hue, sat, val); + return Colorset_addColor(self, RGBColor_fromHSV(&hsv)); } -void Colorset::addColorWithValueStyle(Random &ctx, uint8_t hue, uint8_t sat, ValueStyle valStyle, uint8_t numColors, uint8_t colorPos) +void Colorset_addColorWithValueStyle(Colorset *self, Random *ctx, uint8_t hue, uint8_t sat, ColorsetValueStyle valStyle, uint8_t numColors, uint8_t colorPos) { if (numColors == 1) { - addColorHSV(hue, sat, ctx.next8(16, 255)); + Colorset_addColorHSV(self, hue, sat, Random_next8(ctx, 16, 255)); return; } switch (valStyle) { default: case VAL_STYLE_RANDOM: - addColorHSV(hue, sat, 85 * ctx.next8(1, 4)); + Colorset_addColorHSV(self, hue, sat, 85 * Random_next8(ctx, 1, 4)); break; case VAL_STYLE_LOW_FIRST_COLOR: - if (m_numColors == 0) { - addColorHSV(hue, sat, ctx.next8(0, 86)); + if (self->m_numColors == 0) { + Colorset_addColorHSV(self, hue, sat, Random_next8(ctx, 0, 86)); } else { - addColorHSV(hue, sat, 85 * ctx.next8(1, 4)); + Colorset_addColorHSV(self, hue, sat, 85 * Random_next8(ctx, 1, 4)); } break; case VAL_STYLE_HIGH_FIRST_COLOR: - if (m_numColors == 0) { - addColorHSV(hue, sat, 255); + if (self->m_numColors == 0) { + Colorset_addColorHSV(self, hue, sat, 255); } else { - addColorHSV(hue, sat, ctx.next8(0, 86)); + Colorset_addColorHSV(self, hue, sat, Random_next8(ctx, 0, 86)); } break; case VAL_STYLE_ALTERNATING: - if (m_numColors % 2 == 0) { - addColorHSV(hue, sat, 255); + if (self->m_numColors % 2 == 0) { + Colorset_addColorHSV(self, hue, sat, 255); } else { - addColorHSV(hue, sat, 85); + Colorset_addColorHSV(self, hue, sat, 85); } break; case VAL_STYLE_ASCENDING: - addColorHSV(hue, sat, (colorPos + 1) * (255 / numColors)); + Colorset_addColorHSV(self, hue, sat, (colorPos + 1) * (255 / numColors)); break; case VAL_STYLE_DESCENDING: - addColorHSV(hue, sat, 255 - (colorPos * (255 / numColors))); + Colorset_addColorHSV(self, hue, sat, 255 - (colorPos * (255 / numColors))); break; case VAL_STYLE_CONSTANT: - addColorHSV(hue, sat, 255); + Colorset_addColorHSV(self, hue, sat, 255); } } -void Colorset::removeColor(uint8_t index) +void Colorset_removeColor(Colorset *self, uint8_t index) { - if (index >= m_numColors) { + if (index >= self->m_numColors) { return; } - for (uint8_t i = index; i < (m_numColors - 1); ++i) { - m_palette[i] = m_palette[i + 1]; + for (uint8_t i = index; i < (self->m_numColors - 1); ++i) { + self->m_palette[i] = self->m_palette[i + 1]; } - m_palette[--m_numColors].clear(); + RGBColor_clear(&self->m_palette[--self->m_numColors]); } // create a set of truely random colors -void Colorset::randomize(Random &ctx, uint8_t numColors) +void Colorset_randomize(Colorset *self, Random *ctx, uint8_t numColors) { - clear(); + Colorset_clear(self); if (!numColors) { - numColors = ctx.next8(2, 9); + numColors = Random_next8(ctx, 2, 9); } - ValueStyle valStyle = (ValueStyle)ctx.next8(0, VAL_STYLE_COUNT); + ColorsetValueStyle valStyle = (ColorsetValueStyle)Random_next8(ctx, 0, VAL_STYLE_COUNT); for (uint8_t i = 0; i < numColors; ++i) { // call next8 explicitly in this order because the order they @@ -210,31 +209,31 @@ void Colorset::randomize(Random &ctx, uint8_t numColors) // ex: f(a,b,c) may call in the order a,b,c or c,b,a depending on compiler. // So different compilers may produce different results, // but like this it is explicit - uint8_t sat = ctx.next8(); - uint8_t hue = ctx.next8(); - addColorWithValueStyle(ctx, hue, sat, valStyle, numColors, i); + uint8_t sat = Random_next8Default(ctx); + uint8_t hue = Random_next8Default(ctx); + Colorset_addColorWithValueStyle(self, ctx, hue, sat, valStyle, numColors, i); } } -void Colorset::randomizeColors(Random &ctx, uint8_t numColors, ColorMode mode) +void Colorset_randomizeColors(Colorset *self, Random *ctx, uint8_t numColors, ColorsetColorMode mode) { - clear(); + Colorset_clear(self); if (!numColors) { - numColors = ctx.next8(mode == MONOCHROMATIC ? 2 : 1, 9); + numColors = Random_next8(ctx, mode == MONOCHROMATIC ? 2 : 1, 9); } - uint8_t randomizedHue = ctx.next8(); + uint8_t randomizedHue = Random_next8Default(ctx); uint8_t colorGap = 0; if (mode == THEORY && numColors > 1) { - colorGap = ctx.next8(16, 256 / (numColors - 1)); + colorGap = Random_next8(ctx, 16, 256 / (numColors - 1)); } - ValueStyle valStyle = (ValueStyle)ctx.next8(0, VAL_STYLE_COUNT); + ColorsetValueStyle valStyle = (ColorsetValueStyle)Random_next8(ctx, 0, VAL_STYLE_COUNT); // the doubleStyle decides if some colors are added to the set twice uint8_t doubleStyle = 0; if (numColors <= 7) { - doubleStyle = (ctx.next8(0, 1)); + doubleStyle = (Random_next8(ctx, 0, 1)); } if (numColors <= 4) { - doubleStyle = (ctx.next8(0, 2)); + doubleStyle = (Random_next8(ctx, 0, 2)); } for (uint8_t i = 0; i < numColors; i++) { uint8_t hueToUse; @@ -247,237 +246,237 @@ void Colorset::randomizeColors(Random &ctx, uint8_t numColors, ColorMode mode) } else { // EVENLY_SPACED hueToUse = (randomizedHue + (256 / numColors) * i); } - addColorWithValueStyle(ctx, hueToUse, valueToUse, valStyle, numColors, i); + Colorset_addColorWithValueStyle(self, ctx, hueToUse, valueToUse, valStyle, numColors, i); // double all colors or only first color if (doubleStyle == 2 || (doubleStyle == 1 && !i)) { - addColorWithValueStyle(ctx, hueToUse, valueToUse, valStyle, numColors, i); + Colorset_addColorWithValueStyle(self, ctx, hueToUse, valueToUse, valStyle, numColors, i); } } } -void Colorset::randomizeColors2(Random &ctx, ColorMode2 mode) +void Colorset_randomizeColors2(Colorset *self, Random *ctx, ColorsetColorMode2 mode) { - clear(); - uint8_t primaryHue = ctx.next8(); + Colorset_clear(self); + uint8_t primaryHue = Random_next8Default(ctx); if (mode == DOUBLE_SPLIT_COMPLIMENTARY) { - uint8_t splitGap = ctx.next8(1, 64); - ValueStyle valStyle = (ValueStyle)ctx.next8(0, VAL_STYLE_COUNT); - addColorWithValueStyle(ctx, (primaryHue + splitGap + 128), 255, valStyle, 5, 0); - addColorWithValueStyle(ctx, (primaryHue - splitGap), 255, valStyle, 5, 1); - addColorWithValueStyle(ctx, primaryHue, 255, valStyle, 5, 2); - addColorWithValueStyle(ctx, (primaryHue + splitGap), 255, valStyle, 5, 3); - addColorWithValueStyle(ctx, (primaryHue - splitGap + 128), 255, valStyle, 5, 4); + uint8_t splitGap = Random_next8(ctx, 1, 64); + ColorsetValueStyle valStyle = (ColorsetValueStyle)Random_next8(ctx, 0, VAL_STYLE_COUNT); + Colorset_addColorWithValueStyle(self, ctx, (primaryHue + splitGap + 128), 255, valStyle, 5, 0); + Colorset_addColorWithValueStyle(self, ctx, (primaryHue - splitGap), 255, valStyle, 5, 1); + Colorset_addColorWithValueStyle(self, ctx, primaryHue, 255, valStyle, 5, 2); + Colorset_addColorWithValueStyle(self, ctx, (primaryHue + splitGap), 255, valStyle, 5, 3); + Colorset_addColorWithValueStyle(self, ctx, (primaryHue - splitGap + 128), 255, valStyle, 5, 4); } else if (mode == TETRADIC) { - uint8_t secondaryHue = ctx.next8(); - ValueStyle valStyle = (ValueStyle)ctx.next8(0, VAL_STYLE_COUNT); - addColorWithValueStyle(ctx, primaryHue, 255, valStyle, 4, 0); - addColorWithValueStyle(ctx, secondaryHue, 255, valStyle, 4, 1); - addColorWithValueStyle(ctx, (primaryHue + 128), 255, valStyle, 4, 2); - addColorWithValueStyle(ctx, (secondaryHue + 128), 255, valStyle, 4, 3); + uint8_t secondaryHue = Random_next8Default(ctx); + ColorsetValueStyle valStyle = (ColorsetValueStyle)Random_next8(ctx, 0, VAL_STYLE_COUNT); + Colorset_addColorWithValueStyle(self, ctx, primaryHue, 255, valStyle, 4, 0); + Colorset_addColorWithValueStyle(self, ctx, secondaryHue, 255, valStyle, 4, 1); + Colorset_addColorWithValueStyle(self, ctx, (primaryHue + 128), 255, valStyle, 4, 2); + Colorset_addColorWithValueStyle(self, ctx, (secondaryHue + 128), 255, valStyle, 4, 3); } } -void Colorset::adjustBrightness(uint8_t fadeby) +void Colorset_adjustBrightness(Colorset *self, uint8_t fadeby) { - for (uint8_t i = 0; i < m_numColors; ++i) { - m_palette[i].adjustBrightness(fadeby); + for (uint8_t i = 0; i < self->m_numColors; ++i) { + RGBColor_adjustBrightness(&self->m_palette[i], fadeby); } } // get a color from the colorset -RGBColor Colorset::get(uint8_t index) const +RGBColor Colorset_get(const Colorset *self, uint8_t index) { - if (index >= m_numColors) { - return RGBColor(0, 0, 0); + if (index >= self->m_numColors) { + return RGBColor_make(); } - return m_palette[index]; + return self->m_palette[index]; } // set an rgb color in a slot, or add a new color if you specify // a slot higher than the number of colors in the colorset -void Colorset::set(uint8_t index, RGBColor col) +void Colorset_setColor(Colorset *self, uint8_t index, RGBColor col) { // special case for 'setting' a color at the edge of the palette, // ie adding a new color when you set an index higher than the max - if (index >= m_numColors) { - if (!addColor(col)) { + if (index >= self->m_numColors) { + if (!Colorset_addColor(self, col)) { ERROR_LOGF("Failed to add new color at index %u", index); } return; } - m_palette[index] = col; + self->m_palette[index] = col; } // skip some amount of colors -void Colorset::skip(int32_t amount) +void Colorset_skip(Colorset *self, int32_t amount) { - if (!m_numColors) { + if (!self->m_numColors) { return; } // if the colorset hasn't started yet - if (m_curIndex == INDEX_NONE) { - m_curIndex = 0; + if (self->m_curIndex == INDEX_NONE) { + self->m_curIndex = 0; } // first modulate the amount to skip to be within +/- the number of colors - amount %= (int32_t)m_numColors; + amount %= (int32_t)self->m_numColors; // max = 3 - // m_curIndex = 2 + // self->m_curIndex = 2 // amount = -10 - m_curIndex = ((int32_t)m_curIndex + (int32_t)amount) % (int32_t)m_numColors; - if (m_curIndex > m_numColors) { // must have wrapped + self->m_curIndex = ((int32_t)self->m_curIndex + (int32_t)amount) % (int32_t)self->m_numColors; + if (self->m_curIndex > self->m_numColors) { // must have wrapped // simply wrap it back - m_curIndex += m_numColors; + self->m_curIndex += self->m_numColors; } } -RGBColor Colorset::cur() +RGBColor Colorset_cur(Colorset *self) { - if (m_curIndex >= m_numColors) { - return RGBColor(0, 0, 0); + if (self->m_curIndex >= self->m_numColors) { + return RGBColor_make(); } - if (m_curIndex == INDEX_NONE) { - return m_palette[0]; + if (self->m_curIndex == INDEX_NONE) { + return self->m_palette[0]; } - return m_palette[m_curIndex]; + return self->m_palette[self->m_curIndex]; } -void Colorset::setCurIndex(uint8_t index) +void Colorset_setCurIndex(Colorset *self, uint8_t index) { - if (!m_numColors) { + if (!self->m_numColors) { return; } - if (index > (m_numColors - 1)) { + if (index > (self->m_numColors - 1)) { return; } - m_curIndex = index; + self->m_curIndex = index; } -void Colorset::resetIndex() +void Colorset_resetIndex(Colorset *self) { - m_curIndex = INDEX_NONE; + self->m_curIndex = INDEX_NONE; } -RGBColor Colorset::getPrev() +RGBColor Colorset_getPrev(Colorset *self) { - if (!m_numColors) { - return RGB_OFF; + if (!self->m_numColors) { + return RGBColor_make(); } // handle wrapping at 0 - if (m_curIndex == 0 || m_curIndex == INDEX_NONE) { - m_curIndex = numColors() - 1; + if (self->m_curIndex == 0 || self->m_curIndex == INDEX_NONE) { + self->m_curIndex = Colorset_numColors(self) - 1; } else { - m_curIndex--; + self->m_curIndex--; } // return the color - return m_palette[m_curIndex]; + return self->m_palette[self->m_curIndex]; } -RGBColor Colorset::getNext() +RGBColor Colorset_getNext(Colorset *self) { - if (!m_numColors) { - return RGB_OFF; + if (!self->m_numColors) { + return RGBColor_make(); } // iterate current index, let it wrap at max uint8 - m_curIndex++; + self->m_curIndex++; // then modulate the result within max colors - m_curIndex %= numColors(); + self->m_curIndex %= Colorset_numColors(self); // return the color - return m_palette[m_curIndex]; + return self->m_palette[self->m_curIndex]; } // peek at the next color but don't iterate -RGBColor Colorset::peek(int32_t offset) const +RGBColor Colorset_peek(const Colorset *self, int32_t offset) { - if (!m_numColors) { - return RGB_OFF; + if (!self->m_numColors) { + return RGBColor_make(); } uint8_t nextIndex = 0; // get index of the next color if (offset >= 0) { - nextIndex = (m_curIndex + offset) % numColors(); + nextIndex = (self->m_curIndex + offset) % Colorset_numColors(self); } else { - if (offset < -1 * (int32_t)(numColors())) { - return RGB_OFF; + if (offset < -1 * (int32_t)(Colorset_numColors(self))) { + return RGBColor_make(); } - nextIndex = ((m_curIndex + numColors()) + (int)offset) % numColors(); + nextIndex = ((self->m_curIndex + Colorset_numColors(self)) + (int)offset) % Colorset_numColors(self); } // return the color - return m_palette[nextIndex]; + return self->m_palette[nextIndex]; } -bool Colorset::onStart() const +bool Colorset_onStart(const Colorset *self) { - return (m_curIndex == 0); + return (self->m_curIndex == 0); } -bool Colorset::onEnd() const +bool Colorset_onEnd(const Colorset *self) { - if (!m_numColors) { + if (!self->m_numColors) { return false; } - return (m_curIndex == m_numColors - 1); + return (self->m_curIndex == self->m_numColors - 1); } -bool Colorset::serialize(ByteStream &buffer) const +bool Colorset_serialize(const Colorset *self, ByteStream *buffer) { - if (!buffer.serialize8(m_numColors)) { + if (!ByteStream_serialize8(buffer, self->m_numColors)) { return false; } // write all the reds/greens/blues together to maximize chance of // repeated values to improve RLE compression - for (uint8_t i = 0; i < m_numColors; ++i) { - if (!buffer.serialize8(m_palette[i].red)) { + for (uint8_t i = 0; i < self->m_numColors; ++i) { + if (!ByteStream_serialize8(buffer, self->m_palette[i].red)) { return false; } } - for (uint8_t i = 0; i < m_numColors; ++i) { - if (!buffer.serialize8(m_palette[i].green)) { + for (uint8_t i = 0; i < self->m_numColors; ++i) { + if (!ByteStream_serialize8(buffer, self->m_palette[i].green)) { return false; } } - for (uint8_t i = 0; i < m_numColors; ++i) { - if (!buffer.serialize8(m_palette[i].blue)) { + for (uint8_t i = 0; i < self->m_numColors; ++i) { + if (!ByteStream_serialize8(buffer, self->m_palette[i].blue)) { return false; } } return true; } -bool Colorset::unserialize(ByteStream &buffer) +bool Colorset_unserialize(Colorset *self, ByteStream *buffer) { - if (!buffer.unserialize8(&m_numColors)) { + if (!ByteStream_unserialize8(buffer, &self->m_numColors)) { return false; } - if (m_numColors > MAX_COLOR_SLOTS) { + if (self->m_numColors > MAX_COLOR_SLOTS) { return false; } - if (!initPalette(m_numColors)) { + if (!Colorset_initPalette(self, self->m_numColors)) { return false; } - for (uint8_t i = 0; i < m_numColors; ++i) { - if (!buffer.unserialize8(&m_palette[i].red)) { + for (uint8_t i = 0; i < self->m_numColors; ++i) { + if (!ByteStream_unserialize8(buffer, &self->m_palette[i].red)) { return false; } } - for (uint8_t i = 0; i < m_numColors; ++i) { - if (!buffer.unserialize8(&m_palette[i].green)) { + for (uint8_t i = 0; i < self->m_numColors; ++i) { + if (!ByteStream_unserialize8(buffer, &self->m_palette[i].green)) { return false; } } - for (uint8_t i = 0; i < m_numColors; ++i) { - if (!buffer.unserialize8(&m_palette[i].blue)) { + for (uint8_t i = 0; i < self->m_numColors; ++i) { + if (!ByteStream_unserialize8(buffer, &self->m_palette[i].blue)) { return false; } } return true; } -bool Colorset::initPalette(uint8_t numColors) +bool Colorset_initPalette(Colorset *self, uint8_t numColors) { - clear(); - m_numColors = numColors; + Colorset_clear(self); + self->m_numColors = numColors; return true; } diff --git a/VortexEngine/src/Colors/Colorset.h b/VortexEngine/src/Colors/Colorset.h index 2c34f32c0d..be884285fd 100644 --- a/VortexEngine/src/Colors/Colorset.h +++ b/VortexEngine/src/Colors/Colorset.h @@ -1,155 +1,15 @@ #ifndef COLORSET_H #define COLORSET_H +#include #include "ColorTypes.h" #include "../VortexConfig.h" -class ByteStream; -class Random; - -class Colorset -{ -public: - // empty colorset - Colorset(); - // constructor for 1-8 color slots - Colorset(RGBColor c1, RGBColor c2 = RGB_OFF, RGBColor c3 = RGB_OFF, - RGBColor c4 = RGB_OFF, RGBColor c5 = RGB_OFF, RGBColor c6 = RGB_OFF, - RGBColor c7 = RGB_OFF, RGBColor c8 = RGB_OFF); - Colorset(uint8_t numCols, const uint32_t *cols); - ~Colorset(); - - // move constructor - Colorset(Colorset &&other) noexcept; - - // copy and assignment operators - Colorset(const Colorset &other); - void operator=(const Colorset &other); - - // equality operators - bool operator==(const Colorset &other) const; - bool operator!=(const Colorset &other) const; - - // initialize the colorset - void init(RGBColor c1 = RGB_OFF, RGBColor c2 = RGB_OFF, RGBColor c3 = RGB_OFF, - RGBColor c4 = RGB_OFF, RGBColor c5 = RGB_OFF, RGBColor c6 = RGB_OFF, - RGBColor c7 = RGB_OFF, RGBColor c8 = RGB_OFF); - - // clear the colorset - void clear(); - - // pointer comparison - bool equals(const Colorset &set) const; - bool equals(const Colorset *set) const; - - // index operator to access color index - RGBColor operator[](int index) const; - - enum ValueStyle : uint8_t - { - // Random values - VAL_STYLE_RANDOM = 0, - // First color low value, the rest are random - VAL_STYLE_LOW_FIRST_COLOR, - // First color high value, the rest are low - VAL_STYLE_HIGH_FIRST_COLOR, - // Alternat between high and low value - VAL_STYLE_ALTERNATING, - // Ascending values from low to high - VAL_STYLE_ASCENDING, - // Descending values from high to low - VAL_STYLE_DESCENDING, - // Constant value - VAL_STYLE_CONSTANT, - // Total number of value styles - VAL_STYLE_COUNT - }; - - // add a single color - bool addColor(RGBColor col); - bool addColorHSV(uint8_t hue, uint8_t sat, uint8_t val); - void addColorWithValueStyle(Random &ctx, uint8_t hue, uint8_t sat, - ValueStyle valStyle, uint8_t numColors, uint8_t colorPos); - void removeColor(uint8_t index); - - // randomize a colorset with a specific number of colors with - // various different randomization techniques - void randomize(Random &ctx, uint8_t numColors = 0); - - // function to randomize the colors with various different modes of randomization - enum ColorMode { - THEORY, - MONOCHROMATIC, - EVENLY_SPACED - }; - void randomizeColors(Random &ctx, uint8_t numColors, ColorMode mode); - - // similar function but with some different modes - enum ColorMode2 { - DOUBLE_SPLIT_COMPLIMENTARY, - TETRADIC - }; - void randomizeColors2(Random &ctx, ColorMode2 mode); - - // wrappers for various spacings - void randomizeSolid(Random &ctx) { randomizeColors(ctx, 1, Colorset::ColorMode::EVENLY_SPACED); } - void randomizeComplimentary(Random &ctx) { randomizeColors(ctx, 2, Colorset::ColorMode::EVENLY_SPACED); } - void randomizeTriadic(Random &ctx) { randomizeColors(ctx, 3, Colorset::ColorMode::EVENLY_SPACED); } - void randomizeSquare(Random &ctx) { randomizeColors(ctx, 4, Colorset::ColorMode::EVENLY_SPACED); } - void randomizePentadic(Random &ctx) { randomizeColors(ctx, 5, Colorset::ColorMode::EVENLY_SPACED); } - void randomizeRainbow(Random &ctx) { randomizeColors(ctx, 8, Colorset::ColorMode::EVENLY_SPACED); } - - // fade all of the colors in the set - void adjustBrightness(uint8_t fadeby); - - // get a color from the colorset - RGBColor get(uint8_t index = 0) const; - - // set an rgb color in a slot, or add a new color if you specify - // a slot higher than the number of colors in the colorset - void set(uint8_t index, RGBColor col); - - // skip some amount of colors - void skip(int32_t amount = 1); - - // get current color in cycle - RGBColor cur(); - - // set the current index of the colorset - void setCurIndex(uint8_t index); - void resetIndex(); - - // the current index - uint8_t curIndex() const { return m_curIndex; } - - // get the prev color in cycle - RGBColor getPrev(); - - // get the next color in cycle - RGBColor getNext(); - - // peek at the color indexes from current but don't iterate - RGBColor peek(int32_t offset) const; - - // better wording for peek 1 ahead - RGBColor peekNext() const { return peek(1); } - - // the number of colors in the palette - uint8_t numColors() const { return m_numColors; } - - // whether the colorset is currently on the first color or last color - bool onStart() const; - bool onEnd() const; - - // serialize the colorset to save/load - bool serialize(ByteStream &buffer) const; - bool unserialize(ByteStream &buffer); - -private: - // pre-allocate the palette - bool initPalette(uint8_t numColors); +typedef struct ByteStream ByteStream; +typedef struct Random Random; +typedef struct Colorset { // palette of colors RGBColor m_palette[MAX_COLOR_SLOTS]; // the current index, starts at UINT8_MAX so that @@ -157,6 +17,160 @@ class Colorset uint8_t m_curIndex; // the actual number of colors in the set uint8_t m_numColors; +} Colorset; + +// empty colorset +void Colorset_make(Colorset *self); +// constructor for 1-8 color slots (missing slots default to RGB_OFF) +void Colorset_makeRGB(Colorset *self, RGBColor c1, RGBColor c2, RGBColor c3, RGBColor c4, + RGBColor c5, RGBColor c6, RGBColor c7, RGBColor c8); +void Colorset_makeCols(Colorset *self, uint8_t numCols, const uint32_t *cols); +void Colorset_destroy(Colorset *self); + +// pads the color list out to the full 8 slots with RGB_OFF +#define Colorset_make1(...) Colorset_makeRGB(__VA_ARGS__, RGB_OFF, RGB_OFF, RGB_OFF, RGB_OFF, RGB_OFF, RGB_OFF, RGB_OFF) +#define Colorset_make2(...) Colorset_makeRGB(__VA_ARGS__, RGB_OFF, RGB_OFF, RGB_OFF, RGB_OFF, RGB_OFF, RGB_OFF) + +// move constructor +void Colorset_moveFrom(Colorset *self, Colorset *other); + +// copy and assignment operators +void Colorset_set(Colorset *self, const Colorset *other); + +// equality operators +bool Colorset_equal(const Colorset *self, const Colorset *other); +bool Colorset_notEqual(const Colorset *self, const Colorset *other); + +// initialize the colorset +void Colorset_init(Colorset *self, RGBColor c1, RGBColor c2, RGBColor c3, RGBColor c4, + RGBColor c5, RGBColor c6, RGBColor c7, RGBColor c8); + +// clear the colorset +void Colorset_clear(Colorset *self); + +// pointer comparison +bool Colorset_equals(const Colorset *self, const Colorset *set); +bool Colorset_equalsPtr(const Colorset *self, const Colorset *set); + +// index operator to access color index +RGBColor Colorset_at(const Colorset *self, int index); + +// value styles for randomizing colors +typedef uint8_t ColorsetValueStyle; +enum { + // Random values + VAL_STYLE_RANDOM = 0, + // First color low value, the rest are random + VAL_STYLE_LOW_FIRST_COLOR, + // First color high value, the rest are low + VAL_STYLE_HIGH_FIRST_COLOR, + // Alternat between high and low value + VAL_STYLE_ALTERNATING, + // Ascending values from low to high + VAL_STYLE_ASCENDING, + // Descending values from high to low + VAL_STYLE_DESCENDING, + // Constant value + VAL_STYLE_CONSTANT, + // Total number of value styles + VAL_STYLE_COUNT +}; + +// add a single color +bool Colorset_addColor(Colorset *self, RGBColor col); +bool Colorset_addColorHSV(Colorset *self, uint8_t hue, uint8_t sat, uint8_t val); +void Colorset_addColorWithValueStyle(Colorset *self, Random *ctx, uint8_t hue, uint8_t sat, + ColorsetValueStyle valStyle, uint8_t numColors, uint8_t colorPos); +void Colorset_removeColor(Colorset *self, uint8_t index); + +// randomization modes for randomizeColors() +typedef uint8_t ColorsetColorMode; +enum { + THEORY, + MONOCHROMATIC, + EVENLY_SPACED +}; + +// randomize a colorset with a specific number of colors with +// various different randomization techniques +void Colorset_randomize(Colorset *self, Random *ctx, uint8_t numColors); + +// function to randomize the colors with various different modes of randomization +void Colorset_randomizeColors(Colorset *self, Random *ctx, uint8_t numColors, ColorsetColorMode mode); + +// similar function but with some different modes +typedef uint8_t ColorsetColorMode2; +enum { + DOUBLE_SPLIT_COMPLIMENTARY, + TETRADIC }; +void Colorset_randomizeColors2(Colorset *self, Random *ctx, ColorsetColorMode2 mode); + +// wrappers for various spacings +static inline void Colorset_randomizeSolid(Colorset *self, Random *ctx) { Colorset_randomizeColors(self, ctx, 1, EVENLY_SPACED); } +static inline void Colorset_randomizeComplimentary(Colorset *self, Random *ctx) { Colorset_randomizeColors(self, ctx, 2, EVENLY_SPACED); } +static inline void Colorset_randomizeTriadic(Colorset *self, Random *ctx) { Colorset_randomizeColors(self, ctx, 3, EVENLY_SPACED); } +static inline void Colorset_randomizeSquare(Colorset *self, Random *ctx) { Colorset_randomizeColors(self, ctx, 4, EVENLY_SPACED); } +static inline void Colorset_randomizePentadic(Colorset *self, Random *ctx) { Colorset_randomizeColors(self, ctx, 5, EVENLY_SPACED); } +static inline void Colorset_randomizeRainbow(Colorset *self, Random *ctx) { Colorset_randomizeColors(self, ctx, 8, EVENLY_SPACED); } + +// fade all of the colors in the set +void Colorset_adjustBrightness(Colorset *self, uint8_t fadeby); + +// get a color from the colorset +RGBColor Colorset_get(const Colorset *self, uint8_t index); + +// set an rgb color in a slot, or add a new color if you specify +// a slot higher than the number of colors in the colorset +void Colorset_setColor(Colorset *self, uint8_t index, RGBColor col); + +// skip some amount of colors +void Colorset_skip(Colorset *self, int32_t amount); + +// get current color in cycle +RGBColor Colorset_cur(Colorset *self); + +// set the current index of the colorset +void Colorset_setCurIndex(Colorset *self, uint8_t index); +void Colorset_resetIndex(Colorset *self); + +// the current index +static inline uint8_t Colorset_curIndex(const Colorset *self) +{ + return self->m_curIndex; +} + +// get the prev color in cycle +RGBColor Colorset_getPrev(Colorset *self); + +// get the next color in cycle +RGBColor Colorset_getNext(Colorset *self); + +// peek at the color indexes from current but don't iterate +RGBColor Colorset_peek(const Colorset *self, int32_t offset); + +// better wording for peek 1 ahead +static inline RGBColor Colorset_peekNext(const Colorset *self) +{ + return Colorset_peek(self, 1); +} + +// the number of colors in the palette +static inline uint8_t Colorset_numColors(const Colorset *self) +{ + return self->m_numColors; +} + +// whether the colorset is currently on the first color or last color +bool Colorset_onStart(const Colorset *self); +bool Colorset_onEnd(const Colorset *self); + +// serialize the colorset to save/load +bool Colorset_serialize(const Colorset *self, ByteStream *buffer); +bool Colorset_unserialize(Colorset *self, ByteStream *buffer); + +// pre-allocate the palette +bool Colorset_initPalette(Colorset *self, uint8_t numColors); + #endif diff --git a/VortexEngine/src/Leds/LedStash.cpp b/VortexEngine/src/Leds/LedStash.cpp index c02242edd6..54f36c9acf 100644 --- a/VortexEngine/src/Leds/LedStash.cpp +++ b/VortexEngine/src/Leds/LedStash.cpp @@ -1,31 +1,33 @@ #include "LedStash.h" -LedStash::LedStash() : - m_ledColorsStash() +#include + +void LedStash_make(LedStash *self) { + memset((void *)self->m_ledColorsStash, 0, sizeof(self->m_ledColorsStash)); } -void LedStash::setIndex(LedPos pos, RGBColor col) +void LedStash_setIndex(LedStash *self, LedPos pos, RGBColor col) { if (pos > LED_LAST) { return; } - m_ledColorsStash[pos] = col; + self->m_ledColorsStash[pos] = col; } -void LedStash::clear() +void LedStash_clear(LedStash *self) { for (LedPos pos = LED_FIRST; pos < LED_LAST; ++pos) { - m_ledColorsStash[pos].clear(); + RGBColor_clear(&self->m_ledColorsStash[pos]); } } // index operator to access color index -RGBColor &LedStash::operator[](int index) +RGBColor *LedStash_at(LedStash *self, int index) { - return m_ledColorsStash[index]; + return &self->m_ledColorsStash[index]; } -LedStash::~LedStash() +void LedStash_destroy(LedStash *self) { } diff --git a/VortexEngine/src/Leds/LedStash.h b/VortexEngine/src/Leds/LedStash.h index 1089ca40c9..29f0153476 100644 --- a/VortexEngine/src/Leds/LedStash.h +++ b/VortexEngine/src/Leds/LedStash.h @@ -4,22 +4,18 @@ #include "../Colors/ColorTypes.h" #include "LedTypes.h" -class LedStash -{ - friend class Leds; -public: - LedStash(); - ~LedStash(); +typedef struct LedStash { + RGBColor m_ledColorsStash[LED_COUNT]; +} LedStash; - void setIndex(LedPos pos, RGBColor col); +void LedStash_make(LedStash *self); +void LedStash_destroy(LedStash *self); - void clear(); +void LedStash_setIndex(LedStash *self, LedPos pos, RGBColor col); - // index operator to access color index - RGBColor &operator[](int index); +void LedStash_clear(LedStash *self); -private: - RGBColor m_ledColorsStash[LED_COUNT]; -}; +// index operator to access color index +RGBColor *LedStash_at(LedStash *self, int index); -#endif \ No newline at end of file +#endif diff --git a/VortexEngine/src/Leds/LedTypes.h b/VortexEngine/src/Leds/LedTypes.h index 953ffa4c99..326f6e6e04 100644 --- a/VortexEngine/src/Leds/LedTypes.h +++ b/VortexEngine/src/Leds/LedTypes.h @@ -3,11 +3,18 @@ #include #include +#include #include "../VortexConfig.h" -// Defined the LED positions, their order, and index -enum LedPos : uint8_t +// Defined the LED positions, their order, and index. +// LedPos and Pair are plain uint8_t typedefs so that the native integer +// operators (++, --, +, -, +=, -=) work as they did with the C++ enum +// operator overloads. +typedef uint8_t LedPos; +typedef uint8_t Pair; + +enum { // this should always be first LED_FIRST = 0, @@ -59,7 +66,7 @@ enum LedPos : uint8_t #define LED_TIP LED_0 #define LED_TOP LED_1 -enum Pair : uint8_t +enum { PAIR_FIRST = 0, @@ -71,7 +78,7 @@ enum Pair : uint8_t }; // Compile-time check on the number of pairs and leds -static_assert(LED_COUNT == (PAIR_COUNT * 2), "Incorrect number of Pairs for Leds! Adjust the Led enum or Pair enum to match"); +STATIC_ASSERT(LED_COUNT == (PAIR_COUNT * 2), "Incorrect number of Pairs for Leds! Adjust the Led enum or Pair enum to match"); // backwards compat with bigger patterns #define LED_2 LED_0 @@ -93,30 +100,30 @@ static_assert(LED_COUNT == (PAIR_COUNT * 2), "Incorrect number of Pairs for Leds #define isOdd(pos) ((pos % 2) != 0) // convert a pair to even or odd led position -#define pairEven(pair) (LedPos)((uint32_t)pair * 2) -#define pairOdd(pair) (LedPos)(((uint32_t)pair * 2) + 1) +#define pairEven(pair) ((LedPos)((uint32_t)(pair) * 2)) +#define pairOdd(pair) ((LedPos)(((uint32_t)(pair) * 2) + 1)) // convert an led position to a pair -#define ledToPair(pos) (Pair)((uint32_t)pos / 2) +#define ledToPair(pos) ((Pair)((uint32_t)(pos) / 2)) // LedMap is a bitmap of leds, used for expressing whether to turn certain leds on // or off with a single integer typedef uint64_t LedMap; // various macros for mapping leds to an LedMap -#define MAP_LED(led) (LedMap)((uint64_t)1 << led) +#define MAP_LED(led) (LedMap)((uint64_t)1 << (led)) #define MAP_PAIR_EVEN(pair) MAP_LED(pairEven(pair)) #define MAP_PAIR_ODD(pair) MAP_LED(pairOdd(pair)) #define MAP_PAIR(pair) (MAP_PAIR_EVEN(pair) | MAP_PAIR_ODD(pair)) // check if a map is purely just 1 led or not -#define MAP_IS_ONE_LED(map) (map && !(map & (map-1))) +#define MAP_IS_ONE_LED(map) ((map) && !((map) & ((map)-1))) // foreach led macro (only iterates singles) #define MAP_FOREACH_LED(map) for (LedPos pos = ledmapGetFirstLed(map); pos != LED_COUNT; pos = ledmapGetNextLed(map, pos)) // convert a map to the first Led position in the map -inline LedPos ledmapGetFirstLed(LedMap map) +static inline LedPos ledmapGetFirstLed(LedMap map) { if (map == MAP_LED(LED_MULTI)) { return LED_MULTI; @@ -133,7 +140,7 @@ inline LedPos ledmapGetFirstLed(LedMap map) } // given an led map and a position, find the next position in the map -inline LedPos ledmapGetNextLed(LedMap map, LedPos pos) +static inline LedPos ledmapGetNextLed(LedMap map, LedPos pos) { pos = (LedPos)(pos + 1); map >>= pos; @@ -153,7 +160,7 @@ inline LedPos ledmapGetNextLed(LedMap map, LedPos pos) // blank map #define MAP_LED_NONE 0 -#define MAP_INVERSE(map) ((~map) & MAP_LED_ALL) +#define MAP_INVERSE(map) ((~(map)) & MAP_LED_ALL) // macro for all evens and odds #define MAP_PAIR_EVENS (((1 << LED_COUNT) - 1) & 0x55555555) @@ -167,78 +174,26 @@ inline LedPos ledmapGetNextLed(LedMap map, LedPos pos) #define MAP_PAIR_EVEN_ODDS (MAP_PAIR_ODD(PAIR_3) | MAP_PAIR_ODD(PAIR_1)) // set a single led -inline void ledmapSetLed(LedMap &map, LedPos pos) +static inline void ledmapSetLed(LedMap *map, LedPos pos) { - if (pos < LED_COUNT) map |= (1ull << pos); + if (pos < LED_COUNT) *map |= (1ull << pos); } // set a single pair -inline void ledmapSetPair(LedMap &map, Pair pair) +static inline void ledmapSetPair(LedMap *map, Pair pair) { ledmapSetLed(map, pairEven(pair)); ledmapSetLed(map, pairOdd(pair)); } // check if an led is set in the map -inline bool ledmapCheckLed(LedMap map, LedPos pos) +static inline bool ledmapCheckLed(LedMap map, LedPos pos) { return ((map & (1ull << pos)) != 0); } // check if a pair is set in the map (both leds) -inline bool ledmapCheckPair(LedMap map, Pair pair) +static inline bool ledmapCheckPair(LedMap map, Pair pair) { return ledmapCheckLed(map, pairEven(pair)) && ledmapCheckLed(map, pairOdd(pair)); } -// LedPos operators -inline LedPos &operator++(LedPos &c) -{ - c = LedPos(((uint32_t)c) + 1); - return c; -} -inline LedPos operator++(LedPos &c, int) -{ - LedPos temp = c; - ++c; - return temp; -} -inline LedPos operator+(LedPos &c, int b) -{ - return (LedPos)((uint32_t)c + b); -} -inline LedPos &operator+=(LedPos &c, int b) -{ - c = LedPos(((uint32_t)c) + b); - return c; -} -inline LedPos operator-(LedPos &c, int b) -{ - return (LedPos)((uint32_t)c - b); -} -inline LedPos &operator-=(LedPos &c, int b) -{ - c = LedPos(((uint32_t)c) - b); - return c; -} - -// pair operators -inline Pair &operator++(Pair &c) -{ - c = Pair(((uint32_t)c) + 1); - return c; -} -inline Pair operator++(Pair &c, int) -{ - Pair temp = c; - ++c; - return temp; -} -inline Pair operator+(Pair &c, int b) -{ - return (Pair)((uint32_t)c + b); -} -inline Pair operator-(Pair &c, int b) -{ - return (Pair)((uint32_t)c - b); -} - #endif diff --git a/VortexEngine/src/Leds/Leds.cpp b/VortexEngine/src/Leds/Leds.cpp index 2149378462..ee2493e56c 100644 --- a/VortexEngine/src/Leds/Leds.cpp +++ b/VortexEngine/src/Leds/Leds.cpp @@ -23,16 +23,16 @@ #define LED_DATA_PIN 7 // global brightness -uint8_t Leds::m_brightness = DEFAULT_BRIGHTNESS; +static uint8_t m_brightness = DEFAULT_BRIGHTNESS; // array of led color values -RGBColor Leds::m_ledColors[LED_COUNT] = { RGB_OFF }; +static RGBColor m_ledColors[LED_COUNT] = { RGB_OFF }; // Output PORT register -volatile uint8_t *Leds::m_port = nullptr; +static volatile uint8_t *m_port = NULL; // Output PORT bitmask -uint8_t Leds::m_pinMask = 0; +static uint8_t m_pinMask = 0; -bool Leds::init() +bool Leds_init() { #ifdef VORTEX_EMBEDDED // clear the onboard led so it displays nothing @@ -49,240 +49,244 @@ bool Leds::init() return true; } -void Leds::cleanup() +void Leds_cleanup() { for (uint8_t i = 0; i < LED_COUNT; ++i) { - m_ledColors[i].clear(); + RGBColor_clear(&m_ledColors[i]); } } -void Leds::setIndex(LedPos target, RGBColor col) +void Leds_setIndex(LedPos target, RGBColor col) { if (target >= LED_COUNT) { - setAll(col); + Leds_setAll(col); return; } - led(target) = col; + *Leds_ledRef(target) = col; } -void Leds::setRange(LedPos first, LedPos last, RGBColor col) +void Leds_setRange(LedPos first, LedPos last, RGBColor col) { for (LedPos pos = first; pos <= last; pos++) { - setIndex(pos, col); + Leds_setIndex(pos, col); } } -void Leds::setAll(RGBColor col) +void Leds_setAll(RGBColor col) { - setRange(LED_FIRST, LED_LAST, col); + Leds_setRange(LED_FIRST, LED_LAST, col); } -void Leds::setPair(Pair pair, RGBColor col) +void Leds_setPair(Pair pair, RGBColor col) { // start from tip and go to top - setRange(pairEven(pair), pairOdd(pair), col); + Leds_setRange(pairEven(pair), pairOdd(pair), col); } -void Leds::setPairs(Pair first, Pair last, RGBColor col) +void Leds_setPairs(Pair first, Pair last, RGBColor col) { // start from tip and go to top - setRange(pairEven(first), pairOdd(last), col); + Leds_setRange(pairEven(first), pairOdd(last), col); } -void Leds::setRangeEvens(Pair first, Pair last, RGBColor col) +void Leds_setRangeEvens(Pair first, Pair last, RGBColor col) { for (Pair pos = first; pos <= last; pos++) { - setIndex(pairEven(pos), col); + Leds_setIndex(pairEven(pos), col); } } -void Leds::setAllEvens(RGBColor col) +void Leds_setAllEvens(RGBColor col) { for (Pair pos = PAIR_FIRST; pos <= PAIR_LAST; pos++) { - setIndex(pairEven(pos), col); + Leds_setIndex(pairEven(pos), col); } } -void Leds::setRangeOdds(Pair first, Pair last, RGBColor col) +void Leds_setRangeOdds(Pair first, Pair last, RGBColor col) { for (Pair pos = first; pos <= last; pos++) { - setIndex(pairOdd(pos), col); + Leds_setIndex(pairOdd(pos), col); } } -void Leds::setAllOdds(RGBColor col) +void Leds_setAllOdds(RGBColor col) { for (Pair pos = PAIR_FIRST; pos <= PAIR_LAST; pos++) { - setIndex(pairOdd(pos), col); + Leds_setIndex(pairOdd(pos), col); } } -void Leds::clearRangeEvens(Pair first, Pair last) +void Leds_clearRangeEvens(Pair first, Pair last) { for (Pair pos = first; pos <= last; pos++) { - clearIndex(pairEven(pos)); + Leds_clearIndex(pairEven(pos)); } } -void Leds::clearAllEvens() +void Leds_clearAllEvens() { for (Pair pos = PAIR_FIRST; pos <= PAIR_LAST; pos++) { - clearIndex(pairEven(pos)); + Leds_clearIndex(pairEven(pos)); } } -void Leds::clearRangeOdds(Pair first, Pair last) +void Leds_clearRangeOdds(Pair first, Pair last) { for (Pair pos = first; pos <= last; pos++) { - clearIndex(pairOdd(pos)); + Leds_clearIndex(pairOdd(pos)); } } -void Leds::clearAllOdds() +void Leds_clearAllOdds() { for (Pair pos = PAIR_FIRST; pos <= PAIR_LAST; pos++) { - clearIndex(pairOdd(pos)); + Leds_clearIndex(pairOdd(pos)); } } -void Leds::setMap(LedMap map, RGBColor col) +void Leds_setMap(LedMap map, RGBColor col) { for (LedPos pos = LED_FIRST; pos <= LED_LAST; pos++) { if (ledmapCheckLed(map, pos)) { - setIndex(pos, col); + Leds_setIndex(pos, col); } } } -void Leds::clearMap(LedMap map) +void Leds_clearMap(LedMap map) { for (LedPos pos = LED_FIRST; pos <= LED_LAST; pos++) { if (ledmapCheckLed(map, pos)) { - clearIndex(pos); + Leds_clearIndex(pos); } } } -void Leds::stashAll(LedStash &stash) +void Leds_stashAll(LedStash *stash) { for (LedPos pos = LED_FIRST; pos <= LED_LAST; pos++) { - stash.m_ledColorsStash[pos] = led(pos); + stash->m_ledColorsStash[pos] = Leds_led(pos); } } -void Leds::restoreAll(const LedStash &stash) +void Leds_restoreAll(const LedStash *stash) { for (LedPos pos = LED_FIRST; pos <= LED_LAST; pos++) { - led(pos) = stash.m_ledColorsStash[pos]; + *Leds_ledRef(pos) = stash->m_ledColorsStash[pos]; } } -void Leds::adjustBrightnessIndex(LedPos target, uint8_t fadeBy) +void Leds_adjustBrightnessIndex(LedPos target, uint8_t fadeBy) { - led(target).adjustBrightness(fadeBy); + RGBColor_adjustBrightness(Leds_ledRef(target), fadeBy); } -void Leds::adjustBrightnessRange(LedPos first, LedPos last, uint8_t fadeBy) +void Leds_adjustBrightnessRange(LedPos first, LedPos last, uint8_t fadeBy) { for (LedPos pos = first; pos <= last; pos++) { - adjustBrightnessIndex(pos, fadeBy); + Leds_adjustBrightnessIndex(pos, fadeBy); } } -void Leds::adjustBrightnessAll(uint8_t fadeBy) +void Leds_adjustBrightnessAll(uint8_t fadeBy) { - adjustBrightnessRange(LED_FIRST, LED_LAST, fadeBy); + Leds_adjustBrightnessRange(LED_FIRST, LED_LAST, fadeBy); } // blinkIndex with the additional optional time offset -void Leds::blinkIndexOffset(LedPos target, uint32_t time, uint16_t offMs, uint16_t onMs, RGBColor col) +void Leds_blinkIndexOffset(LedPos target, uint32_t time, uint16_t offMs, uint16_t onMs, RGBColor col) { if ((time % MS_TO_TICKS(offMs + onMs)) < MS_TO_TICKS(onMs)) { - setIndex(target, col); + Leds_setIndex(target, col); } } -void Leds::blinkRangeOffset(LedPos first, LedPos last, uint32_t time, uint16_t offMs, uint16_t onMs, RGBColor col) +void Leds_blinkRangeOffset(LedPos first, LedPos last, uint32_t time, uint16_t offMs, uint16_t onMs, RGBColor col) { if ((time % MS_TO_TICKS(offMs + onMs)) < MS_TO_TICKS(onMs)) { - setRange(first, last, col); + Leds_setRange(first, last, col); } } -void Leds::blinkIndex(LedPos target, uint16_t offMs, uint16_t onMs, RGBColor col) +void Leds_blinkIndex(LedPos target, uint16_t offMs, uint16_t onMs, RGBColor col) { - if ((Time::getCurtime() % MS_TO_TICKS(offMs + onMs)) < MS_TO_TICKS(onMs)) { - setIndex(target, col); + if ((Time_getCurtime() % MS_TO_TICKS(offMs + onMs)) < MS_TO_TICKS(onMs)) { + Leds_setIndex(target, col); } } -void Leds::blinkRange(LedPos first, LedPos last, uint16_t offMs, uint16_t onMs, RGBColor col) +void Leds_blinkRange(LedPos first, LedPos last, uint16_t offMs, uint16_t onMs, RGBColor col) { - if ((Time::getCurtime() % MS_TO_TICKS(offMs + onMs)) < MS_TO_TICKS(onMs)) { - setRange(first, last, col); + if ((Time_getCurtime() % MS_TO_TICKS(offMs + onMs)) < MS_TO_TICKS(onMs)) { + Leds_setRange(first, last, col); } } -void Leds::blinkMap(LedMap targets, uint16_t offMs, uint16_t onMs, RGBColor col) +void Leds_blinkMap(LedMap targets, uint16_t offMs, uint16_t onMs, RGBColor col) { - if ((Time::getCurtime() % MS_TO_TICKS(offMs + onMs)) < MS_TO_TICKS(onMs)) { + if ((Time_getCurtime() % MS_TO_TICKS(offMs + onMs)) < MS_TO_TICKS(onMs)) { for (LedPos pos = LED_FIRST; pos < LED_COUNT; pos++) { if (ledmapCheckLed(targets, pos)) { - setIndex(pos, col); + Leds_setIndex(pos, col); } } } } -void Leds::blinkAll(uint16_t offMs, uint16_t onMs, RGBColor col) +void Leds_blinkAll(uint16_t offMs, uint16_t onMs, RGBColor col) { - if ((Time::getCurtime() % MS_TO_TICKS(offMs + onMs)) < MS_TO_TICKS(onMs)) { - setRange(LED_FIRST, LED_LAST, col); + if ((Time_getCurtime() % MS_TO_TICKS(offMs + onMs)) < MS_TO_TICKS(onMs)) { + Leds_setRange(LED_FIRST, LED_LAST, col); } } -void Leds::blinkPair(Pair pair, uint16_t offMs, uint16_t onMs, RGBColor col) +void Leds_blinkPair(Pair pair, uint16_t offMs, uint16_t onMs, RGBColor col) { - if ((Time::getCurtime() % MS_TO_TICKS(offMs + onMs)) < MS_TO_TICKS(onMs)) { - setRange(pairEven(pair), pairOdd(pair), col); + if ((Time_getCurtime() % MS_TO_TICKS(offMs + onMs)) < MS_TO_TICKS(onMs)) { + Leds_setRange(pairEven(pair), pairOdd(pair), col); } } -void Leds::blinkPairs(Pair first, Pair last, uint16_t offMs, uint16_t onMs, RGBColor col) +void Leds_blinkPairs(Pair first, Pair last, uint16_t offMs, uint16_t onMs, RGBColor col) { - if ((Time::getCurtime() % MS_TO_TICKS(offMs + onMs)) < MS_TO_TICKS(onMs)) { - setRange(pairEven(first), pairOdd(last), col); + if ((Time_getCurtime() % MS_TO_TICKS(offMs + onMs)) < MS_TO_TICKS(onMs)) { + Leds_setRange(pairEven(first), pairOdd(last), col); } } -void Leds::breatheIndex(LedPos target, uint8_t hue, uint32_t variance, uint32_t magnitude, uint8_t sat, uint8_t val) +void Leds_breatheIndex(LedPos target, uint8_t hue, uint32_t variance, uint32_t magnitude, uint8_t sat, uint8_t val) { - setIndex(target, HSVColor((uint8_t)(hue + ((sin(variance * 0.0174533) + 1) * magnitude)), sat, val)); + HSVColor hsv = HSVColor_makeHSV((uint8_t)(hue + ((sin(variance * 0.0174533) + 1) * magnitude)), sat, val); + Leds_setIndex(target, RGBColor_fromHSV(&hsv)); } -void Leds::breatheRange(LedPos first, LedPos last, uint8_t hue, uint32_t variance, uint32_t magnitude, uint8_t sat, uint8_t val) +void Leds_breatheRange(LedPos first, LedPos last, uint8_t hue, uint32_t variance, uint32_t magnitude, uint8_t sat, uint8_t val) { - setRange(first, last, HSVColor((uint8_t)(hue + ((sin(variance * 0.0174533) + 1) * magnitude)), sat, val)); + HSVColor hsv = HSVColor_makeHSV((uint8_t)(hue + ((sin(variance * 0.0174533) + 1) * magnitude)), sat, val); + Leds_setRange(first, last, RGBColor_fromHSV(&hsv)); } -void Leds::breatheIndexSat(LedPos target, uint8_t hue, uint32_t variance, uint32_t magnitude, uint8_t sat, uint8_t val) +void Leds_breatheIndexSat(LedPos target, uint8_t hue, uint32_t variance, uint32_t magnitude, uint8_t sat, uint8_t val) { - setIndex(target, HSVColor(hue, 255 - (uint8_t)(sat + 128 + ((sin(variance * 0.0174533) + 1) * magnitude)), val)); + HSVColor hsv = HSVColor_makeHSV(hue, 255 - (uint8_t)(sat + 128 + ((sin(variance * 0.0174533) + 1) * magnitude)), val); + Leds_setIndex(target, RGBColor_fromHSV(&hsv)); } -void Leds::breatheIndexVal(LedPos target, uint8_t hue, uint32_t variance, uint32_t magnitude, uint8_t sat, uint8_t val) +void Leds_breatheIndexVal(LedPos target, uint8_t hue, uint32_t variance, uint32_t magnitude, uint8_t sat, uint8_t val) { - setIndex(target, HSVColor(hue, sat, 255 - (uint8_t)(val + 128 + ((sin(variance * 0.0174533) + 1) * magnitude)))); + HSVColor hsv = HSVColor_makeHSV(hue, sat, 255 - (uint8_t)(val + 128 + ((sin(variance * 0.0174533) + 1) * magnitude))); + Leds_setIndex(target, RGBColor_fromHSV(&hsv)); } -void Leds::holdAll(RGBColor col) +void Leds_holdAll(RGBColor col) { - setAll(col); - update(); - Time::delayMilliseconds(250); + Leds_setAll(col); + Leds_update(); + Time_delayMilliseconds(250); } -void Leds::update() +void Leds_update() { #ifdef VORTEX_EMBEDDED RGBColor ledbackups[LED_COUNT]; @@ -461,3 +465,113 @@ void Leds::update() memcpy(m_ledColors, ledbackups, sizeof(m_ledColors)); #endif } + +RGBColor *Leds_ledRef(LedPos pos) +{ + if (pos > LED_LAST) { + pos = LED_LAST; + } + return &m_ledColors[pos]; +} + +uint8_t Leds_getBrightness(void) +{ + return m_brightness; +} + +void Leds_setBrightness(uint8_t brightness) +{ + m_brightness = brightness; +} + +// arity-dispatch variants for the blink/breathe helpers, these forward +// to the full-arity implementations using the defaults in Leds.h +void Leds_blinkIndex1(LedPos target) +{ + Leds_blinkIndex(target, LED_BLINK_OFF_MS, LED_BLINK_ON_MS, RGB_OFF); +} + +void Leds_blinkIndex2(LedPos target, uint16_t offMs) +{ + Leds_blinkIndex(target, offMs, LED_BLINK_ON_MS, RGB_OFF); +} + +void Leds_blinkIndex3(LedPos target, uint16_t offMs, uint16_t onMs) +{ + Leds_blinkIndex(target, offMs, onMs, RGB_OFF); +} + +void Leds_blinkAll0(void) +{ + Leds_blinkAll(LED_BLINK_OFF_MS, LED_BLINK_ON_MS, RGB_OFF); +} + +void Leds_blinkAll1(uint16_t offMs) +{ + Leds_blinkAll(offMs, LED_BLINK_ON_MS, RGB_OFF); +} + +void Leds_blinkAll2(uint16_t offMs, uint16_t onMs) +{ + Leds_blinkAll(offMs, onMs, RGB_OFF); +} + +void Leds_blinkIndexOffset2(LedPos target, uint32_t time) +{ + Leds_blinkIndexOffset(target, time, LED_BLINK_OFF_MS, LED_BLINK_ON_MS, RGB_OFF); +} + +void Leds_blinkIndexOffset3(LedPos target, uint32_t time, uint16_t offMs) +{ + Leds_blinkIndexOffset(target, time, offMs, LED_BLINK_ON_MS, RGB_OFF); +} + +void Leds_blinkIndexOffset4(LedPos target, uint32_t time, uint16_t offMs, uint16_t onMs) +{ + Leds_blinkIndexOffset(target, time, offMs, onMs, RGB_OFF); +} + +void Leds_breatheIndex3(LedPos target, uint8_t hue, uint32_t variance) +{ + Leds_breatheIndex(target, hue, variance, LED_BREATHE_MAG, LED_BREATHE_SAT, LED_BREATHE_VAL); +} + +void Leds_breatheIndex4(LedPos target, uint8_t hue, uint32_t variance, uint32_t magnitude) +{ + Leds_breatheIndex(target, hue, variance, magnitude, LED_BREATHE_SAT, LED_BREATHE_VAL); +} + +void Leds_breatheIndex5(LedPos target, uint8_t hue, uint32_t variance, uint32_t magnitude, uint8_t sat) +{ + Leds_breatheIndex(target, hue, variance, magnitude, sat, LED_BREATHE_VAL); +} + +void Leds_breatheIndexSat3(LedPos target, uint8_t hue, uint32_t variance) +{ + Leds_breatheIndexSat(target, hue, variance, LED_BREATHE_MAG, LED_BREATHE_SAT, LED_BREATHE_VAL); +} + +void Leds_breatheIndexSat4(LedPos target, uint8_t hue, uint32_t variance, uint32_t magnitude) +{ + Leds_breatheIndexSat(target, hue, variance, magnitude, LED_BREATHE_SAT, LED_BREATHE_VAL); +} + +void Leds_breatheIndexSat5(LedPos target, uint8_t hue, uint32_t variance, uint32_t magnitude, uint8_t sat) +{ + Leds_breatheIndexSat(target, hue, variance, magnitude, sat, LED_BREATHE_VAL); +} + +void Leds_breatheIndexVal3(LedPos target, uint8_t hue, uint32_t variance) +{ + Leds_breatheIndexVal(target, hue, variance, LED_BREATHE_MAG, LED_BREATHE_SAT, LED_BREATHE_VAL); +} + +void Leds_breatheIndexVal4(LedPos target, uint8_t hue, uint32_t variance, uint32_t magnitude) +{ + Leds_breatheIndexVal(target, hue, variance, magnitude, LED_BREATHE_SAT, LED_BREATHE_VAL); +} + +void Leds_breatheIndexVal5(LedPos target, uint8_t hue, uint32_t variance, uint32_t magnitude, uint8_t sat) +{ + Leds_breatheIndexVal(target, hue, variance, magnitude, sat, LED_BREATHE_VAL); +} diff --git a/VortexEngine/src/Leds/Leds.h b/VortexEngine/src/Leds/Leds.h index bdcd94af9b..68e105b6ae 100644 --- a/VortexEngine/src/Leds/Leds.h +++ b/VortexEngine/src/Leds/Leds.h @@ -2,134 +2,165 @@ #define LED_CONTROL_H #include +#include #include "../Colors/ColorTypes.h" #include "LedTypes.h" -class LedStash; - -class Leds -{ - // private unimplemented constructor - Leds(); - -public: - // opting for static class here because there should only ever be one - // Led control object and I don't like singletons - static bool init(); - static void cleanup(); - - // control individual LED, these are appropriate to use in internal pattern logic - static void setIndex(LedPos target, RGBColor col); - static void setRange(LedPos first, LedPos last, RGBColor col); - static void setAll(RGBColor col); - - // Turn off individual LEDs, these are appropriate to use in internal pattern logic - static void clearIndex(LedPos target) { setIndex(target, RGB_OFF); } - static void clearRange(LedPos first, LedPos last) { setRange(first, last, RGB_OFF); } - static void clearAll() { setAll(RGB_OFF); } - - // control two LEDs on a pair, these are appropriate for use in internal pattern logic - static void setPair(Pair pair, RGBColor col); - static void setPairs(Pair first, Pair last, RGBColor col); - - // Turn off both LEDs on a pair, these are appropriate for use in internal pattern logic - static void clearPair(Pair pair) { setPair(pair, RGB_OFF); } - static void clearPairs(Pair first, Pair last) { setPairs(first, last, RGB_OFF); } - - // Controll pair evens - static void setRangeEvens(Pair first, Pair last, RGBColor); - static void setAllEvens(RGBColor col); - // Controll pair odds - static void setRangeOdds(Pair first, Pair last, RGBColor); - static void setAllOdds(RGBColor col); - - // Turn off tips - static void clearRangeEvens(Pair first, Pair last); - static void clearAllEvens(); - // Turn off tops - static void clearRangeOdds(Pair first, Pair last); - static void clearAllOdds(); - - // Turn on/off a mapping of leds with a color - static void setMap(LedMap map, RGBColor col); - static void clearMap(LedMap map); - - // stores Led for later use - static void stashAll(LedStash &stash); - - // restores Leds from stash - static void restoreAll(const LedStash &stash); - - // Dim individual LEDs, these are appropriate to use in internal pattern logic - static void adjustBrightnessIndex(LedPos target, uint8_t fadeBy); - static void adjustBrightnessRange(LedPos first, LedPos last, uint8_t fadeBy); - static void adjustBrightnessAll(uint8_t fadeBy); - - // Blink an led to blank or a color - // - // These APIs work by checking if the current time is within the - // 'on duration' then they apply the color to the target, otherwise they do - // nothing. The goal of this behaviour is to allow them to be used in any - // situation to 'blink' an led to either off or some color. - // - // However since these APIs modulate current time to check if within the 'on' - // threshold that makes them unsuitable for internal pattern usage because it - // is unpredictable whether they will blink on or off first - static void blinkIndexOffset(LedPos target, uint32_t time, uint16_t offMs = 250, uint16_t onMs = 500, RGBColor col = RGB_OFF); - static void blinkRangeOffset(LedPos first, LedPos last, uint32_t time, uint16_t offMs = 250, uint16_t onMs = 500, RGBColor col = RGB_OFF); - static void blinkIndex(LedPos target, uint16_t offMs = 250, uint16_t onMs = 500, RGBColor col = RGB_OFF); - static void blinkRange(LedPos first, LedPos last, uint16_t offMs = 250, uint16_t onMs = 500, RGBColor col = RGB_OFF); - static void blinkMap(LedMap targets, uint16_t offMs = 250, uint16_t onMs = 500, RGBColor col = RGB_OFF); - static void blinkAll(uint16_t offMs = 250, uint16_t onMs = 500, RGBColor col = RGB_OFF); - // Blink both LEDs on a pair - static void blinkPair(Pair pair, uint16_t offMs = 250, uint16_t onMs = 500, RGBColor col = RGB_OFF); - static void blinkPairs(Pair first, Pair last, uint16_t offMs = 250, uint16_t onMs = 500, RGBColor col = RGB_OFF); - - // breath the hue on an index - // warning: these use hsv to rgb in realtime! - static void breatheIndex(LedPos target, uint8_t hue, uint32_t variance, - uint32_t magnitude = 15, uint8_t sat = 255, uint8_t val = 210); - static void breatheRange(LedPos first, LedPos last, uint8_t hue, uint32_t variance, - uint32_t magnitude = 15, uint8_t sat = 255, uint8_t val = 210); - static void breatheIndexSat(LedPos target, uint8_t hue, uint32_t variance, - uint32_t magnitude = 15, uint8_t sat = 255, uint8_t val = 210); - static void breatheIndexVal(LedPos target, uint8_t hue, uint32_t variance, - uint32_t magnitude = 15, uint8_t sat = 255, uint8_t val = 210); - - // a very specialized api to hold all leds on a color for 250ms - static void holdAll(RGBColor col); - - // get the RGBColor of an Led index - static RGBColor getLed(LedPos pos) { return led(pos); } - - // global brightness - static uint8_t getBrightness() { return m_brightness; } - static void setBrightness(uint8_t brightness) { m_brightness = brightness; } - - // actually update the LEDs and show the changes - static void update(); - -private: - // accessor for led colors, use this for all access to allow for mapping - static inline RGBColor &led(LedPos pos) - { - if (pos > LED_LAST) { - pos = LED_LAST; - } - return m_ledColors[pos]; - } - - // the global brightness - static uint8_t m_brightness; - - // array of led color values - static RGBColor m_ledColors[LED_COUNT]; - - // Output PORT register - static volatile uint8_t *m_port; - // Output PORT bitmask - static uint8_t m_pinMask; -}; +typedef struct LedStash LedStash; + +// Leds is a stateless namespace of free functions, there should only ever +// be one instance of it and the state lives in Leds.cpp + +// opting for static class here because there should only ever be one +// Led control object and I don't like singletons +bool Leds_init(void); +void Leds_cleanup(void); + +// control individual LED, these are appropriate to use in internal pattern logic +void Leds_setIndex(LedPos target, RGBColor col); +void Leds_setRange(LedPos first, LedPos last, RGBColor col); +void Leds_setAll(RGBColor col); + +// Turn off individual LEDs, these are appropriate to use in internal pattern logic +static inline void Leds_clearIndex(LedPos target) { Leds_setIndex(target, RGB_OFF); } +static inline void Leds_clearRange(LedPos first, LedPos last) { Leds_setRange(first, last, RGB_OFF); } +static inline void Leds_clearAll(void) { Leds_setAll(RGB_OFF); } + +// control two LEDs on a pair, these are appropriate for use in internal pattern logic +void Leds_setPair(Pair pair, RGBColor col); +void Leds_setPairs(Pair first, Pair last, RGBColor col); + +// Turn off both LEDs on a pair, these are appropriate to use in internal pattern logic +static inline void Leds_clearPair(Pair pair) { Leds_setPair(pair, RGB_OFF); } +static inline void Leds_clearPairs(Pair first, Pair last) { Leds_setPairs(first, last, RGB_OFF); } + +// Controll pair evens +void Leds_setRangeEvens(Pair first, Pair last, RGBColor col); +void Leds_setAllEvens(RGBColor col); +// Controll pair odds +void Leds_setRangeOdds(Pair first, Pair last, RGBColor col); +void Leds_setAllOdds(RGBColor col); + +// Turn off tips +void Leds_clearRangeEvens(Pair first, Pair last); +void Leds_clearAllEvens(void); +// Turn off tops +void Leds_clearRangeOdds(Pair first, Pair last); +void Leds_clearAllOdds(void); + +// Turn on/off a mapping of leds with a color +void Leds_setMap(LedMap map, RGBColor col); +void Leds_clearMap(LedMap map); + +// stores Led for later use +void Leds_stashAll(LedStash *stash); + +// restores Leds from stash +void Leds_restoreAll(const LedStash *stash); + +// Dim individual LEDs, these are appropriate to use in internal pattern logic +void Leds_adjustBrightnessIndex(LedPos target, uint8_t fadeBy); +void Leds_adjustBrightnessRange(LedPos first, LedPos last, uint8_t fadeBy); +void Leds_adjustBrightnessAll(uint8_t fadeBy); + +// Blink an led to blank or a color +// +// These APIs work by checking if the current time is within the +// 'on duration' then they apply the color to the target, otherwise they do +// nothing. The goal of this behaviour is to allow them to be used in any +// situation to 'blink' an led to either off or some color. +// +// However since these APIs modulate current time to check if within the 'on' +// threshold that makes them unsuitable for internal pattern usage because it +// is unpredictable whether they will blink on or off first +void Leds_blinkIndexOffset(LedPos target, uint32_t time, uint16_t offMs, uint16_t onMs, RGBColor col); +void Leds_blinkRangeOffset(LedPos first, LedPos last, uint32_t time, uint16_t offMs, uint16_t onMs, RGBColor col); +void Leds_blinkIndex(LedPos target, uint16_t offMs, uint16_t onMs, RGBColor col); +void Leds_blinkRange(LedPos first, LedPos last, uint16_t offMs, uint16_t onMs, RGBColor col); +void Leds_blinkMap(LedMap targets, uint16_t offMs, uint16_t onMs, RGBColor col); +void Leds_blinkAll(uint16_t offMs, uint16_t onMs, RGBColor col); +// Blink both LEDs on a pair +void Leds_blinkPair(Pair pair, uint16_t offMs, uint16_t onMs, RGBColor col); +void Leds_blinkPairs(Pair first, Pair last, uint16_t offMs, uint16_t onMs, RGBColor col); + +// breath the hue on an index +// warning: these use hsv to rgb in realtime! +void Leds_breatheIndex(LedPos target, uint8_t hue, uint32_t variance, uint32_t magnitude, uint8_t sat, uint8_t val); +void Leds_breatheRange(LedPos first, LedPos last, uint8_t hue, uint32_t variance, uint32_t magnitude, uint8_t sat, uint8_t val); +void Leds_breatheIndexSat(LedPos target, uint8_t hue, uint32_t variance, uint32_t magnitude, uint8_t sat, uint8_t val); +void Leds_breatheIndexVal(LedPos target, uint8_t hue, uint32_t variance, uint32_t magnitude, uint8_t sat, uint8_t val); + +// a very specialized api to hold all leds on a color for 250ms +void Leds_holdAll(RGBColor col); + +// get the RGBColor of an Led index +RGBColor *Leds_ledRef(LedPos pos); +static inline RGBColor Leds_led(LedPos pos) { return *Leds_ledRef(pos); } +static inline RGBColor Leds_getLed(LedPos pos) { return Leds_led(pos); } + +// global brightness +uint8_t Leds_getBrightness(void); +void Leds_setBrightness(uint8_t brightness); + +// actually update the LEDs and show the changes +void Leds_update(void); + +// the default blink/breathe parameters, kept here so the arity macros +// below can supply them when the caller omits trailing arguments +#define LED_BLINK_OFF_MS 250 +#define LED_BLINK_ON_MS 500 +#define LED_BREATHE_MAG 15 +#define LED_BREATHE_SAT 255 +#define LED_BREATHE_VAL 210 + +// arity dispatch for the trailing-default blink/breathe helpers, the +// indirection through _() macros is required so LED_NARG() expands +// before being pasted with ## +#define LED_NARG(_1, _2, _3, _4, _5, _6, N, ...) N +#define LED_COUNT(...) LED_NARG(__VA_ARGS__, 6, 5, 4, 3, 2, 1) +#define LED_CAT_(a, b) a##b +#define LED_DISPATCH_(prefix, N, ...) LED_CAT_(prefix, N)(__VA_ARGS__) + +// Leds_blinkIndex(t[, offMs[, onMs[, col]]]) +#define Leds_blinkIndex(...) LED_DISPATCH_(Leds_blinkIndex, LED_COUNT(__VA_ARGS__), __VA_ARGS__) +void Leds_blinkIndex1(LedPos target); +void Leds_blinkIndex2(LedPos target, uint16_t offMs); +void Leds_blinkIndex3(LedPos target, uint16_t offMs, uint16_t onMs); +void Leds_blinkIndex4(LedPos target, uint16_t offMs, uint16_t onMs, RGBColor col); + +// Leds_blinkAll([offMs[, onMs[, col]]]) +#define Leds_blinkAll(...) LED_DISPATCH_(Leds_blinkAll, LED_COUNT(__VA_ARGS__), __VA_ARGS__) +void Leds_blinkAll0(void); +void Leds_blinkAll1(uint16_t offMs); +void Leds_blinkAll2(uint16_t offMs, uint16_t onMs); +void Leds_blinkAll3(uint16_t offMs, uint16_t onMs, RGBColor col); + +// Leds_blinkIndexOffset(t, time[, offMs[, onMs[, col]]]) +#define Leds_blinkIndexOffset(...) LED_DISPATCH_(Leds_blinkIndexOffset, LED_COUNT(__VA_ARGS__), __VA_ARGS__) +void Leds_blinkIndexOffset2(LedPos target, uint32_t time); +void Leds_blinkIndexOffset3(LedPos target, uint32_t time, uint16_t offMs); +void Leds_blinkIndexOffset4(LedPos target, uint32_t time, uint16_t offMs, uint16_t onMs); +void Leds_blinkIndexOffset5(LedPos target, uint32_t time, uint16_t offMs, uint16_t onMs, RGBColor col); + +// Leds_breatheIndex(t, hue, variance[, magnitude[, sat[, val]]]) +#define Leds_breatheIndex(...) LED_DISPATCH_(Leds_breatheIndex, LED_COUNT(__VA_ARGS__), __VA_ARGS__) +void Leds_breatheIndex3(LedPos target, uint8_t hue, uint32_t variance); +void Leds_breatheIndex4(LedPos target, uint8_t hue, uint32_t variance, uint32_t magnitude); +void Leds_breatheIndex5(LedPos target, uint8_t hue, uint32_t variance, uint32_t magnitude, uint8_t sat); +void Leds_breatheIndex6(LedPos target, uint8_t hue, uint32_t variance, uint32_t magnitude, uint8_t sat, uint8_t val); + +// Leds_breatheIndexSat / Leds_breatheIndexVal share the same shape +#define Leds_breatheIndexSat(...) LED_DISPATCH_(Leds_breatheIndexSat, LED_COUNT(__VA_ARGS__), __VA_ARGS__) +void Leds_breatheIndexSat3(LedPos target, uint8_t hue, uint32_t variance); +void Leds_breatheIndexSat4(LedPos target, uint8_t hue, uint32_t variance, uint32_t magnitude); +void Leds_breatheIndexSat5(LedPos target, uint8_t hue, uint32_t variance, uint32_t magnitude, uint8_t sat); +void Leds_breatheIndexSat6(LedPos target, uint8_t hue, uint32_t variance, uint32_t magnitude, uint8_t sat, uint8_t val); + +#define Leds_breatheIndexVal(...) LED_DISPATCH_(Leds_breatheIndexVal, LED_COUNT(__VA_ARGS__), __VA_ARGS__) +void Leds_breatheIndexVal3(LedPos target, uint8_t hue, uint32_t variance); +void Leds_breatheIndexVal4(LedPos target, uint8_t hue, uint32_t variance, uint32_t magnitude); +void Leds_breatheIndexVal5(LedPos target, uint8_t hue, uint32_t variance, uint32_t magnitude, uint8_t sat); +void Leds_breatheIndexVal6(LedPos target, uint8_t hue, uint32_t variance, uint32_t magnitude, uint8_t sat, uint8_t val); #endif diff --git a/VortexEngine/src/Log/Log.cpp b/VortexEngine/src/Log/Log.cpp index 19877548da..d128d76752 100644 --- a/VortexEngine/src/Log/Log.cpp +++ b/VortexEngine/src/Log/Log.cpp @@ -16,7 +16,7 @@ void InfoMsg(const char *msg, ...) { va_list list; va_start(list, msg); - Vortex::printlog(NULL, NULL, 0, msg, list); + Vortex_printlog(NULL, NULL, 0, msg, list); va_end(list); } #endif @@ -26,7 +26,7 @@ void ErrorMsg(const char *func, const char *msg, ...) { va_list list; va_start(list, msg); - Vortex::printlog(NULL, func, 0, msg, list); + Vortex_printlog(NULL, func, 0, msg, list); va_end(list); } #endif @@ -46,7 +46,7 @@ void DebugMsg(const char *file, const char *func, int line, const char *msg, ... } ptr--; } - Vortex::printlog(file, func, line, msg, list); + Vortex_printlog(file, func, line, msg, list); va_end(list); } #endif diff --git a/VortexEngine/src/Log/Log.h b/VortexEngine/src/Log/Log.h index 52bb5f7786..35aa92951e 100644 --- a/VortexEngine/src/Log/Log.h +++ b/VortexEngine/src/Log/Log.h @@ -37,7 +37,12 @@ void DebugMsg(const char *file, const char *func, int line, const char *msg, ... #define DEBUG_LOGF(msg, ...) #endif -// report OOM +// report OOM, on the device an allocation failure flashes red and shuts off +#ifdef VORTEX_EMBEDDED +void VortexEngine_outOfMemory(void); +#define ERROR_OUT_OF_MEMORY() VortexEngine_outOfMemory() +#else #define ERROR_OUT_OF_MEMORY() ERROR_LOG("Out of memory") +#endif #endif diff --git a/VortexEngine/src/Memory/Memory.cpp b/VortexEngine/src/Memory/Memory.cpp index 9172488167..199d76570b 100644 --- a/VortexEngine/src/Memory/Memory.cpp +++ b/VortexEngine/src/Memory/Memory.cpp @@ -117,23 +117,61 @@ uint32_t cur_memory_usage_total() #endif // DEBUG_ALLOCATIONS == 1 -#ifndef VORTEX_LIB +void MemPool_init(MemPool *self, void *storage, uint16_t blockSize, uint8_t numBlocks) +{ + if (self->m_storage) { + // already initialized, blocks may still be in use so leave it be + return; + } + self->m_storage = (uint8_t *)storage; + self->m_blockSize = MEMPOOL_ALIGN(blockSize); + self->m_numBlocks = numBlocks; + self->m_numUsed = 0; + self->m_highWater = 0; + self->m_freeList = NULL; + // thread every block onto the free list, last block first so the + // first block ends up at the front of the list + for (uint8_t i = numBlocks; i > 0; --i) { + void **block = (void **)(self->m_storage + ((uint32_t)self->m_blockSize * (i - 1))); + *block = self->m_freeList; + self->m_freeList = block; + } +} + +void *MemPool_alloc(MemPool *self) +{ + void **block = (void **)self->m_freeList; + if (!block) { + ERROR_OUT_OF_MEMORY(); + return NULL; + } + self->m_freeList = *block; + self->m_numUsed++; + if (self->m_numUsed > self->m_highWater) { + self->m_highWater = self->m_numUsed; + } + return block; +} -// for C++11 need the following: -void *operator new (size_t size) { return vmalloc(size); } -void *operator new[](size_t size) { return vmalloc(size); } -void operator delete (void *ptr) { vfree(ptr); } -void operator delete[](void *ptr) { vfree(ptr); } -void *operator new (size_t size, void *ptr) noexcept { return ptr; } -void *operator new[](size_t size, void *ptr) noexcept { return ptr; } -void operator delete (void *ptr, size_t size) noexcept { vfree(ptr); } -void operator delete[](void *ptr, size_t size) noexcept { vfree(ptr); } -//void *operator new (size_t size, std::align_val_t al) { return vmalloc(size); } -//void *operator new[](size_t size, std::align_val_t al) { return vmalloc(size); } -//void operator delete (void *ptr, std::align_val_t al) noexcept { vfree(ptr); } -//void operator delete[](void *ptr, std::align_val_t al) noexcept { vfree(ptr); } -//void operator delete (void *ptr, size_t size, std::align_val_t al) noexcept { vfree(ptr); } -//void operator delete[](void *ptr, size_t size, std::align_val_t al) noexcept { vfree(ptr); } +void MemPool_free(MemPool *self, void *ptr) +{ + if (!ptr) { + return; + } + void **block = (void **)ptr; + *block = self->m_freeList; + self->m_freeList = block; + self->m_numUsed--; +} + +bool MemPool_contains(const MemPool *self, const void *ptr) +{ + const uint8_t *p = (const uint8_t *)ptr; + return self->m_storage && p >= self->m_storage && + p < self->m_storage + ((uint32_t)self->m_blockSize * self->m_numBlocks); +} + +#ifndef VORTEX_LIB // needed for C++ virtual functions extern "C" void __cxa_pure_virtual(void) {} diff --git a/VortexEngine/src/Memory/Memory.h b/VortexEngine/src/Memory/Memory.h index b851d781ec..70a7b207a7 100644 --- a/VortexEngine/src/Memory/Memory.h +++ b/VortexEngine/src/Memory/Memory.h @@ -2,6 +2,7 @@ #define MEMORY_H #include +#include #include #include "../VortexConfig.h" @@ -36,21 +37,43 @@ uint32_t cur_memory_usage_total(); #endif -#ifndef VORTEX_LIB -void *operator new (size_t size); -void *operator new[](size_t size); -void operator delete (void *ptr); -void operator delete[](void *ptr); -void *operator new (size_t size, void *ptr) noexcept; -void *operator new[](size_t size, void *ptr) noexcept; -void operator delete (void *ptr, size_t size) noexcept; -void operator delete[](void *ptr, size_t size) noexcept; -//void *operator new (size_t size, std::align_val_t al); -//void *operator new[](size_t size, std::align_val_t al); -//void operator delete (void *ptr, std::align_val_t al) noexcept; -//void operator delete[](void *ptr, std::align_val_t al) noexcept; -//void operator delete (void *ptr, size_t size, std::align_val_t al) noexcept; -//void operator delete[](void *ptr, size_t size, std::align_val_t al) noexcept; -#endif +// A fixed block memory pool, every block in the pool is the same size so +// allocating and freeing can never fragment memory. The backing storage is +// a static array (see MEMPOOL_STORAGE) so the memory used by every pool is +// reserved at build time and shows up in the size of the program's globals, +// blocks are handed out and returned through an intrusive free list. + +// blocks are rounded up to the size of a pointer, this keeps every block +// aligned for any struct and leaves room for the free list link +#define MEMPOOL_ALIGN(size) ((((size) + sizeof(void *) - 1) / sizeof(void *)) * sizeof(void *)) + +// declare the static storage for a pool of numBlocks blocks of blockSize, +// it is declared as an array of pointers so that it is pointer aligned +#define MEMPOOL_STORAGE(name, blockSize, numBlocks) \ + void *name[(MEMPOOL_ALIGN(blockSize) * (numBlocks)) / sizeof(void *)] +typedef struct MemPool +{ + // the backing storage of all blocks + uint8_t *m_storage; + // the list of free blocks, each free block stores the next free block + void *m_freeList; + // the size of each block (rounded up for alignment) + uint16_t m_blockSize; + // the total number of blocks and the number currently in use + uint8_t m_numBlocks; + uint8_t m_numUsed; + // the most blocks that were ever in use at once + uint8_t m_highWater; +} MemPool; + +// setup the pool in the given storage, this is only done once and +// calling it again on an initialized pool does nothing +void MemPool_init(MemPool *self, void *storage, uint16_t blockSize, uint8_t numBlocks); +// grab a block from the pool, returns NULL if the pool is exhausted +void *MemPool_alloc(MemPool *self); +// return a block to the pool +void MemPool_free(MemPool *self, void *ptr); +// whether a pointer is a block of this pool +bool MemPool_contains(const MemPool *self, const void *ptr); #endif diff --git a/VortexEngine/src/Menus/Menu.cpp b/VortexEngine/src/Menus/Menu.cpp index 97c904b763..1c652041c5 100644 --- a/VortexEngine/src/Menus/Menu.cpp +++ b/VortexEngine/src/Menus/Menu.cpp @@ -8,60 +8,106 @@ #include "../Modes/Mode.h" #include "../Leds/Leds.h" #include "../Log/Log.h" +#include "../Memory/Memory.h" -Menu::Menu(const RGBColor &col, bool advanced) : - m_previewMode(), - m_menuColor(col), - m_targetLeds(MAP_LED_ALL), - m_curSelection(0), - m_ledSelected(false), - m_advanced(advanced), - m_shouldClose(false) +Menu *Menu_new(const RGBColor *col, bool advanced) { + Menu *self = (Menu *)Menus_allocMenu(); + if (!self) { + return NULL; + } + Menu_initBase(self, NULL); + RGBColor_setRGB(&self->m_menuColor, col); + self->m_advanced = advanced; + return self; +} + +void Menu_initBase(Menu *self, const MenuVtable *vtable) +{ + Mode_init(&self->m_previewMode); + self->m_vtable = vtable; + self->m_menuColor = RGBColor_make(); + self->m_targetLeds = MAP_LED_ALL; + self->m_curSelection = 0; + self->m_ledSelected = false; + self->m_advanced = false; + self->m_shouldClose = false; } -Menu::~Menu() +void Menu_destroy(Menu *self) { + if (!self) { + return; + } + if (self->m_vtable && self->m_vtable->destroy) { + self->m_vtable->destroy(self); + return; + } + Menu_baseDestroy(self); +} + +void Menu_baseDestroy(Menu *self) +{ + Mode_destroy(&self->m_previewMode); + Menus_freeMenu(self); +} + +bool Menu_init(Menu *self) +{ + if (self->m_vtable && self->m_vtable->init) { + return self->m_vtable->init(self); + } + return Menu_baseInit(self); } -bool Menu::init() +bool Menu_baseInit(Menu *self) { // menu is initialized before being run - if (!Modes::curMode()) { + if (!Modes_curMode()) { // if you enter a menu and there's no modes, it will add an empty one - if (Modes::numModes() > 0) { + if (Modes_numModes() > 0) { // some kind of serious error return false; } - if (!Modes::addMode(PATTERN_STROBE, RGBColor(RGB_OFF))) { + if (!Modes_addMode(PATTERN_STROBE, RGBColor_make(), RGBColor_make(), RGBColor_make(), + RGBColor_make(), RGBColor_make(), RGBColor_make(), RGBColor_make(), + RGBColor_make())) { // some kind of serious error return false; } - if (!Modes::curMode()) { + if (!Modes_curMode()) { // serious error again return false; } } // copy the current mode into the demo mode and initialize it - m_previewMode = *Modes::curMode(); - m_previewMode.init(); + Mode_assign(&self->m_previewMode, Modes_curMode()); + Mode_initPatterns(&self->m_previewMode); // just in case - m_shouldClose = false; + self->m_shouldClose = false; return true; } -Menu::MenuAction Menu::run() +MenuAction Menu_run(Menu *self) +{ + if (self->m_vtable && self->m_vtable->run) { + return self->m_vtable->run(self); + } + return Menu_runBase(self); +} + +MenuAction Menu_runBase(Menu *self) { // check whether the close flag has been set - if (m_shouldClose) { + if (self->m_shouldClose) { // reset flag and close - m_shouldClose = false; + self->m_shouldClose = false; return MENU_QUIT; } // if the led has already been selected continue as normal // and allow derived menu logic to perform work - if (m_ledSelected) { + if (self->m_ledSelected) { return MENU_CONTINUE; } @@ -70,23 +116,23 @@ Menu::MenuAction Menu::run() // class's onShortClick and onLongClick functions so // every time the button is clicked, change the target led - if (g_pButton->onShortClick()) { - nextBulbSelection(); + if (Button_onShortClick(g_pButton)) { + Menu_nextBulbSelection(self); } // on a long press of the button, lock in the target led - if (g_pButton->onLongClick()) { - m_ledSelected = true; + if (Button_onLongClick(g_pButton)) { + self->m_ledSelected = true; // call led selected callback - onLedSelected(); + Menu_onLedSelected(self); // log about the selection - DEBUG_LOGF("Led Selected: 0x%x (%s)", m_targetLeds, - (m_targetLeds == MAP_LED(LED_MULTI)) ? "multi" - : (m_targetLeds == MAP_LED_ALL) ? "all" + DEBUG_LOGF("Led Selected: 0x%x (%s)", self->m_targetLeds, + (self->m_targetLeds == MAP_LED(LED_MULTI)) ? "multi" + : (self->m_targetLeds == MAP_LED_ALL) ? "all" : "some singles"); } // render the bulb selection - showBulbSelection(); + Menu_showBulbSelection(self); // return MENU_SKIP to indicate to derived menu classes that // they should skip their logic and just return so that the @@ -94,67 +140,86 @@ Menu::MenuAction Menu::run() return MENU_SKIP; } -void Menu::showBulbSelection() +void Menu_showBulbSelection(Menu *self) { - Leds::clearAll(); - Leds::blinkMap(m_targetLeds, BULB_SELECT_OFF_MS, BULB_SELECT_ON_MS, RGB_MAGENTA1); + Leds_clearAll(); + Leds_blinkMap(self->m_targetLeds, BULB_SELECT_OFF_MS, BULB_SELECT_ON_MS, RGB_MAGENTA1); // blink when selecting - Menus::showSelection(RGB_MAGENTA1); + Menus_showSelection(RGB_MAGENTA1); } -void Menu::showExit() +void Menu_showExit(Menu *self) { - if (g_pButton->isPressed() && g_pButton->holdDuration() > SHORT_CLICK_THRESHOLD_TICKS) { - Leds::setIndex(LED_1, RGB_RED); + if (Button_isPressed(g_pButton) && Button_holdDuration(g_pButton) > SHORT_CLICK_THRESHOLD_TICKS) { + Leds_setIndex(LED_1, RGB_RED); } else { - Leds::clearIndex(LED_1); - Leds::blinkIndex(LED_0, EXIT_MENU_OFF_MS, EXIT_MENU_ON_MS, RGB_WHITE0); - Leds::blinkIndex(LED_1, EXIT_MENU_OFF_MS, EXIT_MENU_ON_MS, RGB_RED0); + Leds_clearIndex(LED_1); + Leds_blinkIndex(LED_0, EXIT_MENU_OFF_MS, EXIT_MENU_ON_MS, RGB_WHITE0); + Leds_blinkIndex(LED_1, EXIT_MENU_OFF_MS, EXIT_MENU_ON_MS, RGB_RED0); } } -void Menu::nextBulbSelection() +void Menu_nextBulbSelection(Menu *self) { // The target led can be 0 through LED_COUNT to represent any led or all leds // modulo by LED_COUNT + 1 to include LED_COUNT (all) as a target - switch (m_targetLeds) { + switch (self->m_targetLeds) { case MAP_LED_ALL: - m_targetLeds = MAP_LED(LED_FIRST); + self->m_targetLeds = MAP_LED(LED_FIRST); break; case MAP_LED(LED_LAST): - m_targetLeds = MAP_LED_ALL; + self->m_targetLeds = MAP_LED_ALL; break; default: // LED_FIRST through LED_LAST // iterate as normal - m_targetLeds = MAP_LED(((ledmapGetFirstLed(m_targetLeds) + 1) % (LED_COUNT + 1))); + self->m_targetLeds = MAP_LED(((ledmapGetFirstLed(self->m_targetLeds) + 1) % (LED_COUNT + 1))); break; } } -void Menu::bypassLedSelection(LedMap map) +void Menu_bypassLedSelection(Menu *self, LedMap map) +{ + self->m_ledSelected = true; + self->m_targetLeds = map; + Menu_onLedSelected(self); +} + +void Menu_onLedSelected(Menu *self) { - m_ledSelected = true; - m_targetLeds = map; - onLedSelected(); + if (self->m_vtable && self->m_vtable->onLedSelected) { + self->m_vtable->onLedSelected(self); + } } -void Menu::onLedSelected() +void Menu_onShortClick(Menu *self) { + if (self->m_vtable && self->m_vtable->onShortClick) { + self->m_vtable->onShortClick(self); + } } -void Menu::onShortClick() +void Menu_onLongClick(Menu *self) { + if (self->m_vtable && self->m_vtable->onLongClick) { + self->m_vtable->onLongClick(self); + return; + } + Menu_leaveMenu(self, false); } -void Menu::onLongClick() +void Menu_leaveMenu(Menu *self, bool doSave) { - leaveMenu(false); + if (self->m_vtable && self->m_vtable->leaveMenu) { + self->m_vtable->leaveMenu(self, doSave); + return; + } + Menu_leaveMenuBase(self, doSave); } -void Menu::leaveMenu(bool doSave) +void Menu_leaveMenuBase(Menu *self, bool doSave) { - m_shouldClose = true; + self->m_shouldClose = true; if (doSave) { - Modes::saveStorage(); + Modes_saveStorage(); } -} +} \ No newline at end of file diff --git a/VortexEngine/src/Menus/Menu.h b/VortexEngine/src/Menus/Menu.h index 997a46ee9d..2f63e057db 100644 --- a/VortexEngine/src/Menus/Menu.h +++ b/VortexEngine/src/Menus/Menu.h @@ -1,52 +1,41 @@ #ifndef MENU_H #define MENU_H +#include #include #include "../Colors/ColorTypes.h" #include "../Leds/LedTypes.h" #include "../Modes/Mode.h" -class Menu -{ -public: - Menu(const RGBColor &col, bool advanced); - virtual ~Menu(); - - virtual bool init(); - - // the action for the menu to execute - enum MenuAction : uint8_t { - // quit the menus - MENU_QUIT, - // continue running the menu - MENU_CONTINUE, - // don't run derived menu code, this is used internally - // by the menu class itself - MENU_SKIP - }; - virtual MenuAction run(); - - // optional handler for initializing data after user selected led - virtual void onLedSelected(); - - // optional handlers for clicks - virtual void onShortClick(); - virtual void onLongClick(); +// the action for the menu to execute +typedef uint8_t MenuAction; - // close the current menu - virtual void leaveMenu(bool doSave = false); +// quit the menus +#define MENU_QUIT 0 +// continue running the menu +#define MENU_CONTINUE 1 +// don't run derived menu code, this is used internally +// by the menu class itself +#define MENU_SKIP 2 -protected: - void showBulbSelection(); - void showExit(); - - // iterate to next bulb selection - void nextBulbSelection(); - - // skip led selection - void bypassLedSelection(LedMap map); +// vtable for the Menu virtuals; hooks may be NULL +typedef struct MenuVtable +{ + // derived initializer (calls Menu_initBase, returns false on failure) + bool (*init)(void *self); + // derived run loop (calls Menu_runBase first, returns MENU_* action) + MenuAction (*run)(void *self); + void (*onLedSelected)(void *self); + void (*onShortClick)(void *self); + void (*onLongClick)(void *self); + void (*leaveMenu)(void *self, bool doSave); + // frees the derived allocation; must call Menu_baseDestroy last + void (*destroy)(void *self); +} MenuVtable; +typedef struct Menu +{ // the mode copied from the current mode used to preview changes Mode m_previewMode; // the color of this menu @@ -60,14 +49,47 @@ class Menu bool m_ledSelected; // whether advanced menu was activated bool m_advanced; - -private: // internal flag to close the menu bool m_shouldClose; + // the derived menu's hooks + const MenuVtable *m_vtable; +} Menu; + +Menu *Menu_new(const RGBColor *col, bool advanced); +void Menu_initBase(Menu *self, const MenuVtable *vtable); +// destroy the menu, dispatching to the derived destroy when provided +void Menu_destroy(Menu *self); +// the base cleanup that derived destroys must call last +void Menu_baseDestroy(Menu *self); + +// initialize the menu, dispatching to the derived init when provided +bool Menu_init(Menu *self); +// the base initializer that derived inits must call +bool Menu_baseInit(Menu *self); + +// run the menu, dispatching through the vtable where provided +MenuAction Menu_run(Menu *self); +// the base run loop that derived runs must call +MenuAction Menu_runBase(Menu *self); + +// optional handlers for clicks +void Menu_onLedSelected(Menu *self); +void Menu_onShortClick(Menu *self); +void Menu_onLongClick(Menu *self); + +// close the current menu +void Menu_leaveMenu(Menu *self, bool doSave); + +// the base implementation that derived leaveMenu overrides call into +void Menu_leaveMenuBase(Menu *self, bool doSave); + +void Menu_showBulbSelection(Menu *self); +void Menu_showExit(Menu *self); + +// iterate to next bulb selection +void Menu_nextBulbSelection(Menu *self); -#ifdef VORTEX_LIB - friend class Vortex; -#endif -}; +// skip led selection +void Menu_bypassLedSelection(Menu *self, LedMap map); -#endif +#endif \ No newline at end of file diff --git a/VortexEngine/src/Menus/MenuList/ColorSelect.cpp b/VortexEngine/src/Menus/MenuList/ColorSelect.cpp index 3e56e6d603..0e73cc8805 100644 --- a/VortexEngine/src/Menus/MenuList/ColorSelect.cpp +++ b/VortexEngine/src/Menus/MenuList/ColorSelect.cpp @@ -1,4 +1,5 @@ #include "ColorSelect.h" +#include "../Menus.h" #include "../../VortexEngine.h" @@ -14,15 +15,55 @@ #include "../../Modes/Mode.h" #include "../../Leds/Leds.h" #include "../../Log/Log.h" +#include "../../Memory/Memory.h" -ColorSelect::ColorSelect(const RGBColor &col, bool advanced) : - Menu(col, advanced), - m_state(STATE_PICK_SLOT), - m_newColor(), - m_colorset(), - m_targetSlot(0), - m_targetHue1(0) +static void ColorSelect_onLedSelectedHook(void *self); +static void ColorSelect_destroyHook(void *self); +static bool ColorSelect_initHook(void *self); +static MenuAction ColorSelect_runHook(void *self); +static void ColorSelect_onShortClickHook(void *self); +static void ColorSelect_onLongClickHook(void *self); + +const MenuVtable ColorSelect_vtable = { + ColorSelect_initHook, + ColorSelect_runHook, + ColorSelect_onLedSelectedHook, + ColorSelect_onShortClickHook, + ColorSelect_onLongClickHook, + NULL, + ColorSelect_destroyHook, +}; + +// the options for saturations +static const uint8_t s_sats[4] = { + SAT_OPTION_4, + SAT_OPTION_3, + SAT_OPTION_2, + SAT_OPTION_1 +}; + +// the options for values +static const uint8_t s_vals[4] = { + VAL_OPTION_4, + VAL_OPTION_3, + VAL_OPTION_2, + VAL_OPTION_1 +}; + +ColorSelect *ColorSelect_new(const RGBColor *col, bool advanced) { + ColorSelect *self = (ColorSelect *)Menus_allocMenu(); + if (!self) { + return NULL; + } + Menu_initBase(&self->m_base, &ColorSelect_vtable); + RGBColor_setRGB(&self->m_base.m_menuColor, col); + self->m_base.m_advanced = advanced; + self->m_state = STATE_PICK_SLOT; + self->m_newColor = HSVColor_make(); + Colorset_make(&self->m_colorset); + self->m_targetSlot = 0; + self->m_targetHue1 = 0; // NOTE! Specifically using hsv_to_rgb_rainbow instead of generic because // it will generate nicer looking colors and a nicer rainbow to select // from than a regular hsv conversion will. This is because the rainbow @@ -30,108 +71,117 @@ ColorSelect::ColorSelect(const RGBColor &col, bool advanced) : // in a smaller color space with less bright colors. The tradeoff is you lose // the bright colors but the rainbow looks a lot better g_hsv_rgb_alg = HSV_TO_RGB_RAINBOW; + return self; } -ColorSelect::~ColorSelect() +void ColorSelect_destroy(ColorSelect *self) { // revert the hsv to rgb algorithm to normal g_hsv_rgb_alg = HSV_TO_RGB_GENERIC; // make sure force sleep is re-enabled when we leave - VortexEngine::toggleForceSleep(true); + VortexEngine_toggleForceSleep(true); + Menu_baseDestroy(&self->m_base); } -bool ColorSelect::init() +bool ColorSelect_init(ColorSelect *self) { - if (!Menu::init()) { + Menu *menu = &self->m_base; + if (!Menu_baseInit(menu)) { return false; } - Mode *cur = Modes::curMode(); - if (cur->isEmpty()) { + Mode *cur = Modes_curMode(); + if (Mode_isEmpty(cur)) { // cannot work with an empty mode return false; } #if VORTEX_SLIM == 0 - if (cur->isMultiLed()) { - bypassLedSelection(MAP_LED(LED_MULTI)); + if (Mode_isMultiLed(cur)) { + Menu_bypassLedSelection(menu, MAP_LED(LED_MULTI)); } #endif - m_state = STATE_INIT; + self->m_state = STATE_INIT; DEBUG_LOG("Entered color select"); return true; } -Menu::MenuAction ColorSelect::run() +MenuAction ColorSelect_run(ColorSelect *self) { - MenuAction result = Menu::run(); + Menu *menu = &self->m_base; + MenuAction result = Menu_runBase(menu); if (result != MENU_CONTINUE) { return result; } // all states start with a blank slate - Leds::clearAll(); - switch (m_state) { + Leds_clearAll(); + switch (self->m_state) { case STATE_INIT: // reset all the selection members - m_newColor = HSVColor(0, 255, 255); - m_targetHue1 = 0; - m_targetSlot = 0; + self->m_newColor = HSVColor_makeHSV(0, 255, 255); + self->m_targetHue1 = 0; + self->m_targetSlot = 0; // move on to picking slot - m_state = STATE_PICK_SLOT; + self->m_state = STATE_PICK_SLOT; break; case STATE_PICK_SLOT: - showSlotSelection(); + ColorSelect_showSlotSelection(self); break; case STATE_PICK_HUE1: case STATE_PICK_HUE2: case STATE_PICK_SAT: case STATE_PICK_VAL: - showSelection(m_state); + ColorSelect_showSelection(self, self->m_state); break; } // show selections - Menus::showSelection(); + Menus_showSelection(RGB_WHITE5); return MENU_CONTINUE; } // callback after the user selects the target led -void ColorSelect::onLedSelected() +void ColorSelect_onLedSelected(ColorSelect *self) { - Mode *cur = Modes::curMode(); + Menu *menu = &self->m_base; + Mode *cur = Modes_curMode(); // grab the colorset from our selected target led - if (m_targetLeds == MAP_LED_ALL) { - m_colorset = cur->getColorset(); + Colorset tmp; + if (menu->m_targetLeds == MAP_LED_ALL) { + tmp = Mode_getColorset(cur, LED_ANY); } else { - m_colorset = cur->getColorset(ledmapGetFirstLed(m_targetLeds)); + tmp = Mode_getColorset(cur, ledmapGetFirstLed(menu->m_targetLeds)); } + Colorset_set(&self->m_colorset, &tmp); } -void ColorSelect::onShortClick() +void ColorSelect_onShortClick(ColorSelect *self) { + Menu *menu = &self->m_base; // increment selection - m_curSelection++; - if (m_state == STATE_PICK_SLOT) { - m_curSelection %= (m_colorset.numColors() + 1 + (m_colorset.numColors() < MAX_COLOR_SLOTS)); + menu->m_curSelection++; + if (self->m_state == STATE_PICK_SLOT) { + menu->m_curSelection %= (Colorset_numColors(&self->m_colorset) + 1 + (Colorset_numColors(&self->m_colorset) < MAX_COLOR_SLOTS)); } else { - m_curSelection %= 5; + menu->m_curSelection %= 5; } } -void ColorSelect::onLongClick() +void ColorSelect_onLongClick(ColorSelect *self) { + Menu *menu = &self->m_base; // if we're on 'exit' and we're on any menu past the slot selection - if (m_curSelection == 4 && m_state > STATE_PICK_SLOT) { + if (menu->m_curSelection == 4 && self->m_state > STATE_PICK_SLOT) { // move back to the previous selection - m_state = (ColorSelectState)(m_state - 1); + self->m_state = (ColorSelectState)(self->m_state - 1); // if we're back to the slot selection then set our position back to the // slot we selected, otherwise it's more visually appealing to just return // to the first selection on the previous menu - m_curSelection = (m_state == STATE_PICK_SLOT) ? m_targetSlot : 0; + menu->m_curSelection = (self->m_state == STATE_PICK_SLOT) ? self->m_targetSlot : 0; return; } // reuse these variables lots - uint8_t numColors = m_colorset.numColors(); - uint32_t holdDur = g_pButton->holdDuration(); - switch (m_state) { + uint8_t numColors = Colorset_numColors(&self->m_colorset); + uint32_t holdDur = Button_holdDuration(g_pButton); + switch (self->m_state) { case STATE_INIT: // nothing return; @@ -141,134 +191,167 @@ void ColorSelect::onLongClick() // just the number of colors (8) but if it's not full then the exit is the // number of colors + 1. Example: with 4 cols, cols are on 0, 1, 2, 3, // add-color is 4, and exit is 5 - if (m_curSelection == numColors + (numColors < MAX_COLOR_SLOTS)) { - Mode *cur = Modes::curMode(); - cur->setColorsetMap(m_targetLeds, m_colorset); - cur->init(); - leaveMenu(true); + if (menu->m_curSelection == numColors + (numColors < MAX_COLOR_SLOTS)) { + Mode *cur = Modes_curMode(); + Mode_setColorsetMap(cur, menu->m_targetLeds, &self->m_colorset); + Mode_initPatterns(cur); + Menu_leaveMenu(menu, true); return; } // handle if user releases during the delete option - if (m_curSelection < numColors && + if (menu->m_curSelection < numColors && holdDur >= DELETE_THRESHOLD_TICKS && (holdDur % (DELETE_CYCLE_TICKS * 2)) > (DELETE_CYCLE_TICKS)) { // delete current slot - m_colorset.removeColor(m_curSelection); - if (m_curSelection > numColors) { - m_curSelection--; + Colorset_removeColor(&self->m_colorset, menu->m_curSelection); + if (menu->m_curSelection > numColors) { + menu->m_curSelection--; } return; } // otherwise store the target slot continue onto the hue selection - m_targetSlot = m_curSelection; + self->m_targetSlot = menu->m_curSelection; break; case STATE_PICK_HUE1: - m_targetHue1 = m_curSelection; - m_newColor.hue = m_targetHue1 * (255 / 4); + self->m_targetHue1 = menu->m_curSelection; + self->m_newColor.hue = self->m_targetHue1 * (255 / 4); break; case STATE_PICK_HUE2: - m_newColor.hue = (m_targetHue1 * (255 / 4)) + m_curSelection * (255 / 16); + self->m_newColor.hue = (self->m_targetHue1 * (255 / 4)) + menu->m_curSelection * (255 / 16); break; case STATE_PICK_SAT: - m_newColor.sat = sats[m_curSelection]; + self->m_newColor.sat = s_sats[menu->m_curSelection]; break; case STATE_PICK_VAL: // no m_targetVal because you can't go back after this - m_newColor.val = vals[m_curSelection]; + self->m_newColor.val = s_vals[menu->m_curSelection]; // specifically using hsv to rgb rainbow to generate the color - m_colorset.set(m_targetSlot, m_newColor); - m_curSelection = m_targetSlot; - m_state = STATE_PICK_SLOT; + Colorset_setColor(&self->m_colorset, self->m_targetSlot, RGBColor_fromHSV(&self->m_newColor)); + menu->m_curSelection = self->m_targetSlot; + self->m_state = STATE_PICK_SLOT; return; } - m_state = (ColorSelectState)(m_state + 1); - m_curSelection = 0; + self->m_state = (ColorSelectState)(self->m_state + 1); + menu->m_curSelection = 0; } -void ColorSelect::showSlotSelection() +void ColorSelect_showSlotSelection(ColorSelect *self) { - uint8_t exitIndex = m_colorset.numColors(); - uint32_t holdDur = g_pButton->holdDuration(); - bool withinNumColors = m_curSelection < exitIndex; - bool holdDurationCheck = g_pButton->isPressed() && holdDur >= DELETE_THRESHOLD_TICKS; + Menu *menu = &self->m_base; + uint8_t exitIndex = Colorset_numColors(&self->m_colorset); + uint32_t holdDur = Button_holdDuration(g_pButton); + bool withinNumColors = menu->m_curSelection < exitIndex; + bool holdDurationCheck = Button_isPressed(g_pButton) && holdDur >= DELETE_THRESHOLD_TICKS; bool holdDurationModCheck = (holdDur % (DELETE_CYCLE_TICKS * 2)) > DELETE_CYCLE_TICKS; - const RGBColor &col = m_colorset[m_curSelection]; + RGBColor col = Colorset_at(&self->m_colorset, menu->m_curSelection); if (withinNumColors && holdDurationCheck && holdDurationModCheck) { // breath red for delete slot - Leds::blinkIndex(LED_0, 50, 100, col); - Leds::breatheIndex(LED_1, 0, holdDur); + Leds_blinkIndex(LED_0, 50, 100, col); + Leds_breatheIndex(LED_1, 0, holdDur); } else if (withinNumColors) { - if (col.empty()) { - Leds::setIndex(LED_0, RGB_WHITE0); + if (RGBColor_empty(&col)) { + Leds_setIndex(LED_0, RGB_WHITE0); } - Leds::blinkAll(150, 650, col); + Leds_blinkAll(150, 650, col); } else if (exitIndex < MAX_COLOR_SLOTS) { - if (m_curSelection == exitIndex) { + if (menu->m_curSelection == exitIndex) { // blink both leds and blink faster to indicate 'add' new color - Leds::blinkAll(100, 150, RGB_WHITE2); + Leds_blinkAll(100, 150, RGB_WHITE2); } exitIndex++; } - if (m_curSelection == exitIndex) { + if (menu->m_curSelection == exitIndex) { // display the full set - showFullSet(50, 100); + ColorSelect_showFullSet(self, 50, 100); // set LED_1 to green to indicate save and exit - Leds::setIndex(LED_1, RGB_GREEN2); + Leds_setIndex(LED_1, RGB_GREEN2); // if not on exitIndex or add new color set LED_1 based on button state - } else if (m_curSelection != m_colorset.numColors() && !holdDurationCheck) { - Leds::setIndex(LED_1, g_pButton->isPressed() ? RGB_OFF : RGB_WHITE2); + } else if (menu->m_curSelection != Colorset_numColors(&self->m_colorset) && !holdDurationCheck) { + Leds_setIndex(LED_1, Button_isPressed(g_pButton) ? RGB_OFF : RGB_WHITE2); } } -void ColorSelect::showSelection(ColorSelectState mode) +void ColorSelect_showSelection(ColorSelect *self, ColorSelectState mode) { - if (m_curSelection >= 4) { - showExit(); + Menu *menu = &self->m_base; + if (menu->m_curSelection >= 4) { + Menu_showExit(menu); return; } - uint32_t now = Time::getCurtime(); - uint8_t hue = m_newColor.hue; - uint8_t sat = m_newColor.sat; + uint32_t now = Time_getCurtime(); + uint8_t hue = self->m_newColor.hue; + uint8_t sat = self->m_newColor.sat; uint8_t val = 255; switch (mode) { default: return; case STATE_PICK_HUE1: - hue = m_curSelection * (255 / 4); - Leds::breatheIndex(LED_0, hue, (now / 2), 22, 255, 180); - Leds::breatheIndex(LED_1, hue, (now / 2) + 125, 22, 255, 180); + hue = menu->m_curSelection * (255 / 4); + Leds_breatheIndex(LED_0, hue, (now / 2), 22, 255, 180); + Leds_breatheIndex(LED_1, hue, (now / 2) + 125, 22, 255, 180); // force sat at hue level1 sat = 255; // NOTE: return here return; case STATE_PICK_HUE2: - hue = m_targetHue1 * (255 / 4) + (m_curSelection * (255 / 16)); - Leds::setIndex(LED_1, RGB_WHITE0); + hue = self->m_targetHue1 * (255 / 4) + (menu->m_curSelection * (255 / 16)); + Leds_setIndex(LED_1, RGB_WHITE0); // force sat at hue level2 sat = 255; break; case STATE_PICK_SAT: - sat = sats[m_curSelection]; - Leds::breatheIndexSat(LED_1, hue, (now / 3), 100, 150, 150); + sat = s_sats[menu->m_curSelection]; + Leds_breatheIndexSat(LED_1, hue, (now / 3), 100, 150, 150); break; case STATE_PICK_VAL: - val = vals[m_curSelection]; - Leds::breatheIndexVal(LED_1, hue, (now / 3), 100, sat, 150); + val = s_vals[menu->m_curSelection]; + Leds_breatheIndexVal(LED_1, hue, (now / 3), 100, sat, 150); break; } - Leds::setMap(MAP_PAIR_EVENS, HSVColor(hue, sat, val)); + HSVColor hsv = HSVColor_makeHSV(hue, sat, val); + Leds_setMap(MAP_PAIR_EVENS, RGBColor_fromHSV(&hsv)); } -void ColorSelect::showFullSet(uint8_t offMs, uint8_t onMs) +void ColorSelect_showFullSet(ColorSelect *self, uint8_t offMs, uint8_t onMs) { - uint8_t numCols = m_colorset.numColors(); + uint8_t numCols = Colorset_numColors(&self->m_colorset); uint8_t offOnMs = MS_TO_TICKS(offMs + onMs); if (!numCols || !offOnMs) { return; } - uint32_t now = Time::getCurtime(); + uint32_t now = Time_getCurtime(); if ((now % offOnMs) < MS_TO_TICKS(onMs)) { - Leds::setAll(m_colorset.get((now / offOnMs) % numCols)); + Leds_setAll(Colorset_get(&self->m_colorset, (now / offOnMs) % numCols)); } - Leds::setIndex(LED_1, RGB_GREEN0); + Leds_setIndex(LED_1, RGB_GREEN0); +} + +static void ColorSelect_onLedSelectedHook(void *self) +{ + ColorSelect_onLedSelected((ColorSelect *)self); +} + +static void ColorSelect_onShortClickHook(void *self) +{ + ColorSelect_onShortClick((ColorSelect *)self); +} + +static void ColorSelect_onLongClickHook(void *self) +{ + ColorSelect_onLongClick((ColorSelect *)self); +} + +static bool ColorSelect_initHook(void *self) +{ + return ColorSelect_init((ColorSelect *)self); +} + +static MenuAction ColorSelect_runHook(void *self) +{ + return ColorSelect_run((ColorSelect *)self); +} + +static void ColorSelect_destroyHook(void *self) +{ + ColorSelect_destroy((ColorSelect *)self); } diff --git a/VortexEngine/src/Menus/MenuList/ColorSelect.h b/VortexEngine/src/Menus/MenuList/ColorSelect.h index e0f0ed496a..8156240fdb 100644 --- a/VortexEngine/src/Menus/MenuList/ColorSelect.h +++ b/VortexEngine/src/Menus/MenuList/ColorSelect.h @@ -1,71 +1,54 @@ #ifndef COLOR_SELECT_H #define COLOR_SELECT_H +#include #include "../Menu.h" #include "../../Colors/Colorset.h" -class ColorSelect : public Menu -{ -public: - ColorSelect(const RGBColor &col, bool advanced); - ~ColorSelect(); - - bool init() override; - MenuAction run() override; - - // callback after the user selects the target led - void onLedSelected() override; - - // handlers for clicks - void onShortClick() override; - void onLongClick() override; - -private: - // private enumeration for internal state of color selection - enum ColorSelectState : uint8_t - { - STATE_INIT, - STATE_PICK_SLOT, - STATE_PICK_HUE1, - STATE_PICK_HUE2, - STATE_PICK_SAT, - STATE_PICK_VAL - }; - - void showSlotSelection(); - void showSelection(ColorSelectState mode); - void showFullSet(uint8_t offMs, uint8_t onMs); - - // the options for saturations - const uint8_t sats[4] = { - SAT_OPTION_4, - SAT_OPTION_3, - SAT_OPTION_2, - SAT_OPTION_1 - }; - - // the options for values - const uint8_t vals[4] = { - VAL_OPTION_4, - VAL_OPTION_3, - VAL_OPTION_2, - VAL_OPTION_1 - }; +// private enumeration for internal state of color selection +typedef uint8_t ColorSelectState; +enum { + STATE_INIT, + STATE_PICK_SLOT, + STATE_PICK_HUE1, + STATE_PICK_HUE2, + STATE_PICK_SAT, + STATE_PICK_VAL +}; +typedef struct ColorSelect +{ + Menu m_base; // the state of the color select menu ColorSelectState m_state; // the new color being built via hue, sat then val HSVColor m_newColor; // A copy of the colorset being changed Colorset m_colorset; - - // below are the selection indexes for each level - // the target values selected at each level to build the color, the value // selected at the last level isn't stored because you can't go back after uint8_t m_targetSlot; uint8_t m_targetHue1; -}; +} ColorSelect; + +extern const MenuVtable ColorSelect_vtable; + +ColorSelect *ColorSelect_new(const RGBColor *col, bool advanced); +void ColorSelect_destroy(ColorSelect *self); + +bool ColorSelect_init(ColorSelect *self); +MenuAction ColorSelect_run(ColorSelect *self); + +// callback after the user selects the target led +void ColorSelect_onLedSelected(ColorSelect *self); + +// handlers for clicks +void ColorSelect_onShortClick(ColorSelect *self); +void ColorSelect_onLongClick(ColorSelect *self); + +void ColorSelect_showSlotSelection(ColorSelect *self); +void ColorSelect_showSelection(ColorSelect *self, ColorSelectState mode); +void ColorSelect_showFullSet(ColorSelect *self, uint8_t offMs, uint8_t onMs); -#endif +#endif \ No newline at end of file diff --git a/VortexEngine/src/Menus/MenuList/EditorConnection.cpp b/VortexEngine/src/Menus/MenuList/EditorConnection.cpp index c1fc1d5927..6e1e2be8bf 100644 --- a/VortexEngine/src/Menus/MenuList/EditorConnection.cpp +++ b/VortexEngine/src/Menus/MenuList/EditorConnection.cpp @@ -1,4 +1,5 @@ #include "EditorConnection.h" +#include "../Menus.h" #include "../../Patterns/PatternArgs.h" #include "../../Serial/ByteStream.h" @@ -11,170 +12,211 @@ #include "../../Modes/Mode.h" #include "../../Leds/Leds.h" #include "../../Log/Log.h" +#include "../../Memory/Memory.h" #include -EditorConnection::EditorConnection(const RGBColor &col, bool advanced) : - Menu(col, advanced), - m_state(STATE_DISCONNECTED), - m_allowReset(true), - m_previousModeIndex(0), - m_numModesToReceive(0), - m_rv(RV_OK) +typedef struct CommandState { + const char *cmd; + EditorConnectionState cmdState; +} CommandState; + +static const CommandState s_commands[] = { + { EDITOR_VERB_PULL_MODES, STATE_PULL_MODES }, + { EDITOR_VERB_PUSH_MODES, STATE_PUSH_MODES }, + { EDITOR_VERB_DEMO_MODE, STATE_DEMO_MODE }, + { EDITOR_VERB_CLEAR_DEMO, STATE_CLEAR_DEMO }, + { EDITOR_VERB_PULL_EACH_MODE, STATE_PULL_EACH_MODE }, + { EDITOR_VERB_PUSH_EACH_MODE, STATE_PUSH_EACH_MODE }, + { EDITOR_VERB_TRANSMIT_VL, STATE_TRANSMIT_MODE_VL }, + { EDITOR_VERB_SET_GLOBAL_BRIGHTNESS, STATE_SET_GLOBAL_BRIGHTNESS }, + { EDITOR_VERB_GET_GLOBAL_BRIGHTNESS, STATE_GET_GLOBAL_BRIGHTNESS }, +}; +#define NUM_COMMANDS (sizeof(s_commands) / sizeof(s_commands[0])) + +static void EditorConnection_destroyHook(void *self); +static bool EditorConnection_initHook(void *self); +static MenuAction EditorConnection_runHook(void *self); +static void EditorConnection_onShortClickHook(void *self); +static void EditorConnection_onLongClickHook(void *self); +static void EditorConnection_leaveMenuHook(void *self, bool doSave); + +const MenuVtable EditorConnection_vtable = { + EditorConnection_initHook, + EditorConnection_runHook, + NULL, + EditorConnection_onShortClickHook, + EditorConnection_onLongClickHook, + EditorConnection_leaveMenuHook, + EditorConnection_destroyHook, +}; + +EditorConnection *EditorConnection_new(const RGBColor *col, bool advanced) +{ + EditorConnection *self = (EditorConnection *)Menus_allocMenu(); + if (!self) { + return NULL; + } + Menu_initBase(&self->m_base, &EditorConnection_vtable); + RGBColor_setRGB(&self->m_base.m_menuColor, col); + self->m_base.m_advanced = advanced; + self->m_state = STATE_DISCONNECTED; + self->m_allowReset = true; + self->m_previousModeIndex = 0; + self->m_numModesToReceive = 0; + self->m_rv = RV_OK; + ByteStream_make(&self->m_receiveBuffer); + return self; } -EditorConnection::~EditorConnection() +void EditorConnection_destroy(EditorConnection *self) { - clearDemo(); + EditorConnection_clearDemo(self); + ByteStream_destroy(&self->m_receiveBuffer); + Menu_baseDestroy(&self->m_base); } -bool EditorConnection::init() +bool EditorConnection_init(EditorConnection *self) { - if (!Menu::init()) { + Menu *menu = &self->m_base; + if (!Menu_baseInit(menu)) { return false; } // skip led selection - m_ledSelected = true; - clearDemo(); + menu->m_ledSelected = true; + EditorConnection_clearDemo(self); DEBUG_LOG("Entering Editor Connection"); return true; } -Menu::MenuAction EditorConnection::run() +MenuAction EditorConnection_run(EditorConnection *self) { - MenuAction result = Menu::run(); + Menu *menu = &self->m_base; + MenuAction result = Menu_runBase(menu); if (result != MENU_CONTINUE) { return result; } // TODO: auto leave the editor menu when unplugged // show the editor - showEditor(); + EditorConnection_showEditor(self); // receive any data from serial into the receive buffer - receiveData(); + EditorConnection_receiveData(self); // handle the current state - handleState(); + EditorConnection_handleState(self); return MENU_CONTINUE; } -void EditorConnection::onShortClick() +void EditorConnection_onShortClick(EditorConnection *self) { // if the device has received any commands do not reset! - if (!m_allowReset) { + if (!self->m_allowReset) { return; } // reset, this won't actually disconnect the com port - m_state = STATE_DISCONNECTED; + self->m_state = STATE_DISCONNECTED; // clear the demo - clearDemo(); + EditorConnection_clearDemo(self); // sent a reset, do not allow another - m_allowReset = false; + self->m_allowReset = false; } -void EditorConnection::onLongClick() +void EditorConnection_onLongClick(EditorConnection *self) { - leaveMenu(true); + EditorConnection_leaveMenu(self, true); } -void EditorConnection::leaveMenu(bool doSave) +void EditorConnection_leaveMenu(EditorConnection *self, bool doSave) { - SerialComs::write(EDITOR_VERB_GOODBYE); - Menu::leaveMenu(true); + (void)doSave; + SerialComs_write(EDITOR_VERB_GOODBYE); + Menu_leaveMenuBase(&self->m_base, true); } -void EditorConnection::clearDemo() +void EditorConnection_clearDemo(EditorConnection *self) { - Colorset set(RGB_WHITE0); - PatternArgs args(1, 0, 0); - m_previewMode.setPattern(PATTERN_STROBE, LED_ALL, &args, &set); - m_previewMode.init(); + Menu *menu = &self->m_base; + Colorset set; + Colorset_make1(&set, RGB_WHITE0); + PatternArgs args; + PatternArgs_init3(&args, 1, 0, 0); + Mode_setPattern(&menu->m_previewMode, PATTERN_STROBE, LED_ALL, &args, &set); + Mode_initPatterns(&menu->m_previewMode); } -void EditorConnection::handleErrors() +void EditorConnection_handleErrors(EditorConnection *self) { - if (m_rv == RV_FAIL) { + if (self->m_rv == RV_FAIL) { // handle failure from before, reset rv - m_rv = RV_OK; + self->m_rv = RV_OK; // clear the buffer I guess - m_receiveBuffer.clear(); + ByteStream_clear(&self->m_receiveBuffer); } // TODO: Custom error codes? } -const EditorConnection::CommandState EditorConnection::commands[] = { - { EDITOR_VERB_PULL_MODES, STATE_PULL_MODES }, - { EDITOR_VERB_PUSH_MODES, STATE_PUSH_MODES }, - { EDITOR_VERB_DEMO_MODE, STATE_DEMO_MODE }, - { EDITOR_VERB_CLEAR_DEMO, STATE_CLEAR_DEMO }, - { EDITOR_VERB_PULL_EACH_MODE, STATE_PULL_EACH_MODE }, - { EDITOR_VERB_PUSH_EACH_MODE, STATE_PUSH_EACH_MODE }, - { EDITOR_VERB_TRANSMIT_VL, STATE_TRANSMIT_MODE_VL }, - { EDITOR_VERB_SET_GLOBAL_BRIGHTNESS, STATE_SET_GLOBAL_BRIGHTNESS }, - { EDITOR_VERB_GET_GLOBAL_BRIGHTNESS, STATE_GET_GLOBAL_BRIGHTNESS }, -}; -#define NUM_COMMANDS (sizeof(commands) / sizeof(commands[0])) - -void EditorConnection::handleCommand() +void EditorConnection_handleCommand(EditorConnection *self) { - if (m_receiveBuffer.size() < 1) { + if (ByteStream_size(&self->m_receiveBuffer) < 1) { return; } for (uint32_t i = 0; i < NUM_COMMANDS; ++i) { // commands are just one letter, don't consume it yet so we can compare // each cmd in the list, this probably should be a lookup table - char receiveCmd = m_receiveBuffer.data()[0]; + char receiveCmd = (char)ByteStream_data(&self->m_receiveBuffer)[0]; // if the command matches - if (receiveCmd != commands[i].cmd[0]) { + if (receiveCmd != s_commands[i].cmd[0]) { continue; } // then consume the data from the receive buffer and update the state - m_receiveBuffer.consume8(); - m_state = commands[i].cmdState; + ByteStream_consume8(&self->m_receiveBuffer, NULL); + self->m_state = s_commands[i].cmdState; // we have now received at least one command, do not allow resetting - m_allowReset = false; + self->m_allowReset = false; // don't need to check any more commands break; } } -void EditorConnection::handleState() +void EditorConnection_handleState(EditorConnection *self) { + Menu *menu = &self->m_base; // operate on the state of the editor connection - switch (m_state) { + switch (self->m_state) { // ------------------------------- // Disconnected case STATE_DISCONNECTED: default: // not connected yet so check for connections - if (!SerialComs::isConnected()) { - if (!SerialComs::checkSerial()) { + if (!SerialComs_isConnected()) { + if (!SerialComs_checkSerial()) { // no connection found just continue waiting break; } } // a connection was found, say hello - m_state = STATE_GREETING; + self->m_state = STATE_GREETING; break; // ------------------------------- // Send Greeting case STATE_GREETING: // send the hello greeting with our version number and build time - SerialComs::write(EDITOR_VERB_GREETING); - m_state = STATE_IDLE; + SerialComs_write(EDITOR_VERB_GREETING); + self->m_state = STATE_IDLE; break; // ------------------------------- // Chillin case STATE_IDLE: // handle any errors that may have occurred last run - handleErrors(); + EditorConnection_handleErrors(self); // parse the receive buffer for any commands from the editor - handleCommand(); + EditorConnection_handleCommand(self); // watch for disconnects - if (!SerialComs::isConnected()) { - Leds::holdAll(RGB_RED); - leaveMenu(true); + if (!SerialComs_isConnected()) { + Leds_holdAll(RGB_RED); + EditorConnection_leaveMenu(self, true); } break; @@ -182,75 +224,75 @@ void EditorConnection::handleState() // Send Modes to PC case STATE_PULL_MODES: // editor requested pull modes, send the modes - sendModes(); - m_state = STATE_PULL_MODES_SEND; + EditorConnection_sendModes(self); + self->m_state = STATE_PULL_MODES_SEND; break; case STATE_PULL_MODES_SEND: // recive the send modes ack from the editor - if (receiveMessage(EDITOR_VERB_PULL_MODES_DONE) == RV_WAIT) { + if (EditorConnection_receiveMessage(self, EDITOR_VERB_PULL_MODES_DONE) == RV_WAIT) { // just wait break; } - m_state = STATE_PULL_MODES_DONE; + self->m_state = STATE_PULL_MODES_DONE; break; case STATE_PULL_MODES_DONE: // send our acknowledgement that the modes were sent - SerialComs::write(EDITOR_VERB_PULL_MODES_ACK); + SerialComs_write(EDITOR_VERB_PULL_MODES_ACK); // go idle - m_state = STATE_IDLE; + self->m_state = STATE_IDLE; break; // ------------------------------- // Receive Modes from PC case STATE_PUSH_MODES: // now say we are ready - SerialComs::write(EDITOR_VERB_READY); + SerialComs_write(EDITOR_VERB_READY); // move to receiving - m_state = STATE_PUSH_MODES_RECEIVE; + self->m_state = STATE_PUSH_MODES_RECEIVE; break; case STATE_PUSH_MODES_RECEIVE: // receive the modes into the receive buffer - if (receiveModes() == RV_WAIT) { + if (EditorConnection_receiveModes(self) == RV_WAIT) { // just keep waiting break; } // success modes were received send the done - m_state = STATE_PUSH_MODES_DONE; + self->m_state = STATE_PUSH_MODES_DONE; break; case STATE_PUSH_MODES_DONE: - SerialComs::write(EDITOR_VERB_PUSH_MODES_ACK); - m_state = STATE_IDLE; + SerialComs_write(EDITOR_VERB_PUSH_MODES_ACK); + self->m_state = STATE_IDLE; break; // ------------------------------- // Demo Mode from PC case STATE_DEMO_MODE: // now say we are ready - SerialComs::write(EDITOR_VERB_READY); + SerialComs_write(EDITOR_VERB_READY); // move to receiving - m_state = STATE_DEMO_MODE_RECEIVE; + self->m_state = STATE_DEMO_MODE_RECEIVE; break; case STATE_DEMO_MODE_RECEIVE: // receive the modes into the receive buffer - if (receiveDemoMode() == RV_WAIT) { + if (EditorConnection_receiveDemoMode(self) == RV_WAIT) { // just wait break; } // success modes were received send the done - m_state = STATE_DEMO_MODE_DONE; + self->m_state = STATE_DEMO_MODE_DONE; break; case STATE_DEMO_MODE_DONE: // say we are done - SerialComs::write(EDITOR_VERB_DEMO_MODE_ACK); - m_state = STATE_IDLE; + SerialComs_write(EDITOR_VERB_DEMO_MODE_ACK); + self->m_state = STATE_IDLE; break; // ------------------------------- // Reset Demo to Nothing case STATE_CLEAR_DEMO: - clearDemo(); - SerialComs::write(EDITOR_VERB_CLEAR_DEMO_ACK); - m_state = STATE_IDLE; + EditorConnection_clearDemo(self); + SerialComs_write(EDITOR_VERB_CLEAR_DEMO_ACK); + self->m_state = STATE_IDLE; break; // ------------------------------- @@ -258,64 +300,64 @@ void EditorConnection::handleState() case STATE_TRANSMIT_MODE_VL: #if VL_ENABLE_SENDER == 1 // immediately load the mode and send it now - VLSender::send(&m_previewMode); + VLSender_send(&menu->m_previewMode); #endif - m_state = STATE_TRANSMIT_MODE_VL_DONE; + self->m_state = STATE_TRANSMIT_MODE_VL_DONE; break; case STATE_TRANSMIT_MODE_VL_DONE: // done transmitting - SerialComs::write(EDITOR_VERB_TRANSMIT_VL_ACK); - m_state = STATE_IDLE; + SerialComs_write(EDITOR_VERB_TRANSMIT_VL_ACK); + self->m_state = STATE_IDLE; break; // ------------------------------- // Send Modes to PC Safer case STATE_PULL_EACH_MODE: // editor requested pull modes, send the modes - sendModeCount(); - m_state = STATE_PULL_EACH_MODE_COUNT; + EditorConnection_sendModeCount(self); + self->m_state = STATE_PULL_EACH_MODE_COUNT; break; case STATE_PULL_EACH_MODE_COUNT: - if (receiveMessage(EDITOR_VERB_PULL_EACH_MODE_ACK) == RV_WAIT) { + if (EditorConnection_receiveMessage(self, EDITOR_VERB_PULL_EACH_MODE_ACK) == RV_WAIT) { // just wait break; } - if (Modes::numModes() == 0) { - m_state = STATE_PULL_EACH_MODE_DONE; + if (Modes_numModes() == 0) { + self->m_state = STATE_PULL_EACH_MODE_DONE; } else { - m_previousModeIndex = Modes::curModeIndex(); - m_state = STATE_PULL_EACH_MODE_SEND; + self->m_previousModeIndex = Modes_curModeIndex(); + self->m_state = STATE_PULL_EACH_MODE_SEND; } break; case STATE_PULL_EACH_MODE_SEND: // send the current mode - sendCurMode(); + EditorConnection_sendCurMode(self); // wait for the ack - m_state = STATE_PULL_EACH_MODE_WAIT; + self->m_state = STATE_PULL_EACH_MODE_WAIT; break; case STATE_PULL_EACH_MODE_WAIT: // recive the ack from the editor to send next mode - if (receiveMessage(EDITOR_VERB_PULL_EACH_MODE_ACK) == RV_WAIT) { + if (EditorConnection_receiveMessage(self, EDITOR_VERB_PULL_EACH_MODE_ACK) == RV_WAIT) { // just wait break; } // if there is still more modes - if (Modes::curModeIndex() < (Modes::numModes() - 1)) { + if (Modes_curModeIndex() < (Modes_numModes() - 1)) { // then iterate to the next mode and send - Modes::nextMode(); - m_state = STATE_PULL_EACH_MODE_SEND; + Modes_nextMode(); + self->m_state = STATE_PULL_EACH_MODE_SEND; } else { // otherwise done sending modes - m_state = STATE_PULL_EACH_MODE_DONE; + self->m_state = STATE_PULL_EACH_MODE_DONE; } break; case STATE_PULL_EACH_MODE_DONE: // send our acknowledgement that the modes were sent - SerialComs::write(EDITOR_VERB_PULL_EACH_MODE_DONE); + SerialComs_write(EDITOR_VERB_PULL_EACH_MODE_DONE); // switch back to the previous mode - Modes::setCurMode(m_previousModeIndex); + Modes_setCurMode(self->m_previousModeIndex); // go idle - m_state = STATE_IDLE; + self->m_state = STATE_IDLE; break; // ------------------------------- @@ -323,77 +365,78 @@ void EditorConnection::handleState() case STATE_PUSH_EACH_MODE: // editor requested to push modes, find out how many // ack the command and wait for the amount of modes - SerialComs::write(EDITOR_VERB_PUSH_EACH_MODE_ACK); - m_state = STATE_PUSH_EACH_MODE_COUNT; + SerialComs_write(EDITOR_VERB_PUSH_EACH_MODE_ACK); + self->m_state = STATE_PUSH_EACH_MODE_COUNT; break; case STATE_PUSH_EACH_MODE_COUNT: - if (receiveModeCount() == RV_WAIT) { + if (EditorConnection_receiveModeCount(self) == RV_WAIT) { // just wait break; } // clear modes and start receiving - Modes::clearModes(); + Modes_clearModes(); // write out an ack - SerialComs::write(EDITOR_VERB_PUSH_EACH_MODE_ACK); + SerialComs_write(EDITOR_VERB_PUSH_EACH_MODE_ACK); // ready to receive a mode - m_state = STATE_PUSH_EACH_MODE_RECEIVE; + self->m_state = STATE_PUSH_EACH_MODE_RECEIVE; break; case STATE_PUSH_EACH_MODE_RECEIVE: // receive the modes into the receive buffer - if (receiveMode() == RV_WAIT) { + if (EditorConnection_receiveMode(self) == RV_WAIT) { // just wait break; } - SerialComs::write(EDITOR_VERB_PUSH_EACH_MODE_ACK); - if (m_numModesToReceive > 0) { - m_numModesToReceive--; + SerialComs_write(EDITOR_VERB_PUSH_EACH_MODE_ACK); + if (self->m_numModesToReceive > 0) { + self->m_numModesToReceive--; } - if (!m_numModesToReceive) { + if (!self->m_numModesToReceive) { // success modes were received send the done - m_state = STATE_PUSH_EACH_MODE_DONE; + self->m_state = STATE_PUSH_EACH_MODE_DONE; } break; case STATE_PUSH_EACH_MODE_DONE: // save the new modes - Modes::saveStorage(); + Modes_saveStorage(); // did originally receive/send a DONE message here but it wasn't working // on lightshow.lol so just skip to IDLE - m_state = STATE_IDLE; + self->m_state = STATE_IDLE; break; // ------------------------------- // Set Global Brightness case STATE_SET_GLOBAL_BRIGHTNESS: - SerialComs::write(EDITOR_VERB_READY); - m_state = STATE_SET_GLOBAL_BRIGHTNESS_RECEIVE; + SerialComs_write(EDITOR_VERB_READY); + self->m_state = STATE_SET_GLOBAL_BRIGHTNESS_RECEIVE; break; case STATE_SET_GLOBAL_BRIGHTNESS_RECEIVE: // set the brightness of the device - if (receiveBrightness() == RV_WAIT) { + if (EditorConnection_receiveBrightness(self) == RV_WAIT) { // just keep waiting break; } - m_state = STATE_IDLE; + self->m_state = STATE_IDLE; break; // ------------------------------- // Get Global Brightness case STATE_GET_GLOBAL_BRIGHTNESS: - sendBrightness(); - m_state = STATE_IDLE; + EditorConnection_sendBrightness(self); + self->m_state = STATE_IDLE; break; } } -void EditorConnection::showEditor() +void EditorConnection_showEditor(EditorConnection *self) { - switch (m_state) { + Menu *menu = &self->m_base; + switch (self->m_state) { case STATE_DISCONNECTED: - Leds::clearAll(); - Leds::blinkAll(250, 150, RGB_WHITE0); + Leds_clearAll(); + Leds_blinkAll(250, 150, RGB_WHITE0); break; case STATE_IDLE: - m_previewMode.play(); + Mode_play(&menu->m_previewMode); break; default: // do nothing! @@ -403,200 +446,265 @@ void EditorConnection::showEditor() } } -void EditorConnection::receiveData() +void EditorConnection_receiveData(EditorConnection *self) { // read more data into the receive buffer - SerialComs::read(m_receiveBuffer); + SerialComs_read(&self->m_receiveBuffer); } -void EditorConnection::sendModes() +void EditorConnection_sendModes(EditorConnection *self) { ByteStream modesBuffer; - Modes::saveToBuffer(modesBuffer); - SerialComs::write(modesBuffer); + ByteStream_make(&modesBuffer); + Modes_saveToBuffer(&modesBuffer); + SerialComs_writeStream(&modesBuffer); + ByteStream_destroy(&modesBuffer); } -void EditorConnection::sendModeCount() +void EditorConnection_sendModeCount(EditorConnection *self) { ByteStream buffer; - buffer.serialize8(Modes::numModes()); - SerialComs::write(buffer); + ByteStream_make(&buffer); + ByteStream_serialize8(&buffer, Modes_numModes()); + SerialComs_writeStream(&buffer); + ByteStream_destroy(&buffer); } -void EditorConnection::sendCurMode() +void EditorConnection_sendCurMode(EditorConnection *self) { ByteStream modeBuffer; - Mode *cur = Modes::curMode(); + ByteStream_make(&modeBuffer); + Mode *cur = Modes_curMode(); if (!cur) { // ?? + ByteStream_destroy(&modeBuffer); return; } - if (!cur->saveToBuffer(modeBuffer)) { + if (!Mode_saveToBuffer(cur, &modeBuffer, 0)) { // ?? + ByteStream_destroy(&modeBuffer); return; } - SerialComs::write(modeBuffer); + SerialComs_writeStream(&modeBuffer); + ByteStream_destroy(&modeBuffer); } -void EditorConnection::sendCurModeVL() +void EditorConnection_sendCurModeVL(EditorConnection *self) { + (void)self; #if VL_ENABLE_SENDER == 1 - m_state = STATE_TRANSMIT_MODE_VL; + self->m_state = STATE_TRANSMIT_MODE_VL; #endif } -ReturnCode EditorConnection::sendBrightness() +ReturnCode EditorConnection_sendBrightness(EditorConnection *self) { ByteStream brightnessBuf; - if (!brightnessBuf.serialize8(Leds::getBrightness())) { + ByteStream_make(&brightnessBuf); + if (!ByteStream_serialize8(&brightnessBuf, Leds_getBrightness())) { + ByteStream_destroy(&brightnessBuf); return RV_FAIL; } - SerialComs::write(brightnessBuf); + SerialComs_writeStream(&brightnessBuf); + ByteStream_destroy(&brightnessBuf); return RV_OK; } -ReturnCode EditorConnection::receiveBuffer(ByteStream &buffer) +ReturnCode EditorConnection_receiveBuffer(EditorConnection *self, ByteStream *buffer) { // need at least the buffer size first uint32_t size = 0; - if (m_receiveBuffer.size() < sizeof(size)) { + if (ByteStream_size(&self->m_receiveBuffer) < sizeof(size)) { // wait, not enough data available yet return RV_WAIT; } // grab the size out of the start - m_receiveBuffer.resetUnserializer(); - size = m_receiveBuffer.peek32(); - if (m_receiveBuffer.size() < (size + sizeof(size))) { + ByteStream_resetUnserializer(&self->m_receiveBuffer); + size = ByteStream_peek32(&self->m_receiveBuffer); + if (ByteStream_size(&self->m_receiveBuffer) < (size + sizeof(size))) { // don't unserialize yet, not ready return RV_WAIT; } // okay unserialize now, first unserialize the size - if (!m_receiveBuffer.consume32(&size)) { + if (!ByteStream_consume32(&self->m_receiveBuffer, &size)) { // this shouldn't fail! return RV_FAIL; } // create a new ByteStream that will hold the full buffer of data - if (!buffer.init(size)) { + if (!ByteStream_init(buffer, size)) { return RV_FAIL; } // then copy everything from the receive buffer into the rawdata // which is going to overwrite the crc/size/flags of the ByteStream - if (!m_receiveBuffer.consume(size, buffer.rawData())) { + if (!ByteStream_consume(&self->m_receiveBuffer, size, ByteStream_rawData(buffer))) { return RV_FAIL; } - buffer.sanity(); - if (!buffer.checkCRC()) { - buffer.clear(); + ByteStream_sanity(buffer); + if (!ByteStream_checkCRC(buffer)) { + ByteStream_clear(buffer); // return true otherwise we'll get locked in this state forever return RV_FAIL; } return RV_OK; } -ReturnCode EditorConnection::receiveModes() +ReturnCode EditorConnection_receiveModes(EditorConnection *self) { // create a new ByteStream that will hold the full buffer of data ByteStream buf; - m_rv = receiveBuffer(buf); - if (m_rv != RV_OK) { - return m_rv; - } - if (!Modes::loadFromBuffer(buf) || !Modes::saveStorage()) { + ByteStream_make(&buf); + self->m_rv = EditorConnection_receiveBuffer(self, &buf); + if (self->m_rv != RV_OK) { + ByteStream_destroy(&buf); + return self->m_rv; + } + if (!Modes_loadFromBuffer(&buf) || !Modes_saveStorage()) { + ByteStream_destroy(&buf); return RV_FAIL; } + ByteStream_destroy(&buf); return RV_OK; } -ReturnCode EditorConnection::receiveModeCount() +ReturnCode EditorConnection_receiveModeCount(EditorConnection *self) { ByteStream buf; - m_rv = receiveBuffer(buf); - if (m_rv != RV_OK) { - return m_rv; + ByteStream_make(&buf); + self->m_rv = EditorConnection_receiveBuffer(self, &buf); + if (self->m_rv != RV_OK) { + ByteStream_destroy(&buf); + return self->m_rv; } // unserialize the mode count - if (!buf.consume8(&m_numModesToReceive)) { + if (!ByteStream_consume8(&buf, &self->m_numModesToReceive)) { + ByteStream_destroy(&buf); return RV_FAIL; } - if (m_numModesToReceive > MAX_MODES) { + if (self->m_numModesToReceive > MAX_MODES) { + ByteStream_destroy(&buf); return RV_FAIL; } // good mode count + ByteStream_destroy(&buf); return RV_OK; } -ReturnCode EditorConnection::receiveMode() +ReturnCode EditorConnection_receiveMode(EditorConnection *self) { ByteStream buf; - m_rv = receiveBuffer(buf); - if (m_rv != RV_OK) { - return m_rv; + ByteStream_make(&buf); + self->m_rv = EditorConnection_receiveBuffer(self, &buf); + if (self->m_rv != RV_OK) { + ByteStream_destroy(&buf); + return self->m_rv; } // unserialize the mode into the demo mode - if (!Modes::addModeFromBuffer(buf)) { + if (!Modes_addModeFromBuffer(&buf)) { + ByteStream_destroy(&buf); return RV_FAIL; } + ByteStream_destroy(&buf); return RV_OK; } -ReturnCode EditorConnection::receiveDemoMode() +ReturnCode EditorConnection_receiveDemoMode(EditorConnection *self) { + Menu *menu = &self->m_base; // create a new ByteStream that will hold the full buffer of data ByteStream buf; - m_rv = receiveBuffer(buf); - if (m_rv != RV_OK) { - return m_rv; + ByteStream_make(&buf); + self->m_rv = EditorConnection_receiveBuffer(self, &buf); + if (self->m_rv != RV_OK) { + ByteStream_destroy(&buf); + return self->m_rv; } // unserialize the mode into the demo mode - if (!m_previewMode.loadFromBuffer(buf)) { + if (!Mode_loadFromBuffer(&menu->m_previewMode, &buf)) { // failure + ByteStream_destroy(&buf); return RV_FAIL; } + ByteStream_destroy(&buf); return RV_OK; } -ReturnCode EditorConnection::receiveMessage(const char *message) +ReturnCode EditorConnection_receiveMessage(EditorConnection *self, const char *message) { size_t len = strlen(message); uint8_t byte = 0; // wait for the editor to ack the idle - if (m_receiveBuffer.size() < len) { + if (ByteStream_size(&self->m_receiveBuffer) < len) { return RV_WAIT; } - if (memcmp(m_receiveBuffer.data(), message, len) != 0) { + if (memcmp(ByteStream_data(&self->m_receiveBuffer), message, len) != 0) { return RV_FAIL; } for (size_t i = 0; i < len; ++i) { - if (!m_receiveBuffer.consume8(&byte)) { + if (!ByteStream_consume8(&self->m_receiveBuffer, &byte)) { return RV_FAIL; } } // we have now received at least one command, do not allow resetting - m_allowReset = false; + self->m_allowReset = false; return RV_OK; } -ReturnCode EditorConnection::receiveBrightness() +ReturnCode EditorConnection_receiveBrightness(EditorConnection *self) { // create a new ByteStream that will hold the full buffer of data ByteStream buf; - m_rv = receiveBuffer(buf); - if (m_rv != RV_OK) { + ByteStream_make(&buf); + self->m_rv = EditorConnection_receiveBuffer(self, &buf); + if (self->m_rv != RV_OK) { // RV_WAIT or RV_FAIL - return m_rv; + ByteStream_destroy(&buf); + return self->m_rv; } - if (!buf.size()) { + if (!ByteStream_size(&buf)) { // failure + ByteStream_destroy(&buf); return RV_FAIL; } uint8_t brightness = 255; - if (!buf.consume8(&brightness) || brightness == 0) { + if (!ByteStream_consume8(&buf, &brightness) || brightness == 0) { // they should never send 0 brightness + ByteStream_destroy(&buf); return RV_FAIL; } if (brightness > 0) { - Leds::setBrightness(brightness); - Modes::saveHeader(); + Leds_setBrightness(brightness); + Modes_saveHeader(); } + ByteStream_destroy(&buf); return RV_OK; } + +static void EditorConnection_onShortClickHook(void *self) +{ + EditorConnection_onShortClick((EditorConnection *)self); +} + +static void EditorConnection_onLongClickHook(void *self) +{ + EditorConnection_onLongClick((EditorConnection *)self); +} + +static void EditorConnection_leaveMenuHook(void *self, bool doSave) +{ + EditorConnection_leaveMenu((EditorConnection *)self, doSave); +} + +static bool EditorConnection_initHook(void *self) +{ + return EditorConnection_init((EditorConnection *)self); +} + +static MenuAction EditorConnection_runHook(void *self) +{ + return EditorConnection_run((EditorConnection *)self); +} + +static void EditorConnection_destroyHook(void *self) +{ + EditorConnection_destroy((EditorConnection *)self); +} diff --git a/VortexEngine/src/Menus/MenuList/EditorConnection.h b/VortexEngine/src/Menus/MenuList/EditorConnection.h index fa499db7fe..26ce5ce73f 100644 --- a/VortexEngine/src/Menus/MenuList/EditorConnection.h +++ b/VortexEngine/src/Menus/MenuList/EditorConnection.h @@ -1,115 +1,78 @@ #ifndef EDITOR_CONNECTION_H #define EDITOR_CONNECTION_H +#include #include "../Menu.h" #include "../../Serial/ByteStream.h" #include "../../Modes/Mode.h" // various return codes that internal functions use -enum ReturnCode -{ +typedef uint8_t ReturnCode; +enum { RV_FAIL = 0, RV_OK, RV_WAIT, }; -class EditorConnection : public Menu -{ -public: - EditorConnection(const RGBColor &col, bool advanced); - ~EditorConnection(); - - bool init() override; - MenuAction run() override; - - // handlers for clicks - void onShortClick() override; - void onLongClick() override; - - void leaveMenu(bool doSave = false) override; - -private: - void clearDemo(); - void handleErrors(); - void handleCommand(); - void handleState(); - void showEditor(); - void receiveData(); - void sendModes(); - void sendModeCount(); - void sendCurMode(); - void sendCurModeVL(); - ReturnCode sendBrightness(); - ReturnCode receiveBuffer(ByteStream &buffer); - ReturnCode receiveModes(); - ReturnCode receiveModeCount(); - ReturnCode receiveMode(); - ReturnCode receiveDemoMode(); - ReturnCode receiveMessage(const char *message); - ReturnCode receiveBrightness(); - - enum EditorConnectionState { - // the editor is not connected - STATE_DISCONNECTED, - - // Sending the greeting message - STATE_GREETING, - - // entirely idle, waiting for commands - STATE_IDLE, - - // editor pulls the modes from device, then wait for the sent modes ack - STATE_PULL_MODES, - STATE_PULL_MODES_SEND, - STATE_PULL_MODES_DONE, - - // editor pushes modes to device, then waits for done - STATE_PUSH_MODES, - STATE_PUSH_MODES_RECEIVE, - STATE_PUSH_MODES_DONE, - - // editor pushes mode to device for demo while idle - STATE_DEMO_MODE, - STATE_DEMO_MODE_RECEIVE, - STATE_DEMO_MODE_DONE, - - // editor tells device to clear the demo preview, device acknowledge - STATE_CLEAR_DEMO, - - // transmit the mode over visible light - STATE_TRANSMIT_MODE_VL, - STATE_TRANSMIT_MODE_VL_DONE, - - // editor pulls the modes from device (safer version) - STATE_PULL_EACH_MODE, - STATE_PULL_EACH_MODE_COUNT, - STATE_PULL_EACH_MODE_SEND, - STATE_PULL_EACH_MODE_WAIT, - STATE_PULL_EACH_MODE_DONE, - - // editor pushes modes to device (safer version) - STATE_PUSH_EACH_MODE, - STATE_PUSH_EACH_MODE_COUNT, - STATE_PUSH_EACH_MODE_RECEIVE, - STATE_PUSH_EACH_MODE_WAIT, - STATE_PUSH_EACH_MODE_DONE, - - // set global brightness - STATE_SET_GLOBAL_BRIGHTNESS, - STATE_SET_GLOBAL_BRIGHTNESS_RECEIVE, - - // get global brightness - STATE_GET_GLOBAL_BRIGHTNESS, - }; - - struct CommandState - { - const char *cmd; - EditorConnection::EditorConnectionState cmdState; - }; - static const CommandState commands[]; +typedef uint8_t EditorConnectionState; +enum { + // the editor is not connected + STATE_DISCONNECTED, + + // Sending the greeting message + STATE_GREETING, + + // entirely idle, waiting for commands + STATE_IDLE, + + // editor pulls the modes from device, then wait for the sent modes ack + STATE_PULL_MODES, + STATE_PULL_MODES_SEND, + STATE_PULL_MODES_DONE, + + // editor pushes modes to device, then waits for done + STATE_PUSH_MODES, + STATE_PUSH_MODES_RECEIVE, + STATE_PUSH_MODES_DONE, + + // editor pushes mode to device for demo while idle + STATE_DEMO_MODE, + STATE_DEMO_MODE_RECEIVE, + STATE_DEMO_MODE_DONE, + + // editor tells device to clear the demo preview, device acknowledge + STATE_CLEAR_DEMO, + + // transmit the mode over visible light + STATE_TRANSMIT_MODE_VL, + STATE_TRANSMIT_MODE_VL_DONE, + + // editor pulls the modes from device (safer version) + STATE_PULL_EACH_MODE, + STATE_PULL_EACH_MODE_COUNT, + STATE_PULL_EACH_MODE_SEND, + STATE_PULL_EACH_MODE_WAIT, + STATE_PULL_EACH_MODE_DONE, + + // editor pushes modes to device (safer version) + STATE_PUSH_EACH_MODE, + STATE_PUSH_EACH_MODE_COUNT, + STATE_PUSH_EACH_MODE_RECEIVE, + STATE_PUSH_EACH_MODE_WAIT, + STATE_PUSH_EACH_MODE_DONE, + + // set global brightness + STATE_SET_GLOBAL_BRIGHTNESS, + STATE_SET_GLOBAL_BRIGHTNESS_RECEIVE, + + // get global brightness + STATE_GET_GLOBAL_BRIGHTNESS, +}; +typedef struct EditorConnection +{ + Menu m_base; // state of the editor EditorConnectionState m_state; // the data that is received @@ -122,6 +85,39 @@ class EditorConnection : public Menu uint8_t m_numModesToReceive; // internal return value tracker ReturnCode m_rv; -}; - -#endif +} EditorConnection; + +extern const MenuVtable EditorConnection_vtable; + +EditorConnection *EditorConnection_new(const RGBColor *col, bool advanced); +void EditorConnection_destroy(EditorConnection *self); + +bool EditorConnection_init(EditorConnection *self); +MenuAction EditorConnection_run(EditorConnection *self); + +// handlers for clicks +void EditorConnection_onShortClick(EditorConnection *self); +void EditorConnection_onLongClick(EditorConnection *self); + +void EditorConnection_leaveMenu(EditorConnection *self, bool doSave); + +void EditorConnection_clearDemo(EditorConnection *self); +void EditorConnection_handleErrors(EditorConnection *self); +void EditorConnection_handleCommand(EditorConnection *self); +void EditorConnection_handleState(EditorConnection *self); +void EditorConnection_showEditor(EditorConnection *self); +void EditorConnection_receiveData(EditorConnection *self); +void EditorConnection_sendModes(EditorConnection *self); +void EditorConnection_sendModeCount(EditorConnection *self); +void EditorConnection_sendCurMode(EditorConnection *self); +void EditorConnection_sendCurModeVL(EditorConnection *self); +ReturnCode EditorConnection_sendBrightness(EditorConnection *self); +ReturnCode EditorConnection_receiveBuffer(EditorConnection *self, ByteStream *buffer); +ReturnCode EditorConnection_receiveModes(EditorConnection *self); +ReturnCode EditorConnection_receiveModeCount(EditorConnection *self); +ReturnCode EditorConnection_receiveMode(EditorConnection *self); +ReturnCode EditorConnection_receiveDemoMode(EditorConnection *self); +ReturnCode EditorConnection_receiveMessage(EditorConnection *self, const char *message); +ReturnCode EditorConnection_receiveBrightness(EditorConnection *self); + +#endif \ No newline at end of file diff --git a/VortexEngine/src/Menus/MenuList/FactoryReset.cpp b/VortexEngine/src/Menus/MenuList/FactoryReset.cpp index 5489144ed0..115030b4ec 100644 --- a/VortexEngine/src/Menus/MenuList/FactoryReset.cpp +++ b/VortexEngine/src/Menus/MenuList/FactoryReset.cpp @@ -1,4 +1,5 @@ #include "FactoryReset.h" +#include "../Menus.h" #include "../../VortexEngine.h" #include "../../Modes/DefaultModes.h" @@ -8,122 +9,180 @@ #include "../../Time/Timings.h" #include "../../Modes/Modes.h" #include "../../Modes/Mode.h" +#include "../../Colors/Colorset.h" #include "../../Leds/Leds.h" #include "../../Log/Log.h" +#include "../../Memory/Memory.h" #include "../../VortexConfig.h" -FactoryReset::FactoryReset(const RGBColor &col, bool advanced) : - Menu(col, advanced) +// the derived hooks, dispatched through the base Menu vtable +static void FactoryReset_destroyHook(void *self); +static bool FactoryReset_initHook(void *self); +static MenuAction FactoryReset_runHook(void *self); +static void FactoryReset_onShortClickHook(void *self); +static void FactoryReset_onLongClickHook(void *self); + +const MenuVtable FactoryReset_vtable = { + FactoryReset_initHook, + FactoryReset_runHook, + NULL, + FactoryReset_onShortClickHook, + FactoryReset_onLongClickHook, + NULL, + FactoryReset_destroyHook, +}; + +FactoryReset *FactoryReset_new(const RGBColor *col, bool advanced) { + FactoryReset *self = (FactoryReset *)Menus_allocMenu(); + if (!self) { + return NULL; + } + Menu_initBase(&self->m_base, &FactoryReset_vtable); + RGBColor_setRGB(&self->m_base.m_menuColor, col); + self->m_base.m_advanced = advanced; + return self; } -FactoryReset::~FactoryReset() +void FactoryReset_destroy(FactoryReset *self) { + Menu_baseDestroy(&self->m_base); } -bool FactoryReset::init() +bool FactoryReset_init(FactoryReset *self) { - if (!Menu::init()) { + Menu *menu = &self->m_base; + if (!Menu_baseInit(menu)) { return false; } // bypass led selection for fac reset if a multi was set on // the current slot because it doesn't make sense to pick - Mode *cur = Modes::curMode(); + Mode *cur = Modes_curMode(); if (!cur) { return false; } - if (cur->isMultiLed()) { - m_ledSelected = true; - m_targetLeds = MAP_LED(LED_MULTI); + if (Mode_isMultiLed(cur)) { + menu->m_ledSelected = true; + menu->m_targetLeds = MAP_LED(LED_MULTI); } - if (!m_advanced) { + if (!menu->m_advanced) { // skip led selection - m_ledSelected = true; + menu->m_ledSelected = true; } // Start on exit by default - m_curSelection = false; + menu->m_curSelection = false; DEBUG_LOG("Entered factory reset"); return true; } -Menu::MenuAction FactoryReset::run() +MenuAction FactoryReset_run(FactoryReset *self) { - MenuAction result = Menu::run(); + Menu *menu = &self->m_base; + MenuAction result = Menu_runBase(menu); if (result != MENU_CONTINUE) { return result; } - showReset(); + FactoryReset_showReset(self); return MENU_CONTINUE; } -void FactoryReset::onShortClick() +void FactoryReset_onShortClick(FactoryReset *self) { - m_curSelection = (uint8_t)!m_curSelection; + Menu *menu = &self->m_base; + menu->m_curSelection = (uint8_t)!menu->m_curSelection; } -void FactoryReset::onLongClick() +void FactoryReset_onLongClick(FactoryReset *self) { - if (m_curSelection == 0) { + Menu *menu = &self->m_base; + if (menu->m_curSelection == 0) { // if the selection isn't actually on factory reset then just leave - leaveMenu(); + Menu_leaveMenu(menu, false); return; } // if the button hasn't been held long enough just return - if (g_pButton->holdDuration() <= (FACTORY_RESET_THRESHOLD_TICKS + MS_TO_TICKS(10))) { + if (Button_holdDuration(g_pButton) <= (FACTORY_RESET_THRESHOLD_TICKS + MS_TO_TICKS(10))) { return; } // the button was held down long enough so actually perform the factory reset // restore defaults and then leave menu and save - if (m_advanced) { + if (menu->m_advanced) { // reset the target mode slot on the target led - const DefaultModeEntry &defMode = defaultModes[Modes::curModeIndex()]; - Mode *cur = Modes::curMode(); - MAP_FOREACH_LED(m_targetLeds) { - const DefaultLedEntry &led = defMode.leds[pos]; - Colorset set(led.numColors, led.cols); - cur->setPattern(led.patternID, pos, nullptr, &set); + const DefaultModeEntry *defMode = &defaultModes[Modes_curModeIndex()]; + Mode *cur = Modes_curMode(); + MAP_FOREACH_LED(menu->m_targetLeds) { + const DefaultLedEntry *led = &defMode->leds[pos]; + Colorset set; + Colorset_makeCols(&set, led->numColors, led->cols); + Mode_setPattern(cur, led->patternID, pos, NULL, &set); } - cur->init(); + Mode_initPatterns(cur); } else { - Leds::setBrightness(DEFAULT_BRIGHTNESS); - VortexEngine::setAutoCycle(false); - Modes::setDefaults(); - Modes::setCurMode(0); - Modes::resetFlags(); + Leds_setBrightness(DEFAULT_BRIGHTNESS); + VortexEngine_setAutoCycle(false); + Modes_setDefaults(); + Modes_setCurMode(0); + Modes_resetFlags(); } - leaveMenu(true); + Menu_leaveMenu(menu, true); } -void FactoryReset::showReset() +void FactoryReset_showReset(FactoryReset *self) { - if (m_curSelection == 0) { - Leds::clearAll(); - Leds::blinkAll(350, 350, RGB_WHITE0); + Menu *menu = &self->m_base; + if (menu->m_curSelection == 0) { + Leds_clearAll(); + Leds_blinkAll(350, 350, RGB_WHITE0); return; } - bool isPressed = g_pButton->isPressed(); + bool isPressed = Button_isPressed(g_pButton); if (!isPressed) { - Leds::clearAll(); - Leds::blinkAll(50, 50, RGB_RED4); + Leds_clearAll(); + Leds_blinkAll(50, 50, RGB_RED4); return; } // don't start the fill until the button has been held for a bit - uint32_t holdDur = g_pButton->holdDuration(); + uint32_t holdDur = Button_holdDuration(g_pButton); if (holdDur < MS_TO_TICKS(100)) { return; } uint16_t progress = ((holdDur * 100) / FACTORY_RESET_THRESHOLD_TICKS); DEBUG_LOGF("progress: %d", progress); if (progress >= 100) { - Leds::setAll(RGB_WHITE); + Leds_setAll(RGB_WHITE); return; } uint8_t offMs = 100; uint8_t onMs = (progress > 60) ? 30 : 100; uint8_t sat = (uint8_t)((progress * 5) >> 1); // Using bit shift for division by 2 - Leds::clearAll(); - Leds::blinkIndex(LED_0, offMs, onMs, HSVColor(0, 255 - sat, 180)); - Leds::blinkIndex(LED_1, offMs, onMs, RGB_WHITE0); + HSVColor hsv = HSVColor_makeHSV(0, 255 - sat, 180); + Leds_clearAll(); + Leds_blinkIndex(LED_0, offMs, onMs, RGBColor_fromHSV(&hsv)); + Leds_blinkIndex(LED_1, offMs, onMs, RGB_WHITE0); +} + +static void FactoryReset_onShortClickHook(void *self) +{ + FactoryReset_onShortClick((FactoryReset *)self); +} + +static void FactoryReset_onLongClickHook(void *self) +{ + FactoryReset_onLongClick((FactoryReset *)self); } +static bool FactoryReset_initHook(void *self) +{ + return FactoryReset_init((FactoryReset *)self); +} + +static MenuAction FactoryReset_runHook(void *self) +{ + return FactoryReset_run((FactoryReset *)self); +} + +static void FactoryReset_destroyHook(void *self) +{ + FactoryReset_destroy((FactoryReset *)self); +} diff --git a/VortexEngine/src/Menus/MenuList/FactoryReset.h b/VortexEngine/src/Menus/MenuList/FactoryReset.h index e1fd64414e..eda2ab0e05 100644 --- a/VortexEngine/src/Menus/MenuList/FactoryReset.h +++ b/VortexEngine/src/Menus/MenuList/FactoryReset.h @@ -1,23 +1,26 @@ #ifndef FACTORY_RESET_H #define FACTORY_RESET_H +#include #include "../Menu.h" -class FactoryReset : public Menu +typedef struct FactoryReset { -public: - FactoryReset(const RGBColor &col, bool advanced); - ~FactoryReset(); + Menu m_base; +} FactoryReset; - bool init() override; - MenuAction run() override; +extern const MenuVtable FactoryReset_vtable; - // handlers for clicks - void onShortClick() override; - void onLongClick() override; +FactoryReset *FactoryReset_new(const RGBColor *col, bool advanced); +void FactoryReset_destroy(FactoryReset *self); -private: - void showReset(); -}; +bool FactoryReset_init(FactoryReset *self); +MenuAction FactoryReset_run(FactoryReset *self); + +// handlers for clicks +void FactoryReset_onShortClick(FactoryReset *self); +void FactoryReset_onLongClick(FactoryReset *self); + +void FactoryReset_showReset(FactoryReset *self); #endif diff --git a/VortexEngine/src/Menus/MenuList/GlobalBrightness.cpp b/VortexEngine/src/Menus/MenuList/GlobalBrightness.cpp index 96b186498e..36824a2405 100644 --- a/VortexEngine/src/Menus/MenuList/GlobalBrightness.cpp +++ b/VortexEngine/src/Menus/MenuList/GlobalBrightness.cpp @@ -1,4 +1,5 @@ #include "GlobalBrightness.h" +#include "../Menus.h" #include "../../VortexEngine.h" @@ -13,102 +14,141 @@ #include "../../Time/Timer.h" #include "../../Leds/Leds.h" #include "../../Log/Log.h" +#include "../../Memory/Memory.h" + +static void GlobalBrightness_destroyHook(void *self); +static bool GlobalBrightness_initHook(void *self); +static MenuAction GlobalBrightness_runHook(void *self); +static void GlobalBrightness_onShortClickHook(void *self); +static void GlobalBrightness_onLongClickHook(void *self); + +const MenuVtable GlobalBrightness_vtable = { + GlobalBrightness_initHook, + GlobalBrightness_runHook, + NULL, + GlobalBrightness_onShortClickHook, + GlobalBrightness_onLongClickHook, + NULL, + GlobalBrightness_destroyHook, +}; + +// the list of brightness options +static const uint8_t s_brightnessOptions[4] = { + BRIGHTNESS_OPTION_1, + BRIGHTNESS_OPTION_2, + BRIGHTNESS_OPTION_3, + BRIGHTNESS_OPTION_4 +}; // allow the number of brightness options to be adjusted dynamically -#define NUM_BRIGHTNESS_OPTIONS (sizeof(m_brightnessOptions) / sizeof(m_brightnessOptions[0])) +#define NUM_BRIGHTNESS_OPTIONS (sizeof(s_brightnessOptions) / sizeof(s_brightnessOptions[0])) -GlobalBrightness::GlobalBrightness(const RGBColor &col, bool advanced) : - Menu(col, advanced), - m_keychain_modeState(KEYCHAIN_MODE_STATE_OFF), - m_lastStateChange(0), - m_colorIndex(0) +GlobalBrightness *GlobalBrightness_new(const RGBColor *col, bool advanced) { + GlobalBrightness *self = (GlobalBrightness *)Menus_allocMenu(); + if (!self) { + return NULL; + } + Menu_initBase(&self->m_base, &GlobalBrightness_vtable); + RGBColor_setRGB(&self->m_base.m_menuColor, col); + self->m_base.m_advanced = advanced; + self->m_keychain_modeState = KEYCHAIN_MODE_STATE_OFF; + self->m_lastStateChange = 0; + self->m_colorIndex = 0; + return self; } -GlobalBrightness::~GlobalBrightness() +void GlobalBrightness_destroy(GlobalBrightness *self) { // ensure force sleep is turned back on when we leave - VortexEngine::toggleForceSleep(true); + VortexEngine_toggleForceSleep(true); + Menu_baseDestroy(&self->m_base); } -bool GlobalBrightness::init() +bool GlobalBrightness_init(GlobalBrightness *self) { - if (!Menu::init()) { + Menu *menu = &self->m_base; + if (!Menu_baseInit(menu)) { return false; } // bypass led selection - m_ledSelected = true; + menu->m_ledSelected = true; // would be nice if there was a more elegant way to do this for (uint8_t i = 0; i < NUM_BRIGHTNESS_OPTIONS; ++i) { - if (m_brightnessOptions[i] == Leds::getBrightness()) { + if (s_brightnessOptions[i] == Leds_getBrightness()) { // make sure the default selection matches cur value - m_curSelection = i; + menu->m_curSelection = i; } } - if (m_advanced) { + if (menu->m_advanced) { // turn off force sleep while running keychain mode - VortexEngine::toggleForceSleep(false); + VortexEngine_toggleForceSleep(false); // enable keychain mode so if the device sleeps it will wake into keychain mode - Modes::setKeychainMode(true); + Modes_setKeychainMode(true, true); // start in the off position, this call is necessary to update the // lastStateChange time to the current time of init - setKeychainModeState(KEYCHAIN_MODE_STATE_SOLID); + GlobalBrightness_setKeychainModeState(self, KEYCHAIN_MODE_STATE_SOLID); // reset the color index to ensure it starts on first color - m_colorIndex = 0; + self->m_colorIndex = 0; } DEBUG_LOG("Entered global brightness"); return true; } -Menu::MenuAction GlobalBrightness::run() +MenuAction GlobalBrightness_run(GlobalBrightness *self) { - MenuAction result = Menu::run(); + Menu *menu = &self->m_base; + MenuAction result = Menu_runBase(menu); if (result != MENU_CONTINUE) { return result; } - if (m_advanced) { - return runKeychainMode(); + if (menu->m_advanced) { + return GlobalBrightness_runKeychainMode(self); } // show the current brightness - showBrightnessSelection(); + GlobalBrightness_showBrightnessSelection(self); // show selections - Menus::showSelection(); + Menus_showSelection(RGB_WHITE5); // continue return MENU_CONTINUE; } -void GlobalBrightness::onShortClick() +void GlobalBrightness_onShortClick(GlobalBrightness *self) { - if (m_advanced) { + Menu *menu = &self->m_base; + if (menu->m_advanced) { return; } // include one extra option for the exit slot - m_curSelection = (m_curSelection + 1) % (NUM_BRIGHTNESS_OPTIONS + 1); + menu->m_curSelection = (menu->m_curSelection + 1) % (NUM_BRIGHTNESS_OPTIONS + 1); } -void GlobalBrightness::onLongClick() +void GlobalBrightness_onLongClick(GlobalBrightness *self) { - if (m_advanced) { + Menu *menu = &self->m_base; + if (menu->m_advanced) { return; } - if (m_curSelection >= NUM_BRIGHTNESS_OPTIONS) { + if (menu->m_curSelection >= NUM_BRIGHTNESS_OPTIONS) { // no save exit - leaveMenu(); + Menu_leaveMenu(menu, false); return; } // set the global brightness - Leds::setBrightness(m_brightnessOptions[m_curSelection]); + Leds_setBrightness(s_brightnessOptions[menu->m_curSelection]); // done here, save settings with new brightness - leaveMenu(true); + Menu_leaveMenu(menu, true); } -void GlobalBrightness::showBrightnessSelection() +void GlobalBrightness_showBrightnessSelection(GlobalBrightness *self) { - if (m_curSelection >= NUM_BRIGHTNESS_OPTIONS) { - showExit(); + Menu *menu = &self->m_base; + if (menu->m_curSelection >= NUM_BRIGHTNESS_OPTIONS) { + Menu_showExit(menu); return; } - Leds::setAll(HSVColor(38, 255, m_brightnessOptions[m_curSelection])); + HSVColor hsv = HSVColor_makeHSV(38, 255, s_brightnessOptions[menu->m_curSelection]); + Leds_setAll(RGBColor_fromHSV(&hsv)); } // ==================== KEYCHAIN_MODE STUFF ==================== @@ -123,79 +163,84 @@ void GlobalBrightness::showBrightnessSelection() #define KEYCHAIN_MODE_SLEEP_TICKS SEC_TO_TICKS(300) // bonus simulate keychain_mode in this menu -Menu::MenuAction GlobalBrightness::runKeychainMode() +MenuAction GlobalBrightness_runKeychainMode(GlobalBrightness *self) { + Menu *menu = &self->m_base; // check for exit - if (g_pButton->onConsecutivePresses(KEYCHAIN_MODE_EXIT_CLICKS)) { + if (Button_onConsecutivePresses(g_pButton, KEYCHAIN_MODE_EXIT_CLICKS)) { // turn off keychain mode now - Modes::setKeychainMode(false); + Modes_setKeychainMode(false, true); return MENU_QUIT; } - uint32_t now = Time::getCurtime(); + uint32_t now = Time_getCurtime(); // whether the button was pressed before the timer expired // when the button is first pressed after the window has expired switch off - if (g_pButton->onPress() && m_keychain_modeState != KEYCHAIN_MODE_STATE_OFF && now > (m_lastStateChange + KEYCHAIN_MODE_TIMER_TICKS)) { - setKeychainModeState(KEYCHAIN_MODE_STATE_OFF); + if (Button_onPress(g_pButton) && self->m_keychain_modeState != KEYCHAIN_MODE_STATE_OFF && now > (self->m_lastStateChange + KEYCHAIN_MODE_TIMER_TICKS)) { + GlobalBrightness_setKeychainModeState(self, KEYCHAIN_MODE_STATE_OFF); return MENU_CONTINUE; } // when the button is released, but only if they pressed it within this state - if (g_pButton->onRelease() && g_pButton->pressTime() > m_lastStateChange) { + if (Button_onRelease(g_pButton) && Button_pressTime(g_pButton) > self->m_lastStateChange) { // advance the state or turn off based on press time - setKeychainModeState((keychain_mode_state)(m_keychain_modeState + 1)); + GlobalBrightness_setKeychainModeState(self, (keychain_mode_state)(self->m_keychain_modeState + 1)); return MENU_CONTINUE; } // as long as the button is held down the leds clear - if (g_pButton->isPressed()) { - Leds::clearAll(); + if (Button_isPressed(g_pButton)) { + Leds_clearAll(); return MENU_CONTINUE; } // check for sleep after some seconds - if (m_keychain_modeState == KEYCHAIN_MODE_STATE_OFF && now > (m_lastStateChange + KEYCHAIN_MODE_SLEEP_TICKS)) { + if (self->m_keychain_modeState == KEYCHAIN_MODE_STATE_OFF && now > (self->m_lastStateChange + KEYCHAIN_MODE_SLEEP_TICKS)) { // Optional: indicate when the keychain is turning off (useful mainly for debugging) - //Leds::holdAll(RGB_PURPLE); - VortexEngine::enterSleep(false); + //Leds_holdAll(RGB_PURPLE); + VortexEngine_enterSleep(false); return MENU_QUIT; } // render the signal mode in a different way - if (m_keychain_modeState == KEYCHAIN_MODE_STATE_SIGNAL) { + if (self->m_keychain_modeState == KEYCHAIN_MODE_STATE_SIGNAL) { // use a custom blink because the SIGNAL_OFF duration is too large for uint8 params - Leds::clearAll(); - Leds::blinkIndexOffset(LED_ALL, - Time::getCurtime() - m_lastStateChange, + Colorset cs = Mode_getColorset(&menu->m_previewMode, LED_FIRST); + Leds_clearAll(); + Leds_blinkIndexOffset(LED_ALL, + Time_getCurtime() - self->m_lastStateChange, SIGNAL_OFF_DURATION, SIGNAL_ON_DURATION, - m_previewMode.getColorset().get(m_colorIndex)); + Colorset_get(&cs, self->m_colorIndex)); } else { // play the keychain_mode mode - m_previewMode.play(); + Mode_play(&menu->m_previewMode); } return MENU_CONTINUE; } -void GlobalBrightness::setKeychainModeState(keychain_mode_state newState) +void GlobalBrightness_setKeychainModeState(GlobalBrightness *self, keychain_mode_state newState) { + Menu *menu = &self->m_base; // update the keychain_mode state - m_keychain_modeState = (keychain_mode_state)(newState % KEYCHAIN_MODE_STATE_COUNT); + self->m_keychain_modeState = (keychain_mode_state)(newState % KEYCHAIN_MODE_STATE_COUNT); // record the time of this statechange - m_lastStateChange = Time::getCurtime(); + self->m_lastStateChange = Time_getCurtime(); // this could - uint8_t numCols = Modes::curMode()->getColorset().numColors(); + Colorset cs = Mode_getColorset(Modes_curMode(), LED_FIRST); + uint8_t numCols = Colorset_numColors(&cs); // the args that will define the timing of the blink on the solid pattern PatternArgs args; - switch (m_keychain_modeState) { + PatternArgs_init0(&args); + switch (self->m_keychain_modeState) { case KEYCHAIN_MODE_STATE_OFF: default: // iterate to next color and wrap to 0, don't use modulus because numCols could be 0 - m_colorIndex++; - if (m_colorIndex >= numCols) { - m_colorIndex = 0; + self->m_colorIndex++; + if (self->m_colorIndex >= numCols) { + self->m_colorIndex = 0; } break; case KEYCHAIN_MODE_STATE_SOLID: - args.init(200); + PatternArgs_init(&args, 200); break; case KEYCHAIN_MODE_STATE_DOPS: - args.init(1, 10); + PatternArgs_init(&args, 1, 10); break; case KEYCHAIN_MODE_STATE_SIGNAL: // unfortunately the signal blink timing is too large to fit into the uint8 params @@ -206,7 +251,32 @@ void GlobalBrightness::setKeychainModeState(keychain_mode_state newState) // update the mode and ensure current colorset is always used, use PATTERN_SOLID // because that will never iterate to the next color and allows us to force // the color index via argument 6 - args.arg6 = m_colorIndex; - m_previewMode.setPattern(PATTERN_SOLID, LED_ALL, &args); - m_previewMode.init(); + args.arg6 = self->m_colorIndex; + Mode_setPattern(&menu->m_previewMode, PATTERN_SOLID, LED_ALL, &args, NULL); + Mode_initPatterns(&menu->m_previewMode); +} + +static void GlobalBrightness_onShortClickHook(void *self) +{ + GlobalBrightness_onShortClick((GlobalBrightness *)self); +} + +static void GlobalBrightness_onLongClickHook(void *self) +{ + GlobalBrightness_onLongClick((GlobalBrightness *)self); +} + +static bool GlobalBrightness_initHook(void *self) +{ + return GlobalBrightness_init((GlobalBrightness *)self); +} + +static MenuAction GlobalBrightness_runHook(void *self) +{ + return GlobalBrightness_run((GlobalBrightness *)self); +} + +static void GlobalBrightness_destroyHook(void *self) +{ + GlobalBrightness_destroy((GlobalBrightness *)self); } diff --git a/VortexEngine/src/Menus/MenuList/GlobalBrightness.h b/VortexEngine/src/Menus/MenuList/GlobalBrightness.h index 5efabf408a..85ed605eae 100644 --- a/VortexEngine/src/Menus/MenuList/GlobalBrightness.h +++ b/VortexEngine/src/Menus/MenuList/GlobalBrightness.h @@ -1,58 +1,47 @@ #ifndef GLOBAL_BRIGHTNESS_H #define GLOBAL_BRIGHTNESS_H +#include #include "../Menu.h" -class GlobalBrightness : public Menu -{ -public: - GlobalBrightness(const RGBColor &col, bool advanced); - ~GlobalBrightness(); - - bool init() override; - MenuAction run() override; - - // handlers for clicks - void onShortClick() override; - void onLongClick() override; - -private: - void showBrightnessSelection(); - - // the list of brightness options - const uint8_t m_brightnessOptions[4] = { - BRIGHTNESS_OPTION_1, - BRIGHTNESS_OPTION_2, - BRIGHTNESS_OPTION_3, - BRIGHTNESS_OPTION_4 - }; +// the keychain mode sub states +typedef uint8_t keychain_mode_state; +enum { + // sleeping / fake off + KEYCHAIN_MODE_STATE_OFF = 0, + // solid/tracer + KEYCHAIN_MODE_STATE_SOLID, + // dops blink 4 / 16 + KEYCHAIN_MODE_STATE_DOPS, + // signal blink 16 / 120 + KEYCHAIN_MODE_STATE_SIGNAL, + // total states + KEYCHAIN_MODE_STATE_COUNT +}; -private: - // don't worry about this stuff - enum keychain_mode_state : uint8_t - { - // sleeping / fake off - KEYCHAIN_MODE_STATE_OFF = 0, +typedef struct GlobalBrightness +{ + Menu m_base; + keychain_mode_state m_keychain_modeState; + uint32_t m_lastStateChange; + uint8_t m_colorIndex; +} GlobalBrightness; - // solid/tracer - KEYCHAIN_MODE_STATE_SOLID, +extern const MenuVtable GlobalBrightness_vtable; - // dops blink 4 / 16 - KEYCHAIN_MODE_STATE_DOPS, +GlobalBrightness *GlobalBrightness_new(const RGBColor *col, bool advanced); +void GlobalBrightness_destroy(GlobalBrightness *self); - // signal blink 16 / 120 - KEYCHAIN_MODE_STATE_SIGNAL, +bool GlobalBrightness_init(GlobalBrightness *self); +MenuAction GlobalBrightness_run(GlobalBrightness *self); - // total states - KEYCHAIN_MODE_STATE_COUNT - }; +// handlers for clicks +void GlobalBrightness_onShortClick(GlobalBrightness *self); +void GlobalBrightness_onLongClick(GlobalBrightness *self); - keychain_mode_state m_keychain_modeState; - uint32_t m_lastStateChange; - uint8_t m_colorIndex; +void GlobalBrightness_showBrightnessSelection(GlobalBrightness *self); - void setKeychainModeState(keychain_mode_state newState); - Menu::MenuAction runKeychainMode(); -}; +void GlobalBrightness_setKeychainModeState(GlobalBrightness *self, keychain_mode_state newState); +MenuAction GlobalBrightness_runKeychainMode(GlobalBrightness *self); -#endif +#endif \ No newline at end of file diff --git a/VortexEngine/src/Menus/MenuList/ModeSharing.cpp b/VortexEngine/src/Menus/MenuList/ModeSharing.cpp index dfee67f5e5..e41b3a2176 100644 --- a/VortexEngine/src/Menus/MenuList/ModeSharing.cpp +++ b/VortexEngine/src/Menus/MenuList/ModeSharing.cpp @@ -1,4 +1,5 @@ #include "ModeSharing.h" +#include "../Menus.h" #include "../../VortexEngine.h" #include "../../Serial/ByteStream.h" @@ -13,172 +14,231 @@ #include "../../Modes/Mode.h" #include "../../Leds/Leds.h" #include "../../Log/Log.h" +#include "../../Memory/Memory.h" -ModeSharing::ModeSharing(const RGBColor &col, bool advanced) : - Menu(col, advanced), - m_sharingMode(ModeShareState::SHARE_SEND_RECEIVE), - m_timeOutStartTime(0), - m_lastPercentChange(0), - m_lastPercent(0) +static void ModeSharing_onLedSelectedHook(void *self); +static void ModeSharing_destroyHook(void *self); +static bool ModeSharing_initHook(void *self); +static MenuAction ModeSharing_runHook(void *self); +static void ModeSharing_onShortClickHook(void *self); +static void ModeSharing_onLongClickHook(void *self); +const MenuVtable ModeSharing_vtable = { + ModeSharing_initHook, + ModeSharing_runHook, + ModeSharing_onLedSelectedHook, + ModeSharing_onShortClickHook, + ModeSharing_onLongClickHook, + NULL, + ModeSharing_destroyHook, +}; + +ModeSharing *ModeSharing_new(const RGBColor *col, bool advanced) { + ModeSharing *self = (ModeSharing *)Menus_allocMenu(); + if (!self) { + return NULL; + } + Menu_initBase(&self->m_base, &ModeSharing_vtable); + RGBColor_setRGB(&self->m_base.m_menuColor, col); + self->m_base.m_advanced = advanced; + self->m_sharingMode = SHARE_SEND_RECEIVE; + self->m_timeOutStartTime = 0; + self->m_lastPercentChange = 0; + self->m_lastPercent = 0; + return self; } -ModeSharing::~ModeSharing() +void ModeSharing_destroy(ModeSharing *self) { - VLReceiver::endReceiving(); - VortexEngine::toggleForceSleep(true); + VLReceiver_endReceiving(); + VortexEngine_toggleForceSleep(true); + Menu_baseDestroy(&self->m_base); } -bool ModeSharing::init() +bool ModeSharing_init(ModeSharing *self) { - if (!Menu::init()) { + Menu *menu = &self->m_base; + if (!Menu_baseInit(menu)) { return false; } // skip led selection - if (!m_advanced) { + if (!menu->m_advanced) { // skip led selection - m_ledSelected = true; + menu->m_ledSelected = true; } // start on receive because it's the more responsive of the two // the odds of opening receive and then accidentally receiving // a mode that is being broadcast nearby is completely unlikely - VLReceiver::beginReceiving(); + VLReceiver_beginReceiving(); // turn off force sleep while using modesharing - VortexEngine::toggleForceSleep(false); + VortexEngine_toggleForceSleep(false); DEBUG_LOG("Entering Mode Sharing"); return true; } -Menu::MenuAction ModeSharing::run() +MenuAction ModeSharing_run(ModeSharing *self) { - MenuAction result = Menu::run(); + Menu *menu = &self->m_base; + MenuAction result = Menu_runBase(menu); if (result != MENU_CONTINUE) { return result; } - if (m_sharingMode == ModeShareState::SHARE_EXIT) { - showExit(); + if (self->m_sharingMode == SHARE_EXIT) { + Menu_showExit(menu); return MENU_CONTINUE; } // if the button is held for at least a long click, then continuously send - if (g_pButton->isPressed() && g_pButton->holdDuration() >= CLICK_THRESHOLD) { + if (Button_isPressed(g_pButton) && Button_holdDuration(g_pButton) >= CLICK_THRESHOLD) { // exit is handled above so the only two options are normal or legacy - if (m_sharingMode == ModeShareState::SHARE_SEND_RECEIVE) { + if (self->m_sharingMode == SHARE_SEND_RECEIVE) { // send normal - VLSender::send(&m_previewMode); + VLSender_send(&menu->m_previewMode); } else { // send legacy - VLSender::sendLegacy(&m_previewMode); + VLSender_sendLegacy(&menu->m_previewMode); } } // render the 'receive mode' lights whether legacy or not - showReceiveMode(); - receiveMode(); + ModeSharing_showReceiveMode(self); + ModeSharing_receiveMode(self); return MENU_CONTINUE; } -void ModeSharing::onLedSelected() +void ModeSharing_onLedSelected(ModeSharing *self) { + Menu *menu = &self->m_base; // if we selected leds that implies advanced mode - if (m_targetLeds == MAP_LED(LED_1)) { + if (menu->m_targetLeds == MAP_LED(LED_1)) { // if we selected the top led then simply swap the two patterns // so that the top led is sent first -- if the receiver is receiving // into one slot then it will only use the first pattern to do so - m_previewMode.swapPatterns(LED_0, LED_1); + Mode_swapPatterns(&menu->m_previewMode, LED_0, LED_1); } } // handlers for clicks -void ModeSharing::onShortClick() +void ModeSharing_onShortClick(ModeSharing *self) { - if (g_pButton->holdDuration() >= CLICK_THRESHOLD) { + if (Button_holdDuration(g_pButton) >= CLICK_THRESHOLD) { return; } - switch (m_sharingMode) { - case ModeShareState::SHARE_SEND_RECEIVE: + switch (self->m_sharingMode) { + case SHARE_SEND_RECEIVE: // stop receiving, send the mode, go back to receiving - VLReceiver::setLegacyReceiver(true); - m_sharingMode = ModeShareState::SHARE_SEND_RECEIVE_LEGACY; + VLReceiver_setLegacyReceiver(true); + self->m_sharingMode = SHARE_SEND_RECEIVE_LEGACY; break; - case ModeShareState::SHARE_SEND_RECEIVE_LEGACY: - VLReceiver::setLegacyReceiver(false); - m_sharingMode = ModeShareState::SHARE_EXIT; + case SHARE_SEND_RECEIVE_LEGACY: + VLReceiver_setLegacyReceiver(false); + self->m_sharingMode = SHARE_EXIT; break; - case ModeShareState::SHARE_EXIT: - m_sharingMode = ModeShareState::SHARE_SEND_RECEIVE; + case SHARE_EXIT: + self->m_sharingMode = SHARE_SEND_RECEIVE; break; default: break; } - Leds::clearAll(); + Leds_clearAll(); } -void ModeSharing::onLongClick() +void ModeSharing_onLongClick(ModeSharing *self) { - if (m_sharingMode == ModeShareState::SHARE_EXIT) { - leaveMenu(); + if (self->m_sharingMode == SHARE_EXIT) { + Menu_leaveMenu(&self->m_base, false); } } -void ModeSharing::receiveMode() +void ModeSharing_receiveMode(ModeSharing *self) { - uint32_t now = Time::getCurtime(); + Menu *menu = &self->m_base; + uint32_t now = Time_getCurtime(); // if reveiving new data set our last data time - if (VLReceiver::onNewData()) { - m_timeOutStartTime = now; + if (VLReceiver_onNewData()) { + self->m_timeOutStartTime = now; // if our last data was more than time out duration reset the recveiver - } else if (m_timeOutStartTime > 0 && (m_timeOutStartTime + MAX_TIMEOUT_DURATION) < now) { - VLReceiver::resetVLState(); - m_timeOutStartTime = 0; + } else if (self->m_timeOutStartTime > 0 && (self->m_timeOutStartTime + MAX_TIMEOUT_DURATION) < now) { + VLReceiver_resetVLState(); + self->m_timeOutStartTime = 0; return; } // check if the VLReceiver has a full packet available - if (!VLReceiver::dataReady()) { - uint8_t percent = VLReceiver::percentReceived(); - if (percent != m_lastPercent) { - m_lastPercent = percent; - m_lastPercentChange = now; + if (!VLReceiver_dataReady()) { + uint8_t percent = VLReceiver_percentReceived(); + if (percent != self->m_lastPercent) { + self->m_lastPercent = percent; + self->m_lastPercentChange = now; } // nothing available yet return; } DEBUG_LOG("Mode ready to receive! Receiving..."); // receive the VL mode into the current mode - if (!VLReceiver::receiveMode(&m_previewMode)) { + if (!VLReceiver_receiveMode(&menu->m_previewMode)) { ERROR_LOG("Failed to receive mode"); return; } - DEBUG_LOGF("Success receiving mode: %u", m_previewMode.getPatternID()); - if (m_advanced && m_targetLeds != MAP_LED_ALL) { - LedPos target = ledmapGetFirstLed(m_targetLeds); + DEBUG_LOGF("Success receiving mode: %u", Mode_getPatternID(&menu->m_previewMode, LED_FIRST)); + if (menu->m_advanced && menu->m_targetLeds != MAP_LED_ALL) { + LedPos target = ledmapGetFirstLed(menu->m_targetLeds); LedPos other = LED_1; // if the user picked the top led to copy into then swap the patterns // in the incoming mode so the 0th pattern is on the top led if (target == LED_1) { other = LED_0; - m_previewMode.swapPatterns(LED_0, LED_1); + Mode_swapPatterns(&menu->m_previewMode, LED_0, LED_1); } - m_previewMode.copyPatternFrom(Modes::curMode(), other, other); + Mode_copyPatternFrom(&menu->m_previewMode, Modes_curMode(), other, other); } - Modes::updateCurMode(&m_previewMode); + Modes_updateCurMode(&menu->m_previewMode); // leave menu and save settings, even if the mode was the same whatever - leaveMenu(true); + Menu_leaveMenu(menu, true); } -void ModeSharing::showReceiveMode() +void ModeSharing_showReceiveMode(ModeSharing *self) { // if the receiver is actively receiving right now then - if (VLReceiver::isReceiving()) { - uint32_t diff = (Time::getCurtime() - m_lastPercentChange); + if (VLReceiver_isReceiving()) { + uint32_t diff = (Time_getCurtime() - self->m_lastPercentChange); // this generates the red flash when the receiver hasn't received something for // some amount of time, 100 is just arbitray idk if it could be a better value if (diff > 100) { - Leds::setIndex(LED_0, RGB_RED3); + Leds_setIndex(LED_0, RGB_RED3); } else { - Leds::setIndex(LED_0, RGBColor(0, VLReceiver::percentReceived(), 0)); + Leds_setIndex(LED_0, RGBColor_makeRGB(0, VLReceiver_percentReceived(), 0)); } - Leds::clearIndex(LED_1); + Leds_clearIndex(LED_1); } else { - Leds::setAll((m_sharingMode == ModeShareState::SHARE_SEND_RECEIVE) ? 0x000F05 : RGB_WHITE0); + Leds_setAll((self->m_sharingMode == SHARE_SEND_RECEIVE) ? RGBColor_fromU32(0x000F05) : RGB_WHITE0); } } + +static void ModeSharing_onLedSelectedHook(void *self) +{ + ModeSharing_onLedSelected((ModeSharing *)self); +} + +static void ModeSharing_onShortClickHook(void *self) +{ + ModeSharing_onShortClick((ModeSharing *)self); +} + +static void ModeSharing_onLongClickHook(void *self) +{ + ModeSharing_onLongClick((ModeSharing *)self); +} + +static bool ModeSharing_initHook(void *self) +{ + return ModeSharing_init((ModeSharing *)self); +} + +static MenuAction ModeSharing_runHook(void *self) +{ + return ModeSharing_run((ModeSharing *)self); +} + +static void ModeSharing_destroyHook(void *self) +{ + ModeSharing_destroy((ModeSharing *)self); +} diff --git a/VortexEngine/src/Menus/MenuList/ModeSharing.h b/VortexEngine/src/Menus/MenuList/ModeSharing.h index 826b08fa06..01e1c7f326 100644 --- a/VortexEngine/src/Menus/MenuList/ModeSharing.h +++ b/VortexEngine/src/Menus/MenuList/ModeSharing.h @@ -1,44 +1,45 @@ #ifndef MODE_SHARING_H #define MODE_SHARING_H +#include #include "../Menu.h" -class ModeSharing : public Menu -{ -public: - ModeSharing(const RGBColor &col, bool advanced); - ~ModeSharing(); - - bool init() override; - MenuAction run() override; - - // callback after the user selects the target led - void onLedSelected() override; - - // handlers for clicks - void onShortClick() override; - void onLongClick() override; - -private: - void receiveMode(); - void showReceiveMode(); - - enum class ModeShareState { - // these three are the main options that can be iterated through - // and if the button is held it will send, otherwise receive - SHARE_SEND_RECEIVE, // send/receive mode - SHARE_SEND_RECEIVE_LEGACY, // send/receive mode legacy - SHARE_EXIT, // exit mode sharing - }; +// these three are the main options that can be iterated through +// and if the button is held it will send, otherwise receive +typedef uint8_t ModeShareState; +enum { + SHARE_SEND_RECEIVE, // send/receive mode + SHARE_SEND_RECEIVE_LEGACY, // send/receive mode legacy + SHARE_EXIT, // exit mode sharing +}; +typedef struct ModeSharing +{ + Menu m_base; ModeShareState m_sharingMode; - // the start time when checking for timing out uint32_t m_timeOutStartTime; - // used to track when the receive percentage changes for timeout purposes uint32_t m_lastPercentChange; uint8_t m_lastPercent; -}; +} ModeSharing; + +extern const MenuVtable ModeSharing_vtable; + +ModeSharing *ModeSharing_new(const RGBColor *col, bool advanced); +void ModeSharing_destroy(ModeSharing *self); + +bool ModeSharing_init(ModeSharing *self); +MenuAction ModeSharing_run(ModeSharing *self); + +// callback after the user selects the target led +void ModeSharing_onLedSelected(ModeSharing *self); + +// handlers for clicks +void ModeSharing_onShortClick(ModeSharing *self); +void ModeSharing_onLongClick(ModeSharing *self); + +void ModeSharing_receiveMode(ModeSharing *self); +void ModeSharing_showReceiveMode(ModeSharing *self); -#endif +#endif \ No newline at end of file diff --git a/VortexEngine/src/Menus/MenuList/PatternSelect.cpp b/VortexEngine/src/Menus/MenuList/PatternSelect.cpp index 227a69f835..b25e082c3f 100644 --- a/VortexEngine/src/Menus/MenuList/PatternSelect.cpp +++ b/VortexEngine/src/Menus/MenuList/PatternSelect.cpp @@ -1,4 +1,5 @@ #include "PatternSelect.h" +#include "../Menus.h" #include "../../Patterns/PatternBuilder.h" #include "../../Patterns/PatternArgs.h" @@ -12,69 +13,128 @@ #include "../../Menus/Menus.h" #include "../../Leds/Leds.h" #include "../../Log/Log.h" +#include "../../Memory/Memory.h" -PatternSelect::PatternSelect(const RGBColor &col, bool advanced) : - Menu(col, advanced), - m_srcLed(LED_FIRST), - m_started(false) +// the derived hooks, dispatched through the base Menu vtable +static void PatternSelect_onLedSelectedHook(void *self); +static void PatternSelect_destroyHook(void *self); +static bool PatternSelect_initHook(void *self); +static MenuAction PatternSelect_runHook(void *self); +static void PatternSelect_onShortClickHook(void *self); +static void PatternSelect_onLongClickHook(void *self); + +const MenuVtable PatternSelect_vtable = { + PatternSelect_initHook, + PatternSelect_runHook, + PatternSelect_onLedSelectedHook, + PatternSelect_onShortClickHook, + PatternSelect_onLongClickHook, + NULL, + PatternSelect_destroyHook, +}; + +PatternSelect *PatternSelect_new(const RGBColor *col, bool advanced) { + PatternSelect *self = (PatternSelect *)Menus_allocMenu(); + if (!self) { + return NULL; + } + Menu_initBase(&self->m_base, &PatternSelect_vtable); + RGBColor_setRGB(&self->m_base.m_menuColor, col); + self->m_base.m_advanced = advanced; + self->m_srcLed = LED_FIRST; + self->m_started = false; + return self; } -PatternSelect::~PatternSelect() +void PatternSelect_destroy(PatternSelect *self) { + Menu_baseDestroy(&self->m_base); } -bool PatternSelect::init() +bool PatternSelect_init(PatternSelect *self) { - if (!Menu::init()) { + if (!Menu_baseInit(&self->m_base)) { return false; } DEBUG_LOG("Entered pattern select"); return true; } -Menu::MenuAction PatternSelect::run() +MenuAction PatternSelect_run(PatternSelect *self) { - MenuAction result = Menu::run(); + Menu *menu = &self->m_base; + MenuAction result = Menu_runBase(menu); if (result != MENU_CONTINUE) { return result; } // run the current mode - m_previewMode.play(); + Mode_play(&menu->m_previewMode); // show dimmer selections in advanced mode - Menus::showSelection(RGB_WHITE5); + Menus_showSelection(RGB_WHITE5); return MENU_CONTINUE; } -void PatternSelect::onLedSelected() +void PatternSelect_onLedSelected(PatternSelect *self) { - m_srcLed = ledmapGetFirstLed(m_targetLeds); + self->m_srcLed = ledmapGetFirstLed(self->m_base.m_targetLeds); } -void PatternSelect::onShortClick() +void PatternSelect_onShortClick(PatternSelect *self) { - PatternID newID = (PatternID)(m_previewMode.getPatternID(m_srcLed) + 1); + Menu *menu = &self->m_base; + PatternID newID = (PatternID)(Mode_getPatternID(&menu->m_previewMode, self->m_srcLed) + 1); if (newID > PATTERN_SINGLE_LAST) { newID = PATTERN_SINGLE_FIRST; - Leds::holdAll(RGB_WHITE); + Leds_holdAll(RGB_WHITE); } - if (!m_started) { - m_started = true; + if (!self->m_started) { + self->m_started = true; newID = PATTERN_FIRST; } // set the new pattern id if (isMultiLedPatternID(newID)) { - m_previewMode.setPattern(newID); + Mode_setPattern(&menu->m_previewMode, newID, LED_FIRST, NULL, NULL); } else { - m_previewMode.setPatternMap(m_targetLeds, newID); + Mode_setPatternMap(&menu->m_previewMode, menu->m_targetLeds, newID, NULL, NULL); } - m_previewMode.init(); + Mode_initPatterns(&menu->m_previewMode); DEBUG_LOGF("Iterated to pattern id %d", newID); } -void PatternSelect::onLongClick() +void PatternSelect_onLongClick(PatternSelect *self) { // store the mode as current mode - Modes::updateCurMode(&m_previewMode); - leaveMenu(true); + Modes_updateCurMode(&self->m_base.m_previewMode); + Menu_leaveMenu(&self->m_base, true); +} + +static void PatternSelect_onLedSelectedHook(void *self) +{ + PatternSelect_onLedSelected((PatternSelect *)self); +} + +static void PatternSelect_onShortClickHook(void *self) +{ + PatternSelect_onShortClick((PatternSelect *)self); +} + +static void PatternSelect_onLongClickHook(void *self) +{ + PatternSelect_onLongClick((PatternSelect *)self); +} + +static bool PatternSelect_initHook(void *self) +{ + return PatternSelect_init((PatternSelect *)self); +} + +static MenuAction PatternSelect_runHook(void *self) +{ + return PatternSelect_run((PatternSelect *)self); +} + +static void PatternSelect_destroyHook(void *self) +{ + PatternSelect_destroy((PatternSelect *)self); } diff --git a/VortexEngine/src/Menus/MenuList/PatternSelect.h b/VortexEngine/src/Menus/MenuList/PatternSelect.h index e825f56b54..76c4e43ccc 100644 --- a/VortexEngine/src/Menus/MenuList/PatternSelect.h +++ b/VortexEngine/src/Menus/MenuList/PatternSelect.h @@ -1,34 +1,36 @@ #ifndef PATTERN_SELECT_H #define PATTERN_SELECT_H +#include #include "../Menu.h" #include "../../Colors/Colorset.h" #include "../../Modes/Mode.h" -class PatternSelect : public Menu +typedef struct PatternSelect { -public: - PatternSelect(const RGBColor &col, bool advanced); - ~PatternSelect(); - - bool init() override; - MenuAction run() override; - - // callback after the user selects the target led - void onLedSelected() override; - - // handlers for clicks - void onShortClick() override; - void onLongClick() override; - -private: + Menu m_base; // helpful member LedPos m_srcLed; - // the pat select starts by showing the current pattern // then the first click begin cycling the list of pats bool m_started; -}; +} PatternSelect; + +// vtable wiring the derived menu back into the Menu base +extern const MenuVtable PatternSelect_vtable; + +PatternSelect *PatternSelect_new(const RGBColor *col, bool advanced); +void PatternSelect_destroy(PatternSelect *self); + +bool PatternSelect_init(PatternSelect *self); +MenuAction PatternSelect_run(PatternSelect *self); + +// callback after the user selects the target led +void PatternSelect_onLedSelected(PatternSelect *self); + +// handlers for clicks +void PatternSelect_onShortClick(PatternSelect *self); +void PatternSelect_onLongClick(PatternSelect *self); -#endif +#endif \ No newline at end of file diff --git a/VortexEngine/src/Menus/MenuList/Randomizer.cpp b/VortexEngine/src/Menus/MenuList/Randomizer.cpp index ffa65bd7db..e6f5e3a486 100644 --- a/VortexEngine/src/Menus/MenuList/Randomizer.cpp +++ b/VortexEngine/src/Menus/MenuList/Randomizer.cpp @@ -1,11 +1,14 @@ #include "Randomizer.h" +#include "../Menus.h" #include "../../Memory/Memory.h" #include "../../Patterns/PatternBuilder.h" #include "../../Patterns/Pattern.h" +#include "../../Patterns/PatternArgs.h" #include "../../Colors/Colorset.h" #include "../../Random/Random.h" +#include "../../Serial/ByteStream.h" #include "../../Time/Timings.h" #include "../../Buttons/Button.h" #include "../../Menus/Menus.h" @@ -14,171 +17,207 @@ #include "../../Leds/Leds.h" #include "../../Log/Log.h" -Randomizer::Randomizer(const RGBColor &col, bool advanced) : - Menu(col, advanced), - m_lastRandomization(0), - m_flags(advanced ? RANDOMIZE_COLORSET : RANDOMIZE_BOTH), - m_displayHue(0), - m_needToSelect(advanced), - m_autoCycle(false) +static void Randomizer_destroyHook(void *self); +static bool Randomizer_initHook(void *self); +static MenuAction Randomizer_runHook(void *self); +static void Randomizer_onShortClickHook(void *self); +static void Randomizer_onLongClickHook(void *self); + +const MenuVtable Randomizer_vtable = { + Randomizer_initHook, + Randomizer_runHook, + NULL, + Randomizer_onShortClickHook, + Randomizer_onLongClickHook, + NULL, + Randomizer_destroyHook, +}; + +Randomizer *Randomizer_new(const RGBColor *col, bool advanced) { + Randomizer *self = (Randomizer *)Menus_allocMenu(); + if (!self) { + return NULL; + } + Menu_initBase(&self->m_base, &Randomizer_vtable); + RGBColor_setRGB(&self->m_base.m_menuColor, col); + self->m_base.m_advanced = advanced; + self->m_lastRandomization = 0; + self->m_flags = advanced ? RANDOMIZE_COLORSET : RANDOMIZE_BOTH; + self->m_displayHue = 0; + self->m_needToSelect = advanced; + self->m_autoCycle = false; + return self; } -Randomizer::~Randomizer() +void Randomizer_destroy(Randomizer *self) { + Menu_baseDestroy(&self->m_base); } -bool Randomizer::init() +bool Randomizer_init(Randomizer *self) { - if (!Menu::init()) { + Menu *menu = &self->m_base; + if (!Menu_baseInit(menu)) { return false; } // grab the multi ld pattern colorset crc if it's present - Mode *cur = Modes::curMode(); + Mode *cur = Modes_curMode(); #if VORTEX_SLIM == 0 - if (cur->hasMultiLed()) { + if (Mode_hasMultiLed(cur)) { ByteStream ledData; - Pattern *pat = cur->getPattern(LED_MULTI); + ByteStream_make(&ledData); + const Pattern *pat = Mode_getPattern(cur, LED_MULTI); if (pat) { - if (!pat->serialize(ledData)) { + if (!Pattern_serialize(pat, &ledData)) { + ByteStream_destroy(&ledData); return false; } } - m_multiRandCtx.seed(ledData.recalcCRC()); + Random_seed(&self->m_multiRandCtx, ByteStream_recalcCRC(&ledData, false)); + ByteStream_destroy(&ledData); } #endif // initialize the randomseed of each led with the // CRC of the colorset on the respective LED for (LedPos l = LED_FIRST; l < LED_COUNT; ++l) { ByteStream ledData; - Pattern *pat = cur->getPattern(l); + ByteStream_make(&ledData); + const Pattern *pat = Mode_getPattern(cur, l); if (pat) { - if (!pat->serialize(ledData)) { + if (!Pattern_serialize(pat, &ledData)) { + ByteStream_destroy(&ledData); return false; } } - m_singlesRandCtx[l].seed(ledData.recalcCRC()); + Random_seed(&self->m_singlesRandCtx[l], ByteStream_recalcCRC(&ledData, false)); + ByteStream_destroy(&ledData); } DEBUG_LOG("Entered randomizer"); return true; } -Menu::MenuAction Randomizer::run() +MenuAction Randomizer_run(Randomizer *self) { + Menu *menu = &self->m_base; // run the base menu logic - MenuAction result = Menu::run(); + MenuAction result = Menu_runBase(menu); if (result != MENU_CONTINUE) { return result; } // if the ranomization flags haven't been set yet just show a selection - if (m_needToSelect) { + if (self->m_needToSelect) { // display the randomization selection menu - showRandomizationSelect(); + Randomizer_showRandomizationSelect(self); return MENU_CONTINUE; } #if VORTEX_SLIM == 0 // if they are trying to randomize a multi-led pattern just convert // the pattern to all singles with the same colorset upon entry - if (m_previewMode.isMultiLed() && m_targetLeds != MAP_LED(LED_MULTI)) { + if (Mode_isMultiLed(&menu->m_previewMode) && menu->m_targetLeds != MAP_LED(LED_MULTI)) { // convert the pattern to a single led pattern, this will map // all multi led patterns to single led patterns using modulo // so no matter which multi-led pattern they have selected it // will convert to a single led pattern of some kind - PatternID newID = (PatternID)((m_previewMode.getPatternID() - PATTERN_MULTI_FIRST) % PATTERN_SINGLE_COUNT); + PatternID newID = (PatternID)((Mode_getPatternID(&menu->m_previewMode, LED_ANY) - PATTERN_MULTI_FIRST) % PATTERN_SINGLE_COUNT); // solid sucks if (newID == PATTERN_SOLID) ++newID; - m_previewMode.setPattern(newID); - m_previewMode.init(); + Mode_setPattern(&menu->m_previewMode, newID, LED_ANY, NULL, NULL); + Mode_initPatterns(&menu->m_previewMode); } #endif - uint32_t now = Time::getCurtime(); - if (m_autoCycle && (m_lastRandomization + AUTO_RANDOM_DELAY_TICKS < now)) { - m_lastRandomization = now; - reRoll(); + uint32_t now = Time_getCurtime(); + if (self->m_autoCycle && (self->m_lastRandomization + AUTO_RANDOM_DELAY_TICKS < now)) { + self->m_lastRandomization = now; + Randomizer_reRoll(self); } // display the randomized mode - m_previewMode.play(); + Mode_play(&menu->m_previewMode); // show the selection - Menus::showSelection(); + Menus_showSelection(RGB_WHITE5); // return true to continue staying in randomizer menu return MENU_CONTINUE; } -void Randomizer::onShortClick() +void Randomizer_onShortClick(Randomizer *self) { - if (m_needToSelect) { - if (m_flags == RANDOMIZE_BOTH) { - m_flags = RANDOMIZE_COLORSET; + if (self->m_needToSelect) { + if (self->m_flags == RANDOMIZE_BOTH) { + self->m_flags = RANDOMIZE_COLORSET; } else { - m_flags = (RandomizeFlags)(m_flags + 1); + self->m_flags = (uint8_t)(self->m_flags + 1); } return; } // if the user fast-clicks 3 times then toggle automode - if (m_autoCycle || g_pButton->onConsecutivePresses(AUTO_CYCLE_RANDOMIZER_CLICKS)) { + if (self->m_autoCycle || Button_onConsecutivePresses(g_pButton, AUTO_CYCLE_RANDOMIZER_CLICKS)) { // toggle the auto cycle flag - m_autoCycle = !m_autoCycle; + self->m_autoCycle = !self->m_autoCycle; // display a quick flash of either green or red to indicate whether auto mode is on or not - Leds::holdAll(m_autoCycle ? RGB_GREEN : RGB_RED); + Leds_holdAll(self->m_autoCycle ? RGB_GREEN : RGB_RED); return; } // shortClick re-roll the randomization - reRoll(); + Randomizer_reRoll(self); } -void Randomizer::onLongClick() +void Randomizer_onLongClick(Randomizer *self) { + Menu *menu = &self->m_base; // if done the randomization selection part - if (m_needToSelect) { + if (self->m_needToSelect) { // we are complete the randomization selection stage - m_needToSelect = false; + self->m_needToSelect = false; return; } // update the current mode with the new randomized mode - Modes::updateCurMode(&m_previewMode); + Modes_updateCurMode(&menu->m_previewMode); // then done here, save if the mode was different - leaveMenu(true); + Menu_leaveMenu(menu, true); } -bool Randomizer::reRoll() +bool Randomizer_reRoll(Randomizer *self) { + Menu *menu = &self->m_base; #if VORTEX_SLIM == 0 - if (m_targetLeds == MAP_LED(LED_MULTI)) { - if (!reRollMulti()) { + if (menu->m_targetLeds == MAP_LED(LED_MULTI)) { + if (!Randomizer_reRollMulti(self)) { return false; } - } else if (!reRollSingles()) { + } else if (!Randomizer_reRollSingles(self)) { return false; } #else - if (!reRollSingles()) { + if (!Randomizer_reRollSingles(self)) { return false; } #endif // initialize the mode with the new pattern and colorset - m_previewMode.init(); + Mode_initPatterns(&menu->m_previewMode); return true; } -void Randomizer::showRandomizationSelect() +void Randomizer_showRandomizationSelect(Randomizer *self) { + HSVColor hsv = HSVColor_makeHSV(self->m_displayHue++, (self->m_flags & RANDOMIZE_COLORSET) * 255, 84); // show iterating rainbow if they are randomizing color, otherwise 0 sat if they // are only randomizing the pattern - Leds::setAll(HSVColor(m_displayHue++, (m_flags & RANDOMIZE_COLORSET) * 255, 84)); - if (m_flags & RANDOMIZE_PATTERN) { + Leds_setAll(RGBColor_fromHSV(&hsv)); + if (self->m_flags & RANDOMIZE_PATTERN) { // this is blinking the light to off so the params are switched but still effectively correct - Leds::blinkAll(DOPS_ON_DURATION, DOPS_OFF_DURATION); + Leds_blinkAll(DOPS_ON_DURATION, DOPS_OFF_DURATION, RGB_OFF); } // indicate on the 2nd led whether the button is pressed - Leds::setIndex(LED_1, g_pButton->isPressed() ? RGB_OFF : RGB_WHITE1); + Leds_setIndex(LED_1, Button_isPressed(g_pButton) ? RGB_OFF : RGB_WHITE1); // render the click selection blink - Menus::showSelection(); + Menus_showSelection(RGB_WHITE5); } #if VORTEX_SLIM == 0 -bool Randomizer::reRollMulti() +bool Randomizer_reRollMulti(Randomizer *self) { - if (m_flags & RANDOMIZE_PATTERN) { + Menu *menu = &self->m_base; + if (self->m_flags & RANDOMIZE_PATTERN) { // TODO: Roll custom multi pattern? //if (m_advanced) { // if (!rollCustomPattern(ctx, &m_previewMode, pos)) { @@ -186,13 +225,14 @@ bool Randomizer::reRollMulti() // return false; // } //} - if (!m_previewMode.setPattern(rollMultiLedPatternID(m_multiRandCtx), LED_MULTI)) { + if (!Mode_setPattern(&menu->m_previewMode, Randomizer_rollMultiLedPatternID(self, &self->m_multiRandCtx), LED_MULTI, NULL, NULL)) { ERROR_LOG("Failed to select pattern"); return false; } } - if (m_flags & RANDOMIZE_COLORSET) { - if (!m_previewMode.setColorset(rollColorset(m_multiRandCtx), LED_MULTI)) { + if (self->m_flags & RANDOMIZE_COLORSET) { + Colorset set = Randomizer_rollColorset(self, &self->m_multiRandCtx); + if (!Mode_setColorset(&menu->m_previewMode, &set, LED_MULTI)) { ERROR_LOG("Failed to roll new colorset"); return false; } @@ -200,34 +240,36 @@ bool Randomizer::reRollMulti() return true; } -PatternID Randomizer::rollMultiLedPatternID(Random &ctx) +PatternID Randomizer_rollMultiLedPatternID(Randomizer *self, Random *ctx) { - return (PatternID)ctx.next8(PATTERN_MULTI_FIRST, PATTERN_MULTI_LAST); + return (PatternID)Random_next8(ctx, PATTERN_MULTI_FIRST, PATTERN_MULTI_LAST); } #endif -bool Randomizer::reRollSingles() +bool Randomizer_reRollSingles(Randomizer *self) { + Menu *menu = &self->m_base; // re-roll each led position with it's respective random context - MAP_FOREACH_LED(m_targetLeds) { - Random &ctx = m_singlesRandCtx[pos]; - if (m_flags & RANDOMIZE_PATTERN) { + MAP_FOREACH_LED(menu->m_targetLeds) { + Random *ctx = &self->m_singlesRandCtx[pos]; + if (self->m_flags & RANDOMIZE_PATTERN) { // in advanced mode, when not randomizing the multi position, use a // special function to randomize totally custom led pattern timings - if (m_advanced) { - if (!rollCustomPattern(ctx, &m_previewMode, pos)) { + if (menu->m_advanced) { + if (!Randomizer_rollCustomPattern(self, ctx, &menu->m_previewMode, pos)) { ERROR_LOG("Failed to roll custom pattern"); return false; } } else { - if (!m_previewMode.setPattern(rollSingleLedPatternID(ctx), pos)) { + if (!Mode_setPattern(&menu->m_previewMode, Randomizer_rollSingleLedPatternID(self, ctx), pos, NULL, NULL)) { ERROR_LOG("Failed to select pattern"); return false; } } } - if (m_flags & RANDOMIZE_COLORSET) { - if (!m_previewMode.setColorset(rollColorset(ctx), pos)) { + if (self->m_flags & RANDOMIZE_COLORSET) { + Colorset set = Randomizer_rollColorset(self, ctx); + if (!Mode_setColorset(&menu->m_previewMode, &set, pos)) { ERROR_LOG("Failed to roll new colorset"); return false; } @@ -236,155 +278,182 @@ bool Randomizer::reRollSingles() return true; } -PatternID Randomizer::rollSingleLedPatternID(Random &ctx) +PatternID Randomizer_rollSingleLedPatternID(Randomizer *self, Random *ctx) { PatternID newPat; // the random range begin/end do { // continuously re-randomize the pattern so we don't get undesirable patterns - newPat = (PatternID)ctx.next8(PATTERN_SINGLE_FIRST, PATTERN_SINGLE_LAST); + newPat = (PatternID)Random_next8(ctx, PATTERN_SINGLE_FIRST, PATTERN_SINGLE_LAST); } while (newPat == PATTERN_SOLID || newPat == PATTERN_RIBBON || newPat == PATTERN_MINIRIBBON); return newPat; } -Colorset Randomizer::rollColorset(Random &ctx) +Colorset Randomizer_rollColorset(Randomizer *self, Random *ctx) { Colorset randomSet; + Colorset_make(&randomSet); // pick a random type of randomizer to use then use // the randomizer to generate a random colorset - uint8_t randType = ctx.next8(0, 8); + uint8_t randType = Random_next8(ctx, 0, 8); switch (randType) { default: case 0: - randomSet.randomize(ctx); + Colorset_randomize(&randomSet, ctx, 0); break; case 1: - randomSet.randomizeColors(ctx, 0, Colorset::ColorMode::THEORY); + Colorset_randomizeColors(&randomSet, ctx, 0, THEORY); break; case 2: - randomSet.randomizeColors(ctx, 0, Colorset::ColorMode::MONOCHROMATIC); + Colorset_randomizeColors(&randomSet, ctx, 0, MONOCHROMATIC); break; case 3: - randomSet.randomizeColors2(ctx, Colorset::ColorMode2::DOUBLE_SPLIT_COMPLIMENTARY); + Colorset_randomizeColors2(&randomSet, ctx, DOUBLE_SPLIT_COMPLIMENTARY); break; case 4: - randomSet.randomizeColors2(ctx, Colorset::ColorMode2::TETRADIC); + Colorset_randomizeColors2(&randomSet, ctx, TETRADIC); break; case 5: - randomSet.randomize(ctx, 1); + Colorset_randomize(&randomSet, ctx, 1); break; case 6: - randomSet.randomizeColors(ctx, 0, Colorset::ColorMode::EVENLY_SPACED); + Colorset_randomizeColors(&randomSet, ctx, 0, EVENLY_SPACED); break; case 7: - randomSet.randomizeColors(ctx, 2, Colorset::ColorMode::EVENLY_SPACED); + Colorset_randomizeColors(&randomSet, ctx, 2, EVENLY_SPACED); break; case 8: - randomSet.randomizeColors(ctx, 3, Colorset::ColorMode::EVENLY_SPACED); + Colorset_randomizeColors(&randomSet, ctx, 3, EVENLY_SPACED); break; } // take the randomized colorset and repeate colors to make an additional pattern - if (randomSet.numColors() <= 4 && ctx.next8(0, 1) != 0) { + if (Colorset_numColors(&randomSet) <= 4 && Random_next8(ctx, 0, 1) != 0) { // using uint8_t here actually costs more bytes than int16_t - int16_t startingNumColors = randomSet.numColors(); + int16_t startingNumColors = Colorset_numColors(&randomSet); for (int16_t i = 0; i < startingNumColors - 1; ++i) { // add a duplicate of each color in the colorset to in reverse - randomSet.addColor(randomSet.get(startingNumColors - (i + 2))); + Colorset_addColor(&randomSet, Colorset_get(&randomSet, startingNumColors - (i + 2))); } } return randomSet; } -bool Randomizer::rollCustomPattern(Random &ctx, Mode *pMode, LedPos pos) +bool Randomizer_rollCustomPattern(Randomizer *self, Random *ctx, Mode *pMode, LedPos pos) { PatternArgs args; + PatternArgs_init0(&args); // pick a random type of randomizer to use then use // the randomizer to generate a random pattern - uint8_t patternType = ctx.next8(0, 3); + uint8_t patternType = Random_next8(ctx, 0, 3); switch (patternType) { default: case 0: - traditionalPattern(ctx, args); + Randomizer_traditionalPattern(ctx, &args); break; case 1: - gapPattern(ctx, args); + Randomizer_gapPattern(ctx, &args); break; case 2: - dashPattern(ctx, args); + Randomizer_dashPattern(ctx, &args); break; case 3: - crushPattern(ctx, args); + Randomizer_crushPattern(ctx, &args); break; } PatternID newPat = PATTERN_STROBE; // 1/3 chance to roll a blend pattern instead which will animate between // colors instead of blinking each color in the set - if (!ctx.next8(0, 3)) { + if (!Random_next8(ctx, 0, 3)) { newPat = PATTERN_BLEND; // set the blend speed - args.arg6 = ctx.next8(1, 10); + args.arg6 = Random_next8(ctx, 1, 10); // 1/2 chance to make the blend a flipping blend of some kind - if (!ctx.next8(0, 2)) { + if (!Random_next8(ctx, 0, 2)) { // this is the number of blinks to a complementary color - args.arg7 = ctx.next8(0, 3); + args.arg7 = Random_next8(ctx, 0, 3); } // up to arg7 is filled now args.numArgs = 7; } - return pMode->setPattern(newPat, pos, &args); + return Mode_setPattern(pMode, newPat, pos, &args, NULL); } -void Randomizer::traditionalPattern(Random &ctx, PatternArgs &outArgs) +void Randomizer_traditionalPattern(Random *ctx, PatternArgs *outArgs) { // call next8 explicitly in this order because the order they // are called is undefined when called as parameters to another function. // ex: f(a,b,c) may call in the order a,b,c or c,b,a depending on compiler. // So different compilers may produce different results, // but like this it is explicit - uint8_t off = ctx.next8(8, 60); // off duration 0 -> 60 - uint8_t on = ctx.next8(1, 20); // on duration 1 -> 20 - outArgs.init(on, off); + uint8_t off = Random_next8(ctx, 8, 60); // off duration 0 -> 60 + uint8_t on = Random_next8(ctx, 1, 20); // on duration 1 -> 20 + PatternArgs_init(outArgs, on, off); } -void Randomizer::gapPattern(Random &ctx, PatternArgs &outArgs) +void Randomizer_gapPattern(Random *ctx, PatternArgs *outArgs) { // call next8 explicitly in this order because the order they // are called is undefined when called as parameters to another function. // ex: f(a,b,c) may call in the order a,b,c or c,b,a depending on compiler. // So different compilers may produce different results, // but like this it is explicit - uint8_t gap = ctx.next8(40, 100); // gap duration 40 -> 100 - uint8_t off = ctx.next8(0, 6); // off duration 0 -> 6 - uint8_t on = ctx.next8(1, 10); // on duration 1 -> 10 - outArgs.init(on, off, gap); + uint8_t gap = Random_next8(ctx, 40, 100); // gap duration 40 -> 100 + uint8_t off = Random_next8(ctx, 0, 6); // off duration 0 -> 6 + uint8_t on = Random_next8(ctx, 1, 10); // on duration 1 -> 10 + PatternArgs_init(outArgs, on, off, gap); } -void Randomizer::dashPattern(Random &ctx, PatternArgs &outArgs) +void Randomizer_dashPattern(Random *ctx, PatternArgs *outArgs) { // call next8 explicitly in this order because the order they // are called is undefined when called as parameters to another function. // ex: f(a,b,c) may call in the order a,b,c or c,b,a depending on compiler. // So different compilers may produce different results, // but like this it is explicit - uint8_t dash = ctx.next8(20, 30); // dash duration 20 -> 30 - uint8_t gap = ctx.next8(20, 30); // need gap 20 -> 30 - uint8_t off = ctx.next8(0, 10); // off duration 0 -> 10 - uint8_t on = ctx.next8(1, 10); // on duration 1 -> 10 - outArgs.init(on, off, gap, dash); + uint8_t dash = Random_next8(ctx, 20, 30); // dash duration 20 -> 30 + uint8_t gap = Random_next8(ctx, 20, 30); // need gap 20 -> 30 + uint8_t off = Random_next8(ctx, 0, 10); // off duration 0 -> 10 + uint8_t on = Random_next8(ctx, 1, 10); // on duration 1 -> 10 + PatternArgs_init(outArgs, on, off, gap, dash); } -void Randomizer::crushPattern(Random &ctx, PatternArgs &outArgs) +void Randomizer_crushPattern(Random *ctx, PatternArgs *outArgs) { // call next8 explicitly in this order because the order they // are called is undefined when called as parameters to another function. // ex: f(a,b,c) may call in the order a,b,c or c,b,a depending on compiler. // So different compilers may produce different results, // but like this it is explicit - uint8_t group = ctx.next8(0, 8); // groupsize 0 to 8 + uint8_t group = Random_next8(ctx, 0, 8); // groupsize 0 to 8 uint8_t dash = 0; // dash 0 - uint8_t gap = ctx.next8(20, 40); // need gap 20 -> 40 - uint8_t off = ctx.next8(0, 10); // off duration 0 -> 5 - uint8_t on = ctx.next8(1, 10); // on duration 1 -> 10 - outArgs.init(on, off, gap, dash, group); + uint8_t gap = Random_next8(ctx, 20, 40); // need gap 20 -> 40 + uint8_t off = Random_next8(ctx, 0, 10); // off duration 0 -> 5 + uint8_t on = Random_next8(ctx, 1, 10); // on duration 1 -> 10 + PatternArgs_init(outArgs, on, off, gap, dash, group); +} + +static void Randomizer_onShortClickHook(void *self) +{ + Randomizer_onShortClick((Randomizer *)self); +} + +static void Randomizer_onLongClickHook(void *self) +{ + Randomizer_onLongClick((Randomizer *)self); +} + +static bool Randomizer_initHook(void *self) +{ + return Randomizer_init((Randomizer *)self); +} + +static MenuAction Randomizer_runHook(void *self) +{ + return Randomizer_run((Randomizer *)self); +} + +static void Randomizer_destroyHook(void *self) +{ + Randomizer_destroy((Randomizer *)self); } diff --git a/VortexEngine/src/Menus/MenuList/Randomizer.h b/VortexEngine/src/Menus/MenuList/Randomizer.h index 0575ab132f..b40f897e1e 100644 --- a/VortexEngine/src/Menus/MenuList/Randomizer.h +++ b/VortexEngine/src/Menus/MenuList/Randomizer.h @@ -1,86 +1,75 @@ #ifndef RANDOMIZER_H #define RANDOMIZER_H +#include #include "../Menu.h" #include "../../Random/Random.h" #include "../../Modes/Mode.h" -class Mode; +// this isn't a valid randomization state, if the flags are +// on this state then the user will be prompted to pick +#define RANDOMIZE_NONE 0 +// the two main kinds of randomization +#define RANDOMIZE_COLORSET (1 << 0) +#define RANDOMIZE_PATTERN (1 << 1) +// compound flags both colorset and pattern randomization +#define RANDOMIZE_BOTH (RANDOMIZE_COLORSET | RANDOMIZE_PATTERN) -class Randomizer : public Menu +typedef struct Randomizer { -public: - Randomizer(const RGBColor &col, bool advanced); - ~Randomizer(); - - bool init() override; - MenuAction run() override; - - // handlers for clicks - void onShortClick() override; - void onLongClick() override; - - // re-roll a new randomization with a given context on an led - bool reRoll(); - -private: + Menu m_base; // random context for each single led Random m_singlesRandCtx[LED_COUNT]; #if VORTEX_SLIM == 0 // random context for the multi led position Random m_multiRandCtx; #endif - // the time of the last randomization uint32_t m_lastRandomization; - - enum RandomizeFlags : uint8_t { - // this isn't a valid randomization state, if the flags are - // on this state then the user will be prompted to pick - RANDOMIZE_NONE = 0, - - // the two main kinds of randomization - RANDOMIZE_COLORSET = (1 << 0), - RANDOMIZE_PATTERN = (1 << 1), - - // compound flags both colorset and pattern randomization - RANDOMIZE_BOTH = (RANDOMIZE_COLORSET | RANDOMIZE_PATTERN) - }; - // the randomization flags above uint8_t m_flags; - // auxilliary variable to display cycling hue at menus uint8_t m_displayHue; - // whether still need to select a randomization type bool m_needToSelect; // whether auto cycling bool m_autoCycle; +} Randomizer; + +extern const MenuVtable Randomizer_vtable; + +Randomizer *Randomizer_new(const RGBColor *col, bool advanced); +void Randomizer_destroy(Randomizer *self); - // show the randomization type selection - void showRandomizationSelect(); +bool Randomizer_init(Randomizer *self); +MenuAction Randomizer_run(Randomizer *self); + +// handlers for clicks +void Randomizer_onShortClick(Randomizer *self); +void Randomizer_onLongClick(Randomizer *self); + +// re-roll a new randomization with a given context on an led +bool Randomizer_reRoll(Randomizer *self); + +void Randomizer_showRandomizationSelect(Randomizer *self); - // main reRoll functions #if VORTEX_SLIM == 0 - bool reRollMulti(); - PatternID rollMultiLedPatternID(Random &ctx); +bool Randomizer_reRollMulti(Randomizer *self); +PatternID Randomizer_rollMultiLedPatternID(Randomizer *self, Random *ctx); #endif - bool reRollSingles(); - PatternID rollSingleLedPatternID(Random &ctx); +bool Randomizer_reRollSingles(Randomizer *self); +PatternID Randomizer_rollSingleLedPatternID(Randomizer *self, Random *ctx); - // generate a random colorset with a random context - Colorset rollColorset(Random &ctx); +// generate a random colorset with a random context +Colorset Randomizer_rollColorset(Randomizer *self, Random *ctx); - // roll a custom pattern by generating random arguments - bool rollCustomPattern(Random &ctx, Mode *pMode, LedPos pos); +// roll a custom pattern by generating random arguments +bool Randomizer_rollCustomPattern(Randomizer *self, Random *ctx, Mode *pMode, LedPos pos); - // more specific random pattern generators that just generate patternargs - void traditionalPattern(Random &ctx, PatternArgs &outArgs); - void gapPattern(Random &ctx, PatternArgs &outArgs); - void dashPattern(Random &ctx, PatternArgs &outArgs); - void crushPattern(Random &ctx, PatternArgs &outArgs); -}; +void Randomizer_traditionalPattern(Random *ctx, PatternArgs *outArgs); +void Randomizer_gapPattern(Random *ctx, PatternArgs *outArgs); +void Randomizer_dashPattern(Random *ctx, PatternArgs *outArgs); +void Randomizer_crushPattern(Random *ctx, PatternArgs *outArgs); -#endif +#endif \ No newline at end of file diff --git a/VortexEngine/src/Menus/Menus.cpp b/VortexEngine/src/Menus/Menus.cpp index abeaffa074..27c556d643 100644 --- a/VortexEngine/src/Menus/Menus.cpp +++ b/VortexEngine/src/Menus/Menus.cpp @@ -17,15 +17,44 @@ #include "../Modes/Modes.h" #include "../Leds/Leds.h" #include "../Log/Log.h" +#include "../Memory/Memory.h" // static members -Menus::MenuState Menus::m_menuState = MENU_STATE_NOT_OPEN; -uint8_t Menus::m_selection = 0; -uint32_t Menus::m_openTime = 0; -Menu *Menus::m_pCurMenu = nullptr; +MenuState Menus_m_menuState = MENU_STATE_NOT_OPEN; +uint8_t Menus_m_selection = 0; +uint32_t Menus_m_openTime = 0; +Menu *Menus_m_pCurMenu = NULL; + +// every menu type, the menu pool blocks are sized to fit the largest +typedef union AnyMenu { + Menu menu; + Randomizer randomizer; + ModeSharing modeSharing; +#if ENABLE_EDITOR_CONNECTION == 1 + EditorConnection editorConnection; +#endif + ColorSelect colorSelect; + PatternSelect patternSelect; + GlobalBrightness globalBrightness; + FactoryReset factoryReset; +} AnyMenu; + +// the pool all menus are allocated from +static MemPool s_menuPool; +static MEMPOOL_STORAGE(s_menuStorage, sizeof(AnyMenu), MENU_POOL_SIZE); + +void *Menus_allocMenu() +{ + return MemPool_alloc(&s_menuPool); +} + +void Menus_freeMenu(Menu *menu) +{ + MemPool_free(&s_menuPool, menu); +} // typedef for the menu initialization function -typedef Menu *(*initMenuFn_t)(const RGBColor &col, bool advanced); +typedef Menu *(*initMenuFn_t)(const RGBColor *col, bool advanced); // entries for the ring menu struct MenuEntry { @@ -37,16 +66,29 @@ struct MenuEntry { RGBColor color; }; -// a template to initialize ringmenu functions -template -Menu *initMenu(const RGBColor &col, bool advanced) { return new T(col, advanced); } +// a factory macro to forward to each derived menu constructor +#define MENU_NEW_FACTORY(name) \ + static Menu *name##_menuNew(const RGBColor *col, bool advanced) \ + { \ + return (Menu *)name##_new(col, advanced); \ + } + +MENU_NEW_FACTORY(Randomizer) +MENU_NEW_FACTORY(ModeSharing) +#if ENABLE_EDITOR_CONNECTION == 1 +MENU_NEW_FACTORY(EditorConnection) +#endif +MENU_NEW_FACTORY(ColorSelect) +MENU_NEW_FACTORY(PatternSelect) +MENU_NEW_FACTORY(GlobalBrightness) +MENU_NEW_FACTORY(FactoryReset) // a simple macro to simplify the entries in the menu list #if LOGGING_LEVEL > 2 // if the logging is enabled then we need to store the name of the menu -#define ENTRY(classname, color) { #classname, initMenu, color } +#define ENTRY(classname, color) { #classname, classname##_menuNew, color } #else -#define ENTRY(classname, color) { initMenu, color } +#define ENTRY(classname, color) { classname##_menuNew, color } #endif // The list of menus that are registered with colors to show in ring menu @@ -67,85 +109,86 @@ const MenuEntry menuList[] = { // the number of menus in the above array #define NUM_MENUS (sizeof(menuList) / sizeof(menuList[0])) -bool Menus::init() +bool Menus_init() { - m_menuState = MENU_STATE_NOT_OPEN; + MemPool_init(&s_menuPool, s_menuStorage, sizeof(AnyMenu), MENU_POOL_SIZE); + Menus_m_menuState = MENU_STATE_NOT_OPEN; // sub-menus are initialized right before they run return true; } -void Menus::cleanup() +void Menus_cleanup() { - if (m_pCurMenu) { - delete m_pCurMenu; - m_pCurMenu = nullptr; + if (Menus_m_pCurMenu) { + Menu_destroy(Menus_m_pCurMenu); + Menus_m_pCurMenu = NULL; } - m_menuState = MENU_STATE_NOT_OPEN; - m_selection = 0; - m_openTime = 0; + Menus_m_menuState = MENU_STATE_NOT_OPEN; + Menus_m_selection = 0; + Menus_m_openTime = 0; } -bool Menus::run() +bool Menus_run() { // if there are no menus, then we don't need to run - switch (m_menuState) { + switch (Menus_m_menuState) { case MENU_STATE_NOT_OPEN: default: // nothing to run return false; case MENU_STATE_MENU_SELECTION: - return runMenuSelection(); + return Menus_runMenuSelection(); case MENU_STATE_IN_MENU: - return runCurMenu(); + return Menus_runCurMenu(); } } -bool Menus::runMenuSelection() +bool Menus_runMenuSelection() { - if (g_pButton->onShortClick()) { + if (Button_onShortClick(g_pButton)) { // otherwise increment selection and wrap around at num menus - m_selection = (m_selection + 1) % NUM_MENUS; + Menus_m_selection = (Menus_m_selection + 1) % NUM_MENUS; #if ENABLE_EDITOR_CONNECTION == 1 // Hide the editor connection menu because it opens automatically // TODO: Create a better way to hide this menu color, this menu // will automatically open when the device is plugged in - if (m_selection == MENU_EDITOR_CONNECTION) { - m_selection++; + if (Menus_m_selection == MENU_EDITOR_CONNECTION) { + Menus_m_selection++; } #endif - DEBUG_LOGF("Cyling to ring menu %u", m_selection); + DEBUG_LOGF("Cyling to ring menu %u", Menus_m_selection); // reset the open time so that it starts again - m_openTime = Time::getCurtime(); + Menus_m_openTime = Time_getCurtime(); // clear the leds - Leds::clearAll(); + Leds_clearAll(); return true; } // clear the leds so it always fills instead of replacing - Leds::clearAll(); + Leds_clearAll(); // timings for blink later uint8_t offtime = 200; uint8_t ontime = 200; // whether advanced menus are enabled - bool advMenus = Modes::advancedMenusEnabled(); + bool advMenus = Modes_advancedMenusEnabled(); // if the button was long pressed then select this menu, but we // need to check the presstime to ensure we don't catch the initial // release after opening the ringmenu - if (g_pButton->pressTime() >= m_openTime) { + if (Button_pressTime(g_pButton) >= Menus_m_openTime) { // whether to open advanced menus or not - bool openAdv = (g_pButton->holdDuration() > ADV_MENU_DURATION_TICKS) && advMenus; - if (g_pButton->onLongClick()) { + bool openAdv = (Button_holdDuration(g_pButton) > ADV_MENU_DURATION_TICKS) && advMenus; + if (Button_onLongClick(g_pButton)) { // ringmenu is open so select the menu - DEBUG_LOGF("Selected ringmenu %s", menuList[m_selection].menuName); + DEBUG_LOGF("Selected ringmenu %s", menuList[Menus_m_selection].menuName); // open the menu we have selected - if (!openMenu(m_selection, openAdv)) { - DEBUG_LOGF("Failed to initialize %s menu", menuList[m_selection].menuName); + if (!Menus_openMenu(Menus_m_selection, openAdv)) { + DEBUG_LOGF("Failed to initialize %s menu", menuList[Menus_m_selection].menuName); return false; } // display the newly opened menu return true; } // if holding down to select the menu option - if (g_pButton->isPressed() && openAdv) { + if (Button_isPressed(g_pButton) && openAdv) { // make it strobe aw yiss offtime = HYPERSTROBE_OFF_DURATION; ontime = HYPERSTROBE_ON_DURATION; @@ -154,51 +197,50 @@ bool Menus::runMenuSelection() // blink every even/odd of every pair for (Pair p = PAIR_FIRST; p < PAIR_COUNT; ++p) { if (pairEven(p) < LED_COUNT) { - Leds::blinkIndex(pairEven(p), offtime, ontime, menuList[m_selection].color); + Leds_blinkIndex(pairEven(p), offtime, ontime, menuList[Menus_m_selection].color); } if (pairOdd(p) < LED_COUNT) { - Leds::setIndex(pairOdd(p), menuList[m_selection].color); - Leds::blinkIndex(pairOdd(p), offtime, ontime, RGB_OFF); + Leds_setIndex(pairOdd(p), menuList[Menus_m_selection].color); + Leds_blinkIndex(pairOdd(p), offtime, ontime, RGB_OFF); } } // check if the advanced menus have been enabled - if (g_pButton->onConsecutivePresses(ADVANCED_MENU_CLICKS)) { + if (Button_onConsecutivePresses(g_pButton, ADVANCED_MENU_CLICKS)) { // toggle the advanced menu - Modes::setAdvancedMenus(!advMenus); + Modes_setAdvancedMenus(!advMenus, true); // display a pink or red depending on whether the menu was enabled for (uint16_t i = 0; i < 2; ++i) { - Leds::holdAll(advMenus ? RGB_RED : RGB_PINK); + Leds_holdAll(advMenus ? RGB_RED : RGB_PINK); } } // show when the user selects a menu option - showSelection(RGBColor( - menuList[m_selection].color.red << 3, - menuList[m_selection].color.green << 3, - menuList[m_selection].color.blue << 3)); + Menus_showSelection(RGBColor_makeRGB(menuList[Menus_m_selection].color.red << 3, + menuList[Menus_m_selection].color.green << 3, + menuList[Menus_m_selection].color.blue << 3)); // continue in the menu return true; } -bool Menus::runCurMenu() +bool Menus_runCurMenu() { // if the menu run handler returns false that signals the // menu was closed by the user leaving the menu - switch (m_pCurMenu->run()) { - case Menu::MENU_QUIT: + switch (Menu_run(Menus_m_pCurMenu)) { + case MENU_QUIT: // close the current menu when run returns false - closeCurMenu(); + Menus_closeCurMenu(); // return false to let the modes play return false; - case Menu::MENU_CONTINUE: + case MENU_CONTINUE: // if Menu continue run the click handlers for the menu - if (g_pButton->onShortClick()) { - m_pCurMenu->onShortClick(); + if (Button_onShortClick(g_pButton)) { + Menu_onShortClick(Menus_m_pCurMenu); } - if (g_pButton->onLongClick()) { - m_pCurMenu->onLongClick(); + if (Button_onLongClick(g_pButton)) { + Menu_onLongClick(Menus_m_pCurMenu); } break; - case Menu::MENU_SKIP: + case MENU_SKIP: // dont run click handlers in this case // so the core menu class can handle them break; @@ -208,89 +250,89 @@ bool Menus::runCurMenu() } // open the menu selection ring -bool Menus::openMenuSelection() +bool Menus_openMenuSelection() { - if (m_menuState != MENU_STATE_NOT_OPEN) { + if (Menus_m_menuState != MENU_STATE_NOT_OPEN) { return false; } // save the time of when we open the menu so we can fill based on curtime from then - m_openTime = Time::getCurtime(); + Menus_m_openTime = Time_getCurtime(); // open the menu - m_menuState = MENU_STATE_MENU_SELECTION; + Menus_m_menuState = MENU_STATE_MENU_SELECTION; // clear the leds - Leds::clearAll(); + Leds_clearAll(); return true; } -bool Menus::openMenu(uint32_t index, bool advanced) +bool Menus_openMenu(uint32_t index, bool advanced) { if (index >= NUM_MENUS) { return false; } - m_selection = index; - Menu *newMenu = menuList[m_selection].initMenu(menuList[m_selection].color, advanced); + Menus_m_selection = index; + Menu *newMenu = menuList[Menus_m_selection].initMenu(&menuList[Menus_m_selection].color, advanced); if (!newMenu) { return false; } // initialiaze the new menu with the current mode - if (!newMenu->init()) { - DEBUG_LOGF("Failed to initialize %s menu", menuList[m_selection].menuName); + if (!Menu_init(newMenu)) { + DEBUG_LOGF("Failed to initialize %s menu", menuList[Menus_m_selection].menuName); // if the menu failed to init, don't open it - delete newMenu; + Menu_destroy(newMenu); return false; } - if (m_pCurMenu) { - delete m_pCurMenu; + if (Menus_m_pCurMenu) { + Menu_destroy(Menus_m_pCurMenu); } // assign the new menu - m_pCurMenu = newMenu; + Menus_m_pCurMenu = newMenu; // and the menus are open just in case // clear all the leds - Leds::clearAll(); - m_menuState = MENU_STATE_IN_MENU; + Leds_clearAll(); + Menus_m_menuState = MENU_STATE_IN_MENU; return true; } -void Menus::showSelection(RGBColor colval) +void Menus_showSelection(RGBColor colval) { // blink the tip led white for 150ms when the short // click threshold has been surpassed - if (g_pButton->isPressed() && - g_pButton->holdDuration() > SHORT_CLICK_THRESHOLD_TICKS && - g_pButton->holdDuration() < (SHORT_CLICK_THRESHOLD_TICKS + MS_TO_TICKS(250))) { - Leds::setAll(colval); + if (Button_isPressed(g_pButton) && + Button_holdDuration(g_pButton) > SHORT_CLICK_THRESHOLD_TICKS && + Button_holdDuration(g_pButton) < (SHORT_CLICK_THRESHOLD_TICKS + MS_TO_TICKS(250))) { + Leds_setAll(colval); } } -bool Menus::checkOpen() +bool Menus_checkOpen() { - return m_menuState != MENU_STATE_NOT_OPEN && g_pButton->releaseTime() > m_openTime; + return Menus_m_menuState != MENU_STATE_NOT_OPEN && Button_releaseTime(g_pButton) > Menus_m_openTime; } -bool Menus::checkInMenu() +bool Menus_checkInMenu() { - return m_menuState == MENU_STATE_IN_MENU; + return Menus_m_menuState == MENU_STATE_IN_MENU; } -Menu *Menus::curMenu() +Menu *Menus_curMenu() { - return m_pCurMenu; + return Menus_m_pCurMenu; } -MenuEntryID Menus::curMenuID() +MenuEntryID Menus_curMenuID() { - return (MenuEntryID)m_selection; + return (MenuEntryID)Menus_m_selection; } -void Menus::closeCurMenu() +void Menus_closeCurMenu() { // delete the currently open menu object - if (m_pCurMenu) { - delete m_pCurMenu; - m_pCurMenu = nullptr; + if (Menus_m_pCurMenu) { + Menu_destroy(Menus_m_pCurMenu); + Menus_m_pCurMenu = NULL; } - m_menuState = MENU_STATE_NOT_OPEN; - m_selection = 0; + Menus_m_menuState = MENU_STATE_NOT_OPEN; + Menus_m_selection = 0; // clear the leds - Leds::clearAll(); + Leds_clearAll(); } diff --git a/VortexEngine/src/Menus/Menus.h b/VortexEngine/src/Menus/Menus.h index d352a69cf9..9dcae9a86d 100644 --- a/VortexEngine/src/Menus/Menus.h +++ b/VortexEngine/src/Menus/Menus.h @@ -1,6 +1,7 @@ #ifndef RING_MENU_H #define RING_MENU_H +#include #include #include "../Colors/ColorTypes.h" @@ -27,73 +28,63 @@ enum MenuEntryID MENU_COUNT }; -class Menu; +typedef struct Menu Menu; -class Menus -{ - // private unimplemented constructor - Menus(); - -public: - // opting for static class here because there should only ever be one - // Menu control object and I don't like singletons - static bool init(); - static void cleanup(); - - // Run the menus returns true if the menu remains open, false if closed - static bool run(); - - // open the ring menu - static bool openMenuSelection(); - - // open a menu by index in the menu table, optionally specify to - // open the advanced version of the menu (default basic version) - static bool openMenu(uint32_t index, bool advanced = false); - - // if you call this then a blink will be shown when the user has - // held the button for at least the long click duration - static void showSelection(RGBColor colval = RGB_WHITE5); - - // whether the menus are actually open (The user has let go after opening) - static bool checkOpen(); - // whether the user is actually in a menu - static bool checkInMenu(); - static Menu *curMenu(); - static MenuEntryID curMenuID(); - -private: - // run the currently open menu - static bool runCurMenu(); - // run the ring filling logic - static bool runMenuSelection(); - // helper to calculate the relative hold time for the current menu - static LedPos calcLedPos(); - // close the currently open menu - static void closeCurMenu(); - - // ===================== - // private members - - enum MenuState { - // menus aren't open at all - MENU_STATE_NOT_OPEN = 0, - - // menus are open and the user is choosing a menu - MENU_STATE_MENU_SELECTION, - - // the menus are open and we opened a menu - MENU_STATE_IN_MENU - }; - - // the current menus states - static MenuState m_menuState; - - // the ring menu section - static uint8_t m_selection; - // the time when we first opened the ringmenu - static uint32_t m_openTime; - // the current sub menu that is open - static Menu *m_pCurMenu; -}; +typedef uint8_t MenuState; -#endif +// menus aren't open at all +#define MENU_STATE_NOT_OPEN 0 +// menus are open and the user is choosing a menu +#define MENU_STATE_MENU_SELECTION 1 +// the menus are open and we opened a menu +#define MENU_STATE_IN_MENU 2 + +// the current menus states +extern MenuState Menus_m_menuState; + +// the ring menu section +extern uint8_t Menus_m_selection; +// the time when we first opened the ringmenu +extern uint32_t Menus_m_openTime; +// the current sub menu that is open +extern Menu *Menus_m_pCurMenu; + +// opting for free functions here because there should only ever be one +// Menu control object and I don't like singletons + +// ===================== +// free functions + +bool Menus_init(); +void Menus_cleanup(); + +bool Menus_run(); + +bool Menus_openMenuSelection(); + +bool Menus_openMenu(uint32_t index, bool advanced); + +void Menus_showSelection(RGBColor colval); + +bool Menus_checkOpen(); + +bool Menus_checkInMenu(); + +Menu *Menus_curMenu(); + +// grab a block from the menu pool to construct a menu in +void *Menus_allocMenu(void); +// return a menu's block to the pool +void Menus_freeMenu(Menu *menu); + +MenuEntryID Menus_curMenuID(); + +bool Menus_runCurMenu(); + +bool Menus_runMenuSelection(); + +LedPos Menus_calcLedPos(); + +void Menus_closeCurMenu(); + +#endif \ No newline at end of file diff --git a/VortexEngine/src/Modes/DefaultModes.cpp b/VortexEngine/src/Modes/DefaultModes.cpp index a2f0e7b7e4..fb1d542e4d 100644 --- a/VortexEngine/src/Modes/DefaultModes.cpp +++ b/VortexEngine/src/Modes/DefaultModes.cpp @@ -4,7 +4,7 @@ // all the different colorsets used in defaults defined separately from the default list // so we can save space instead of using fixed sized arrays in the DefaultModeEntry for colors -const uint32_t rgbCols[] = { RGB_RED, RGB_GREEN, RGB_BLUE, }; +const uint32_t rgbCols[] = { 0xFF0000, 0x00FF00, 0x0000FF, }; const uint32_t pat2TipCols = 0x97709F; const uint32_t pat2TopCols = 0x4D00B2; const uint32_t pat4TipCols[] = { 0xC4003B, 0x700000, 0x000000, 0x0017E9, 0x22004E, 0x000000, }; diff --git a/VortexEngine/src/Modes/Mode.cpp b/VortexEngine/src/Modes/Mode.cpp index 9a0447e228..ad2b4c2c1e 100644 --- a/VortexEngine/src/Modes/Mode.cpp +++ b/VortexEngine/src/Modes/Mode.cpp @@ -14,217 +14,223 @@ #include "../Log/Log.h" -Mode::Mode(const DefaultModeEntry &entry) : - Mode() -{ - for (LedPos pos = LED_FIRST; pos < LED_COUNT; ++pos) { - Colorset set(entry.leds[pos].numColors, entry.leds[pos].cols); - setPattern(entry.leds[pos].patternID, pos, nullptr, &set); - } -} - #if FIXED_LED_COUNT == 0 // for internal reference to the led count -#define MODE_LEDCOUNT m_numLeds -Mode::Mode(uint8_t numLeds) : -#if VORTEX_SLIM == 0 - m_multiPat(nullptr), -#endif - m_numLeds(numLeds), - m_singlePats(nullptr) -{ - setLedCount(m_numLeds); -} -Mode::Mode() : - Mode(LED_COUNT) -{ -} +#define MODE_LEDCOUNT self->m_numLeds #else // for internal reference to the led count #define MODE_LEDCOUNT LED_COUNT -Mode::Mode() : -#if VORTEX_SLIM == 0 - m_multiPat(nullptr), #endif - m_singlePats() + +void Mode_initLeds(Mode *self, uint8_t numLeds) { +#if VORTEX_SLIM == 0 + self->m_multiPat = NULL; +#endif +#if FIXED_LED_COUNT == 0 + self->m_numLeds = numLeds; + self->m_singlePats = (Pattern **)vmalloc(sizeof(Pattern *) * Mode_getLedCount(self)); + for (LedPos i = LED_FIRST; i < Mode_getLedCount(self); ++i) { + self->m_singlePats[i] = NULL; + } + Mode_setLedCount(self, self->m_numLeds); +#else + (void)numLeds; for (LedPos i = LED_FIRST; i < LED_COUNT; ++i) { - m_singlePats[i] = nullptr; + self->m_singlePats[i] = NULL; } +#endif } + +void Mode_init(Mode *self) +{ +#if FIXED_LED_COUNT == 0 + Mode_initLeds(self, LED_COUNT); +#else + Mode_initLeds(self, LED_COUNT); #endif +} -Mode::Mode(PatternID id, const Colorset &set) : - Mode() +void Mode_initDefault(Mode *self, const DefaultModeEntry *entry) { - setPattern(id, LED_ANY, nullptr, &set); + Mode_init(self); + for (LedPos pos = LED_FIRST; pos < LED_COUNT; ++pos) { + Colorset set; + Colorset_make(&set); + set.m_numColors = entry->leds[pos].numColors; + for (uint8_t i = 0; i < entry->leds[pos].numColors; ++i) { + RGBColor_setU32(&set.m_palette[i], entry->leds[pos].cols[i]); + } + Mode_setPattern(self, entry->leds[pos].patternID, pos, NULL, &set); + } } -Mode::Mode(PatternID id, const PatternArgs &args, const Colorset &set) : - Mode() +void Mode_initPatternSet(Mode *self, PatternID id, const Colorset *set) { - setPattern(id, LED_ANY, &args, &set); + Mode_init(self); + Mode_setPattern(self, id, LED_ANY, NULL, set); } -Mode::Mode(PatternID id, const PatternArgs *args, const Colorset *set) : - Mode() +void Mode_initPatternArgsSet(Mode *self, PatternID id, const PatternArgs *args, const Colorset *set) { - setPattern(id, LED_ANY, args, set); + Mode_init(self); + Mode_setPattern(self, id, LED_ANY, args, set); } -Mode::Mode(const Mode *other) : - Mode() +void Mode_copy(Mode *self, const Mode *other) { + Mode_init(self); if (!other) { return; } - *this = *other; + Mode_assign(self, other); } -Mode::~Mode() +void Mode_destroy(Mode *self) { - clearPattern(LED_ALL); + Mode_clearPattern(self, LED_ALL); #if FIXED_LED_COUNT == 0 - free(m_singlePats); + vfree(self->m_singlePats); + self->m_singlePats = NULL; #endif } -// copy and assignment operators -Mode::Mode(const Mode &other) : - Mode() -{ - *this = other; -} - -void Mode::operator=(const Mode &other) +void Mode_assign(Mode *self, const Mode *other) { #if FIXED_LED_COUNT == 0 - setLedCount(other.getLedCount()); + Mode_setLedCount(self, Mode_getLedCount(other)); #endif - clearPattern(LED_ALL); + Mode_clearPattern(self, LED_ALL); #if VORTEX_SLIM == 0 - if (other.m_multiPat) { - m_multiPat = PatternBuilder::dupe(other.m_multiPat); + if (other->m_multiPat) { + self->m_multiPat = PatternBuilder_dupe(other->m_multiPat); } #endif - for (LedPos i = LED_FIRST; i < other.getLedCount(); ++i) { - Pattern *otherPat = other.m_singlePats[i]; + for (LedPos i = LED_FIRST; i < Mode_getLedCount(other); ++i) { + Pattern *otherPat = other->m_singlePats[i]; if (!otherPat) { continue; } - m_singlePats[i] = PatternBuilder::dupe(otherPat); + self->m_singlePats[i] = PatternBuilder_dupe(otherPat); } } + // equality operators -bool Mode::operator==(const Mode &other) const +bool Mode_opEquals(const Mode *self, const Mode *other) { - return equals(&other); + + return Mode_equals(self, other); } -bool Mode::operator!=(const Mode &other) const +bool Mode_opNotEquals(const Mode *self, const Mode *other) { - return !equals(&other); + + return !Mode_equals(self, other); } -void Mode::init() +void Mode_initPatterns(Mode *self) { #if VORTEX_SLIM == 0 // initialize the multi pattern if it's present - if (m_multiPat) { - m_multiPat->init(); + if (self->m_multiPat) { + Pattern_init(self->m_multiPat); } #endif // otherwise regular init for (LedPos pos = LED_FIRST; pos < MODE_LEDCOUNT; ++pos) { // grab the entry for this led and initialize it - Pattern *entry = m_singlePats[pos]; + Pattern *entry = self->m_singlePats[pos]; if (!entry) { continue; } - entry->init(); + Pattern_init(entry); } } -void Mode::play() +void Mode_play(Mode *self) { + #if VORTEX_SLIM == 0 // play multi pattern first so that the singles can override - if (m_multiPat) { - m_multiPat->play(); + if (self->m_multiPat) { + Pattern_play(self->m_multiPat); } #endif // now iterate all singles and play for (LedPos pos = LED_FIRST; pos < MODE_LEDCOUNT; ++pos) { - // grab the entry for this led - Pattern *entry = m_singlePats[pos]; + // grab the entry for self led + Pattern *entry = self->m_singlePats[pos]; if (!entry) { #if VORTEX_SLIM == 0 // incomplete pattern/set or empty slot - if (!m_multiPat) { + if (!self->m_multiPat) { // only clear if the multi pattern isn't playing - Leds::clearIndex(pos); + Leds_clearIndex(pos); } #else // just clear the index if slim, don't check for multi - Leds::clearIndex(pos); + Leds_clearIndex(pos); #endif continue; } // play the current pattern with current color set on the current finger - entry->play(); + Pattern_play(entry); } } -bool Mode::saveToBuffer(ByteStream &modeBuffer, uint8_t numLeds) const +bool Mode_saveToBuffer(const Mode *self, ByteStream *modeBuffer, uint8_t numLeds) { + // serialize the engine version into the modes buffer - VortexEngine::serializeVersion(modeBuffer); + VortexEngine_serializeVersion(modeBuffer); // serialize all mode data into the modeBuffer - serialize(modeBuffer, numLeds); - DEBUG_LOGF("Serialized mode, uncompressed size: %u", modeBuffer.size()); - return modeBuffer.compress(); + Mode_serialize(self, modeBuffer, numLeds); + DEBUG_LOGF("Serialized mode, uncompressed size: %u", modeBuffer->size); + return ByteStream_compress(modeBuffer); } -bool Mode::loadFromBuffer(ByteStream &modeBuffer) +bool Mode_loadFromBuffer(Mode *self, ByteStream *modeBuffer) { - if (!modeBuffer.decompress()) { + + if (!ByteStream_decompress(modeBuffer)) { // failed to decompress the mode return false; } // reset the unserializer index before unserializing anything - modeBuffer.resetUnserializer(); + ByteStream_resetUnserializer(modeBuffer); uint8_t major = 0; uint8_t minor = 0; // unserialize the vortex version - if (!modeBuffer.unserialize8(&major)) { + if (!ByteStream_unserialize8(modeBuffer, &major)) { return false; } - if (!modeBuffer.unserialize8(&minor)) { + if (!ByteStream_unserialize8(modeBuffer, &minor)) { return false; } // check the version for incompatibility - if (!VortexEngine::checkVersion(major, minor)) { + if (!VortexEngine_checkVersion(major, minor)) { // incompatible version ERROR_LOGF("Incompatible savefile version: %u.%u", major, minor); return false; } // now just unserialize the list of patterns - if (!unserialize(modeBuffer)) { + if (!Mode_unserialize(self, modeBuffer)) { return false; } // then initialize the mode so that it is ready to play - init(); + Mode_initPatterns(self); return true; } -bool Mode::serialize(ByteStream &buffer, uint8_t numLeds) const +bool Mode_serialize(const Mode *self, ByteStream *buffer, uint8_t numLeds) { + if (!numLeds) { numLeds = MODE_LEDCOUNT; } // serialize the number of leds - if (!buffer.serialize8(numLeds)) { + if (!ByteStream_serialize8(buffer, numLeds)) { return false; } // empty mode? @@ -232,15 +238,15 @@ bool Mode::serialize(ByteStream &buffer, uint8_t numLeds) const return true; } // serialize the flags - ModeFlags flags = getFlags(); - if (!buffer.serialize8(flags)) { + ModeFlags flags = Mode_getFlags(self); + if (!ByteStream_serialize8(buffer, flags)) { return false; } #if VORTEX_SLIM == 0 // serialiaze the multi led? - if ((flags & MODE_FLAG_MULTI_LED) && m_multiPat) { + if ((flags & MODE_FLAG_MULTI_LED) && self->m_multiPat) { // serialize the multi led - if (!m_multiPat->serialize(buffer)) { + if (!Pattern_serialize(self->m_multiPat, buffer)) { return false; } } @@ -252,18 +258,18 @@ bool Mode::serialize(ByteStream &buffer, uint8_t numLeds) const // if there are any sparse singles (spaces) then we need to // serialize an led map of which singles are set if (flags & MODE_FLAG_SPARSE_SINGLES) { - if (!buffer.serialize32((uint32_t)getSingleLedMap())) { + if (!ByteStream_serialize32(buffer, (uint32_t)Mode_getSingleLedMap(self))) { return false; } } // then iterate each single led and serialize it for (LedPos pos = LED_FIRST; pos < numLeds; ++pos) { - const Pattern *entry = m_singlePats[pos]; + const Pattern *entry = self->m_singlePats[pos]; if (!entry) { continue; } // just serialize the pattern then colorset - if (!entry->serialize(buffer)) { + if (!Pattern_serialize(entry, buffer)) { return false; } // if they are all same single then only serialize one @@ -274,23 +280,24 @@ bool Mode::serialize(ByteStream &buffer, uint8_t numLeds) const return true; } -// this is a hairy function, but a bit of a necessary complexity -bool Mode::unserialize(ByteStream &buffer) +// self is a hairy function, but a bit of a necessary complexity +bool Mode_unserialize(Mode *self, ByteStream *buffer) { - clearPattern(LED_ALL); + + Mode_clearPattern(self, LED_ALL); uint8_t ledCount = LED_COUNT; // unserialize the number of leds - if (!buffer.unserialize8(&ledCount)) { + if (!ByteStream_unserialize8(buffer, &ledCount)) { return false; } #if FIXED_LED_COUNT == 0 // it's important that we only increase the led count if necessary // otherwise we may end up reducing our led count and only rendering // a few leds on the device - if (ledCount > m_numLeds) { - // adjust the internal LED count of the mode itself, this allows it to + if (ledCount > self->m_numLeds) { + // adjust the internal LED count of the mode itself, self allows it to // actually manage that many patterns at once - setLedCount(ledCount); + Mode_setLedCount(self, ledCount); } #endif // FIXED_LED_COUNT if (!ledCount) { @@ -299,21 +306,21 @@ bool Mode::unserialize(ByteStream &buffer) } // unserialize the flags value ModeFlags flags = 0; - if (!buffer.unserialize8(&flags)) { + if (!ByteStream_unserialize8(buffer, &flags)) { return false; } - Pattern *firstPat = nullptr; + Pattern *firstPat = NULL; // if there is a multi led pattern then unserialize it if (flags & MODE_FLAG_MULTI_LED) { #if VORTEX_SLIM == 1 return false; #else // otherwise in normal build actually unserialize it - m_multiPat = PatternBuilder::unserialize(buffer); - if (!m_multiPat) { + self->m_multiPat = PatternBuilder_unserialize(buffer); + if (!self->m_multiPat) { return false; } - m_multiPat->init(); + Pattern_init(self->m_multiPat); #endif } // if there is no single led patterns just stop here @@ -328,82 +335,83 @@ bool Mode::unserialize(ByteStream &buffer) // unserialize all singleled patterns into their positions MAP_FOREACH_LED(map) { if (pos >= LED_COUNT) { - // in case the map encodes led positions this device doesn't support + // in case the map encodes led positions self device doesn't support continue; } //if (!firstPat) { - // save the first pattern so that it can be duped if this is 'all same' + // save the first pattern so that it can be duped if self is 'all same' if (pos == LED_FIRST || (flags & MODE_FLAG_ALL_SAME_SINGLE) == 0) { - m_singlePats[pos] = firstPat = PatternBuilder::unserialize(buffer); + self->m_singlePats[pos] = firstPat = PatternBuilder_unserialize(buffer); } else { // if all same then just dupe first - m_singlePats[pos] = PatternBuilder::dupe(m_singlePats[LED_FIRST]); + self->m_singlePats[pos] = PatternBuilder_dupe(self->m_singlePats[LED_FIRST]); } - if (!m_singlePats[pos]) { - clearPattern(LED_ALL); + if (!self->m_singlePats[pos]) { + Mode_clearPattern(self, LED_ALL); return false; } - if (!m_singlePats[pos]) { - clearPattern(LED_ALL); + if (!self->m_singlePats[pos]) { + Mode_clearPattern(self, LED_ALL); return false; } - m_singlePats[pos]->bind(pos); + Pattern_bind(self->m_singlePats[pos], pos); } // there is a few different possibilities here: // 1. The provided ledCount is less than our current LED_COUNT - // -> if this happens we need to repeat the first ledCount leds + // -> if self happens we need to repeat the first ledCount leds // into the remaining LED_COUNT - ledCount // 2. The provided ledCount is more than our current LED_COUNT - // -> if this happens then we can just chop the leds and continue + // -> if self happens then we can just chop the leds and continue // 3. The provided ledCount is the same as our current LED_COUNT - // -> if this happens all is good we can continue + // -> if self happens all is good we can continue if (ledCount >= LED_COUNT) { - // in this case we either chopped some off or have the exact amount + // in self case we either chopped some off or have the exact amount return true; } - // in this case we have to repeat them so we loop LED_COUNT - ledCount + // in self case we have to repeat them so we loop LED_COUNT - ledCount // times and walk the first ledCount that are already set and copy them LedPos src = LED_FIRST; // start from ledCount (the first index we didn't load) and loop till // LED_COUNT and dupe the pattern in the src position, but wrap the src // around at ledCount so that we repeat the first ledCount over again for (LedPos pos = (LedPos)ledCount; pos < LED_COUNT; ++pos) { - m_singlePats[pos] = PatternBuilder::dupe(m_singlePats[src]); - if (!m_singlePats[pos]) { + self->m_singlePats[pos] = PatternBuilder_dupe(self->m_singlePats[src]); + if (!self->m_singlePats[pos]) { return false; } - m_singlePats[pos]->bind(pos); + Pattern_bind(self->m_singlePats[pos], pos); // have to modulate the source by the source mode's led count src = (LedPos)((src + 1) % ledCount); } return true; } -bool Mode::equals(const Mode *other) const +bool Mode_equals(const Mode *self, const Mode *other) { + if (!other) { return false; } // compare the led count - if (other->getLedCount() != MODE_LEDCOUNT) { + if (Mode_getLedCount(other) != MODE_LEDCOUNT) { return false; } #if VORTEX_SLIM == 0 // compare the multi pattern, either: // have a local pat and it's not equal to other (other could be null) // or have no local pat and other has one - if ((m_multiPat && !m_multiPat->equals(other->m_multiPat)) || - (!m_multiPat && other->m_multiPat)) { + if ((self->m_multiPat && !Pattern_equals(self->m_multiPat, other->m_multiPat)) || + (!self->m_multiPat && other->m_multiPat)) { return false; } #endif // compare all the singles for (LedPos pos = LED_FIRST; pos < MODE_LEDCOUNT; ++pos) { // if entry is valid, do a comparison - if (m_singlePats[pos]) { + if (self->m_singlePats[pos]) { // checks if other is not null and equal - if (!m_singlePats[pos]->equals(other->m_singlePats[pos])) { + if (!Pattern_equals(self->m_singlePats[pos], other->m_singlePats[pos])) { return false; } continue; @@ -419,15 +427,16 @@ bool Mode::equals(const Mode *other) const #if FIXED_LED_COUNT == 0 // change the internal pattern count in the mode object -bool Mode::setLedCount(uint8_t numLeds) +bool Mode_setLedCount(Mode *self, uint8_t numLeds) { - if (m_singlePats) { - clearPattern(LED_ALL); - free(m_singlePats); + + if (self->m_singlePats) { + Mode_clearPattern(self, LED_ALL); + free(self->m_singlePats); } - m_numLeds = numLeds; - m_singlePats = (Pattern **)vcalloc(m_numLeds, sizeof(Pattern *)); - if (!m_singlePats) { + self->m_numLeds = numLeds; + self->m_singlePats = (Pattern **)vcalloc(self->m_numLeds, sizeof(Pattern *)); + if (!self->m_singlePats) { ERROR_OUT_OF_MEMORY(); return false; } @@ -435,32 +444,35 @@ bool Mode::setLedCount(uint8_t numLeds) } #endif -uint8_t Mode::getLedCount() const +uint8_t Mode_getLedCount(const Mode *self) { + #if FIXED_LED_COUNT == 0 - return m_numLeds; + return self->m_numLeds; #else return LED_COUNT; #endif } -const Pattern *Mode::getPattern(LedPos pos) const +const Pattern * Mode_getPattern(const Mode *self, LedPos pos) { - return ((Mode *)this)->getPattern(pos); + + return Mode_getPatternMutable((Mode *)self, pos); } -Pattern *Mode::getPattern(LedPos pos) +Pattern * Mode_getPatternMutable(Mode *self, LedPos pos) { + switch (pos) { case LED_ALL: // makes no sense - return nullptr; + return NULL; case LED_ANY: // fallthrough #if VORTEX_SLIM == 0 case LED_MULTI: - if (m_multiPat) { - return m_multiPat; + if (self->m_multiPat) { + return self->m_multiPat; } if (pos == LED_MULTI) { // don't fallthrough if actually multi, it's possible @@ -472,77 +484,85 @@ Pattern *Mode::getPattern(LedPos pos) case LED_ALL_SINGLE: // count as 'any' single #if FIXED_LED_COUNT == 0 - if (!m_singlePats) { - return nullptr; + if (!self->m_singlePats) { + return NULL; } #endif for (LedPos pos = LED_FIRST; pos < LED_COUNT; ++pos) { - if (m_singlePats[pos]) { - return m_singlePats[pos]; + if (self->m_singlePats[pos]) { + return self->m_singlePats[pos]; } } // actually break here break; default: #if FIXED_LED_COUNT == 0 - if (!m_singlePats) { - return nullptr; + if (!self->m_singlePats) { + return NULL; } #endif - if (pos < LED_COUNT && m_singlePats[pos]) { - return m_singlePats[pos]; + if (pos < LED_COUNT && self->m_singlePats[pos]) { + return self->m_singlePats[pos]; } break; } - return nullptr; + return NULL; } -const Colorset Mode::getColorset(LedPos pos) const +static Colorset Mode_getColorsetImpl(Mode *self, LedPos pos); + +Colorset Mode_getColorset(const Mode *self, LedPos pos) { - return ((Mode *)this)->getColorset(pos); + return Mode_getColorsetImpl((Mode *)self, pos); } -Colorset Mode::getColorset(LedPos pos) +static Colorset Mode_getColorsetImpl(Mode *self, LedPos pos) { - Pattern *pat = getPattern(pos); + + Pattern *pat = Mode_getPatternMutable(self, pos); if (!pat) { - return Colorset(); + Colorset empty; + Colorset_make(&empty); + return empty; } - return pat->getColorset(); + return Pattern_getColorset(pat); } -PatternID Mode::getPatternID(LedPos pos) const +PatternID Mode_getPatternID(const Mode *self, LedPos pos) { - const Pattern *pat = getPattern(pos); + + const Pattern *pat = Mode_getPattern(self, pos); if (!pat) { return PATTERN_NONE; } - return pat->getPatternID(); + return Pattern_getPatternID(pat); } -bool Mode::setPattern(PatternID pat, LedPos pos, const PatternArgs *args, const Colorset *set) +bool Mode_setPattern(Mode *self, PatternID pat, LedPos pos, const PatternArgs *args, const Colorset *set) { + // Use provided colorset, or colorset from pos if valid, otherwise use effective colorset - Colorset newSet = set ? *set : getColorset(((pos < LED_COUNT) && m_singlePats[pos]) ? pos : LED_ANY); + Colorset newSet = set ? *set : Mode_getColorset(self, ((pos < LED_COUNT) && self->m_singlePats[pos]) ? pos : LED_ANY); switch (pos) { case LED_ANY: case LED_ALL: // fallthrough #if VORTEX_SLIM == 0 case LED_MULTI: - if (m_multiPat) { - delete m_multiPat; - m_multiPat = nullptr; + if (self->m_multiPat) { + Pattern_destroy(self->m_multiPat); + PatternBuilder_freePattern(self->m_multiPat); + self->m_multiPat = NULL; } if (isMultiLedPatternID(pat)) { - m_multiPat = PatternBuilder::makeMulti(pat, args); - if (m_multiPat) { + self->m_multiPat = (Pattern *)PatternBuilder_makeMulti(pat, args); + if (self->m_multiPat) { // they could set PATTERN_NONE to clear - m_multiPat->setColorset(newSet); + Pattern_setColorset(self->m_multiPat, &newSet); } // TODO: don't clear single leds if we're setting multi // but for now it's easier to just clear them - clearPattern(LED_ALL_SINGLE); + Mode_clearPattern(self, LED_ALL_SINGLE); return true; } if (pos == LED_MULTI) { @@ -554,12 +574,12 @@ bool Mode::setPattern(PatternID pat, LedPos pos, const PatternArgs *args, const #endif case LED_ALL_SINGLE: #if FIXED_LED_COUNT == 0 - if (!m_singlePats) { + if (!self->m_singlePats) { return false; } #endif for (LedPos pos = LED_FIRST; pos < LED_COUNT; ++pos) { - if (!setPattern(pat, pos, args, &newSet)) { + if (!Mode_setPattern(self, pat, pos, args, &newSet)) { return false; } } @@ -567,68 +587,78 @@ bool Mode::setPattern(PatternID pat, LedPos pos, const PatternArgs *args, const return true; default: #if FIXED_LED_COUNT == 0 - if (!m_singlePats) { + if (!self->m_singlePats) { return false; } #endif if (pos >= LED_COUNT) { return false; } - if (m_singlePats[pos]) { - delete m_singlePats[pos]; + if (self->m_singlePats[pos]) { + Pattern_destroy(self->m_singlePats[pos]); + PatternBuilder_freePattern(self->m_singlePats[pos]); + self->m_singlePats[pos] = NULL; } - m_singlePats[pos] = PatternBuilder::makeSingle(pat, args); + self->m_singlePats[pos] = PatternBuilder_makeSingle(pat, args); // they could set PATTERN_NONE to clear - if (m_singlePats[pos]) { - m_singlePats[pos]->setColorset(newSet); - m_singlePats[pos]->bind(pos); + if (self->m_singlePats[pos]) { + Pattern_setColorset(self->m_singlePats[pos], &newSet); + Pattern_bind(self->m_singlePats[pos], pos); } return true; } return false; } -bool Mode::setPatternMap(LedMap map, PatternID pat, const PatternArgs *args, const Colorset *set) +bool Mode_setPatternMap(Mode *self, LedMap map, PatternID pat, const PatternArgs *args, const Colorset *set) { + MAP_FOREACH_LED(map) { - if (!setPattern(pat, pos, args, set)) { + if (!Mode_setPattern(self, pat, pos, args, set)) { return false; } } return true; } -void Mode::copyPatternFrom(const Mode *other, LedPos to, LedPos from) +void Mode_copyPatternFrom(Mode *self, const Mode *other, LedPos to, LedPos from) { + if (to >= LED_COUNT || from >= LED_COUNT) { return; } - delete m_singlePats[to]; - m_singlePats[to] = PatternBuilder::dupe(other->m_singlePats[from]); + if (self->m_singlePats[to]) { + Pattern_destroy(self->m_singlePats[to]); + PatternBuilder_freePattern(self->m_singlePats[to]); + self->m_singlePats[to] = NULL; + } + self->m_singlePats[to] = PatternBuilder_dupe(other->m_singlePats[from]); } -void Mode::swapPatterns(LedPos a, LedPos b) +void Mode_swapPatterns(Mode *self, LedPos a, LedPos b) { + if (a >= LED_COUNT || b >= LED_COUNT) { return; } - Pattern *temp = m_singlePats[a]; - m_singlePats[a] = m_singlePats[b]; - m_singlePats[b] = temp; + Pattern *temp = self->m_singlePats[a]; + self->m_singlePats[a] = self->m_singlePats[b]; + self->m_singlePats[b] = temp; } // set colorset at a specific position -bool Mode::setColorset(const Colorset &set, LedPos pos) +bool Mode_setColorset(Mode *self, const Colorset *set, LedPos pos) { + switch (pos) { case LED_ANY: case LED_ALL: // fallthrough #if VORTEX_SLIM == 0 case LED_MULTI: - if (m_multiPat) { - m_multiPat->setColorset(set); + if (self->m_multiPat) { + Pattern_setColorset(self->m_multiPat, set); } if (pos == LED_MULTI) { // don't fallthrough if actually multi, it's possible @@ -639,25 +669,25 @@ bool Mode::setColorset(const Colorset &set, LedPos pos) #endif case LED_ALL_SINGLE: #if FIXED_LED_COUNT == 0 - if (!m_singlePats) { + if (!self->m_singlePats) { return false; } #endif for (LedPos pos = LED_FIRST; pos < LED_COUNT; ++pos) { - if (m_singlePats[pos]) { - m_singlePats[pos]->setColorset(set); + if (self->m_singlePats[pos]) { + Pattern_setColorset(self->m_singlePats[pos], set); } } // actually break here return true; default: #if FIXED_LED_COUNT == 0 - if (!m_singlePats) { + if (!self->m_singlePats) { return false; } #endif - if (pos < LED_COUNT && m_singlePats[pos]) { - m_singlePats[pos]->setColorset(set); + if (pos < LED_COUNT && self->m_singlePats[pos]) { + Pattern_setColorset(self->m_singlePats[pos], set); return true; } break; @@ -666,86 +696,97 @@ bool Mode::setColorset(const Colorset &set, LedPos pos) } // set colorset at each position in a map -bool Mode::setColorsetMap(LedMap map, const Colorset &set) +bool Mode_setColorsetMap(Mode *self, LedMap map, const Colorset *set) { + MAP_FOREACH_LED(map) { - if (!setColorset(set, pos)) { + if (!Mode_setColorset(self, set, pos)) { return false; } } return true; } -void Mode::clearPattern(LedPos pos) +void Mode_clearPattern(Mode *self, LedPos pos) { - setPattern(PATTERN_NONE, pos); + + Mode_setPattern(self, PATTERN_NONE, pos, NULL, NULL); } -void Mode::clearPatternMap(LedMap map) +void Mode_clearPatternMap(Mode *self, LedMap map) { + MAP_FOREACH_LED(map) { - clearPattern(pos); + Mode_clearPattern(self, pos); } } -void Mode::clearColorset(LedPos pos) +void Mode_clearColorset(Mode *self, LedPos pos) { + Colorset empty; - setColorset(empty, pos); + Colorset_make(&empty); + Mode_setColorset(self, &empty, pos); } -void Mode::clearColorsetMap(LedMap map) +void Mode_clearColorsetMap(Mode *self, LedMap map) { + MAP_FOREACH_LED(map) { - clearColorset(pos); + Mode_clearColorset(self, pos); } } -void Mode::setArg(uint8_t param, uint8_t value, LedMap map) +void Mode_setArg(Mode *self, uint8_t param, uint8_t value, LedMap map) { + MAP_FOREACH_LED(map) { - Pattern *pat = getPattern(pos); + Pattern *pat = Mode_getPatternMutable(self, pos); if (!pat) { continue; } - pat->setArg(param, value); + Pattern_setArg(pat, param, value); } } -uint8_t Mode::getArg(uint8_t index, LedPos pos) +uint8_t Mode_getArg(Mode *self, uint8_t index, LedPos pos) { - Pattern *pat = getPattern(pos); + + Pattern *pat = Mode_getPatternMutable(self, pos); if (!pat) { return 0; } - return pat->getArg(index); + return Pattern_getArg(pat, index); } -ModeFlags Mode::getFlags() const +ModeFlags Mode_getFlags(const Mode *self) { + ModeFlags flags = 0; - if (hasMultiLed()) flags |= MODE_FLAG_MULTI_LED; - if (hasSingleLed()) flags |= MODE_FLAG_SINGLE_LED; - if (hasSameSingleLed()) flags |= MODE_FLAG_ALL_SAME_SINGLE; - if (hasSparseSingleLed()) flags |= MODE_FLAG_SPARSE_SINGLES; + if (Mode_hasMultiLed(self)) flags |= MODE_FLAG_MULTI_LED; + if (Mode_hasSingleLed(self)) flags |= MODE_FLAG_SINGLE_LED; + if (Mode_hasSameSingleLed(self)) flags |= MODE_FLAG_ALL_SAME_SINGLE; + if (Mode_hasSparseSingleLed(self)) flags |= MODE_FLAG_SPARSE_SINGLES; return flags; } // whether a multi-led pattern is present in the mode -bool Mode::hasMultiLed() const +bool Mode_hasMultiLed(const Mode *self) { + #if VORTEX_SLIM == 0 - return (m_multiPat != nullptr); + return (self->m_multiPat != NULL); #else return false; #endif } // whether at least one single-led pattern is present in the mode -bool Mode::hasSingleLed() const +bool Mode_hasSingleLed(const Mode *self) { + for (LedPos i = LED_FIRST; i < MODE_LEDCOUNT; ++i) { - if (m_singlePats[i]) { + if (self->m_singlePats[i]) { return true; } } @@ -753,28 +794,30 @@ bool Mode::hasSingleLed() const } // are all the single led patterns and colorsets equal? -bool Mode::hasSameSingleLed() const +bool Mode_hasSameSingleLed(const Mode *self) { - Pattern *firstPat = nullptr; + + Pattern *firstPat = NULL; for (LedPos i = LED_FIRST; i < MODE_LEDCOUNT; ++i) { - if (!m_singlePats[i]) { + if (!self->m_singlePats[i]) { continue; } if (!firstPat) { - firstPat = m_singlePats[i]; + firstPat = self->m_singlePats[i]; continue; } - if (!firstPat->equals(m_singlePats[i])) { + if (!Pattern_equals(firstPat, self->m_singlePats[i])) { return false; } } // all the same if at least one pattern was found - return (firstPat != nullptr); + return (firstPat != NULL); } -bool Mode::hasSparseSingleLed() const +bool Mode_hasSparseSingleLed(const Mode *self) { - switch (getSingleLedMap()) { + + switch (Mode_getSingleLedMap(self)) { case MAP_LED_ALL: case 0: // if all or none are set it's not sparse @@ -785,39 +828,42 @@ bool Mode::hasSparseSingleLed() const } } -bool Mode::isEmpty() const +bool Mode_isEmpty(const Mode *self) { + #if VORTEX_SLIM == 0 - if (m_multiPat && m_multiPat->getPatternID() != PATTERN_NONE) { + if (self->m_multiPat && Pattern_getPatternID(self->m_multiPat) != PATTERN_NONE) { return false; } #endif for (LedPos i = LED_FIRST; i < MODE_LEDCOUNT; ++i) { // if any patterns are non-null and not PATTERN_NONE - if (m_singlePats[i] && m_singlePats[i]->getPatternID() != PATTERN_NONE) { - // then this mode is not empty + if (self->m_singlePats[i] && Pattern_getPatternID(self->m_singlePats[i]) != PATTERN_NONE) { + // then self mode is not empty return false; } } return true; } -LedMap Mode::getSingleLedMap() const +LedMap Mode_getSingleLedMap(const Mode *self) { + LedMap map = 0; for (LedPos pos = LED_FIRST; pos < MODE_LEDCOUNT; pos++) { - if (m_singlePats[pos]) { - ledmapSetLed(map, pos); + if (self->m_singlePats[pos]) { + ledmapSetLed(&map, pos); } } return map; } -// is this a multi-led pattern in the mode? -bool Mode::isMultiLed() const +// is self a multi-led pattern in the mode? +bool Mode_isMultiLed(const Mode *self) { + #if VORTEX_SLIM == 0 - return hasMultiLed() && !hasSingleLed(); + return Mode_hasMultiLed(self) && !Mode_hasSingleLed(self); #else return false; #endif @@ -828,8 +874,9 @@ bool Mode::isMultiLed() const #include // Mode::Test function -void Mode::test() +void Mode_test(Mode *self) { + INFO_LOG("== Beginning Mode Tests ==\n"); INFO_LOG("= Testing Mode::setPattern =\n"); @@ -837,14 +884,14 @@ void Mode::test() Mode modeTest; assert(modeTest.getMultiPatID() == PATTERN_NONE); - assert(modeTest.getMultiColorset() == nullptr); - assert(modeTest.getMultiPat() == nullptr); + assert(modeTest.getMultiColorset() == NULL); + assert(modeTest.getMultiPat() == NULL); // Check if default constructor initializes Mode object correctly for (LedPos pos = LED_FIRST; pos < LED_COUNT; ++pos) { assert(modeTest.getPatternIDAt(pos) == PATTERN_NONE); - assert(modeTest.getColorsetAt(pos) == nullptr); - assert(modeTest.getPatternAt(pos) == nullptr); + assert(modeTest.getColorsetAt(pos) == NULL); + assert(modeTest.getPatternAt(pos) == NULL); } INFO_LOG("Test Mode default constructor passed.\n"); @@ -1084,7 +1131,7 @@ void Mode::test() modeSetClearAtTest.clearPatternAt(LED_5); assert(modeSetClearAtTest.getPatternIDAt(LED_5) == PATTERN_NONE); - assert(modeSetClearAtTest.getColorsetAt(LED_5) == nullptr); + assert(modeSetClearAtTest.getColorsetAt(LED_5) == NULL); INFO_LOG("Test setPatternAt and clearPatternAt for specific position passed.\n"); // Test setColorsetAt for specific position and clearColorsetAt for a specific position @@ -1143,7 +1190,7 @@ void Mode::test() modeInvalidLEDPositionClearPatternTest.setPattern(PATTERN_BLENDSTROBE); modeInvalidLEDPositionClearPatternTest.clearPatternAt(invalidPos); for (LedPos pos = LED_FIRST; pos < LED_COUNT; ++pos) { - assert(modeInvalidLEDPositionClearPatternTest.getPatternAt(pos) != nullptr); + assert(modeInvalidLEDPositionClearPatternTest.getPatternAt(pos) != NULL); } INFO_LOG("Test clearPatternAt with invalid LED position passed.\n"); @@ -1152,7 +1199,7 @@ void Mode::test() modeInvalidLEDPositionClearColorsetTest.setPattern(PATTERN_BLEND); modeInvalidLEDPositionClearColorsetTest.clearColorsetAt(invalidPos); for (LedPos pos = LED_FIRST; pos < LED_COUNT; ++pos) { - assert(modeInvalidLEDPositionClearColorsetTest.getColorsetAt(pos) != nullptr); + assert(modeInvalidLEDPositionClearColorsetTest.getColorsetAt(pos) != NULL); } INFO_LOG("Test clearColorsetAt with invalid LED position passed.\n"); diff --git a/VortexEngine/src/Modes/Mode.h b/VortexEngine/src/Modes/Mode.h index 3c0c574b6d..4283d0bcde 100644 --- a/VortexEngine/src/Modes/Mode.h +++ b/VortexEngine/src/Modes/Mode.h @@ -1,17 +1,14 @@ #ifndef MODE_H #define MODE_H +#include #include "../Leds/LedTypes.h" #include "../Patterns/Patterns.h" #include "../VortexConfig.h" #include "DefaultModes.h" -class SingleLedPattern; -class MultiLedPattern; -class PatternArgs; -class ByteStream; -class Colorset; -class Pattern; +#include "../Patterns/Pattern.h" +#include "../Colors/Colorset.h" typedef uint8_t ModeFlags; @@ -29,136 +26,134 @@ typedef uint8_t ModeFlags; #define MODE_FLAG_SPARSE_SINGLES (1 << 3) // A mode is the container for instances of patterns. A pattern -// must be bound to a colorset and led position with bind() and -// the Mode class allows that to be done in one step. Other things -// can be done in the Mode class such as accelerometer reaction, +// must be bound to a colorset and led position with Pattern_bind() +// and the mode allows that to be done in one step. Other things +// can be done in the mode such as accelerometer reaction, // microphone reaction, timed colorset or pattern switches, or any // other kind of logic that would involve manipulating the pattern // or colorset as a whole -class Mode +typedef struct Mode { -public: +#if VORTEX_SLIM == 0 + // the multi-led pattern slot is only present in non-slim builds + Pattern *m_multiPat; +#endif #if FIXED_LED_COUNT == 0 - Mode(uint8_t numLeds); + // the number of leds the mode is targetting + uint8_t m_numLeds; + // list of pointers to Patterns, one for each led + Pattern **m_singlePats; +#else + Pattern *m_singlePats[LED_COUNT]; #endif - Mode(); - - Mode(const DefaultModeEntry &entry); - Mode(PatternID id, const Colorset &set); - Mode(PatternID id, const PatternArgs &args, const Colorset &set); - Mode(PatternID id, const PatternArgs *args, const Colorset *set); - Mode(const Mode *other); - ~Mode(); - - // copy and assignment operators - Mode(const Mode &other); - void operator=(const Mode &other); - - // equality operators - bool operator==(const Mode &other) const; - bool operator!=(const Mode &other) const; - - // initialize the mode to initial state - void init(); - - // Play the mode - void play(); - - // save/load from a buffer for individual mode sharing - bool saveToBuffer(ByteStream &saveBuffer, uint8_t numLeds = 0) const; - bool loadFromBuffer(ByteStream &saveBuffer); +} Mode; - // save the mode to serial - bool serialize(ByteStream &buffer, uint8_t numLeds = 0) const; - // load the mode from serial (optional led count) - bool unserialize(ByteStream &buffer); - - // mode comparison - bool equals(const Mode *other) const; +// initialize the mode to its initial state +void Mode_init(Mode *self); +// initialize all the patterns held by the mode (play/led setup) +void Mode_initPatterns(Mode *self); +#if FIXED_LED_COUNT == 0 +// initialize the mode with a specific led count +void Mode_initLeds(Mode *self, uint8_t numLeds); +#endif +// initialize the mode from a default mode table entry +void Mode_initDefault(Mode *self, const DefaultModeEntry *entry); +// initialize the mode with a single pattern across all leds +void Mode_initPatternSet(Mode *self, PatternID id, const Colorset *set); +void Mode_initPatternArgsSet(Mode *self, PatternID id, const PatternArgs *args, const Colorset *set); +// deep copy another mode into this one (initializes self first) +void Mode_copy(Mode *self, const Mode *other); +// release all patterns held by the mode +void Mode_destroy(Mode *self); + +// copy the contents of another mode over this one (assignment operator) +void Mode_assign(Mode *self, const Mode *other); + +// equality operators +bool Mode_opEquals(const Mode *self, const Mode *other); +bool Mode_opNotEquals(const Mode *self, const Mode *other); + +// Play the mode +void Mode_play(Mode *self); + +// save/load from a buffer for individual mode sharing +bool Mode_saveToBuffer(const Mode *self, ByteStream *saveBuffer, uint8_t numLeds); +bool Mode_loadFromBuffer(Mode *self, ByteStream *saveBuffer); + +// save the mode to serial +bool Mode_serialize(const Mode *self, ByteStream *buffer, uint8_t numLeds); +// load the mode from serial (optional led count) +bool Mode_unserialize(Mode *self, ByteStream *buffer); + +// mode comparison +bool Mode_equals(const Mode *self, const Mode *other); #if FIXED_LED_COUNT == 0 - // change the internal pattern count in the mode object - bool setLedCount(uint8_t numLeds); +// change the internal pattern count in the mode object +bool Mode_setLedCount(Mode *self, uint8_t numLeds); #endif - uint8_t getLedCount() const; +uint8_t Mode_getLedCount(const Mode *self); - // get the pattern at a position - const Pattern *getPattern(LedPos pos = LED_ANY) const; - Pattern *getPattern(LedPos pos = LED_ANY); +// get the pattern at a position +const Pattern *Mode_getPattern(const Mode *self, LedPos pos); +Pattern *Mode_getPatternMutable(Mode *self, LedPos pos); - // get the colorset at a position - const Colorset getColorset(LedPos pos = LED_ANY) const; - Colorset getColorset(LedPos pos = LED_ANY); +// get the colorset at a position +Colorset Mode_getColorset(const Mode *self, LedPos pos); - // get the pattern ID of the given pattern - PatternID getPatternID(LedPos pos = LED_ANY) const; +// get the pattern ID of the given pattern +PatternID Mode_getPatternID(const Mode *self, LedPos pos); - // change the pattern on a mode (NOTE: you may need to call init() after!) - bool setPattern(PatternID pat, LedPos pos = LED_ANY, const PatternArgs *args = nullptr, const Colorset *set = nullptr); - bool setPatternMap(LedMap pos, PatternID pat, const PatternArgs *args = nullptr, const Colorset *set = nullptr); +// change the pattern on a mode (NOTE: you may need to call Mode_init() after!) +bool Mode_setPattern(Mode *self, PatternID pat, LedPos pos, const PatternArgs *args, const Colorset *set); +bool Mode_setPatternMap(Mode *self, LedMap map, PatternID pat, const PatternArgs *args, const Colorset *set); - // copy a pattern from another mode ledpos into this mode - void copyPatternFrom(const Mode *other, LedPos to, LedPos from); +// copy a pattern from another mode ledpos into this mode +void Mode_copyPatternFrom(Mode *self, const Mode *other, LedPos to, LedPos from); - // swap two patterns - void swapPatterns(LedPos a, LedPos b); +// swap two patterns +void Mode_swapPatterns(Mode *self, LedPos a, LedPos b); - // set colorset at a specific position - bool setColorset(const Colorset &set, LedPos pos = LED_ANY); - // set colorset at each position in a map - bool setColorsetMap(LedMap map, const Colorset &set); +// set colorset at a specific position +bool Mode_setColorset(Mode *self, const Colorset *set, LedPos pos); +// set colorset at each position in a map +bool Mode_setColorsetMap(Mode *self, LedMap map, const Colorset *set); - // clear stored patterns in various ways - void clearPattern(LedPos pos = LED_ALL); - void clearPatternMap(LedMap map); +// clear stored patterns in various ways +void Mode_clearPattern(Mode *self, LedPos pos); +void Mode_clearPatternMap(Mode *self, LedMap map); - // clear colorset in various ways - void clearColorset(LedPos pos = LED_ALL); - void clearColorsetMap(LedMap map); +// clear colorset in various ways +void Mode_clearColorset(Mode *self, LedPos pos); +void Mode_clearColorsetMap(Mode *self, LedMap map); - // set/get a single argument on various positions - void setArg(uint8_t index, uint8_t value, LedMap map = MAP_LED_ALL); - uint8_t getArg(uint8_t index, LedPos pos = LED_ANY); +// set/get a single argument on various positions +void Mode_setArg(Mode *self, uint8_t param, uint8_t value, LedMap map); +uint8_t Mode_getArg(Mode *self, uint8_t index, LedPos pos); - // get the flags associated with this mode - ModeFlags getFlags() const; +// get the flags associated with this mode +ModeFlags Mode_getFlags(const Mode *self); - // whether a multi-led pattern is present in the mode - bool hasMultiLed() const; - // whether at least one single-led pattern is present in the mode - bool hasSingleLed() const; - // whether this mode has all same single-led patterns - bool hasSameSingleLed() const; - // whether this mode has sparse single-led patterns (missing some slots) - bool hasSparseSingleLed() const; +// whether a multi-led pattern is present in the mode +bool Mode_hasMultiLed(const Mode *self); +// whether at least one single-led pattern is present in the mode +bool Mode_hasSingleLed(const Mode *self); +// whether this mode has all same single-led patterns +bool Mode_hasSameSingleLed(const Mode *self); +// whether this mode has sparse single-led patterns (missing some slots) +bool Mode_hasSparseSingleLed(const Mode *self); - // whether the mode contains no patterns, or all patterns are PATTERN_NONE - bool isEmpty() const; +// whether the mode contains no patterns, or all patterns are PATTERN_NONE +bool Mode_isEmpty(const Mode *self); - // get the led map of which singles are set - LedMap getSingleLedMap() const; +// get the led map of which singles are set +LedMap Mode_getSingleLedMap(const Mode *self); - // whether this mode purely a multi-led pattern with no singles - bool isMultiLed() const; +// whether this mode purely a multi-led pattern with no singles +bool Mode_isMultiLed(const Mode *self); #if MODES_TEST == 1 - static void test(); -#endif - -private: -#if VORTEX_SLIM == 0 - // the multi-led pattern slot is only present in non-slim builds - Pattern *m_multiPat; -#endif - // a list of slots for each single led pattern -#if FIXED_LED_COUNT == 0 - // the number of leds the mode is targetting - uint8_t m_numLeds; - // list of pointers to Patterns, one for each led - Pattern **m_singlePats; -#else - Pattern *m_singlePats[LED_COUNT]; +void Mode_test(Mode *self); #endif -}; #endif diff --git a/VortexEngine/src/Modes/Modes.cpp b/VortexEngine/src/Modes/Modes.cpp index 759f0f7b0f..af887ed9b4 100644 --- a/VortexEngine/src/Modes/Modes.cpp +++ b/VortexEngine/src/Modes/Modes.cpp @@ -2,6 +2,7 @@ #include "DefaultModes.h" #include "../VortexEngine.h" +#include "../Memory/Memory.h" #include "../Patterns/Pattern.h" @@ -16,147 +17,166 @@ #include "../Log/Log.h" // static members -bool Modes::m_loaded = false; -uint8_t Modes::m_curMode = 0; -uint8_t Modes::m_numModes = 0; +bool Modes_m_loaded = false; +uint8_t Modes_m_curMode = 0; +uint8_t Modes_m_numModes = 0; // the current instantiated mode and it's respective link -Modes::ModeLink *Modes::m_pCurModeLink = nullptr; +ModeLink *Modes_m_pCurModeLink = NULL; // list of serialized version of bufers -Modes::ModeLink *Modes::m_storedModes = nullptr; +ModeLink *Modes_m_storedModes = NULL; // global flags for all modes -uint8_t Modes::m_globalFlags = 0; +uint32_t Modes_m_globalFlags = 0; // the last switch time of the modes -uint32_t Modes::m_lastSwitchTime = 0; +uint32_t Modes_m_lastSwitchTime = 0; -bool Modes::init() +// the pools the mode links and the instantiated modes are allocated from +static MemPool s_modeLinkPool; +static MEMPOOL_STORAGE(s_modeLinkStorage, sizeof(ModeLink), MODELINK_POOL_SIZE); +static MemPool s_modePool; +static MEMPOOL_STORAGE(s_modeStorage, sizeof(Mode), MODE_POOL_SIZE); + +bool Modes_init() { + MemPool_init(&s_modeLinkPool, s_modeLinkStorage, sizeof(ModeLink), MODELINK_POOL_SIZE); + MemPool_init(&s_modePool, s_modeStorage, sizeof(Mode), MODE_POOL_SIZE); #if MODES_TEST == 1 - Mode::test(); - test(); + Mode_test(); + Modes_test(); return true; #endif ByteStream headerBuffer; - if (!Storage::read(0, headerBuffer) || !unserializeSaveHeader(headerBuffer)) { + ByteStream_make(&headerBuffer); + if (!Storage_read(0, &headerBuffer) || !Modes_unserializeSaveHeader(&headerBuffer)) { // cannot read or load header? corrupted header? // just mark this as a new firmware and it will trigger a fresh header // write later in the main loop when modes get loaded - m_globalFlags |= MODES_FLAG_NEW_FIRMWARE; + Modes_m_globalFlags |= MODES_FLAG_NEW_FIRMWARE; } - m_loaded = false; + ByteStream_destroy(&headerBuffer); + Modes_m_loaded = false; #ifdef VORTEX_LIB // enable the adv menus by default in vortex lib - m_globalFlags |= MODES_FLAG_ADV_MENUS; + Modes_m_globalFlags |= MODES_FLAG_ADV_MENUS; #endif return true; } -void Modes::cleanup() +void Modes_cleanup() { - clearModes(); + Modes_clearModes(); } -bool Modes::load() +bool Modes_load() { - if (m_loaded) { + if (Modes_m_loaded) { return true; } // try to load the saved settings or set defaults - if (!loadStorage()) { - if (!setDefaults()) { + if (!Modes_loadStorage()) { + if (!Modes_setDefaults()) { return false; } - if (!saveStorage()) { + if (!Modes_saveStorage()) { return false; } } - m_loaded = true; + Modes_m_loaded = true; return true; } -void Modes::play() +void Modes_play() { - if (!m_numModes) { + if (!Modes_m_numModes) { // nothing to do just keep the leds cleared - Leds::clearAll(); + Leds_clearAll(); return; } // should be able to initialize the current mode - if (!m_pCurModeLink && !initCurMode()) { + if (!Modes_m_pCurModeLink && !Modes_initCurMode(false)) { DEBUG_LOG("Error failed to load any modes!"); return; } // shortclick either turns off the lights, cycles to the next mode // or possibly locks the lights based on the situation - if (g_pButton->onShortClick()) { - if (Modes::oneClickModeEnabled()) { + if (Button_onShortClick(g_pButton)) { + if (Modes_oneClickModeEnabled()) { // enter sleep doesn't return on arduino, but it does on vortexlib // so we need to return right after -- we can't just use an else // don't need to save when switching on/off in one click mode - VortexEngine::enterSleep(false); + VortexEngine_enterSleep(false); return; } - nextMode(); + Modes_nextMode(); } // play the current mode - m_pCurModeLink->play(); + ModeLink_play(Modes_m_pCurModeLink); } // full save/load to/from buffer -bool Modes::saveToBuffer(ByteStream &modesBuffer) +bool Modes_saveToBuffer(ByteStream *modesBuffer) { // This API is removed for the DUO return true; } // load modes from a save buffer -bool Modes::loadFromBuffer(ByteStream &modesBuffer) +bool Modes_loadFromBuffer(ByteStream *modesBuffer) { - if (!modesBuffer.decompress()) { + if (!ByteStream_decompress(modesBuffer)) { // failed to decompress? return false; } - if (!unserializeSaveHeader(modesBuffer)) { + if (!Modes_unserializeSaveHeader(modesBuffer)) { return false; } // now just unserialize the list of modes - if (!unserialize(modesBuffer)) { + if (!Modes_unserialize(modesBuffer)) { return false; } - if (oneClickModeEnabled()) { + if (Modes_oneClickModeEnabled()) { // set the current mode to the startup mode - switchToStartupMode(); + Modes_switchToStartupMode(); } return true; } -bool Modes::saveHeader() +bool Modes_saveHeader() { - ByteStream headerBuffer(MAX_MODE_SIZE); - if (!serializeSaveHeader(headerBuffer)) { + ByteStream headerBuffer; + ByteStream_make(&headerBuffer); + ByteStream_init(&headerBuffer, MAX_MODE_SIZE); + if (!Modes_serializeSaveHeader(&headerBuffer)) { + ByteStream_destroy(&headerBuffer); return false; } - if (!Storage::write(0, headerBuffer)) { + if (!Storage_write(0, &headerBuffer)) { + ByteStream_destroy(&headerBuffer); return false; } + ByteStream_destroy(&headerBuffer); return true; } -bool Modes::loadHeader() +bool Modes_loadHeader() { ByteStream headerBuffer; + ByteStream_make(&headerBuffer); // only read storage if the modebuffer isn't filled - if (!Storage::read(0, headerBuffer) || !headerBuffer.size()) { + if (!Storage_read(0, &headerBuffer) || !headerBuffer.m_pData->size) { DEBUG_LOG("Empty buffer read from storage"); // this kinda sucks whatever they had loaded is gone + ByteStream_destroy(&headerBuffer); return false; } // this erases what is stored before we know whether there is data // but it's the easiest way to just re-load new data from storage - clearModes(); + Modes_clearModes(); // read the header and load the data - if (!unserializeSaveHeader(headerBuffer)) { + if (!Modes_unserializeSaveHeader(&headerBuffer)) { + ByteStream_destroy(&headerBuffer); return false; } + ByteStream_destroy(&headerBuffer); // NOTE: We do not bother loading the number of modes because // we can't really do anything with it anyway return true; @@ -164,72 +184,83 @@ bool Modes::loadHeader() // NOTE: Flash storage is limited to about 10,000 writes so // use this function sparingly! -bool Modes::saveStorage() +bool Modes_saveStorage() { DEBUG_LOG("Saving modes..."); - saveHeader(); + Modes_saveHeader(); // make sure the current mode is saved in case it has changed somehow - saveCurMode(); + Modes_saveCurMode(); // uninstantiate cur mode so we have stack space to serialize - if (m_pCurModeLink) { - m_pCurModeLink->uninstantiate(); + if (Modes_m_pCurModeLink) { + ModeLink_uninstantiate(Modes_m_pCurModeLink); } uint8_t i = 0; - ModeLink *ptr = m_storedModes; + ModeLink *ptr = Modes_m_storedModes; while (ptr && i < MAX_MODES) { - ByteStream modeBuffer(MAX_MODE_SIZE); + ByteStream modeBuffer; + ByteStream_make(&modeBuffer); + ByteStream_init(&modeBuffer, MAX_MODE_SIZE); // instantiate the mode temporarily - Mode *mode = ptr->instantiate(); + Mode *mode = ModeLink_instantiate(ptr); if (!mode) { ERROR_OUT_OF_MEMORY(); + ByteStream_destroy(&modeBuffer); return false; } // serialize it into the target modes buffer - if (!mode->serialize(modeBuffer)) { + if (!Mode_serialize(mode, &modeBuffer, 0)) { + ByteStream_destroy(&modeBuffer); return false; } // just uninstansiate the mode after serializing - ptr->uninstantiate(); + ModeLink_uninstantiate(ptr); // next mode - ptr = ptr->next(); + ptr = ModeLink_next(ptr); // now write this mode into a storage slot (skip first slot, that's header) - if (!Storage::write(++i, modeBuffer)) { + if (!Storage_write(++i, &modeBuffer)) { + ByteStream_destroy(&modeBuffer); return false; } + ByteStream_destroy(&modeBuffer); } // reinstanstiate the current mode - if (m_pCurModeLink && !m_pCurModeLink->instantiate()) { + if (Modes_m_pCurModeLink && !ModeLink_instantiate(Modes_m_pCurModeLink)) { return false; } - DEBUG_LOGF("Serialized num modes: %u", m_numModes); + DEBUG_LOGF("Serialized num modes: %u", Modes_m_numModes); return true; } -bool Modes::loadStorage() +bool Modes_loadStorage() { // NOTE: We could call loadHeader here but then we wouldn't have the headerBuffer // and in turn wouldn't be able to unserialize the number of modes. The number // of modes is a weird case, it's technically part of the mode list not the // header but it is stored in the same storage slot as the header ByteStream headerBuffer; + ByteStream_make(&headerBuffer); // only read storage if the modebuffer isn't filled - if (!Storage::read(0, headerBuffer) || !headerBuffer.size()) { + if (!Storage_read(0, &headerBuffer) || !headerBuffer.m_pData->size) { DEBUG_LOG("Empty buffer read from storage"); // this kinda sucks whatever they had loaded is gone + ByteStream_destroy(&headerBuffer); return false; } // this erases what is stored before we know whether there is data // but it's the easiest way to just re-load new data from storage - clearModes(); + Modes_clearModes(); // read the header and load the data - if (!unserializeSaveHeader(headerBuffer)) { + if (!Modes_unserializeSaveHeader(&headerBuffer)) { + ByteStream_destroy(&headerBuffer); return false; } // unserialize the number of modes next uint8_t numModes = 0; - if (!headerBuffer.unserialize8(&numModes)) { + if (!ByteStream_unserialize8(&headerBuffer, &numModes)) { + ByteStream_destroy(&headerBuffer); return false; } + ByteStream_destroy(&headerBuffer); if (!numModes) { DEBUG_LOG("Did not find any modes"); // this kinda sucks whatever they had loaded is gone @@ -237,34 +268,38 @@ bool Modes::loadStorage() } // iterate each mode and read it out of it's storage slot then add it for (uint8_t i = 0; i < numModes; ++i) { - ByteStream modeBuffer(MAX_MODE_SIZE); + ByteStream modeBuffer; + ByteStream_make(&modeBuffer); + ByteStream_init(&modeBuffer, MAX_MODE_SIZE); // read each mode from a storage slot and load it - if (!Storage::read(i + 1, modeBuffer) || !addSerializedMode(modeBuffer)) { + if (!Storage_read(i + 1, &modeBuffer) || !Modes_addSerializedMode(&modeBuffer)) { + ByteStream_destroy(&modeBuffer); return false; } + ByteStream_destroy(&modeBuffer); } - if (oneClickModeEnabled()) { + if (Modes_oneClickModeEnabled()) { // set the current mode to the startup mode - switchToStartupMode(); + Modes_switchToStartupMode(); } return true; } -bool Modes::serializeSaveHeader(ByteStream &saveBuffer) +bool Modes_serializeSaveHeader(ByteStream *saveBuffer) { // serialize the engine version into the modes buffer - if (!VortexEngine::serializeVersion(saveBuffer)) { + if (!VortexEngine_serializeVersion(saveBuffer)) { return false; } - if (!saveBuffer.serialize8(m_globalFlags)) { + if (!ByteStream_serialize8(saveBuffer, Modes_m_globalFlags)) { return false; } // serialize the global brightness - if (!saveBuffer.serialize8((uint8_t)Leds::getBrightness())) { + if (!ByteStream_serialize8(saveBuffer, (uint8_t)Leds_getBrightness())) { return false; } // the number of modes - if (!saveBuffer.serialize8(m_numModes)) { + if (!ByteStream_serialize8(saveBuffer, Modes_m_numModes)) { return false; } // only on the duo just save some extra stuff to the header slot @@ -273,99 +308,101 @@ bool Modes::serializeSaveHeader(ByteStream &saveBuffer) // read it out, other devices just have the version hardcoded into their // editor connection hello message. Don't do this in VortexLib because // it will alter the savefile format and break compatibility - if (!saveBuffer.serialize8((uint8_t)VORTEX_BUILD_NUMBER)) { + if (!ByteStream_serialize8(saveBuffer, (uint8_t)VORTEX_BUILD_NUMBER)) { return false; } #endif - DEBUG_LOGF("Serialized all modes, uncompressed size: %u", saveBuffer.size()); + DEBUG_LOGF("Serialized all modes, uncompressed size: %u", saveBuffer.m_pData->size()); return true; } -bool Modes::unserializeSaveHeader(ByteStream &saveHeader) +bool Modes_unserializeSaveHeader(ByteStream *saveHeader) { - if (!saveHeader.decompress()) { + if (!ByteStream_decompress(saveHeader)) { // failed to decompress? return false; } // reset the unserializer index before unserializing anything - saveHeader.resetUnserializer(); + ByteStream_resetUnserializer(saveHeader); uint8_t major = 0; uint8_t minor = 0; // unserialize the vortex version - if (!saveHeader.unserialize8(&major)) { + if (!ByteStream_unserialize8(saveHeader, &major)) { return false; } - if (!saveHeader.unserialize8(&minor)) { + if (!ByteStream_unserialize8(saveHeader, &minor)) { return false; } // check the version for incompatibility - if (!VortexEngine::checkVersion(major, minor)) { + if (!VortexEngine_checkVersion(major, minor)) { // incompatible version ERROR_LOGF("Incompatible savefile version: %u.%u", major, minor); return false; } - if (!saveHeader.unserialize8(&m_globalFlags)) { + uint8_t flags8 = 0; + if (!ByteStream_unserialize8(saveHeader, &flags8)) { return false; } + Modes_m_globalFlags = flags8; // unserialize the global brightness uint8_t brightness = 0; - if (!saveHeader.unserialize8(&brightness)) { + if (!ByteStream_unserialize8(saveHeader, &brightness)) { return false; } if (brightness) { - Leds::setBrightness(brightness); + Leds_setBrightness(brightness); } return true; } // Save all of the modes to a serial buffer -bool Modes::serialize(ByteStream &modesBuffer) +bool Modes_serialize(ByteStream *modesBuffer) { // serialize the number of modes - if (!modesBuffer.serialize8(m_numModes)) { + if (!ByteStream_serialize8(modesBuffer, Modes_m_numModes)) { return false; } // make sure the current mode is saved in case it has changed somehow - saveCurMode(); + Modes_saveCurMode(); // uninstantiate cur mode so we have stack space to serialize - if (m_pCurModeLink) { - m_pCurModeLink->uninstantiate(); + if (Modes_m_pCurModeLink) { + ModeLink_uninstantiate(Modes_m_pCurModeLink); } - ModeLink *ptr = m_storedModes; + ModeLink *ptr = Modes_m_storedModes; while (ptr) { // instantiate the mode temporarily - Mode *mode = ptr->instantiate(); + Mode *mode = ModeLink_instantiate(ptr); if (!mode) { ERROR_OUT_OF_MEMORY(); return false; } // serialize it into the target modes buffer - if (!mode->serialize(modesBuffer)) { + if (!Mode_serialize(mode, modesBuffer, 0)) { return false; } // just uninstansiate the mode after serializing - ptr->uninstantiate(); + ModeLink_uninstantiate(ptr); // next mode - ptr = ptr->next(); + ptr = ModeLink_next(ptr); } // reinstanstiate the current mode - if (m_pCurModeLink && !m_pCurModeLink->instantiate()) { + if (Modes_m_pCurModeLink && !ModeLink_instantiate(Modes_m_pCurModeLink)) { return false; } - DEBUG_LOGF("Serialized num modes: %u", m_numModes); + DEBUG_LOGF("Serialized num modes: %u", Modes_m_numModes); return true; } // load all modes from a serial buffer -bool Modes::unserialize(ByteStream &modesBuffer) +bool Modes_unserialize(ByteStream *modesBuffer) { DEBUG_LOG("Loading modes..."); // this is good on memory, but it erases what they have stored before we // know whether there is something actually saved in the serial buffer - clearModes(); + Modes_clearModes(); // unserialize the number of modes next uint8_t numModes = 0; - if (!modesBuffer.unserialize8(&numModes)) { + if (!ByteStream_unserialize8(modesBuffer, &numModes)) { return false; } if (!numModes) { @@ -378,337 +415,352 @@ bool Modes::unserialize(ByteStream &modesBuffer) // just copy the serialized mode into the internal storage because // we store the modes in a serialized manner so that they are smaller // then we unpack them when we instantiate the mode - if (!addSerializedMode(modesBuffer)) { + if (!Modes_addSerializedMode(modesBuffer)) { DEBUG_LOGF("Failed to add mode %u after unserialization", i); // clear work so far? - clearModes(); + Modes_clearModes(); return false; } } - DEBUG_LOGF("Loaded %u modes from storage (%u bytes)", numModes, modesBuffer.size()); - return (m_numModes == numModes); + DEBUG_LOGF("Loaded %u modes from storage (%u bytes)", numModes, modesBuffer.m_pData->size()); + return (Modes_m_numModes == numModes); } -bool Modes::setDefaults() +bool Modes_setDefaults() { - clearModes(); + Modes_clearModes(); // add each default mode with each of the given colors for (uint8_t i = 0; i < MAX_MODES; ++i) { - Mode defMode(defaultModes[i]); - if (!addMode(&defMode)) { + Mode defMode; + Mode_initDefault(&defMode, &defaultModes[i]); + if (!Modes_addMode(&defMode)) { ERROR_LOGF("Failed to add default mode %u", i); + Mode_destroy(&defMode); return false; } + Mode_destroy(&defMode); } return true; } -bool Modes::addSerializedMode(ByteStream &serializedMode) +bool Modes_addSerializedMode(ByteStream *serializedMode) { #if MAX_MODES != 0 - if (m_numModes >= MAX_MODES) { + if (Modes_m_numModes >= MAX_MODES) { return false; } #endif // we must unserialize then re-serialize here because the // input argument may contain other patterns in the buffer - // so we cannot just append the input arg to m_storedModes + // so we cannot just append the input arg to Modes_m_storedModes Mode tmpMode; - if (!tmpMode.unserialize(serializedMode)) { + Mode_init(&tmpMode); + if (!Mode_unserialize(&tmpMode, serializedMode)) { + Mode_destroy(&tmpMode); return false; } // initialize the mode because a pattern could theoretically serialize // differently after it has initialized - tmpMode.init(); - return addMode(&tmpMode); + Mode_initPatterns(&tmpMode); + bool rv = Modes_addMode(&tmpMode); + Mode_destroy(&tmpMode); + return rv; } -bool Modes::addModeFromBuffer(ByteStream &serializedMode) +bool Modes_addModeFromBuffer(ByteStream *serializedMode) { #if MAX_MODES != 0 - if (m_numModes >= MAX_MODES) { + if (Modes_m_numModes >= MAX_MODES) { return false; } #endif - if (!m_storedModes) { - m_storedModes = new ModeLink(serializedMode); - if (!m_storedModes) { + if (!Modes_m_storedModes) { + Modes_m_storedModes = (ModeLink *)MemPool_alloc(&s_modeLinkPool); + if (!Modes_m_storedModes) { ERROR_OUT_OF_MEMORY(); return false; } + ModeLink_initBuf(Modes_m_storedModes, serializedMode, false); } else { - if (!m_storedModes->append(serializedMode)) { + if (!ModeLink_appendBuf(Modes_m_storedModes, serializedMode)) { ERROR_OUT_OF_MEMORY(); return false; } } // increment mode counter - m_numModes++; + Modes_m_numModes++; return true; } // shift the current mode to a different position relative to current position // negative values for up, positive values for down, 0 for no move -bool Modes::shiftCurMode(int32_t offset) +bool Modes_shiftCurMode(int32_t offset) { - uint32_t newPos = (uint32_t)((int32_t)m_curMode + offset); - if (newPos >= m_numModes) { + uint32_t newPos = (uint32_t)((int32_t)Modes_m_curMode + offset); + if (newPos >= Modes_m_numModes) { return false; } - if (newPos == m_curMode) { + if (newPos == Modes_m_curMode) { return true; } // get the target mode at the position of the shift - ModeLink *target = getModeLink(newPos); + ModeLink *target = Modes_getModeLink(newPos); if (!target) { // invalid new position? return false; } // special case for moving first in list forward - if (!m_curMode && offset > 0) { + if (!Modes_m_curMode && offset > 0) { // update main list ptr - m_storedModes = m_storedModes->next(); + Modes_m_storedModes = ModeLink_next(Modes_m_storedModes); } // unlink the current link - m_pCurModeLink->unlinkSelf(); + ModeLink_unlinkSelf(Modes_m_pCurModeLink); // update the current position to reflect our new pos - m_curMode = newPos; + Modes_m_curMode = newPos; // then re-link the mode at the new spot if (offset < 0) { // link the link before our target link - target->linkBefore(m_pCurModeLink); + ModeLink_linkBefore(target, Modes_m_pCurModeLink); // special case for moving into first in list - if (!m_curMode) { - m_storedModes = m_pCurModeLink; + if (!Modes_m_curMode) { + Modes_m_storedModes = Modes_m_pCurModeLink; } } else { // link the link after our target link - target->linkAfter(m_pCurModeLink); + ModeLink_linkAfter(target, Modes_m_pCurModeLink); } return true; } -bool Modes::addMode(PatternID id, RGBColor c1, RGBColor c2, RGBColor c3, +bool Modes_addMode(PatternID id, RGBColor c1, RGBColor c2, RGBColor c3, RGBColor c4, RGBColor c5, RGBColor c6, RGBColor c7, RGBColor c8) { - Colorset set(c1, c2, c3, c4, c5, c6, c7, c8); - return addMode(id, nullptr, &set); + Colorset set; + Colorset_makeRGB(&set, c1, c2, c3, c4, c5, c6, c7, c8); + return Modes_addMode(id, NULL, &set); } -bool Modes::addMode(PatternID id, const PatternArgs *args, const Colorset *set) +bool Modes_addMode(PatternID id, const PatternArgs *args, const Colorset *set) { // max modes #if MAX_MODES != 0 - if (m_numModes >= MAX_MODES) { + if (Modes_m_numModes >= MAX_MODES) { return false; } #endif if (id >= PATTERN_COUNT) { return false; } - Mode tmpMode(id, args, set); + Mode tmpMode; + Mode_initPatternArgsSet(&tmpMode, id, args, set); // must init the mode so that it can be serialized - tmpMode.init(); + Mode_initPatterns(&tmpMode); // not a very good way to do this but it ensures the mode is // added in the same way - return addMode(&tmpMode); + bool rv = Modes_addMode(&tmpMode); + Mode_destroy(&tmpMode); + return rv; } -bool Modes::addMode(const Mode *mode) +bool Modes_addMode(const Mode *mode) { #if MAX_MODES != 0 // max modes - if (m_numModes >= MAX_MODES) { + if (Modes_m_numModes >= MAX_MODES) { return false; } #endif - if (!m_storedModes) { - m_storedModes = new ModeLink(mode); - if (!m_storedModes) { + if (!Modes_m_storedModes) { + Modes_m_storedModes = (ModeLink *)MemPool_alloc(&s_modeLinkPool); + if (!Modes_m_storedModes) { ERROR_OUT_OF_MEMORY(); return false; } + ModeLink_init(Modes_m_storedModes, mode, false); } else { - if (!m_storedModes->append(mode)) { + if (!ModeLink_appendMode(Modes_m_storedModes, mode)) { ERROR_OUT_OF_MEMORY(); return false; } } - m_numModes++; + Modes_m_numModes++; return true; } -bool Modes::updateCurMode(const Mode *mode) +bool Modes_updateCurMode(const Mode *mode) { if (!mode) { return false; } - Mode *pCur = curMode(); + Mode *pCur = Modes_curMode(); if (!pCur) { return false; } // utilize copy operator - *pCur = *mode; + Mode_assign(pCur, mode); // immediately save this mode to the internal mode storage - if (!saveCurMode()) { + if (!Modes_saveCurMode()) { return false; } // initialize the new mode - return initCurMode(); + return Modes_initCurMode(false); } // set the current active mode by index -Mode *Modes::setCurMode(uint8_t index) +Mode *Modes_setCurMode(uint8_t index) { - if (!m_numModes) { - return nullptr; + if (!Modes_m_numModes) { + return NULL; } // clear the LEDs when switching modes - Leds::clearAll(); + Leds_clearAll(); // if we have a current mode open, close it - if (m_pCurModeLink) { - m_pCurModeLink->uninstantiate(); + if (Modes_m_pCurModeLink) { + ModeLink_uninstantiate(Modes_m_pCurModeLink); } - int8_t newModeIdx = index % m_numModes; + int8_t newModeIdx = index % Modes_m_numModes; // lookup the new mode link - ModeLink *newCurLink = getModeLink(newModeIdx); + ModeLink *newCurLink = Modes_getModeLink(newModeIdx); if (!newCurLink) { // what - return nullptr; + return NULL; } // instantiate the new mode so it is ready, also so it can be checked for PATTERN_NONE - Mode *newCur = newCurLink->instantiate(); + Mode *newCur = ModeLink_instantiate(newCurLink); if (!newCur) { ERROR_OUT_OF_MEMORY(); - return nullptr; + return NULL; } // update to the new mode - m_curMode = newModeIdx; - m_pCurModeLink = newCurLink; + Modes_m_curMode = newModeIdx; + Modes_m_pCurModeLink = newCurLink; // record the current time as the last switch time - m_lastSwitchTime = Time::getCurtime(); + Modes_m_lastSwitchTime = Time_getCurtime(); // update the global startup mode to be this mode, if we turn off // and turn back on into one click mode it will select this one - Modes::setStartupMode(newModeIdx); + Modes_setStartupMode(newModeIdx); // log the change DEBUG_LOGF("Switch to Mode: %u / %u (pattern id: %u)", - m_curMode, m_numModes - 1, newCur->getPatternID()); + Modes_m_curMode, Modes_m_numModes - 1, Pattern_getPatternID(newCur)); // return the new current mode return newCur; } // the current mode -Mode *Modes::curMode() +Mode *Modes_curMode() { // empty mode list - if (!m_numModes) { - return nullptr; + if (!Modes_m_numModes) { + return NULL; } - if (!m_pCurModeLink) { - if (!initCurMode()) { + if (!Modes_m_pCurModeLink) { + if (!Modes_initCurMode(false)) { ERROR_LOG("Failed to initialize current mode"); - return nullptr; + return NULL; } } // get current mode, instantiate it if necessary - return m_pCurModeLink->instantiate(); + return ModeLink_instantiate(Modes_m_pCurModeLink); } // iterate to next mode and return it -Mode *Modes::nextMode() +Mode *Modes_nextMode() { - if (!m_numModes) { - return nullptr; + if (!Modes_m_numModes) { + return NULL; } // iterate the cur mode forward - return setCurMode(m_curMode + 1); + return Modes_setCurMode(Modes_m_curMode + 1); } // iterate to previous mode and return it -Mode *Modes::previousMode() +Mode *Modes_previousMode() { - if (!m_numModes) { - return nullptr; + if (!Modes_m_numModes) { + return NULL; } // iterate the cur mode backwards - if (!m_curMode) { - return setCurMode(numModes() - 1); + if (!Modes_m_curMode) { + return Modes_setCurMode(Modes_numModes() - 1); } - return setCurMode(m_curMode - 1); + return Modes_setCurMode(Modes_m_curMode - 1); } -Mode *Modes::nextModeSkipEmpty() +Mode *Modes_nextModeSkipEmpty() { do { - if (setCurMode(m_curMode + 1) && !curMode()->isEmpty()) { + if (Modes_setCurMode(Modes_m_curMode + 1) && !Mode_isEmpty(Modes_curMode())) { break; } - } while (m_curMode != 0); - return curMode(); + } while (Modes_m_curMode != 0); + return Modes_curMode(); } -void Modes::deleteCurMode() +void Modes_deleteCurMode() { - if (!m_numModes || !m_pCurModeLink) { + if (!Modes_m_numModes || !Modes_m_pCurModeLink) { return; } // unlink the current mode so it can be deleted and // update the current mode link accordingly - ModeLink *newCur = m_pCurModeLink->unlinkSelf(); - delete m_pCurModeLink; - m_pCurModeLink = newCur; - if (m_curMode) { - m_curMode--; + ModeLink *newCur = ModeLink_unlinkSelf(Modes_m_pCurModeLink); + ModeLink_destroy(Modes_m_pCurModeLink); + MemPool_free(&s_modeLinkPool, Modes_m_pCurModeLink); + Modes_m_pCurModeLink = newCur; + if (Modes_m_curMode) { + Modes_m_curMode--; } else { - m_storedModes = m_pCurModeLink; + Modes_m_storedModes = Modes_m_pCurModeLink; } - m_numModes--; - if (!m_numModes) { - m_storedModes = nullptr; + Modes_m_numModes--; + if (!Modes_m_numModes) { + Modes_m_storedModes = NULL; } } -void Modes::clearModes() +void Modes_clearModes() { - if (!m_numModes || !m_storedModes) { + if (!Modes_m_numModes || !Modes_m_storedModes) { return; } // delete the first node and it will delete the entire chain - delete m_storedModes; - m_pCurModeLink = nullptr; - m_storedModes = nullptr; - m_numModes = 0; + ModeLink_destroy(Modes_m_storedModes); + MemPool_free(&s_modeLinkPool, Modes_m_storedModes); + Modes_m_pCurModeLink = NULL; + Modes_m_storedModes = NULL; + Modes_m_numModes = 0; // might as well clear the leds - Leds::clearAll(); + Leds_clearAll(); } -void Modes::setStartupMode(uint8_t index) +void Modes_setStartupMode(uint8_t index) { // zero out the upper nibble to clear it - m_globalFlags &= 0x0F; + Modes_m_globalFlags &= 0x0F; // OR in the index value shifted into the upper nibble - m_globalFlags |= (index << 4) & 0xF0; + Modes_m_globalFlags |= (index << 4) & 0xF0; } -uint8_t Modes::startupMode() +uint8_t Modes_startupMode() { // return the upper nibble of the global flags - return (m_globalFlags & 0xF0) >> 4; + return (Modes_m_globalFlags & 0xF0) >> 4; } -Mode *Modes::switchToStartupMode() +Mode *Modes_switchToStartupMode() { - return setCurMode(startupMode()); + return Modes_setCurMode(Modes_startupMode()); } -bool Modes::setFlag(uint8_t flag, bool enable, bool save) +bool Modes_setFlag(uint8_t flag, bool enable, bool save) { // then actually if it's enabled ensure the upper nibble is set if (enable) { // set the cur mode index as the upper nibble - m_globalFlags |= flag; + Modes_m_globalFlags |= flag; } else { - m_globalFlags &= ~flag; + Modes_m_globalFlags &= ~flag; } DEBUG_LOGF("Toggled instant on/off to %s", enable ? "on" : "off"); if (!save) { @@ -717,14 +769,16 @@ bool Modes::setFlag(uint8_t flag, bool enable, bool save) } // otherwise need to update the global flags field of the save header in storage ByteStream headerBuffer; + ByteStream_make(&headerBuffer); // read out the storage header so we can update the flag field - if (!Storage::read(0, headerBuffer) || !headerBuffer.size()) { + if (!Storage_read(0, &headerBuffer) || !headerBuffer.m_pData->size) { + ByteStream_destroy(&headerBuffer); // if cannot read the save header then just save it normally - return saveHeader(); + return Modes_saveHeader(); } // layout of the save header, this struct is never really used anywhere else // except here the actual layout of the save header is dictated by - // saveHeader() and serializeSaveHeader() + // Modes_saveHeader() and Modes_serializeSaveHeader() struct SaveHeader { uint8_t vMajor; uint8_t vMinor; @@ -733,297 +787,330 @@ bool Modes::setFlag(uint8_t flag, bool enable, bool save) uint8_t numModes; }; // data cannot be NULL since size is non zero - SaveHeader *pHeader = (SaveHeader *)headerBuffer.data(); + SaveHeader *pHeader = (SaveHeader *)headerBuffer.m_pData->buf; // update the global flags field in the header - pHeader->globalFlags = m_globalFlags; + pHeader->globalFlags = Modes_m_globalFlags; // need to force the crc to recalc since we modified the data, just mark the - // CRC as dirty and Storage::write() will re-calculate the CRC if it's dirty - headerBuffer.setCRCDirty(); + // CRC as dirty and Storage_write() will re-calculate the CRC if it's dirty + ByteStream_setCRCDirty(&headerBuffer); // write the save header back to storage - return Storage::write(0, headerBuffer); + bool rv = Storage_write(0, &headerBuffer); + ByteStream_destroy(&headerBuffer); + return rv; } #ifdef VORTEX_LIB #include "Patterns/PatternBuilder.h" // get the maximum size a mode can occupy -uint32_t Modes::maxModeSize() +uint32_t Modes_maxModeSize() { Mode maxMode; + Mode_init(&maxMode); uint8_t x = 0; for (LedPos p = LED_FIRST; p < LED_COUNT; ++p) { // blend takes up 8 params - PatternArgs maxArgs((uint8_t)p + 0xd2, (uint8_t)p + 0xd3, (uint8_t)p + 0xd4, (uint8_t)p + 0xd5, + PatternArgs maxArgs; + PatternArgs_make8(&maxArgs, (uint8_t)p + 0xd2, (uint8_t)p + 0xd3, (uint8_t)p + 0xd4, (uint8_t)p + 0xd5, (uint8_t)p + 0xd6, (uint8_t)p + 0xd7, (uint8_t)p + 0xd8, (uint8_t)p + 0xd9); - //PatternArgs typicalArgs = PatternBuilder::getDefaultArgs(PATTERN_BLEND); + //PatternArgs typicalArgs = PatternBuilder_getDefaultArgs(PATTERN_BLEND); Colorset maxSet; + Colorset_make(&maxSet); for (uint8_t i = 0; i < MAX_COLOR_SLOTS; ++i) { // different color in each slot RGBColor col; col.red = ++x; col.green = ++x; col.blue = ++x; - maxSet.addColor(col); + Colorset_addColor(&maxSet, col); } - maxMode.setPattern(PATTERN_BLEND, p, &maxArgs, &maxSet); + Mode_setPattern(&maxMode, PATTERN_BLEND, p, &maxArgs, &maxSet); } ByteStream stream; - maxMode.saveToBuffer(stream); - return stream.size(); + ByteStream_make(&stream); + Mode_saveToBuffer(&maxMode, &stream, 0); + uint32_t size = stream.m_pData->size; + ByteStream_destroy(&stream); + Mode_destroy(&maxMode); + return size; } // get the maximum size a savefile can occupy -uint32_t Modes::maxSaveSize() +uint32_t Modes_maxSaveSize() { #if MAX_MODES == 0 // unbounded return 0; #else ByteStream backupModes; - saveToBuffer(backupModes); + ByteStream_make(&backupModes); + Modes_saveToBuffer(&backupModes); for (uint32_t i = 0; i < MAX_MODES; ++i) { Mode maxMode; + Mode_init(&maxMode); for (LedPos p = LED_FIRST; p < LED_COUNT; ++p) { // blend takes up 8 params - PatternArgs maxArgs((uint8_t)p + 2, (uint8_t)p + 3, (uint8_t)p + 4, (uint8_t)p + 5, + PatternArgs maxArgs; + PatternArgs_make8(&maxArgs, (uint8_t)p + 2, (uint8_t)p + 3, (uint8_t)p + 4, (uint8_t)p + 5, (uint8_t)p + 6, (uint8_t)p + 7, (uint8_t)p + 8, (uint8_t)p + 9); Colorset maxSet; + Colorset_make(&maxSet); for (uint32_t i = 0; i < MAX_COLOR_SLOTS; ++i) { // different color in each slot - maxSet.addColor(RGBColor((uint8_t)p + (i * 3), (uint8_t)p + (i * 3) + 1, (uint8_t)p + (i * 3) + 2)); + Colorset_addColor(&maxSet, RGBColor_makeRGB((uint8_t)p + (i * 3), (uint8_t)p + (i * 3) + 1, (uint8_t)p + (i * 3) + 2)); } - maxMode.setPattern(PATTERN_BLEND, p, &maxArgs, &maxSet); + Mode_setPattern(&maxMode, PATTERN_BLEND, p, &maxArgs, &maxSet); } - addMode(&maxMode); + Modes_addMode(&maxMode); + Mode_destroy(&maxMode); } // grab the size of the new buffer ByteStream stream; - saveToBuffer(stream); - uint32_t size = stream.size(); + ByteStream_make(&stream); + Modes_saveToBuffer(&stream); + uint32_t size = stream.m_pData->size; + ByteStream_destroy(&stream); // restore backed up modes - loadFromBuffer(backupModes); + Modes_loadFromBuffer(&backupModes); + ByteStream_destroy(&backupModes); return size; #endif } -uint8_t Modes::globalFlags() +uint8_t Modes_globalFlags() { - return m_globalFlags; + return Modes_m_globalFlags; } #endif // fetch a link from the chain by index -Modes::ModeLink *Modes::getModeLink(uint32_t index) +ModeLink *Modes_getModeLink(uint32_t index) { - if (index >= m_numModes) { - return nullptr; + if (index >= Modes_m_numModes) { + return NULL; } - ModeLink *ptr = m_storedModes; + ModeLink *ptr = Modes_m_storedModes; while (index > 0 && ptr) { - ptr = ptr->next(); + ptr = ModeLink_next(ptr); index--; } return ptr; } -Mode *Modes::initCurMode(bool force) +Mode *Modes_initCurMode(bool force) { - if (!m_numModes) { - return nullptr; + if (!Modes_m_numModes) { + return NULL; } // cleanup the current mode link - if (m_pCurModeLink) { - m_pCurModeLink->uninstantiate(); + if (Modes_m_pCurModeLink) { + ModeLink_uninstantiate(Modes_m_pCurModeLink); } // update the current mode link based on the curmode index - m_pCurModeLink = getModeLink(m_curMode); - if (!m_pCurModeLink) { - return nullptr; + Modes_m_pCurModeLink = Modes_getModeLink(Modes_m_curMode); + if (!Modes_m_pCurModeLink) { + return NULL; } if (force) { - m_pCurModeLink->uninstantiate(); + ModeLink_uninstantiate(Modes_m_pCurModeLink); } - return m_pCurModeLink->instantiate(); + return ModeLink_instantiate(Modes_m_pCurModeLink); } -bool Modes::saveCurMode() +bool Modes_saveCurMode() { - if (!m_pCurModeLink) { + if (!Modes_m_pCurModeLink) { // if there's no loaded mode currently then there's nothing // to save so there's no error return false; } // force the current mode to save back to serial to catch changes - return m_pCurModeLink->save(); + return ModeLink_save(Modes_m_pCurModeLink); } -Modes::ModeLink::ModeLink(const Mode *src, bool inst) : - m_pInstantiatedMode(nullptr), - m_storedMode(), - m_next(nullptr), - m_prev(nullptr) +void ModeLink_init(ModeLink *self, const Mode *src, bool inst) { + self->m_pInstantiatedMode = NULL; + ByteStream_make(&self->m_storedMode); + self->m_next = NULL; + self->m_prev = NULL; if (src) { - init(src); + ModeLink_initMode(self, src); } if (src && inst) { - instantiate(); + ModeLink_instantiate(self); } } -Modes::ModeLink::ModeLink(const ByteStream &src, bool inst) : - m_pInstantiatedMode(nullptr), - m_storedMode(src), - m_next(nullptr), - m_prev(nullptr) +void ModeLink_initBuf(ModeLink *self, const ByteStream *src, bool inst) { - if (src.size() && inst) { - instantiate(); + self->m_pInstantiatedMode = NULL; + ByteStream_make(&self->m_storedMode); + ByteStream_set(&self->m_storedMode, src); + self->m_next = NULL; + self->m_prev = NULL; + if (src->m_pData->size && inst) { + ModeLink_instantiate(self); } } -Modes::ModeLink::~ModeLink() +void ModeLink_destroy(ModeLink *self) { - if (m_next) { - delete m_next; + ModeLink *next = self->m_next; + if (next) { + ModeLink_destroy(next); } - if (m_pInstantiatedMode) { - delete m_pInstantiatedMode; + MemPool_free(&s_modeLinkPool, next); + if (self->m_pInstantiatedMode) { + Mode_destroy(self->m_pInstantiatedMode); + MemPool_free(&s_modePool, self->m_pInstantiatedMode); } + ByteStream_destroy(&self->m_storedMode); } -bool Modes::ModeLink::init(const Mode *mode) +bool ModeLink_initMode(ModeLink *self, const Mode *mode) { if (!mode) { return false; } - m_storedMode.clear(); + ByteStream_clear(&self->m_storedMode); // serialize the mode so it can be instantiated anytime - if (!mode->saveToBuffer(m_storedMode)) { + if (!Mode_saveToBuffer(mode, &self->m_storedMode, 0)) { return false; } return true; } -bool Modes::ModeLink::append(const Mode *next) +bool ModeLink_appendMode(ModeLink *self, const Mode *next) { if (!next) { return false; } // if not end of chain, recurse on next link - if (m_next) { - return m_next->append(next); + if (self->m_next) { + return ModeLink_appendMode(self->m_next, next); } - m_next = new ModeLink(next); - if (!m_next) { + self->m_next = (ModeLink *)MemPool_alloc(&s_modeLinkPool); + if (!self->m_next) { ERROR_OUT_OF_MEMORY(); return false; } - m_next->m_prev = this; + ModeLink_init(self->m_next, next, false); + self->m_next->m_prev = self; return true; } -bool Modes::ModeLink::append(const ByteStream &next) +bool ModeLink_appendBuf(ModeLink *self, const ByteStream *next) { - if (!next.size()) { + if (!next->m_pData->size) { return false; } // if not end of chain, recurse on next link - if (m_next) { - return m_next->append(next); + if (self->m_next) { + return ModeLink_appendBuf(self->m_next, next); } - m_next = new ModeLink(next); - if (!m_next) { + self->m_next = (ModeLink *)MemPool_alloc(&s_modeLinkPool); + if (!self->m_next) { ERROR_OUT_OF_MEMORY(); return false; } - m_next->m_prev = this; + ModeLink_initBuf(self->m_next, next, false); + self->m_next->m_prev = self; return true; } -void Modes::ModeLink::play() +void ModeLink_play(ModeLink *self) { - if (!m_pInstantiatedMode) { + if (!self->m_pInstantiatedMode) { return; } - m_pInstantiatedMode->play(); + Mode_play(self->m_pInstantiatedMode); +} + +ModeLink *ModeLink_next(ModeLink *self) +{ + return self->m_next; } -Modes::ModeLink *Modes::ModeLink::unlinkSelf() +ModeLink *ModeLink_unlinkSelf(ModeLink *self) { // unlink this node from the chain - if (m_prev) { - m_prev->m_next = m_next; + if (self->m_prev) { + self->m_prev->m_next = self->m_next; } - if (m_next) { - m_next->m_prev = m_prev; + if (self->m_next) { + self->m_next->m_prev = self->m_prev; } // grab the new link that will take this place - ModeLink *newLink = m_prev ? m_prev : m_next; + ModeLink *newLink = self->m_prev ? self->m_prev : self->m_next; // clear the links of this node - m_prev = nullptr; - m_next = nullptr; + self->m_prev = NULL; + self->m_next = NULL; return newLink; } -void Modes::ModeLink::linkAfter(ModeLink *link) +void ModeLink_linkAfter(ModeLink *self, ModeLink *link) { if (!link) { return; } - if (m_next) { - m_next->m_prev = link; - link->m_next = m_next; + if (self->m_next) { + self->m_next->m_prev = link; + link->m_next = self->m_next; } - m_next = link; - link->m_prev = this; + self->m_next = link; + link->m_prev = self; } -void Modes::ModeLink::linkBefore(ModeLink *link) +void ModeLink_linkBefore(ModeLink *self, ModeLink *link) { if (!link) { return; } - if (m_prev) { - m_prev->m_next = link; - link->m_prev = m_prev; + if (self->m_prev) { + self->m_prev->m_next = link; + link->m_prev = self->m_prev; } - m_prev = link; - link->m_next = this; + self->m_prev = link; + link->m_next = self; } -Mode *Modes::ModeLink::instantiate() +Mode *ModeLink_instantiate(ModeLink *self) { - if (m_pInstantiatedMode) { - return m_pInstantiatedMode; + if (self->m_pInstantiatedMode) { + return self->m_pInstantiatedMode; } - Mode *newMode = new Mode(); + Mode *newMode = (Mode *)MemPool_alloc(&s_modePool); if (!newMode) { ERROR_OUT_OF_MEMORY(); - return nullptr; + return NULL; } - m_storedMode.resetUnserializer(); - if (!newMode->loadFromBuffer(m_storedMode)) { - return nullptr; + Mode_init(newMode); + ByteStream_resetUnserializer(&self->m_storedMode); + if (!Mode_loadFromBuffer(newMode, &self->m_storedMode)) { + Mode_destroy(newMode); + MemPool_free(&s_modePool, newMode); + return NULL; } - m_pInstantiatedMode = newMode; - return m_pInstantiatedMode; + self->m_pInstantiatedMode = newMode; + return self->m_pInstantiatedMode; } -void Modes::ModeLink::uninstantiate() +void ModeLink_uninstantiate(ModeLink *self) { - if (m_pInstantiatedMode) { - delete m_pInstantiatedMode; - m_pInstantiatedMode = nullptr; + if (self->m_pInstantiatedMode) { + Mode_destroy(self->m_pInstantiatedMode); + MemPool_free(&s_modePool, self->m_pInstantiatedMode); + self->m_pInstantiatedMode = NULL; } } -bool Modes::ModeLink::save() +bool ModeLink_save(ModeLink *self) { - if (!m_pInstantiatedMode) { + if (!self->m_pInstantiatedMode) { return false; } - m_storedMode.clear(); - return m_pInstantiatedMode->saveToBuffer(m_storedMode); + ByteStream_clear(&self->m_storedMode); + return Mode_saveToBuffer(self->m_pInstantiatedMode, &self->m_storedMode, 0); } #if MODES_TEST == 1 @@ -1032,95 +1119,95 @@ bool Modes::ModeLink::save() #include "../Patterns/PatternBuilder.h" -void Modes::test() +void Modes_test() { INFO_LOG("== Beginning Modes Test ==\n"); RGBColor col = RGB_RED; - assert(!addMode(PATTERN_COUNT, col)); + assert(!Modes_addMode(PATTERN_COUNT, col)); for (PatternID pat = PATTERN_FIRST; pat < PATTERN_COUNT; ++pat) { - assert(addMode(pat, col)); + assert(Modes_addMode(pat, col)); } - assert(numModes() == PATTERN_COUNT); - clearModes(); - assert(numModes() == 0); + assert(Modes_numModes() == PATTERN_COUNT); + Modes_clearModes(); + assert(Modes_numModes() == 0); Colorset set(RGB_RED, RGB_GREEN, RGB_BLUE); - assert(!addMode(PATTERN_COUNT, nullptr, &set)); + assert(!Modes_addMode(PATTERN_COUNT, NULL, &set)); for (PatternID pat = PATTERN_FIRST; pat < PATTERN_COUNT; ++pat) { - assert(addMode(pat, nullptr, &set)); + assert(Modes_addMode(pat, NULL, &set)); } - assert(numModes() == PATTERN_COUNT); - clearModes(); - assert(numModes() == 0); + assert(Modes_numModes() == PATTERN_COUNT); + Modes_clearModes(); + assert(Modes_numModes() == 0); // add a new mode in various different ways - assert(!addMode(PATTERN_COUNT, col)); + assert(!Modes_addMode(PATTERN_COUNT, col)); Colorset set2(RGB_RED, RGB_GREEN, RGB_BLUE); for (PatternID pat = PATTERN_FIRST; pat < PATTERN_COUNT; ++pat) { - Mode tmpMode(pat, nullptr, &set2); - assert(addMode(&tmpMode)); + Mode tmpMode(pat, NULL, &set2); + assert(Modes_addMode(&tmpMode)); } - assert(numModes() == PATTERN_COUNT); - clearModes(); - assert(numModes() == 0); + assert(Modes_numModes() == PATTERN_COUNT); + Modes_clearModes(); + assert(Modes_numModes() == 0); - INFO_LOG("addMode(): success\n"); + INFO_LOG("Modes_addMode(): success\n"); ByteStream modebuf; ByteStream modesave; - PatternArgs args = PatternBuilder::getDefaultArgs(PATTERN_BASIC); + PatternArgs args = PatternBuilder_getDefaultArgs(PATTERN_BASIC); Mode tmpMode(PATTERN_BASIC, &args, &set); - tmpMode.serialize(modebuf); + tmpMode.Mode_serialize(&modebuf); tmpMode.saveToBuffer(modesave); - assert(addSerializedMode(modebuf)); - assert(numModes() == 1); - assert(addModeFromBuffer(modesave)); - assert(numModes() == 2); - assert(getModeLink(0) != nullptr); - Mode *mode1 = getModeLink(0)->instantiate(); - assert(mode1 != nullptr); - Mode *mode2 = getModeLink(1)->instantiate(); - assert(mode2 != nullptr); - assert(mode1->equals(mode2)); - - INFO_LOG("addSerializedMode(): success\n"); + assert(Modes_addSerializedMode(&modebuf)); + assert(Modes_numModes() == 1); + assert(Modes_addModeFromBuffer(&modesave)); + assert(Modes_numModes() == 2); + assert(Modes_getModeLink(0) != NULL); + Mode *mode1 = Modes_getModeLink(0)->instantiate(); + assert(mode1 != NULL); + Mode *mode2 = Modes_getModeLink(1)->instantiate(); + assert(mode2 != NULL); + assert(Mode_equals(mode1, mode2)); + + INFO_LOG("Modes_addSerializedMode(): success\n"); Colorset newset(RGB_BLUE, RGB_RED, RGB_GREEN); - assert(updateCurMode(PATTERN_HYPERSTROBE, nullptr)); - assert(getModeLink(0)->mode()->getPatternID() == PATTERN_HYPERSTROBE); - assert(setCurMode(1)); + assert(Modes_updateCurMode(PATTERN_HYPERSTROBE, NULL)); + assert(Modes_getModeLink(0)->mode()->getPatternID() == PATTERN_HYPERSTROBE); + assert(Modes_setCurMode(1)); // update the current mode to match the given mode - assert(updateCurMode(PATTERN_DOPS, &newset)); - assert(getModeLink(1)->mode()->getPatternID() == PATTERN_DOPS); - Mode newTmp(PATTERN_BLEND, PatternBuilder::getDefaultArgs(PATTERN_BLEND), + assert(Modes_updateCurMode(PATTERN_DOPS, &newset)); + assert(Modes_getModeLink(1)->mode()->getPatternID() == PATTERN_DOPS); + Mode newTmp(PATTERN_BLEND, PatternBuilder_getDefaultArgs(PATTERN_BLEND), Colorset(RGB_YELLOW, RGB_ORANGE, RGB_CYAN, RGB_BLUE, RGB_WHITE, RGB_RED)); - INFO_LOG("updateCurMode(): success\n"); - - assert(shiftCurMode(-1)); - assert(m_curMode == 0); - assert(getModeLink(0)->instantiate()->getPatternID() == PATTERN_DOPS); - assert(getModeLink(1)->instantiate()->getPatternID() == PATTERN_HYPERSTROBE); - assert(shiftCurMode(0)); - assert(m_curMode == 0); - assert(getModeLink(0)->instantiate()->getPatternID() == PATTERN_DOPS); - assert(getModeLink(1)->instantiate()->getPatternID() == PATTERN_HYPERSTROBE); - assert(shiftCurMode(1)); - assert(m_curMode == 1); - assert(getModeLink(0)->instantiate()->getPatternID() == PATTERN_HYPERSTROBE); - assert(getModeLink(1)->instantiate()->getPatternID() == PATTERN_DOPS); - - INFO_LOG("shiftCurMode(): success\n"); - - deleteCurMode(); - assert(m_numModes == 1); - assert(m_curMode == 0); - deleteCurMode(); - assert(m_numModes == 0); - assert(m_curMode == 0); - - INFO_LOG("deleteCurMode(): success\n"); + INFO_LOG("Modes_updateCurMode(): success\n"); + + assert(Modes_shiftCurMode(-1)); + assert(Modes_m_curMode == 0); + assert(Modes_getModeLink(0)->instantiate()->getPatternID() == PATTERN_DOPS); + assert(Modes_getModeLink(1)->instantiate()->getPatternID() == PATTERN_HYPERSTROBE); + assert(Modes_shiftCurMode(0)); + assert(Modes_m_curMode == 0); + assert(Modes_getModeLink(0)->instantiate()->getPatternID() == PATTERN_DOPS); + assert(Modes_getModeLink(1)->instantiate()->getPatternID() == PATTERN_HYPERSTROBE); + assert(Modes_shiftCurMode(1)); + assert(Modes_m_curMode == 1); + assert(Modes_getModeLink(0)->instantiate()->getPatternID() == PATTERN_HYPERSTROBE); + assert(Modes_getModeLink(1)->instantiate()->getPatternID() == PATTERN_DOPS); + + INFO_LOG("Modes_shiftCurMode(): success\n"); + + Modes_deleteCurMode(); + assert(Modes_m_numModes == 1); + assert(Modes_m_curMode == 0); + Modes_deleteCurMode(); + assert(Modes_m_numModes == 0); + assert(Modes_m_curMode == 0); + + INFO_LOG("Modes_deleteCurMode(): success\n"); INFO_LOG("== Success Running Modes Test ==\n"); } diff --git a/VortexEngine/src/Modes/Modes.h b/VortexEngine/src/Modes/Modes.h index 4e415a49b5..563027bfec 100644 --- a/VortexEngine/src/Modes/Modes.h +++ b/VortexEngine/src/Modes/Modes.h @@ -1,6 +1,7 @@ #ifndef MODES_H #define MODES_H +#include #include "../Serial/ByteStream.h" #include "../Colors/ColorTypes.h" #include "../Patterns/Patterns.h" @@ -10,9 +11,9 @@ #include -class PatternArgs; -class Colorset; -class Mode; +typedef struct PatternArgs PatternArgs; +typedef struct Colorset Colorset; +typedef struct Mode Mode; // the device is locked and needs to be unlocked to turn on #define MODES_FLAG_LOCKED (1 << 0) @@ -39,223 +40,202 @@ class Mode; // will still have the old version number saved inside #define MODES_FLAG_NEW_FIRMWARE 0xF0 -class Modes -{ - // private unimplemented constructor - Modes(); +typedef struct ModeLink ModeLink; + +extern bool Modes_m_loaded; +extern uint8_t Modes_m_curMode; +extern uint8_t Modes_m_numModes; +extern ModeLink * Modes_m_pCurModeLink; +extern ModeLink * Modes_m_storedModes; +extern uint32_t Modes_m_globalFlags; +extern uint32_t Modes_m_lastSwitchTime; -public: - // opting for static class here because there should only ever be one +// Everything here is a free function operating on a single set of globals; +// there should only ever be one // Modes control object and I don't like singletons - static bool init(); - static void cleanup(); +bool Modes_init(); +void Modes_cleanup(); // load modes so they are ready to play - static bool load(); +bool Modes_load(); // play the current mode - static void play(); +void Modes_play(); // full save/load to/from buffer - static bool saveToBuffer(ByteStream &saveBuffer); - static bool loadFromBuffer(ByteStream &saveBuffer); +bool Modes_saveToBuffer(ByteStream *saveBuffer); +bool Modes_loadFromBuffer(ByteStream *saveBuffer); // save/load the global settings to/from storage - static bool saveHeader(); - static bool loadHeader(); +bool Modes_saveHeader(); +bool Modes_loadHeader(); // full save/load to/from storage - static bool saveStorage(); - static bool loadStorage(); +bool Modes_saveStorage(); +bool Modes_loadStorage(); // save load the savefile header from storage - static bool serializeSaveHeader(ByteStream &saveBuffer); - static bool unserializeSaveHeader(ByteStream &saveBuffer); +bool Modes_serializeSaveHeader(ByteStream *saveBuffer); +bool Modes_unserializeSaveHeader(ByteStream *saveBuffer); // saves all modes to a buffer - static bool serialize(ByteStream &buffer); +bool Modes_serialize(ByteStream *buffer); // load all modes from a buffer, optionally specify number of leds - static bool unserialize(ByteStream &buffer); +bool Modes_unserialize(ByteStream *buffer); // set default settings (must save after) - static bool setDefaults(); +bool Modes_setDefaults(); // shift the current mode to a different position relative to current position // negative values for up, positive values for down, 0 for no move - static bool shiftCurMode(int32_t offset = 1); +bool Modes_shiftCurMode(int32_t offset); // add a new mode in various different ways - static bool addMode(PatternID id, RGBColor c1, RGBColor c2 = RGB_OFF, - RGBColor c3 = RGB_OFF, RGBColor c4 = RGB_OFF, RGBColor c5 = RGB_OFF, - RGBColor c6 = RGB_OFF, RGBColor c7 = RGB_OFF, RGBColor c8 = RGB_OFF); - static bool addMode(PatternID id, const PatternArgs *args, const Colorset *set); - static bool addMode(const Mode *mode); +bool Modes_addMode(PatternID id, RGBColor c1, RGBColor c2, + RGBColor c3, RGBColor c4, RGBColor c5, + RGBColor c6, RGBColor c7, RGBColor c8); +bool Modes_addMode(PatternID id, const PatternArgs *args, const Colorset *set); +bool Modes_addMode(const Mode *mode); // add a new mode by unserializing raw - static bool addSerializedMode(ByteStream &serializedMode); +bool Modes_addSerializedMode(ByteStream *serializedMode); // add a new mode by loading from a save buffer - static bool addModeFromBuffer(ByteStream &serializedMode); +bool Modes_addModeFromBuffer(ByteStream *serializedMode); // update the current mode to match the given mode, optionally save - static bool updateCurMode(const Mode *mode); +bool Modes_updateCurMode(const Mode *mode); // set the current active mode by index - static Mode *setCurMode(uint8_t index); +Mode *Modes_setCurMode(uint8_t index); // get the current mode - static Mode *curMode(); +Mode *Modes_curMode(); // iterate to next mode and return it - static Mode *nextMode(); +Mode *Modes_nextMode(); // iterate to previous mode and return it - static Mode *previousMode(); +Mode *Modes_previousMode(); // iterate to the next mode but skip empty modes, will not skip mode 0 // to prevent possibly skipping all modes and ending in an infinite loop - static Mode *nextModeSkipEmpty(); +Mode *Modes_nextModeSkipEmpty(); // the number of modes - static uint8_t numModes() { return m_numModes; } - static uint8_t curModeIndex() { return m_curMode; } +static inline uint8_t Modes_numModes() { return Modes_m_numModes; } +static inline uint8_t Modes_curModeIndex() { return Modes_m_curMode; } // the last time the modes switched to a new mode - static uint32_t lastSwitchTime() { return m_lastSwitchTime; } +static inline uint32_t Modes_lastSwitchTime() { return Modes_m_lastSwitchTime; } // delete the current mode - static void deleteCurMode(); +void Modes_deleteCurMode(); // delete all modes in the list - static void clearModes(); +void Modes_clearModes(); // set the startup mode index (which mode will be displayed on startup) - static void setStartupMode(uint8_t index); - static uint8_t startupMode(); - static Mode *switchToStartupMode(); +void Modes_setStartupMode(uint8_t index); +uint8_t Modes_startupMode(); +Mode *Modes_switchToStartupMode(); // set or get flags - static bool setFlag(uint8_t flag, bool enable, bool save = true); - static bool getFlag(uint8_t flag) { return ((m_globalFlags & flag) == flag); } +bool Modes_setFlag(uint8_t flag, bool enable, bool save); +static inline bool Modes_getFlag(uint8_t flag) { return ((Modes_m_globalFlags & flag) == flag); } // reset flags to factory default (must save after) - static void resetFlags() { m_globalFlags = 0; } +static inline void Modes_resetFlags() { Modes_m_globalFlags = 0; } // inline functions to toggle the various flags - static bool setOneClickMode(bool enable, bool save = true) { - return setFlag(MODES_FLAG_ONE_CLICK, enable, save); +static inline bool Modes_setOneClickMode(bool enable, bool save) { + return Modes_setFlag(MODES_FLAG_ONE_CLICK, enable, save); } - static bool oneClickModeEnabled() { - return getFlag(MODES_FLAG_ONE_CLICK); +static inline bool Modes_oneClickModeEnabled() { + return Modes_getFlag(MODES_FLAG_ONE_CLICK); } // toggle the locked state - static bool setLocked(bool locked, bool save = true) { - return setFlag(MODES_FLAG_LOCKED, locked, save); +static inline bool Modes_setLocked(bool locked, bool save) { + return Modes_setFlag(MODES_FLAG_LOCKED, locked, save); } - static bool locked() { - return getFlag(MODES_FLAG_LOCKED); +static inline bool Modes_locked() { + return Modes_getFlag(MODES_FLAG_LOCKED); } // toggle advanced menus - static bool setAdvancedMenus(bool active, bool save = true) { - return setFlag(MODES_FLAG_ADV_MENUS, active, save); +static inline bool Modes_setAdvancedMenus(bool active, bool save) { + return Modes_setFlag(MODES_FLAG_ADV_MENUS, active, save); } - static bool advancedMenusEnabled() { - return getFlag(MODES_FLAG_ADV_MENUS); +static inline bool Modes_advancedMenusEnabled() { + return Modes_getFlag(MODES_FLAG_ADV_MENUS); } // toggle the keychain light mode - static bool setKeychainMode(bool active, bool save = true) { - return setFlag(MODES_FLAG_KEYCHAIN, active, save); +static inline bool Modes_setKeychainMode(bool active, bool save) { + return Modes_setFlag(MODES_FLAG_KEYCHAIN, active, save); } - static bool keychainModeEnabled() { - return getFlag(MODES_FLAG_KEYCHAIN); +static inline bool Modes_keychainModeEnabled() { + return Modes_getFlag(MODES_FLAG_KEYCHAIN); } #if MODES_TEST == 1 - static void test(); +void Modes_test(); #endif #ifdef VORTEX_LIB // get the maximum size a mode can occupy - static uint32_t maxModeSize(); +uint32_t Modes_maxModeSize(); // get the maximum size a savefile can occupy - static uint32_t maxSaveSize(); +uint32_t Modes_maxSaveSize(); // global flags value - static uint8_t globalFlags(); +uint8_t Modes_globalFlags(); #endif -private: - // linked list of internal mode storage - class ModeLink { - public: - // construct a link and optionally instantiate the link - ModeLink(const Mode *src = nullptr, bool inst = false); - ModeLink(const ByteStream &src, bool inst = false); - ~ModeLink(); - - // init the link and append another link - bool init(const Mode *mode = nullptr); - bool append(const Mode *next); - bool append(const ByteStream &next); - - // play the instantiated mode inside - void play(); - - // unlink self from the chain, returns link that takes position - ModeLink *unlinkSelf(); - - // link in a node before or after self - void linkAfter(ModeLink *link); - void linkBefore(ModeLink *link); - - // instantiate/destroy the mode - Mode *instantiate(); - void uninstantiate(); - - // if the mode is instantiated and the instantiated version - // has changed at all then save will re-save it to the buffer - bool save(); - - // accessors - ByteStream &buffer() { return m_storedMode; } - Mode *mode() { return m_pInstantiatedMode; } - ModeLink *next() { return m_next; } - ModeLink *prev() { return m_prev; } - - operator ByteStream &() { return m_storedMode; } - operator ByteStream() { return m_storedMode; } - operator Mode *() { return m_pInstantiatedMode; } - private: - Mode *m_pInstantiatedMode; - ByteStream m_storedMode; - ModeLink *m_next; - ModeLink *m_prev; - }; +// a linked list node of internal mode storage +struct ModeLink +{ + Mode *m_pInstantiatedMode; + ByteStream m_storedMode; + ModeLink *m_next; + ModeLink *m_prev; +}; + + +void ModeLink_init(ModeLink *self, const Mode *src, bool inst); +void ModeLink_initBuf(ModeLink *self, const ByteStream *src, bool inst); +void ModeLink_destroy(ModeLink *self); +bool ModeLink_initMode(ModeLink *self, const Mode *mode); +bool ModeLink_appendMode(ModeLink *self, const Mode *next); +bool ModeLink_appendBuf(ModeLink *self, const ByteStream *next); +void ModeLink_play(ModeLink *self); +ModeLink *ModeLink_unlinkSelf(ModeLink *self); +void ModeLink_linkAfter(ModeLink *self, ModeLink *link); +void ModeLink_linkBefore(ModeLink *self, ModeLink *link); +Mode *ModeLink_instantiate(ModeLink *self); +void ModeLink_uninstantiate(ModeLink *self); +bool ModeLink_save(ModeLink *self); +ByteStream *ModeLink_buffer(ModeLink *self); +Mode *ModeLink_mode(ModeLink *self); +ModeLink *ModeLink_next(ModeLink *self); +ModeLink *ModeLink_prev(ModeLink *self); + // fetch a link from the chain by index - static ModeLink *getModeLink(uint32_t index); +ModeLink *Modes_getModeLink(uint32_t index); // initialize current mode from ByteStream, optionally force re-init which // will destroy the current instantiated mode and re-load it from serial - static Mode *initCurMode(bool force = false); - static bool saveCurMode(); +Mode *Modes_initCurMode(bool force); +bool Modes_saveCurMode(); // whether modes have been loaded - static bool m_loaded; // the current mode we're on - static uint8_t m_curMode; // the number of modes loaded - static uint8_t m_numModes; // the current instantiated mode and it's respective link - static ModeLink *m_pCurModeLink; // list of serialized version of bufers - static ModeLink *m_storedModes; // global flags for all modes - static uint8_t m_globalFlags; // the last switch time of the modes - static uint32_t m_lastSwitchTime; -}; + #endif diff --git a/VortexEngine/src/Patterns/Multi/BackStrobePattern.cpp b/VortexEngine/src/Patterns/Multi/BackStrobePattern.cpp index 31a459556a..76030203aa 100644 --- a/VortexEngine/src/Patterns/Multi/BackStrobePattern.cpp +++ b/VortexEngine/src/Patterns/Multi/BackStrobePattern.cpp @@ -1,55 +1,67 @@ #include "BackStrobePattern.h" -BackStrobePattern::BackStrobePattern(const PatternArgs &args) : - CompoundPattern(args), - m_stepSpeed(0), - m_stepTimer(), - m_switch(), - m_firstPatternArgs(0, 0, 0), - m_secondPatternArgs(0, 0, 0), - m_firstPat(PATTERN_DOPS), - m_secPat(PATTERN_STROBE) +#include +#include "../../Memory/Memory.h" +#include "../Pattern.h" + +const PatternVtable BackStrobePattern_vtable = { + .destroy = BackStrobePattern_destroy, + .bind = MultiLedPattern_bind, + .init = BackStrobePattern_initImpl, + .play = BackStrobePattern_play +}; + +void BackStrobePattern_init(BackStrobePattern *self, const PatternArgs *args) { - m_patternID = PATTERN_BACKSTROBE; - REGISTER_ARG(m_firstPatternArgs.arg1); - REGISTER_ARG(m_firstPatternArgs.arg2); - REGISTER_ARG(m_firstPatternArgs.arg3); - REGISTER_ARG(m_secondPatternArgs.arg1); - REGISTER_ARG(m_secondPatternArgs.arg2); - REGISTER_ARG(m_stepSpeed); - REGISTER_ARG(m_firstPat); - REGISTER_ARG(m_secPat); - setArgs(args); + CompoundPattern_init(&self->base, args, &BackStrobePattern_vtable); + self->m_stepSpeed = 0; + Timer_make(&self->m_stepTimer); + self->m_switch = false; + PatternArgs_make(&self->m_firstPatternArgs, 0, 0, 0); + PatternArgs_make(&self->m_secondPatternArgs, 0, 0, 0); + self->m_firstPat = PATTERN_DOPS; + self->m_secPat = PATTERN_STROBE; + Pattern_setPatternID((&self->base.base.base), PATTERN_BACKSTROBE); + REGISTER_ARG((&self->base.base.base), BackStrobePattern, m_firstPatternArgs.arg1); + REGISTER_ARG((&self->base.base.base), BackStrobePattern, m_firstPatternArgs.arg2); + REGISTER_ARG((&self->base.base.base), BackStrobePattern, m_firstPatternArgs.arg3); + REGISTER_ARG((&self->base.base.base), BackStrobePattern, m_secondPatternArgs.arg1); + REGISTER_ARG((&self->base.base.base), BackStrobePattern, m_secondPatternArgs.arg2); + REGISTER_ARG((&self->base.base.base), BackStrobePattern, m_stepSpeed); + REGISTER_ARG((&self->base.base.base), BackStrobePattern, m_firstPat); + REGISTER_ARG((&self->base.base.base), BackStrobePattern, m_secPat); + Pattern_setArgs((&self->base.base.base), args); } -BackStrobePattern::~BackStrobePattern() +void BackStrobePattern_destroy(Pattern *pat) { + BackStrobePattern *self = (BackStrobePattern *)pat; + (void)self; } -// init the pattern to initial state -void BackStrobePattern::init() +void BackStrobePattern_initImpl(Pattern *pat) { - CompoundPattern::init(); - + BackStrobePattern *self = (BackStrobePattern *)pat; + CompoundPattern_initImpl(pat); // timer for switch - m_stepTimer.reset(); - m_stepTimer.addAlarm(m_stepSpeed * 100); - m_stepTimer.start(); - + Timer_reset(&self->m_stepTimer); + Timer_addAlarm(&self->m_stepTimer, self->m_stepSpeed * 100); + Timer_start0(&self->m_stepTimer); // initialize the sub patterns one time first - setEvensOdds((PatternID)m_firstPat, (PatternID)m_secPat, &m_firstPatternArgs, &m_secondPatternArgs); + CompoundPattern_setEvensOdds(&self->base, (PatternID)self->m_firstPat, (PatternID)self->m_secPat, &self->m_firstPatternArgs, &self->m_secondPatternArgs); } -void BackStrobePattern::play() +void BackStrobePattern_play(Pattern *pat) { - if (m_stepTimer.alarm() == 0) { + BackStrobePattern *self = (BackStrobePattern *)pat; + if (Timer_alarm(&self->m_stepTimer) == 0) { // switch which patterns are displayed - m_switch = !m_switch; + self->m_switch = !self->m_switch; // update the tip/top patterns based on the switch - setEvensOdds(m_switch ? (PatternID)m_firstPat : (PatternID)m_secPat, - m_switch ? (PatternID)m_secPat : (PatternID)m_firstPat, - m_switch ? &m_firstPatternArgs : &m_secondPatternArgs, - m_switch ? &m_secondPatternArgs : &m_firstPatternArgs); + CompoundPattern_setEvensOdds(&self->base, self->m_switch ? (PatternID)self->m_firstPat : (PatternID)self->m_secPat, + self->m_switch ? (PatternID)self->m_secPat : (PatternID)self->m_firstPat, + self->m_switch ? &self->m_firstPatternArgs : &self->m_secondPatternArgs, + self->m_switch ? &self->m_secondPatternArgs : &self->m_firstPatternArgs); } - CompoundPattern::play(); -} \ No newline at end of file + CompoundPattern_play(pat); +} diff --git a/VortexEngine/src/Patterns/Multi/BackStrobePattern.h b/VortexEngine/src/Patterns/Multi/BackStrobePattern.h index f2ffb35d9a..80d7839339 100644 --- a/VortexEngine/src/Patterns/Multi/BackStrobePattern.h +++ b/VortexEngine/src/Patterns/Multi/BackStrobePattern.h @@ -1,34 +1,32 @@ #ifndef BACKSTROBE_PATTERN_H #define BACKSTROBE_PATTERN_H +#include #include "CompoundPattern.h" - +#include #include "../../Time/Timer.h" #include "../../Time/Timings.h" -class BackStrobePattern : public CompoundPattern -{ -public: - BackStrobePattern(const PatternArgs &args); - virtual ~BackStrobePattern(); - - // init the pattern to initial state - virtual void init() override; - virtual void play() override; - -private: - // the speed for the step timer in x100 ms chunks, so a value of 10 - // will be 1000ms +typedef struct BackStrobePattern { + // must remain the first member + CompoundPattern base; uint8_t m_stepSpeed; - // the step timer Timer m_stepTimer; - bool m_switch; - PatternArgs m_firstPatternArgs; PatternArgs m_secondPatternArgs; uint8_t m_firstPat; uint8_t m_secPat; -}; +} BackStrobePattern; + +// the vtable for this pattern +extern const PatternVtable BackStrobePattern_vtable; + +// initialize a new BackStrobePattern +void BackStrobePattern_init(BackStrobePattern *self, const PatternArgs *args); +void BackStrobePattern_destroy(Pattern *self); + +void BackStrobePattern_initImpl(Pattern *self); +void BackStrobePattern_play(Pattern *self); #endif diff --git a/VortexEngine/src/Patterns/Multi/BlinkStepPattern.cpp b/VortexEngine/src/Patterns/Multi/BlinkStepPattern.cpp index 3f0b3e47fb..865101bead 100644 --- a/VortexEngine/src/Patterns/Multi/BlinkStepPattern.cpp +++ b/VortexEngine/src/Patterns/Multi/BlinkStepPattern.cpp @@ -5,85 +5,106 @@ #include "../../Leds/Leds.h" #include "../../Log/Log.h" -BlinkStepPattern::BlinkStepPattern(const PatternArgs &args) : - MultiLedPattern(args), - m_blinkOnDuration(0), - m_blinkOffDuration(0), - m_stepDuration(0), - m_blinkTimer(), - m_stepTimer() -{ - // BlinkStep is an abstract class it cannot be directly - // instantiated so we do not need to assign a pattern id - REGISTER_ARG(m_blinkOnDuration); - REGISTER_ARG(m_blinkOffDuration); - REGISTER_ARG(m_stepDuration); -} +#include "../../Memory/Memory.h" + +const BlinkStepVtable BlinkStepPattern_bvtable = { + .base = { + .destroy = Pattern_destroy, + .bind = MultiLedPattern_bind, + .init = BlinkStepPattern_initImpl, + .play = BlinkStepPattern_play + }, + .blinkOn = BlinkStepPattern_blinkOn, + .blinkOff = BlinkStepPattern_blinkOff, + .prestep = BlinkStepPattern_prestep, + .poststep = BlinkStepPattern_poststep +}; -BlinkStepPattern::~BlinkStepPattern() +void BlinkStepPattern_init(BlinkStepPattern *self, const PatternArgs *args, const BlinkStepVtable *vtable) { + self->bvtable = vtable; + MultiLedPattern_init(&self->base, args, vtable ? &vtable->base : NULL); + + self->m_blinkOnDuration = 0; + self->m_blinkOffDuration = 0; + self->m_stepDuration = 0; + Timer_make(&self->m_blinkTimer); + Timer_make(&self->m_stepTimer); + + Pattern_setPatternID(&self->base.base, PATTERN_NONE); + Pattern_setFlags(&self->base.base, Pattern_getFlags(&self->base.base) | PATTERN_FLAG_MULTI); + + REGISTER_ARG((Pattern *)&self->base.base, BlinkStepPattern, m_blinkOnDuration); + REGISTER_ARG((Pattern *)&self->base.base, BlinkStepPattern, m_blinkOffDuration); + REGISTER_ARG((Pattern *)&self->base.base, BlinkStepPattern, m_stepDuration); + + Pattern_setArgs(&self->base.base, args); } // init the pattern to initial state -void BlinkStepPattern::init() +void BlinkStepPattern_initImpl(Pattern *pat) { - MultiLedPattern::init(); + BlinkStepPattern *self = (BlinkStepPattern *)pat; + + MultiLedPattern_doInit(pat); // reset the blink timer entirely - m_blinkTimer.reset(); + Timer_reset(&self->m_blinkTimer); // dops timing - m_blinkTimer.addAlarm(m_blinkOnDuration); - m_blinkTimer.addAlarm(m_blinkOffDuration); + Timer_addAlarm1(&self->m_blinkTimer, self->m_blinkOnDuration); + Timer_addAlarm1(&self->m_blinkTimer, self->m_blinkOffDuration); // start the blink timer from the next frame - m_blinkTimer.start(); + Timer_start0(&self->m_blinkTimer); // reset and add alarm - m_stepTimer.reset(); - m_stepTimer.addAlarm(m_stepDuration); - m_stepTimer.start(); + Timer_reset(&self->m_stepTimer); + Timer_addAlarm1(&self->m_stepTimer, self->m_stepDuration); + Timer_start0(&self->m_stepTimer); } // pure virtual must override the play function -void BlinkStepPattern::play() +void BlinkStepPattern_play(Pattern *pat) { + BlinkStepPattern *self = (BlinkStepPattern *)pat; + // whether to trigger a step event based on the step timer - bool shouldStep = (m_stepTimer.alarm() == 0); + bool shouldStep = (Timer_alarm(&self->m_stepTimer) == 0); if (shouldStep) { - prestep(); + self->bvtable->prestep(self); } - switch (m_blinkTimer.alarm()) { + switch (Timer_alarm(&self->m_blinkTimer)) { case -1: // just break and still run post-step break; case 0: // turn on the leds for given mapping - blinkOn(); + self->bvtable->blinkOn(self); break; case 1: // turn off the leds - blinkOff(); + self->bvtable->blinkOff(self); break; } if (shouldStep) { - poststep(); + self->bvtable->poststep(self); } } -void BlinkStepPattern::blinkOn() +void BlinkStepPattern_blinkOn(BlinkStepPattern *self) { // override me } -void BlinkStepPattern::blinkOff() +void BlinkStepPattern_blinkOff(BlinkStepPattern *self) { // override me - Leds::clearAll(); + Leds_clearAll(); } -void BlinkStepPattern::prestep() +void BlinkStepPattern_prestep(BlinkStepPattern *self) { // override me } -void BlinkStepPattern::poststep() +void BlinkStepPattern_poststep(BlinkStepPattern *self) { // override me - m_colorset.getNext(); + Colorset_getNext(&self->base.base.m_colorset); } diff --git a/VortexEngine/src/Patterns/Multi/BlinkStepPattern.h b/VortexEngine/src/Patterns/Multi/BlinkStepPattern.h index 22c9f104cd..7f67269aff 100644 --- a/VortexEngine/src/Patterns/Multi/BlinkStepPattern.h +++ b/VortexEngine/src/Patterns/Multi/BlinkStepPattern.h @@ -1,39 +1,60 @@ #ifndef BLINKSTEP_PATTERN_H #define BLINKSTEP_PATTERN_H +#include #include "MultiLedPattern.h" #include "../../Time/Timer.h" #include "../../Leds/LedTypes.h" -class BlinkStepPattern : public MultiLedPattern -{ -protected: - // You must derive from BlinkStep to use it - BlinkStepPattern(const PatternArgs &args); +typedef struct BlinkStepPattern BlinkStepPattern; + +// BlinkStepVtable adds the four overridable steps on top of the base pattern +// vtable. Concrete patterns supply their own table and reuse the shared +// blink/step play loop, exactly like the old virtual overrides did. +typedef struct BlinkStepVtable { + // the leading PatternVtable, so this can be used as a PatternVtable* + PatternVtable base; + + // when the leds blink on (must be provided) + void (*blinkOn)(BlinkStepPattern *self); + // when the leds blink off + void (*blinkOff)(BlinkStepPattern *self); + // when a step triggers, before blink on + void (*prestep)(BlinkStepPattern *self); + // when a step triggers, after blink on + void (*poststep)(BlinkStepPattern *self); +} BlinkStepVtable; + +// BlinkStepPattern is abstract, it cannot be directly instantiated +typedef struct BlinkStepPattern { + // must remain the first member + MultiLedPattern base; + + // the step hooks, points at a BlinkStepVtable + const BlinkStepVtable *bvtable; -public: - virtual ~BlinkStepPattern(); - - // init the pattern to initial state - virtual void init() override; - - // pure virtual must override the play function - virtual void play() override; - -protected: - // overrideable members: - virtual void blinkOn(); // when the leds blink on (must override) - virtual void blinkOff(); // when the leds blink off - virtual void prestep(); // when step triggers (before blink on) - virtual void poststep(); // when step triggers (after blink on) - -private: uint8_t m_blinkOnDuration; uint8_t m_blinkOffDuration; uint8_t m_stepDuration; Timer m_blinkTimer; Timer m_stepTimer; -}; -#endif +} BlinkStepPattern; +// the default hooks, which do nothing useful on their own +extern const BlinkStepVtable BlinkStepPattern_bvtable; + +// initialize the base portion of a BlinkStepPattern +void BlinkStepPattern_init(BlinkStepPattern *self, const PatternArgs *args, const BlinkStepVtable *vtable); + +// vtable entry points, shared by every BlinkStep derived pattern +void BlinkStepPattern_initImpl(Pattern *self); +void BlinkStepPattern_play(Pattern *self); + +// the default hook implementations +void BlinkStepPattern_blinkOn(BlinkStepPattern *self); +void BlinkStepPattern_blinkOff(BlinkStepPattern *self); +void BlinkStepPattern_prestep(BlinkStepPattern *self); +void BlinkStepPattern_poststep(BlinkStepPattern *self); + +#endif diff --git a/VortexEngine/src/Patterns/Multi/BouncePattern.cpp b/VortexEngine/src/Patterns/Multi/BouncePattern.cpp index a4afaa1f71..60d4d15b87 100644 --- a/VortexEngine/src/Patterns/Multi/BouncePattern.cpp +++ b/VortexEngine/src/Patterns/Multi/BouncePattern.cpp @@ -1,52 +1,71 @@ #include "BouncePattern.h" +#include #include "../../Serial/ByteStream.h" #include "../../Time/TimeControl.h" #include "../../Leds/Leds.h" #include "../../Log/Log.h" -// safety to prevent divide by 0 #define TOTAL_STEPS (((PAIR_COUNT * 2) - 2) ? ((PAIR_COUNT * 2) - 2) : 1) #define HALF_STEPS (TOTAL_STEPS / 2) -BouncePattern::BouncePattern(const PatternArgs &args) : - BlinkStepPattern(args), - m_progress(0), - m_fadeAmount(0) +#include "../../Memory/Memory.h" +#include "../Pattern.h" + +const BlinkStepVtable BouncePattern_bvtable = { + .base = { + .destroy = BouncePattern_destroy, + .bind = MultiLedPattern_bind, + .init = BouncePattern_initImpl, + .play = BlinkStepPattern_play + }, + .blinkOn = BouncePattern_blinkOn, + .blinkOff = BlinkStepPattern_blinkOff, + .prestep = BlinkStepPattern_prestep, + .poststep = BouncePattern_poststep, +}; + +void BouncePattern_init(BouncePattern *self, const PatternArgs *args) { - m_patternID = PATTERN_BOUNCE; - REGISTER_ARG(m_fadeAmount); - setArgs(args); + BlinkStepPattern_init(&self->base, args, &BouncePattern_bvtable); + self->m_fadeAmount = 0; + Pattern_setPatternID((&self->base.base.base), PATTERN_BOUNCE); + REGISTER_ARG((&self->base.base.base), BouncePattern, m_fadeAmount); + Pattern_setArgs((&self->base.base.base), args); } -BouncePattern::~BouncePattern() +void BouncePattern_destroy(Pattern *pat) { + BouncePattern *self = (BouncePattern *)pat; + (void)self; } -// init the pattern to initial state -void BouncePattern::init() +void BouncePattern_initImpl(Pattern *pat) { - BlinkStepPattern::init(); + BouncePattern *self = (BouncePattern *)pat; + BlinkStepPattern_initImpl(pat); // reset progress - m_progress = 0; + self->m_progress = 0; // start colorset at index 0 so cur() works - m_colorset.setCurIndex(0); + Colorset_setCurIndex(&(&self->base.base.base)->m_colorset, 0); } -void BouncePattern::blinkOn() +void BouncePattern_blinkOn(BlinkStepPattern *bsp) { - Leds::setAll(m_colorset.cur()); - if (m_progress < PAIR_COUNT) { - Leds::setPair((Pair)m_progress, m_colorset.peekNext()); + BouncePattern *self = (BouncePattern *)bsp; + Leds_setAll(Colorset_cur(&(&self->base.base.base)->m_colorset)); + if (self->m_progress < PAIR_COUNT) { + Leds_setPair((Pair)self->m_progress, Colorset_peekNext(&(&self->base.base.base)->m_colorset)); } else { - Leds::setPair((Pair)(TOTAL_STEPS - m_progress), m_colorset.peekNext()); + Leds_setPair((Pair)(TOTAL_STEPS - self->m_progress), Colorset_peekNext(&(&self->base.base.base)->m_colorset)); } } -void BouncePattern::poststep() +void BouncePattern_poststep(BlinkStepPattern *bsp) { - m_progress = (m_progress + 1) % TOTAL_STEPS; - if (m_progress == 0 || m_progress == HALF_STEPS) { - m_colorset.getNext(); + BouncePattern *self = (BouncePattern *)bsp; + self->m_progress = (self->m_progress + 1) % TOTAL_STEPS; + if (self->m_progress == 0 || self->m_progress == HALF_STEPS) { + Colorset_getNext(&(&self->base.base.base)->m_colorset); } } diff --git a/VortexEngine/src/Patterns/Multi/BouncePattern.h b/VortexEngine/src/Patterns/Multi/BouncePattern.h index 383c27fd6a..ba33b19d31 100644 --- a/VortexEngine/src/Patterns/Multi/BouncePattern.h +++ b/VortexEngine/src/Patterns/Multi/BouncePattern.h @@ -1,24 +1,26 @@ -#include "BlinkStepPattern.h" +#ifndef BOUNCE_PATTERN_H +#define BOUNCE_PATTERN_H +#include +#include "BlinkStepPattern.h" #include "../../Time/Timings.h" -class BouncePattern : public BlinkStepPattern -{ -public: - BouncePattern(const PatternArgs &args); - virtual ~BouncePattern(); +typedef struct BouncePattern { + // must remain the first member + BlinkStepPattern base; + uint8_t m_progress; + uint8_t m_fadeAmount; +} BouncePattern; - // init the pattern to initial state - virtual void init() override; +// the blink/step vtable for this pattern +extern const BlinkStepVtable BouncePattern_bvtable; -protected: - virtual void blinkOn() override; - virtual void poststep() override; +// initialize a new BouncePattern +void BouncePattern_init(BouncePattern *self, const PatternArgs *args); +void BouncePattern_destroy(Pattern *self); -private: - // how much the warp has progressed - uint8_t m_progress; - // the fade amount - uint8_t m_fadeAmount; -}; +void BouncePattern_initImpl(Pattern *self); +void BouncePattern_blinkOn(BlinkStepPattern *self); +void BouncePattern_poststep(BlinkStepPattern *self); +#endif diff --git a/VortexEngine/src/Patterns/Multi/CompoundPattern.cpp b/VortexEngine/src/Patterns/Multi/CompoundPattern.cpp index c69c1684ac..7f980f3be0 100644 --- a/VortexEngine/src/Patterns/Multi/CompoundPattern.cpp +++ b/VortexEngine/src/Patterns/Multi/CompoundPattern.cpp @@ -1,91 +1,102 @@ #include "CompoundPattern.h" -#include "../Single/SingleLedPattern.h" -#include "../../Colors/Colorset.h" +#include + +#include "../../Memory/Memory.h" #include "../PatternBuilder.h" -CompoundPattern::CompoundPattern(const PatternArgs &args) : - MultiLedPattern(args), - m_ledPatterns() +const PatternVtable CompoundPattern_vtable = { + .destroy = CompoundPattern_destroy, + .bind = MultiLedPattern_bind, + .init = CompoundPattern_initImpl, + .play = CompoundPattern_play, +}; + +void CompoundPattern_init(CompoundPattern *self, const PatternArgs *args, const PatternVtable *vtable) { - // Hybrid is an abstract class it cannot be directly - // instantiated so we do not need to assign a pattern id + memset(self->m_ledPatterns, 0, sizeof(self->m_ledPatterns)); + MultiLedPattern_init(&self->base, args, vtable); } -CompoundPattern::~CompoundPattern() +// clean up all the sub patterns +void CompoundPattern_destroy(Pattern *pat) { - // clean up all the sub patterns + CompoundPattern *self = (CompoundPattern *)pat; for (LedPos pos = LED_FIRST; pos <= LED_LAST; pos++) { - if (m_ledPatterns[pos]) { - delete m_ledPatterns[pos]; - m_ledPatterns[pos] = nullptr; + if (self->m_ledPatterns[pos]) { + Pattern_destroy(self->m_ledPatterns[pos]); + PatternBuilder_freePattern(self->m_ledPatterns[pos]); + self->m_ledPatterns[pos] = NULL; } } } // init the pattern to initial state -void CompoundPattern::init() +void CompoundPattern_initImpl(Pattern *pat) { - MultiLedPattern::init(); + CompoundPattern *self = (CompoundPattern *)pat; + MultiLedPattern_doInit(pat); } -// play the hybrid pattern (play all led patterns) -void CompoundPattern::play() +// play the compound pattern (play all led patterns) +void CompoundPattern_play(Pattern *pat) { + CompoundPattern *self = (CompoundPattern *)pat; for (LedPos pos = LED_FIRST; pos <= LED_LAST; pos++) { - SingleLedPattern *pat = m_ledPatterns[pos]; - if (!pat) { + SingleLedPattern *sub = self->m_ledPatterns[pos]; + if (!sub) { // should never happen, but just in case continue; } - pat->play(); + Pattern_play(sub); } } -void CompoundPattern::clearPatterns() +void CompoundPattern_clearPatterns(CompoundPattern *self) { for (LedPos pos = LED_FIRST; pos <= LED_LAST; pos++) { - if (!m_ledPatterns[pos]) { + if (!self->m_ledPatterns[pos]) { continue; } - delete m_ledPatterns[pos]; - m_ledPatterns[pos] = nullptr; + Pattern_destroy(self->m_ledPatterns[pos]); + PatternBuilder_freePattern(self->m_ledPatterns[pos]); + self->m_ledPatterns[pos] = NULL; } } -void CompoundPattern::setPatternAt(LedPos pos, SingleLedPattern *pat, - const Colorset *set) +void CompoundPattern_setPatternAt(CompoundPattern *self, LedPos pos, SingleLedPattern *pat, const Colorset *set) { if (!pat || pos >= LED_COUNT) { return; } - pat->bind(pos); - pat->setColorset(set ? *set : m_colorset); - pat->init(); + Colorset use = set ? *set : self->base.base.m_colorset; + Pattern_bind(pat, pos); + Pattern_setColorset(pat, &use); + Pattern_init(pat); // handle re-initialization and prevent leaks - if (m_ledPatterns[pos]) { - delete m_ledPatterns[pos]; + if (self->m_ledPatterns[pos]) { + Pattern_destroy(self->m_ledPatterns[pos]); + PatternBuilder_freePattern(self->m_ledPatterns[pos]); } // store the pattern for the given led - m_ledPatterns[pos] = pat; + self->m_ledPatterns[pos] = pat; } -void CompoundPattern::setPatternAt(LedPos pos, PatternID id, - const PatternArgs *args, const Colorset *set) +void CompoundPattern_setPatternAtId(CompoundPattern *self, LedPos pos, PatternID id, const PatternArgs *args, const Colorset *set) { if (pos >= LED_COUNT) { return; } - setPatternAt(pos, PatternBuilder::makeSingle(id, args), set); + CompoundPattern_setPatternAt(self, pos, PatternBuilder_makeSingle(id, args), set); } -void CompoundPattern::setEvensOdds(PatternID evenPattern, PatternID oddPattern, +void CompoundPattern_setEvensOdds(CompoundPattern *self, PatternID evenPattern, PatternID oddPattern, const PatternArgs *evenArgs, const PatternArgs *oddArgs) { // Set the evenPattern on all evens and oddPattern on all odds for (LedPos p = LED_FIRST; p <= LED_LAST; p++) { const PatternArgs *args = isEven(p) ? evenArgs : oddArgs; PatternID id = isEven(p) ? evenPattern : oddPattern; - setPatternAt(p, PatternBuilder::makeSingle(id, args)); + CompoundPattern_setPatternAt(self, p, PatternBuilder_makeSingle(id, args), NULL); } } diff --git a/VortexEngine/src/Patterns/Multi/CompoundPattern.h b/VortexEngine/src/Patterns/Multi/CompoundPattern.h index 18ac4ad361..da5348a591 100644 --- a/VortexEngine/src/Patterns/Multi/CompoundPattern.h +++ b/VortexEngine/src/Patterns/Multi/CompoundPattern.h @@ -1,39 +1,39 @@ -#ifndef HYBRID_PATTERN_H -#define HYBRID_PATTERN_H +#ifndef COMPOUND_PATTERN_H +#define COMPOUND_PATTERN_H +#include #include "MultiLedPattern.h" +#include "../Single/SingleLedPattern.h" -class SingleLedPattern; - -// A hybrid pattern is a type of multi-led pattern that works by displaying -// pre-existing patterns on different leds simultaneously -class CompoundPattern : public MultiLedPattern -{ -protected: - // CompoundPattern is an abstract class - CompoundPattern(const PatternArgs &args); - -public: - virtual ~CompoundPattern(); - - // init the pattern to initial state - virtual void init() override; - - // play the hybrid pattern (play all led patterns) - virtual void play() override; - -protected: - // set a pattern at an index of the hybrid pattern (optionally replace colorset) - void setPatternAt(LedPos pos, SingleLedPattern *pat, const Colorset *set = nullptr); - void setPatternAt(LedPos pos, PatternID id, const PatternArgs *args = nullptr, const Colorset *set = nullptr); - void clearPatterns(); - - // change the patterns on the evens/odds respectively - void setEvensOdds(PatternID tipPattern, PatternID topPattern, - const PatternArgs *tipArgs = nullptr, const PatternArgs *topArgs = nullptr); +// CompoundPattern is a multi-led pattern that displays pre-existing single +// led patterns simultaneously, one per led. It is abstract. +typedef struct CompoundPattern { + // must remain the first member + MultiLedPattern base; // array of single LED patterns, one for each LED SingleLedPattern *m_ledPatterns[LED_COUNT]; -}; +} CompoundPattern; + +// CompoundPattern is abstract, so it supplies its own vtable rather than the +// plain MultiLedPattern one. Derived compound patterns reuse this table +// unless they override play(). +extern const PatternVtable CompoundPattern_vtable; + +// initialize the base portion of a CompoundPattern. No pattern id is +// assigned because CompoundPattern cannot be directly instantiated. +void CompoundPattern_init(CompoundPattern *self, const PatternArgs *args, const PatternVtable *vtable); + +// vtable entry points, shared by every Compound derived pattern +void CompoundPattern_destroy(Pattern *self); +void CompoundPattern_initImpl(Pattern *self); +void CompoundPattern_play(Pattern *self); + +// non-virtual helpers +void CompoundPattern_clearPatterns(CompoundPattern *self); +void CompoundPattern_setPatternAt(CompoundPattern *self, LedPos pos, SingleLedPattern *pat, const Colorset *set); +void CompoundPattern_setPatternAtId(CompoundPattern *self, LedPos pos, PatternID id, const PatternArgs *args, const Colorset *set); +void CompoundPattern_setEvensOdds(CompoundPattern *self, PatternID evenPattern, PatternID oddPattern, + const PatternArgs *evenArgs, const PatternArgs *oddArgs); #endif diff --git a/VortexEngine/src/Patterns/Multi/CrossDopsPattern.cpp b/VortexEngine/src/Patterns/Multi/CrossDopsPattern.cpp index 98ecfe4c78..345f19003b 100644 --- a/VortexEngine/src/Patterns/Multi/CrossDopsPattern.cpp +++ b/VortexEngine/src/Patterns/Multi/CrossDopsPattern.cpp @@ -1,38 +1,58 @@ #include "CrossDopsPattern.h" +#include #include "../../Leds/Leds.h" +#include "../../Memory/Memory.h" +#include "../Pattern.h" -CrossDopsPattern::CrossDopsPattern(const PatternArgs &args) : - BlinkStepPattern(args), - m_ledMap(0) +const BlinkStepVtable CrossDopsPattern_bvtable = { + .base = { + .destroy = CrossDopsPattern_destroy, + .bind = MultiLedPattern_bind, + .init = CrossDopsPattern_initImpl, + .play = BlinkStepPattern_play + }, + .blinkOn = CrossDopsPattern_blinkOn, + .blinkOff = BlinkStepPattern_blinkOff, + .prestep = BlinkStepPattern_prestep, + .poststep = CrossDopsPattern_poststep, +}; + +void CrossDopsPattern_init(CrossDopsPattern *self, const PatternArgs *args) { - m_patternID = PATTERN_CROSSDOPS; - setArgs(args); + BlinkStepPattern_init(&self->base, args, &CrossDopsPattern_bvtable); + self->m_ledMap = 0; + Pattern_setPatternID((&self->base.base.base), PATTERN_CROSSDOPS); + Pattern_setArgs((&self->base.base.base), args); } -CrossDopsPattern::~CrossDopsPattern() +void CrossDopsPattern_destroy(Pattern *pat) { + CrossDopsPattern *self = (CrossDopsPattern *)pat; + (void)self; } -// init the pattern to initial state -void CrossDopsPattern::init() +void CrossDopsPattern_initImpl(Pattern *pat) { - BlinkStepPattern::init(); + CrossDopsPattern *self = (CrossDopsPattern *)pat; + BlinkStepPattern_initImpl(pat); // Alternating evens and odds mapping of leds to turn on/off - m_ledMap = MAP_PAIR_EVENS; + self->m_ledMap = MAP_PAIR_EVENS; // start colorset at index 0 so cur() works - m_colorset.setCurIndex(0); + Colorset_setCurIndex(&(&self->base.base.base)->m_colorset, 0); } -void CrossDopsPattern::blinkOn() +void CrossDopsPattern_blinkOn(BlinkStepPattern *bsp) { + CrossDopsPattern *self = (CrossDopsPattern *)bsp; // set the current color on all the given leds - Leds::setMap(m_ledMap, m_colorset.cur()); + Leds_setMap(self->m_ledMap, Colorset_cur(&(&self->base.base.base)->m_colorset)); } -void CrossDopsPattern::poststep() +void CrossDopsPattern_poststep(BlinkStepPattern *bsp) { + CrossDopsPattern *self = (CrossDopsPattern *)bsp; // iterate to next color and invert of all the evens/odds - m_colorset.getNext(); - m_ledMap = MAP_INVERSE(m_ledMap); + Colorset_getNext(&(&self->base.base.base)->m_colorset); + self->m_ledMap = MAP_INVERSE(self->m_ledMap); } diff --git a/VortexEngine/src/Patterns/Multi/CrossDopsPattern.h b/VortexEngine/src/Patterns/Multi/CrossDopsPattern.h index 083bf40ff1..0df0063701 100644 --- a/VortexEngine/src/Patterns/Multi/CrossDopsPattern.h +++ b/VortexEngine/src/Patterns/Multi/CrossDopsPattern.h @@ -1,26 +1,25 @@ #ifndef CROSSDOPS_PATTERN_H #define CROSSDOPS_PATTERN_H +#include #include "BlinkStepPattern.h" - #include "../../Time/Timings.h" -class CrossDopsPattern : public BlinkStepPattern -{ -public: - CrossDopsPattern(const PatternArgs &args); - virtual ~CrossDopsPattern(); +typedef struct CrossDopsPattern { + // must remain the first member + BlinkStepPattern base; + LedMap m_ledMap; +} CrossDopsPattern; - // init the pattern to initial state - virtual void init() override; +// the blink/step vtable for this pattern +extern const BlinkStepVtable CrossDopsPattern_bvtable; -protected: - // overrideable members: - virtual void blinkOn() override; - virtual void poststep() override; +// initialize a new CrossDopsPattern +void CrossDopsPattern_init(CrossDopsPattern *self, const PatternArgs *args); +void CrossDopsPattern_destroy(Pattern *self); -private: - LedMap m_ledMap; -}; +void CrossDopsPattern_initImpl(Pattern *self); +void CrossDopsPattern_blinkOn(BlinkStepPattern *self); +void CrossDopsPattern_poststep(BlinkStepPattern *self); #endif diff --git a/VortexEngine/src/Patterns/Multi/DoubleStrobePattern.cpp b/VortexEngine/src/Patterns/Multi/DoubleStrobePattern.cpp index 577a514adc..a8d60ad461 100644 --- a/VortexEngine/src/Patterns/Multi/DoubleStrobePattern.cpp +++ b/VortexEngine/src/Patterns/Multi/DoubleStrobePattern.cpp @@ -1,29 +1,48 @@ #include "DoubleStrobePattern.h" +#include #include "../../Serial/ByteStream.h" - #include "../../Leds/Leds.h" +#include "../../Memory/Memory.h" +#include "../Pattern.h" + +const BlinkStepVtable DoubleStrobePattern_bvtable = { + .base = { + .destroy = DoubleStrobePattern_destroy, + .bind = MultiLedPattern_bind, + .init = DoubleStrobePattern_initImpl, + .play = BlinkStepPattern_play + }, + .blinkOn = DoubleStrobePattern_blinkOn, + .blinkOff = BlinkStepPattern_blinkOff, + .prestep = BlinkStepPattern_prestep, + .poststep = BlinkStepPattern_poststep, +}; -DoubleStrobePattern::DoubleStrobePattern(const PatternArgs &args) : - BlinkStepPattern(args) +void DoubleStrobePattern_init(DoubleStrobePattern *self, const PatternArgs *args) { - m_patternID = PATTERN_DOUBLESTROBE; - setArgs(args); + BlinkStepPattern_init(&self->base, args, &DoubleStrobePattern_bvtable); + Pattern_setPatternID((&self->base.base.base), PATTERN_DOUBLESTROBE); + Pattern_setArgs((&self->base.base.base), args); } -DoubleStrobePattern::~DoubleStrobePattern() +void DoubleStrobePattern_destroy(Pattern *pat) { + DoubleStrobePattern *self = (DoubleStrobePattern *)pat; + (void)self; } -void DoubleStrobePattern::init() +void DoubleStrobePattern_initImpl(Pattern *pat) { - BlinkStepPattern::init(); + DoubleStrobePattern *self = (DoubleStrobePattern *)pat; + BlinkStepPattern_initImpl(pat); // start colorset at index 0 so cur() works - m_colorset.setCurIndex(0); + Colorset_setCurIndex(&(&self->base.base.base)->m_colorset, 0); } -void DoubleStrobePattern::blinkOn() +void DoubleStrobePattern_blinkOn(BlinkStepPattern *bsp) { - Leds::setAllOdds(m_colorset.cur()); - Leds::setAllEvens(m_colorset.peekNext()); + DoubleStrobePattern *self = (DoubleStrobePattern *)bsp; + Leds_setAllOdds(Colorset_cur(&(&self->base.base.base)->m_colorset)); + Leds_setAllEvens(Colorset_peekNext(&(&self->base.base.base)->m_colorset)); } diff --git a/VortexEngine/src/Patterns/Multi/DoubleStrobePattern.h b/VortexEngine/src/Patterns/Multi/DoubleStrobePattern.h index fea548e06d..486b9bac64 100644 --- a/VortexEngine/src/Patterns/Multi/DoubleStrobePattern.h +++ b/VortexEngine/src/Patterns/Multi/DoubleStrobePattern.h @@ -1,19 +1,23 @@ #ifndef DOUBLESTROBE_PATTERN_H #define DOUBLESTROBE_PATTERN_H +#include #include "BlinkStepPattern.h" - #include "../../Time/Timings.h" -class DoubleStrobePattern : public BlinkStepPattern -{ -public: - DoubleStrobePattern(const PatternArgs &args); - virtual ~DoubleStrobePattern(); +typedef struct DoubleStrobePattern { + // must remain the first member + BlinkStepPattern base; +} DoubleStrobePattern; + +// the blink/step vtable for this pattern +extern const BlinkStepVtable DoubleStrobePattern_bvtable; + +// initialize a new DoubleStrobePattern +void DoubleStrobePattern_init(DoubleStrobePattern *self, const PatternArgs *args); +void DoubleStrobePattern_destroy(Pattern *self); - virtual void init() override; +void DoubleStrobePattern_initImpl(Pattern *self); +void DoubleStrobePattern_blinkOn(BlinkStepPattern *self); -protected: - virtual void blinkOn() override; -}; #endif diff --git a/VortexEngine/src/Patterns/Multi/DripMorphPattern.cpp b/VortexEngine/src/Patterns/Multi/DripMorphPattern.cpp index bc940ba11a..7d9785d473 100644 --- a/VortexEngine/src/Patterns/Multi/DripMorphPattern.cpp +++ b/VortexEngine/src/Patterns/Multi/DripMorphPattern.cpp @@ -1,84 +1,96 @@ #include "DripMorphPattern.h" +#include #include "../../Serial/ByteStream.h" #include "../../Leds/Leds.h" #include "../../Log/Log.h" +#include "../../Memory/Memory.h" +#include "../Pattern.h" -DripMorphPattern::DripMorphPattern(const PatternArgs &args) : - MultiLedPattern(args), - m_blinkOnDuration(0), - m_blinkOffDuration(0), - m_blinkTimer(), - m_speed(0), - m_cur(), - m_next() +const PatternVtable DripMorphPattern_vtable = { + .destroy = DripMorphPattern_destroy, + .bind = MultiLedPattern_bind, + .init = DripMorphPattern_initImpl, + .play = DripMorphPattern_play +}; + +void DripMorphPattern_init(DripMorphPattern *self, const PatternArgs *args) { - m_patternID = PATTERN_DRIPMORPH; - REGISTER_ARG(m_blinkOnDuration); - REGISTER_ARG(m_blinkOffDuration); - REGISTER_ARG(m_speed); - setArgs(args); + MultiLedPattern_init(&self->base, args, &DripMorphPattern_vtable); + self->m_blinkOnDuration = 0; + self->m_blinkOffDuration = 0; + Timer_make(&self->m_blinkTimer); + self->m_speed = 0; + self->m_cur = HSVColor_make(); + self->m_next = HSVColor_make(); + Pattern_setPatternID((&self->base.base), PATTERN_DRIPMORPH); + REGISTER_ARG((&self->base.base), DripMorphPattern, m_blinkOnDuration); + REGISTER_ARG((&self->base.base), DripMorphPattern, m_blinkOffDuration); + REGISTER_ARG((&self->base.base), DripMorphPattern, m_speed); + Pattern_setArgs((&self->base.base), args); } -DripMorphPattern::~DripMorphPattern() +void DripMorphPattern_destroy(Pattern *pat) { + DripMorphPattern *self = (DripMorphPattern *)pat; + (void)self; } -void DripMorphPattern::init() +void DripMorphPattern_initImpl(Pattern *pat) { - MultiLedPattern::init(); + DripMorphPattern *self = (DripMorphPattern *)pat; + MultiLedPattern_doInit(pat); // reset the blink timer entirely - m_blinkTimer.reset(); + Timer_reset(&self->m_blinkTimer); // dops timing - m_blinkTimer.addAlarm(m_blinkOnDuration); - m_blinkTimer.addAlarm(m_blinkOffDuration); + Timer_addAlarm(&self->m_blinkTimer, self->m_blinkOnDuration); + Timer_addAlarm(&self->m_blinkTimer, self->m_blinkOffDuration); // start the blink timer from the next frame - m_blinkTimer.start(); - + Timer_start0(&self->m_blinkTimer); // convert current/next colors to HSV - m_cur = m_colorset.getNext(); - m_next = m_colorset.getNext(); + { RGBColor tmp = Colorset_getNext(&(&self->base.base)->m_colorset); HSVColor_setRGB(&self->m_cur, &tmp); } + { RGBColor tmp = Colorset_getNext(&(&self->base.base)->m_colorset); HSVColor_setRGB(&self->m_next, &tmp); } } -// pure virtual must override the play function -void DripMorphPattern::play() +void DripMorphPattern_play(Pattern *pat) { - switch (m_blinkTimer.alarm()) { + DripMorphPattern *self = (DripMorphPattern *)pat; + switch (Timer_alarm(&self->m_blinkTimer)) { case -1: // just break break; case 0: // turn on the leds for given mapping - blinkOn(); + DripMorphPattern_blinkOn(self); break; case 1: // turn off the leds - blinkOff(); + DripMorphPattern_blinkOff(self); break; } } -void DripMorphPattern::blinkOn() +void DripMorphPattern_blinkOn(DripMorphPattern *self) { // if the current hue has reached the next hue - if (m_cur.hue == m_next.hue) { + if (self->m_cur.hue == self->m_next.hue) { // copy over the sat/val - m_cur = m_next; + RGBColor_fromHSV(&self->m_cur) = RGBColor_fromHSV(&self->m_next); // get the next color and convert to hsv - m_next = m_colorset.getNext(); + { RGBColor tmp = Colorset_getNext(&(&self->base.base)->m_colorset); HSVColor_setRGB(&self->m_next, &tmp); } } // check which direction is closer for the next hue // tbh idk really how this even works anymore but it does - int sign = ((((m_next.hue + 255) - m_cur.hue) % 256) <= 128) ? 1 : -1; + int sign = ((((self->m_next.hue + 255) - self->m_cur.hue) % 256) <= 128) ? 1 : -1; // move hue in the direction of next hue at chosen speed // NOTE: if the speed isn't a multiple of the hue values then // it will cause oscillation around the target hue // because it will never reach the target hue and // always over/under shoot - m_cur.hue += m_speed * sign; + self->m_cur.hue += self->m_speed * sign; // set the target led with the current HSV color - Leds::setAllEvens(m_cur); - Leds::setAllOdds(m_colorset.cur()); + Leds_setAllEvens(RGBColor_fromHSV(&self->m_cur)); + Leds_setAllOdds(Colorset_cur(&(&self->base.base)->m_colorset)); } -void DripMorphPattern::blinkOff() +void DripMorphPattern_blinkOff(DripMorphPattern *self) { - Leds::clearAll(); + Leds_clearAll(); } diff --git a/VortexEngine/src/Patterns/Multi/DripMorphPattern.h b/VortexEngine/src/Patterns/Multi/DripMorphPattern.h index a3bf2bfeef..6891720b6b 100644 --- a/VortexEngine/src/Patterns/Multi/DripMorphPattern.h +++ b/VortexEngine/src/Patterns/Multi/DripMorphPattern.h @@ -1,38 +1,32 @@ #ifndef DRIPMORPH_PATTERN_H #define DRIPMORPH_PATTERN_H +#include #include "MultiLedPattern.h" - #include "../../Time/Timer.h" #include "../../Time/Timings.h" -class DripMorphPattern : public MultiLedPattern -{ -public: - DripMorphPattern(const PatternArgs &args); - virtual ~DripMorphPattern(); - - // init the pattern to initial state - virtual void init() override; - - // pure virtual must override the play function - virtual void play() override; - -protected: - virtual void blinkOn(); // when the leds blink on - virtual void blinkOff(); // when the leds blink off - -private: +typedef struct DripMorphPattern { + // must remain the first member + MultiLedPattern base; uint8_t m_blinkOnDuration; uint8_t m_blinkOffDuration; Timer m_blinkTimer; - // the speed of the blend uint8_t m_speed; - - // current color and target blend color HSVColor m_cur; HSVColor m_next; -}; +} DripMorphPattern; -#endif +// the vtable for this pattern +extern const PatternVtable DripMorphPattern_vtable; + +// initialize a new DripMorphPattern +void DripMorphPattern_init(DripMorphPattern *self, const PatternArgs *args); +void DripMorphPattern_destroy(Pattern *self); +void DripMorphPattern_initImpl(Pattern *self); +void DripMorphPattern_play(Pattern *self); +void DripMorphPattern_blinkOn(DripMorphPattern *self); +void DripMorphPattern_blinkOff(DripMorphPattern *self); + +#endif diff --git a/VortexEngine/src/Patterns/Multi/DripPattern.cpp b/VortexEngine/src/Patterns/Multi/DripPattern.cpp index a907b641cf..d272a5d8c1 100644 --- a/VortexEngine/src/Patterns/Multi/DripPattern.cpp +++ b/VortexEngine/src/Patterns/Multi/DripPattern.cpp @@ -1,42 +1,62 @@ #include "DripPattern.h" +#include #include "../../Leds/Leds.h" +#include "../../Memory/Memory.h" +#include "../Pattern.h" -DripPattern::DripPattern(const PatternArgs &args) : - BlinkStepPattern(args), - m_sync(true) +const BlinkStepVtable DripPattern_bvtable = { + .base = { + .destroy = DripPattern_destroy, + .bind = MultiLedPattern_bind, + .init = DripPattern_initImpl, + .play = BlinkStepPattern_play + }, + .blinkOn = DripPattern_blinkOn, + .blinkOff = BlinkStepPattern_blinkOff, + .prestep = BlinkStepPattern_prestep, + .poststep = DripPattern_poststep, +}; + +void DripPattern_init(DripPattern *self, const PatternArgs *args) { - m_patternID = PATTERN_DRIP; - setArgs(args); + BlinkStepPattern_init(&self->base, args, &DripPattern_bvtable); + Pattern_setPatternID((&self->base.base.base), PATTERN_DRIP); + Pattern_setArgs((&self->base.base.base), args); } -DripPattern::~DripPattern() +void DripPattern_destroy(Pattern *pat) { + DripPattern *self = (DripPattern *)pat; + (void)self; } -void DripPattern::init() +void DripPattern_initImpl(Pattern *pat) { - BlinkStepPattern::init(); + DripPattern *self = (DripPattern *)pat; + BlinkStepPattern_initImpl(pat); // reset sync bool - m_sync = true; + self->m_sync = true; // start colorset at index 0 so cur() works - m_colorset.setCurIndex(0); + Colorset_setCurIndex(&(&self->base.base.base)->m_colorset, 0); } -void DripPattern::blinkOn() +void DripPattern_blinkOn(BlinkStepPattern *bsp) { - if (!m_sync) { - Leds::setAllEvens(m_colorset.cur()); - Leds::setAllOdds(m_colorset.peekNext()); + DripPattern *self = (DripPattern *)bsp; + if (!self->m_sync) { + Leds_setAllEvens(Colorset_cur(&(&self->base.base.base)->m_colorset)); + Leds_setAllOdds(Colorset_peekNext(&(&self->base.base.base)->m_colorset)); } else { - Leds::setAll(m_colorset.cur()); + Leds_setAll(Colorset_cur(&(&self->base.base.base)->m_colorset)); } } -void DripPattern::poststep() +void DripPattern_poststep(BlinkStepPattern *bsp) { - m_sync = !m_sync; - if (m_sync) { - m_colorset.getNext(); + DripPattern *self = (DripPattern *)bsp; + self->m_sync = !self->m_sync; + if (self->m_sync) { + Colorset_getNext(&(&self->base.base.base)->m_colorset); } } diff --git a/VortexEngine/src/Patterns/Multi/DripPattern.h b/VortexEngine/src/Patterns/Multi/DripPattern.h index 3287a466d8..d9b01ed21c 100644 --- a/VortexEngine/src/Patterns/Multi/DripPattern.h +++ b/VortexEngine/src/Patterns/Multi/DripPattern.h @@ -1,24 +1,26 @@ #ifndef DRIP_PATTERN_H #define DRIP_PATTERN_H +#include #include "BlinkStepPattern.h" - +#include #include "../../Time/Timings.h" -class DripPattern : public BlinkStepPattern -{ -public: - DripPattern(const PatternArgs &args); - virtual ~DripPattern(); +typedef struct DripPattern { + // must remain the first member + BlinkStepPattern base; + bool m_sync; +} DripPattern; - virtual void init() override; +// the blink/step vtable for this pattern +extern const BlinkStepVtable DripPattern_bvtable; -protected: - virtual void blinkOn() override; - virtual void poststep() override; +// initialize a new DripPattern +void DripPattern_init(DripPattern *self, const PatternArgs *args); +void DripPattern_destroy(Pattern *self); -private: - bool m_sync; -}; +void DripPattern_initImpl(Pattern *self); +void DripPattern_blinkOn(BlinkStepPattern *self); +void DripPattern_poststep(BlinkStepPattern *self); #endif diff --git a/VortexEngine/src/Patterns/Multi/FillPattern.cpp b/VortexEngine/src/Patterns/Multi/FillPattern.cpp index 87e034e4c1..82902b4848 100644 --- a/VortexEngine/src/Patterns/Multi/FillPattern.cpp +++ b/VortexEngine/src/Patterns/Multi/FillPattern.cpp @@ -1,41 +1,61 @@ #include "FillPattern.h" +#include #include "../../Serial/ByteStream.h" #include "../../Time/TimeControl.h" #include "../../Leds/Leds.h" #include "../../Log/Log.h" +#include "../../Memory/Memory.h" +#include "../Pattern.h" -FillPattern::FillPattern(const PatternArgs &args) : - BlinkStepPattern(args), - m_progress(0) +const BlinkStepVtable FillPattern_bvtable = { + .base = { + .destroy = FillPattern_destroy, + .bind = MultiLedPattern_bind, + .init = FillPattern_initImpl, + .play = BlinkStepPattern_play + }, + .blinkOn = FillPattern_blinkOn, + .blinkOff = BlinkStepPattern_blinkOff, + .prestep = BlinkStepPattern_prestep, + .poststep = FillPattern_poststep, +}; + +void FillPattern_init(FillPattern *self, const PatternArgs *args) { - m_patternID = PATTERN_FILL; - setArgs(args); + BlinkStepPattern_init(&self->base, args, &FillPattern_bvtable); + Pattern_setPatternID((&self->base.base.base), PATTERN_FILL); + Pattern_setArgs((&self->base.base.base), args); } -FillPattern::~FillPattern() +void FillPattern_destroy(Pattern *pat) { + FillPattern *self = (FillPattern *)pat; + (void)self; } -void FillPattern::init() +void FillPattern_initImpl(Pattern *pat) { - BlinkStepPattern::init(); + FillPattern *self = (FillPattern *)pat; + BlinkStepPattern_initImpl(pat); // reset progress - m_progress = 0; + self->m_progress = 0; // start colorset at index 0 so cur() works - m_colorset.setCurIndex(0); + Colorset_setCurIndex(&(&self->base.base.base)->m_colorset, 0); } -void FillPattern::blinkOn() +void FillPattern_blinkOn(BlinkStepPattern *bsp) { - Leds::setPairs(PAIR_FIRST, (Pair)m_progress, m_colorset.peekNext()); - Leds::setPairs((Pair)m_progress, PAIR_COUNT, m_colorset.cur()); + FillPattern *self = (FillPattern *)bsp; + Leds_setPairs(PAIR_FIRST, (Pair)self->m_progress, Colorset_peekNext(&(&self->base.base.base)->m_colorset)); + Leds_setPairs((Pair)self->m_progress, PAIR_COUNT, Colorset_cur(&(&self->base.base.base)->m_colorset)); } -void FillPattern::poststep() +void FillPattern_poststep(BlinkStepPattern *bsp) { - m_progress = (m_progress + 1) % PAIR_COUNT; - if (m_progress == 0) { - m_colorset.getNext(); + FillPattern *self = (FillPattern *)bsp; + self->m_progress = (self->m_progress + 1) % PAIR_COUNT; + if (self->m_progress == 0) { + Colorset_getNext(&(&self->base.base.base)->m_colorset); } } diff --git a/VortexEngine/src/Patterns/Multi/FillPattern.h b/VortexEngine/src/Patterns/Multi/FillPattern.h index d48d622337..484654f686 100644 --- a/VortexEngine/src/Patterns/Multi/FillPattern.h +++ b/VortexEngine/src/Patterns/Multi/FillPattern.h @@ -1,25 +1,25 @@ #ifndef FILL_PATTERN_H #define FILL_PATTERN_H +#include #include "BlinkStepPattern.h" - #include "../../Time/Timings.h" -class FillPattern : public BlinkStepPattern -{ -public: - FillPattern(const PatternArgs &args); - virtual ~FillPattern(); +typedef struct FillPattern { + // must remain the first member + BlinkStepPattern base; + uint8_t m_progress; +} FillPattern; - virtual void init() override; +// the blink/step vtable for this pattern +extern const BlinkStepVtable FillPattern_bvtable; -protected: - virtual void blinkOn() override; - virtual void poststep() override; +// initialize a new FillPattern +void FillPattern_init(FillPattern *self, const PatternArgs *args); +void FillPattern_destroy(Pattern *self); -private: - // how much the fill has progressed - uint8_t m_progress; -}; +void FillPattern_initImpl(Pattern *self); +void FillPattern_blinkOn(BlinkStepPattern *self); +void FillPattern_poststep(BlinkStepPattern *self); #endif diff --git a/VortexEngine/src/Patterns/Multi/HueShiftPattern.cpp b/VortexEngine/src/Patterns/Multi/HueShiftPattern.cpp index d86a35810f..89eaf01593 100644 --- a/VortexEngine/src/Patterns/Multi/HueShiftPattern.cpp +++ b/VortexEngine/src/Patterns/Multi/HueShiftPattern.cpp @@ -1,83 +1,93 @@ #include "HueShiftPattern.h" +#include #include "../../Serial/ByteStream.h" #include "../../Time/TimeControl.h" #include "../../Leds/Leds.h" #include "../../Log/Log.h" +#include "../../Memory/Memory.h" +#include "../Pattern.h" -HueShiftPattern::HueShiftPattern(const PatternArgs &args) : - MultiLedPattern(args), - m_blinkOnDuration(0), - m_blinkOffDuration(0), - m_blendDelay(0), - m_delayCounter(0), - m_blinkTimer(), - m_cur(0), - m_next(0) +const PatternVtable HueShiftPattern_vtable = { + .destroy = HueShiftPattern_destroy, + .bind = MultiLedPattern_bind, + .init = HueShiftPattern_initImpl, + .play = HueShiftPattern_play +}; + +void HueShiftPattern_init(HueShiftPattern *self, const PatternArgs *args) { - m_patternID = PATTERN_HUE_SCROLL; - REGISTER_ARG(m_blinkOnDuration); - REGISTER_ARG(m_blinkOffDuration); - REGISTER_ARG(m_blendDelay); - setArgs(args); + MultiLedPattern_init(&self->base, args, &HueShiftPattern_vtable); + self->m_blinkOnDuration = 0; + self->m_blinkOffDuration = 0; + self->m_blendDelay = 0; + Timer_make(&self->m_blinkTimer); + self->m_cur = HSVColor_make(); + self->m_next = HSVColor_make(); + Pattern_setPatternID((&self->base.base), PATTERN_HUE_SCROLL); + REGISTER_ARG((&self->base.base), HueShiftPattern, m_blinkOnDuration); + REGISTER_ARG((&self->base.base), HueShiftPattern, m_blinkOffDuration); + REGISTER_ARG((&self->base.base), HueShiftPattern, m_blendDelay); + Pattern_setArgs((&self->base.base), args); } -HueShiftPattern::~HueShiftPattern() +void HueShiftPattern_destroy(Pattern *pat) { + HueShiftPattern *self = (HueShiftPattern *)pat; + (void)self; } -// init the pattern to initial state -void HueShiftPattern::init() +void HueShiftPattern_initImpl(Pattern *pat) { - MultiLedPattern::init(); - m_cur = m_colorset.getNext(); - m_next = m_colorset.getNext(); + HueShiftPattern *self = (HueShiftPattern *)pat; + MultiLedPattern_doInit(pat); + { RGBColor tmp = Colorset_getNext(&(&self->base.base)->m_colorset); HSVColor_setRGB(&self->m_cur, &tmp); } + { RGBColor tmp = Colorset_getNext(&(&self->base.base)->m_colorset); HSVColor_setRGB(&self->m_next, &tmp); } // reset the blink timer entirely - m_blinkTimer.reset(); + Timer_reset(&self->m_blinkTimer); // dops timing - m_blinkTimer.addAlarm(m_blinkOnDuration); - m_blinkTimer.addAlarm(m_blinkOffDuration); + Timer_addAlarm(&self->m_blinkTimer, self->m_blinkOnDuration); + Timer_addAlarm(&self->m_blinkTimer, self->m_blinkOffDuration); // start the blink timer from the next frame - m_blinkTimer.start(); + Timer_start0(&self->m_blinkTimer); } -// pure virtual must override the play function -void HueShiftPattern::play() +void HueShiftPattern_play(Pattern *pat) { - switch (m_blinkTimer.alarm()) { + HueShiftPattern *self = (HueShiftPattern *)pat; + switch (Timer_alarm(&self->m_blinkTimer)) { case 0: // turn on the leds for given mapping break; case 1: // turn off the leds - Leds::clearAll(); + Leds_clearAll(); case -1: // just break and still run post-step return; } - // if the current hue has reached the next hue - if (m_cur.hue == m_next.hue) { + if (self->m_cur.hue == self->m_next.hue) { // copy over the sat/val - m_cur = m_next; + RGBColor_fromHSV(&self->m_cur) = RGBColor_fromHSV(&self->m_next); // get the next color and convert to hsv - m_next = m_colorset.getNext(); + { RGBColor tmp = Colorset_getNext(&(&self->base.base)->m_colorset); HSVColor_setRGB(&self->m_next, &tmp); } } // check which direction is closer for the next hue // tbh idk really how this even works anymore but it does - int sign = ((((m_next.hue + 255) - m_cur.hue) % 256) <= 128) ? 1 : -1; + int sign = ((((self->m_next.hue + 255) - self->m_cur.hue) % 256) <= 128) ? 1 : -1; // move hue in the direction of next hue at chosen speed // NOTE: if the speed isn't a multiple of the hue values then // it will cause oscillation around the target hue // because it will never reach the target hue and // always over/under shoot // only increment every blendDelay times - ++m_delayCounter; - if (m_delayCounter >= m_blendDelay) { - m_delayCounter = 0; - m_cur.hue += sign; + ++self->m_delayCounter; + if (self->m_delayCounter >= self->m_blendDelay) { + self->m_delayCounter = 0; + self->m_cur.hue += sign; } - HSVColor showColor = HSVColor(m_cur.hue, 255, 255); + HSVColor showColor = HSVColor_makeHSV(self->m_cur.hue, 255, 255); // set the target led with the current HSV color for (LedPos pos = LED_FIRST; pos < LED_COUNT; ++pos) { - Leds::setIndex(pos, hsv_to_rgb_generic(showColor)); + Leds_setIndex(pos, hsv_to_rgb_generic(&showColor)); showColor.hue = (showColor.hue + 5) % 256; } } diff --git a/VortexEngine/src/Patterns/Multi/HueShiftPattern.h b/VortexEngine/src/Patterns/Multi/HueShiftPattern.h index 691b3cb2e9..6fb06dc3c0 100644 --- a/VortexEngine/src/Patterns/Multi/HueShiftPattern.h +++ b/VortexEngine/src/Patterns/Multi/HueShiftPattern.h @@ -1,33 +1,31 @@ #ifndef HUE_SHIFT_PATTERN_H #define HUE_SHIFT_PATTERN_H +#include #include "MultiLedPattern.h" - #include "../../Time/Timings.h" #include "../../Time/Timer.h" -class HueShiftPattern : public MultiLedPattern -{ -public: - HueShiftPattern(const PatternArgs &args); - virtual ~HueShiftPattern(); - - // init the pattern to initial state - virtual void init() override; - - // pure virtual must override the play function - virtual void play() override; - -private: +typedef struct HueShiftPattern { + // must remain the first member + MultiLedPattern base; uint8_t m_blinkOnDuration; uint8_t m_blinkOffDuration; uint8_t m_blendDelay; uint8_t m_delayCounter; - Timer m_blinkTimer; - - // current color and target blend color HSVColor m_cur; HSVColor m_next; -}; +} HueShiftPattern; + +// the vtable for this pattern +extern const PatternVtable HueShiftPattern_vtable; + +// initialize a new HueShiftPattern +void HueShiftPattern_init(HueShiftPattern *self, const PatternArgs *args); +void HueShiftPattern_destroy(Pattern *self); + +void HueShiftPattern_initImpl(Pattern *self); +void HueShiftPattern_play(Pattern *self); + #endif diff --git a/VortexEngine/src/Patterns/Multi/LighthousePattern.cpp b/VortexEngine/src/Patterns/Multi/LighthousePattern.cpp index 395a543ac3..e250f61269 100644 --- a/VortexEngine/src/Patterns/Multi/LighthousePattern.cpp +++ b/VortexEngine/src/Patterns/Multi/LighthousePattern.cpp @@ -1,74 +1,92 @@ #include "LighthousePattern.h" +#include #include "../../Serial/ByteStream.h" #include "../../Time/TimeControl.h" #include "../../Leds/Leds.h" #include "../../Log/Log.h" +#include "../../Memory/Memory.h" +#include "../Pattern.h" -LighthousePattern::LighthousePattern(const PatternArgs &args) : - BlinkStepPattern(args), - m_fadeAmount(0), - m_fadeRate(0), - m_fadeTimer(), - m_stash(), - m_progress(0) +const BlinkStepVtable LighthousePattern_bvtable = { + .base = { + .destroy = LighthousePattern_destroy, + .bind = MultiLedPattern_bind, + .init = LighthousePattern_initImpl, + .play = LighthousePattern_play + }, + .blinkOn = LighthousePattern_blinkOn, + .blinkOff = LighthousePattern_blinkOff, + .prestep = BlinkStepPattern_prestep, + .poststep = LighthousePattern_poststep, +}; + +void LighthousePattern_init(LighthousePattern *self, const PatternArgs *args) { - m_patternID = PATTERN_LIGHTHOUSE; - REGISTER_ARG(m_fadeAmount); - REGISTER_ARG(m_fadeRate); - setArgs(args); + BlinkStepPattern_init(&self->base, args, &LighthousePattern_bvtable); + self->m_fadeAmount = 0; + self->m_fadeRate = 0; + Timer_make(&self->m_fadeTimer); + LedStash_make(&self->m_stash); + self->m_progress = 0; + Pattern_setPatternID((&self->base.base.base), PATTERN_LIGHTHOUSE); + REGISTER_ARG((&self->base.base.base), LighthousePattern, m_fadeAmount); + REGISTER_ARG((&self->base.base.base), LighthousePattern, m_fadeRate); + Pattern_setArgs((&self->base.base.base), args); } -LighthousePattern::~LighthousePattern() +void LighthousePattern_destroy(Pattern *pat) { + LighthousePattern *self = (LighthousePattern *)pat; + (void)self; } -// init the pattern to initial state -void LighthousePattern::init() +void LighthousePattern_initImpl(Pattern *pat) { - BlinkStepPattern::init(); - + LighthousePattern *self = (LighthousePattern *)pat; + BlinkStepPattern_initImpl(pat); // fade timer setup - m_fadeTimer.reset(); - m_fadeTimer.addAlarm(m_fadeRate); - m_fadeTimer.start(); - + Timer_reset(&self->m_fadeTimer); + Timer_addAlarm(&self->m_fadeTimer, self->m_fadeRate); + Timer_start0(&self->m_fadeTimer); // start colorset at index 0 so cur() works - m_colorset.setCurIndex(0); + Colorset_setCurIndex(&(&self->base.base.base)->m_colorset, 0); } -// pure virtual must override the play function -void LighthousePattern::play() +void LighthousePattern_play(Pattern *pat) { - if (m_fadeTimer.alarm() == 0) { - fade(); + LighthousePattern *self = (LighthousePattern *)pat; + if (Timer_alarm(&self->m_fadeTimer) == 0) { + LighthousePattern_fade(self); } - - BlinkStepPattern::play(); + BlinkStepPattern_play(pat); } -void LighthousePattern::blinkOn() +void LighthousePattern_blinkOn(BlinkStepPattern *bsp) { - Leds::restoreAll(m_stash); - Leds::setIndex((LedPos)m_progress, m_colorset.cur()); - Leds::stashAll(m_stash); + LighthousePattern *self = (LighthousePattern *)bsp; + Leds_restoreAll(&self->m_stash); + Leds_setIndex((LedPos)self->m_progress, Colorset_cur(&(&self->base.base.base)->m_colorset)); + Leds_stashAll(&self->m_stash); } -void LighthousePattern::blinkOff() +void LighthousePattern_blinkOff(BlinkStepPattern *bsp) { - Leds::stashAll(m_stash); - Leds::clearAll(); + LighthousePattern *self = (LighthousePattern *)bsp; + Leds_stashAll(&self->m_stash); + Leds_clearAll(); } -void LighthousePattern::poststep() +void LighthousePattern_poststep(BlinkStepPattern *bsp) { - m_progress = (m_progress + 1) % LED_COUNT; - m_colorset.getNext(); + LighthousePattern *self = (LighthousePattern *)bsp; + self->m_progress = (self->m_progress + 1) % LED_COUNT; + Colorset_getNext(&(&self->base.base.base)->m_colorset); } -void LighthousePattern::fade() +void LighthousePattern_fade(LighthousePattern *self) { for (int i = 0; i < LED_COUNT; ++i) { - m_stash[i].adjustBrightness(m_fadeAmount); + RGBColor_adjustBrightness(LedStash_at(&self->m_stash, i), self->m_fadeAmount); } } diff --git a/VortexEngine/src/Patterns/Multi/LighthousePattern.h b/VortexEngine/src/Patterns/Multi/LighthousePattern.h index e42da89b66..e2f4cbbfa4 100644 --- a/VortexEngine/src/Patterns/Multi/LighthousePattern.h +++ b/VortexEngine/src/Patterns/Multi/LighthousePattern.h @@ -1,39 +1,34 @@ -#include "BlinkStepPattern.h" +#ifndef LIGHTHOUSE_PATTERN_H +#define LIGHTHOUSE_PATTERN_H +#include +#include "BlinkStepPattern.h" #include "../../Leds/LedStash.h" #include "../../Time/Timings.h" #include "../../Time/Timer.h" -class LighthousePattern : public BlinkStepPattern -{ -public: - LighthousePattern(const PatternArgs &args); - virtual ~LighthousePattern(); - - // init the pattern to initial state - virtual void init() override; - - // pure virtual must override the play function - virtual void play() override; - -protected: - // overrideable members: - virtual void blinkOn() override; - virtual void blinkOff() override; - virtual void poststep() override; - - // new function for this pattern - virtual void fade(); - -private: - // the amount of fade to apply +typedef struct LighthousePattern { + // must remain the first member + BlinkStepPattern base; uint8_t m_fadeAmount; - // the frequency at which the fade is applied uint8_t m_fadeRate; - // a timer for controling fade frqeuency Timer m_fadeTimer; - // storage for leds LedStash m_stash; - // how much the warp has progressed uint8_t m_progress; -}; +} LighthousePattern; + +// the blink/step vtable for this pattern +extern const BlinkStepVtable LighthousePattern_bvtable; + +// initialize a new LighthousePattern +void LighthousePattern_init(LighthousePattern *self, const PatternArgs *args); +void LighthousePattern_destroy(Pattern *self); + +void LighthousePattern_initImpl(Pattern *self); +void LighthousePattern_play(Pattern *self); +void LighthousePattern_blinkOn(BlinkStepPattern *self); +void LighthousePattern_blinkOff(BlinkStepPattern *self); +void LighthousePattern_poststep(BlinkStepPattern *self); +void LighthousePattern_fade(LighthousePattern *self); + +#endif diff --git a/VortexEngine/src/Patterns/Multi/MeteorPattern.cpp b/VortexEngine/src/Patterns/Multi/MeteorPattern.cpp index 004da9867c..7a1a2e8c38 100644 --- a/VortexEngine/src/Patterns/Multi/MeteorPattern.cpp +++ b/VortexEngine/src/Patterns/Multi/MeteorPattern.cpp @@ -1,43 +1,64 @@ #include "MeteorPattern.h" +#include #include "../../Time/TimeControl.h" #include "../../Leds/Leds.h" #include "../../Log/Log.h" +#include "../../Memory/Memory.h" +#include "../Pattern.h" -MeteorPattern::MeteorPattern(const PatternArgs &args) : - BlinkStepPattern(args), - m_fadeAmount(0), - m_randCtx(), - m_stash() +const BlinkStepVtable MeteorPattern_bvtable = { + .base = { + .destroy = MeteorPattern_destroy, + .bind = MultiLedPattern_bind, + .init = BlinkStepPattern_initImpl, + .play = BlinkStepPattern_play + }, + .blinkOn = MeteorPattern_blinkOn, + .blinkOff = MeteorPattern_blinkOff, + .prestep = BlinkStepPattern_prestep, + .poststep = MeteorPattern_poststep, +}; + +void MeteorPattern_init(MeteorPattern *self, const PatternArgs *args) { - m_patternID = PATTERN_METEOR; - REGISTER_ARG(m_fadeAmount); - setArgs(args); + BlinkStepPattern_init(&self->base, args, &MeteorPattern_bvtable); + self->m_fadeAmount = 0; + Random_make(&self->m_randCtx); + LedStash_make(&self->m_stash); + Pattern_setPatternID((&self->base.base.base), PATTERN_METEOR); + REGISTER_ARG((&self->base.base.base), MeteorPattern, m_fadeAmount); + Pattern_setArgs((&self->base.base.base), args); } -MeteorPattern::~MeteorPattern() +void MeteorPattern_destroy(Pattern *pat) { + MeteorPattern *self = (MeteorPattern *)pat; + (void)self; } -void MeteorPattern::blinkOn() +void MeteorPattern_blinkOn(BlinkStepPattern *bsp) { + MeteorPattern *self = (MeteorPattern *)bsp; for (int i = 0; i < LED_COUNT; ++i) { - m_stash[i].adjustBrightness(m_fadeAmount); + RGBColor_adjustBrightness(LedStash_at(&self->m_stash, i), self->m_fadeAmount); } - Leds::restoreAll(m_stash); + Leds_restoreAll(&self->m_stash); } -void MeteorPattern::blinkOff() +void MeteorPattern_blinkOff(BlinkStepPattern *bsp) { - Leds::clearAll(); + MeteorPattern *self = (MeteorPattern *)bsp; + Leds_clearAll(); } -void MeteorPattern::poststep() +void MeteorPattern_poststep(BlinkStepPattern *bsp) { + MeteorPattern *self = (MeteorPattern *)bsp; for (uint8_t meteorCount = 0; meteorCount < (LED_COUNT / 2); ++meteorCount) { // when a new meteor is created it is incerted into the stash so the blinking pattern is not interrupted - LedPos target = (LedPos)m_randCtx.next8(LED_FIRST, LED_LAST); - RGBColor col = m_colorset.getNext(); - m_stash.setIndex(target, col); + LedPos target = (LedPos)Random_next8(&self->m_randCtx, LED_FIRST, LED_LAST); + RGBColor col = Colorset_getNext(&(&self->base.base.base)->m_colorset); + LedStash_setIndex(&self->m_stash, target, col); } } diff --git a/VortexEngine/src/Patterns/Multi/MeteorPattern.h b/VortexEngine/src/Patterns/Multi/MeteorPattern.h index 805954d2db..b5570b9837 100644 --- a/VortexEngine/src/Patterns/Multi/MeteorPattern.h +++ b/VortexEngine/src/Patterns/Multi/MeteorPattern.h @@ -1,30 +1,29 @@ #ifndef METEOR_PATTERN_H #define METEOR_PATTERN_H +#include #include "BlinkStepPattern.h" - #include "../../Time/Timings.h" #include "../../Leds/LedStash.h" #include "../../Random/Random.h" -class MeteorPattern : public BlinkStepPattern -{ -public: - MeteorPattern(const PatternArgs &args); - virtual ~MeteorPattern(); +typedef struct MeteorPattern { + // must remain the first member + BlinkStepPattern base; + uint8_t m_fadeAmount; + Random m_randCtx; + LedStash m_stash; +} MeteorPattern; -protected: - virtual void blinkOn() override; - virtual void blinkOff() override; - virtual void poststep() override; +// the blink/step vtable for this pattern +extern const BlinkStepVtable MeteorPattern_bvtable; -private: - // the fade amount - uint8_t m_fadeAmount; +// initialize a new MeteorPattern +void MeteorPattern_init(MeteorPattern *self, const PatternArgs *args); +void MeteorPattern_destroy(Pattern *self); - // for random operations - Random m_randCtx; +void MeteorPattern_blinkOn(BlinkStepPattern *self); +void MeteorPattern_blinkOff(BlinkStepPattern *self); +void MeteorPattern_poststep(BlinkStepPattern *self); - LedStash m_stash; -}; #endif diff --git a/VortexEngine/src/Patterns/Multi/MultiLedPattern.cpp b/VortexEngine/src/Patterns/Multi/MultiLedPattern.cpp index 3b187850d2..e55b9f546c 100644 --- a/VortexEngine/src/Patterns/Multi/MultiLedPattern.cpp +++ b/VortexEngine/src/Patterns/Multi/MultiLedPattern.cpp @@ -1,28 +1,29 @@ #include "MultiLedPattern.h" -MultiLedPattern::MultiLedPattern(const PatternArgs &args) : - Pattern(args) -{ - // this is a multi led pattern - m_patternFlags |= PATTERN_FLAG_MULTI; +const PatternVtable MultiLedPattern_vtable = { + .destroy = Pattern_destroy, + .bind = MultiLedPattern_bind, + .init = MultiLedPattern_doInit, + .play = Pattern_basePlay +}; - // MultiLed is an abstract class it cannot be directly - // instantiated so we do not need to assign a pattern id -} - -MultiLedPattern::~MultiLedPattern() +void MultiLedPattern_init(MultiLedPattern *self, const PatternArgs *args, const PatternVtable *vtable) { + Pattern_initBase(&self->base, PATTERN_NONE, vtable ? vtable : &MultiLedPattern_vtable); + // this is a multi led pattern + Pattern_setFlags(&self->base, Pattern_getFlags(&self->base) | PATTERN_FLAG_MULTI); + Pattern_setArgs(&self->base, args); } -void MultiLedPattern::bind(LedPos pos) +void MultiLedPattern_bind(Pattern *self, LedPos pos) { // the 'LedPos' is LED_COUNT when binding a multi-led pattern // as opposed to being an individual LED when it's single - Pattern::bind(LED_COUNT); + Pattern_baseBind(self, LED_COUNT); } // init the pattern to initial state -void MultiLedPattern::init() +void MultiLedPattern_doInit(Pattern *self) { - Pattern::init(); + Pattern_baseInit(self); } diff --git a/VortexEngine/src/Patterns/Multi/MultiLedPattern.h b/VortexEngine/src/Patterns/Multi/MultiLedPattern.h index 9e105be4ff..b6d326165b 100644 --- a/VortexEngine/src/Patterns/Multi/MultiLedPattern.h +++ b/VortexEngine/src/Patterns/Multi/MultiLedPattern.h @@ -1,25 +1,28 @@ #ifndef MULTI_LED_PATTERN_H #define MULTI_LED_PATTERN_H +#include #include "../Pattern.h" -class MultiLedPattern : public Pattern -{ -protected: - // MultiLedPattern is an abstract class - MultiLedPattern(const PatternArgs &args); - -public: - virtual ~MultiLedPattern(); - - // multi-led and single-led have different bind functions - virtual void bind(LedPos pos) override; - - // init the pattern to initial state - virtual void init() override; - -private: - -}; +// MultiLedPattern is the base for every pattern that drives the whole led +// map at once rather than a single led. It adds no data of its own, it only +// sets PATTERN_FLAG_MULTI and forces the bound position to LED_COUNT. +typedef struct MultiLedPattern { + // must remain the first member + Pattern base; +} MultiLedPattern; + +// the vtable a multi-led pattern dispatches through by default +extern const PatternVtable MultiLedPattern_vtable; + +// initialize the base portion of a multi-led pattern. The pattern id is +// not assigned here because MultiLedPattern is abstract. +// 'vtable' is the table the derived pattern dispatches through, or +// &MultiLedPattern_vtable to keep the default behaviour. +void MultiLedPattern_init(MultiLedPattern *self, const PatternArgs *args, const PatternVtable *vtable); + +// the default vtable implementations +void MultiLedPattern_bind(Pattern *self, LedPos pos); +void MultiLedPattern_doInit(Pattern *self); #endif diff --git a/VortexEngine/src/Patterns/Multi/PulsishPattern.cpp b/VortexEngine/src/Patterns/Multi/PulsishPattern.cpp index 9c9e9b49b9..6639b55f44 100644 --- a/VortexEngine/src/Patterns/Multi/PulsishPattern.cpp +++ b/VortexEngine/src/Patterns/Multi/PulsishPattern.cpp @@ -1,94 +1,102 @@ #include "PulsishPattern.h" +#include #include "../../Time/TimeControl.h" #include "../../Leds/Leds.h" +#include "../../Memory/Memory.h" +#include "../Pattern.h" -PulsishPattern::PulsishPattern(const PatternArgs &args) : - MultiLedPattern(args), - m_progress(), - m_stepDuration(0), - m_stepTimer(), - m_onDuration1(0), - m_offDuration1(0), - m_onDuration2(0), - m_offDuration2(0), - m_blinkTimer(), - m_blink2Timer() +const PatternVtable PulsishPattern_vtable = { + .destroy = PulsishPattern_destroy, + .bind = MultiLedPattern_bind, + .init = PulsishPattern_initImpl, + .play = PulsishPattern_play +}; + +void PulsishPattern_init(PulsishPattern *self, const PatternArgs *args) { - m_patternID = PATTERN_PULSISH; - REGISTER_ARG(m_onDuration1); - REGISTER_ARG(m_offDuration1); - REGISTER_ARG(m_onDuration2); - REGISTER_ARG(m_offDuration2); - REGISTER_ARG(m_stepDuration); - setArgs(args); + MultiLedPattern_init(&self->base, args, &PulsishPattern_vtable); + self->m_progress = 0; + self->m_stepDuration = 0; + Timer_make(&self->m_stepTimer); + self->m_onDuration1 = 0; + self->m_offDuration1 = 0; + self->m_onDuration2 = 0; + self->m_offDuration2 = 0; + Timer_make(&self->m_blinkTimer); + Timer_make(&self->m_blink2Timer); + Pattern_setPatternID((&self->base.base), PATTERN_PULSISH); + REGISTER_ARG((&self->base.base), PulsishPattern, m_onDuration1); + REGISTER_ARG((&self->base.base), PulsishPattern, m_offDuration1); + REGISTER_ARG((&self->base.base), PulsishPattern, m_onDuration2); + REGISTER_ARG((&self->base.base), PulsishPattern, m_offDuration2); + REGISTER_ARG((&self->base.base), PulsishPattern, m_stepDuration); + Pattern_setArgs((&self->base.base), args); } -PulsishPattern::~PulsishPattern() +void PulsishPattern_destroy(Pattern *pat) { + PulsishPattern *self = (PulsishPattern *)pat; + (void)self; } -// init the pattern to initial state -void PulsishPattern::init() +void PulsishPattern_initImpl(Pattern *pat) { - MultiLedPattern::init(); - + PulsishPattern *self = (PulsishPattern *)pat; + MultiLedPattern_doInit(pat); // reset and add alarm - m_stepTimer.reset(); - m_stepTimer.addAlarm(m_stepDuration); - m_stepTimer.start(); - + Timer_reset(&self->m_stepTimer); + Timer_addAlarm(&self->m_stepTimer, self->m_stepDuration); + Timer_start0(&self->m_stepTimer); // make blink timer - m_blinkTimer.reset(); - m_blinkTimer.addAlarm(m_onDuration1); - m_blinkTimer.addAlarm(m_offDuration1); - m_blinkTimer.start(); - + Timer_reset(&self->m_blinkTimer); + Timer_addAlarm(&self->m_blinkTimer, self->m_onDuration1); + Timer_addAlarm(&self->m_blinkTimer, self->m_offDuration1); + Timer_start0(&self->m_blinkTimer); // make 2nd blink timer - m_blink2Timer.reset(); - m_blink2Timer.addAlarm(m_onDuration2); - m_blink2Timer.addAlarm(m_offDuration2); - m_blink2Timer.start(); + Timer_reset(&self->m_blink2Timer); + Timer_addAlarm(&self->m_blink2Timer, self->m_onDuration2); + Timer_addAlarm(&self->m_blink2Timer, self->m_offDuration2); + Timer_start0(&self->m_blink2Timer); } -// pure virtual must override the play function -void PulsishPattern::play() + +void PulsishPattern_play(Pattern *pat) { + PulsishPattern *self = (PulsishPattern *)pat; // when the step timer triggers - if (m_stepTimer.alarm() == 0) { - m_progress = (m_progress + 1) % PAIR_COUNT; + if (Timer_alarm(&self->m_stepTimer) == 0) { + self->m_progress = (self->m_progress + 1) % PAIR_COUNT; } - - switch (m_blinkTimer.alarm()) { + switch (Timer_alarm(&self->m_blinkTimer)) { case -1: // just return return; case 0: // turn on the leds for (Pair pair = PAIR_FIRST; pair < PAIR_COUNT; ++pair) { - if (pair != m_progress) { - Leds::setPair(pair, m_colorset.cur()); + if (pair != self->m_progress) { + Leds_setPair(pair, Colorset_cur(&(&self->base.base)->m_colorset)); } } - m_colorset.skip(); - if (m_colorset.curIndex() == 0) { - m_colorset.skip(); + Colorset_skip(&(&self->base.base)->m_colorset, 1); + if (Colorset_curIndex(&(&self->base.base)->m_colorset) == 0) { + Colorset_skip(&(&self->base.base)->m_colorset, 1); } break; case 1: for (Pair pair = PAIR_FIRST; pair < PAIR_COUNT; ++pair) { - if (pair != m_progress) { - Leds::clearPair(pair); + if (pair != self->m_progress) { + Leds_clearPair(pair); } } break; } - - switch (m_blink2Timer.alarm()) { + switch (Timer_alarm(&self->m_blink2Timer)) { case -1: // just return return; case 0: // turn on the leds - Leds::setPair((Pair)m_progress, m_colorset.get(0)); + Leds_setPair((Pair)self->m_progress, Colorset_get(&(&self->base.base)->m_colorset, 0)); break; case 1: - Leds::clearPair((Pair)m_progress); + Leds_clearPair((Pair)self->m_progress); break; } } diff --git a/VortexEngine/src/Patterns/Multi/PulsishPattern.h b/VortexEngine/src/Patterns/Multi/PulsishPattern.h index 16435c872d..586ba60ac1 100644 --- a/VortexEngine/src/Patterns/Multi/PulsishPattern.h +++ b/VortexEngine/src/Patterns/Multi/PulsishPattern.h @@ -1,34 +1,33 @@ -#include "MultiLedPattern.h" +#ifndef PULSISH_PATTERN_H +#define PULSISH_PATTERN_H +#include +#include "MultiLedPattern.h" #include "../../Time/Timings.h" #include "../../Time/Timer.h" -class PulsishPattern : public MultiLedPattern -{ -public: - PulsishPattern(const PatternArgs &args); - virtual ~PulsishPattern(); - - // init the pattern to initial state - virtual void init() override; - - // pure virtual must override the play function - virtual void play() override; - -private: - // how much the warp has progressed +typedef struct PulsishPattern { + // must remain the first member + MultiLedPattern base; uint8_t m_progress; - - // the step duration uint8_t m_stepDuration; - // the step timer Timer m_stepTimer; - // the on and off durations uint8_t m_onDuration1; uint8_t m_offDuration1; uint8_t m_onDuration2; uint8_t m_offDuration2; - // the blink timer Timer m_blinkTimer; Timer m_blink2Timer; -}; +} PulsishPattern; + +// the vtable for this pattern +extern const PatternVtable PulsishPattern_vtable; + +// initialize a new PulsishPattern +void PulsishPattern_init(PulsishPattern *self, const PatternArgs *args); +void PulsishPattern_destroy(Pattern *self); + +void PulsishPattern_initImpl(Pattern *self); +void PulsishPattern_play(Pattern *self); + +#endif diff --git a/VortexEngine/src/Patterns/Multi/Sequencer/ChaserPattern.cpp b/VortexEngine/src/Patterns/Multi/Sequencer/ChaserPattern.cpp index 2868c09e97..9921c8989a 100644 --- a/VortexEngine/src/Patterns/Multi/Sequencer/ChaserPattern.cpp +++ b/VortexEngine/src/Patterns/Multi/Sequencer/ChaserPattern.cpp @@ -3,23 +3,30 @@ // This controls the ratio of chaser dots to LED_COUNT. Default 1 chaser per 7 LEDs. Range: 1-LED_COUNT. #define CHASER_RATIO 7 +const PatternVtable ChaserPattern_vtable = { + .destroy = SequencedPattern_destroy, + .bind = MultiLedPattern_bind, + .init = SequencedPattern_initImpl, + .play = SequencedPattern_play, +}; // This pattern aims to be a demonstration of the sequencer. // There are always many ways to implement a pattern, it's best // to choose the method that is most suitable for the pattern. -ChaserPattern::ChaserPattern(const PatternArgs &args) : - SequencedPattern(args) +void ChaserPattern_init(ChaserPattern *self, const PatternArgs *args) { - setArgs(args); + SequencedPattern_init(&self->base, args, &ChaserPattern_vtable); + Pattern_setArgs((&self->base.base.base.base), args); uint32_t numChasers = LED_COUNT / CHASER_RATIO; if (!numChasers) { numChasers = 1; } for (uint8_t i = 0; i < (LED_COUNT / numChasers); ++i) { - PatternMap patMap(PATTERN_DOPS); + PatternMap patMap; + PatternMap_makeWith(&patMap, PATTERN_DOPS, MAP_LED_ALL); for (uint8_t chaserCount = 0; chaserCount < numChasers; ++chaserCount) { - patMap.setPatternAt(PATTERN_SOLID, MAP_LED((i + (chaserCount * CHASER_RATIO)) % LED_COUNT)); + PatternMap_setPatternAt(&patMap, PATTERN_SOLID, MAP_LED((i + (chaserCount * CHASER_RATIO)) % LED_COUNT)); } - m_sequence.addStep(150, patMap); + Sequence_addStepWith(&self->base.m_sequence, 150, &patMap, NULL); } } diff --git a/VortexEngine/src/Patterns/Multi/Sequencer/ChaserPattern.h b/VortexEngine/src/Patterns/Multi/Sequencer/ChaserPattern.h index 403e95f871..f6c86b2c26 100644 --- a/VortexEngine/src/Patterns/Multi/Sequencer/ChaserPattern.h +++ b/VortexEngine/src/Patterns/Multi/Sequencer/ChaserPattern.h @@ -3,11 +3,15 @@ #include "SequencedPattern.h" -class ChaserPattern : public SequencedPattern -{ -public: - // Just provide a constructor to build the sequence - ChaserPattern(const PatternArgs &args); -}; +// ChaserPattern is the demonstration sequenced pattern, it builds a sequence +// of steps where a single solid led chases around the map. +typedef struct ChaserPattern { + // must remain the first member + SequencedPattern base; +} ChaserPattern; + +extern const PatternVtable ChaserPattern_vtable; + +void ChaserPattern_init(ChaserPattern *self, const PatternArgs *args); #endif diff --git a/VortexEngine/src/Patterns/Multi/Sequencer/Sequence.cpp b/VortexEngine/src/Patterns/Multi/Sequencer/Sequence.cpp index 33a7239b92..a0daca125e 100644 --- a/VortexEngine/src/Patterns/Multi/Sequencer/Sequence.cpp +++ b/VortexEngine/src/Patterns/Multi/Sequencer/Sequence.cpp @@ -10,276 +10,270 @@ // some arbitrary number #define MAX_SEQUENCE_STEPS 64 -PatternMap::PatternMap() : - m_patternMap() +// =================== +// PatternMap + +void PatternMap_make(PatternMap *self) { for (LedPos i = LED_FIRST; i < LED_COUNT; ++i) { - m_patternMap[i] = PATTERN_NONE; + self->m_patternMap[i] = PATTERN_NONE; } } -PatternMap::PatternMap(PatternID pattern, LedMap positions) : - PatternMap() +void PatternMap_makeWith(PatternMap *self, PatternID pattern, LedMap positions) { - setPatternAt(pattern, positions); + PatternMap_make(self); + PatternMap_setPatternAt(self, pattern, positions); } // set a pattern at each position in the LedMap -void PatternMap::setPatternAt(PatternID pattern, LedMap positions) +void PatternMap_setPatternAt(PatternMap *self, PatternID pattern, LedMap positions) { for (LedPos pos = LED_FIRST; pos < LED_COUNT; ++pos) { if (ledmapCheckLed(positions, pos)) { - m_patternMap[pos] = pattern; + self->m_patternMap[pos] = pattern; } } } -PatternID PatternMap::operator[](LedPos index) const +PatternID PatternMap_at(const PatternMap *self, LedPos index) { - return m_patternMap[index]; + return self->m_patternMap[index]; } -bool PatternMap::serialize(ByteStream &buffer) const +bool PatternMap_serialize(const PatternMap *self, ByteStream *buffer) { for (LedPos i = LED_FIRST; i < LED_COUNT; ++i) { // ensure the PatternID is interpreted as uint8_t - if (!buffer.serialize8((uint8_t)m_patternMap[i])) { + if (!ByteStream_serialize8(buffer, (uint8_t)self->m_patternMap[i])) { return false; } } return true; } -bool PatternMap::unserialize(ByteStream &buffer) +bool PatternMap_unserialize(PatternMap *self, ByteStream *buffer) { for (LedPos i = LED_FIRST; i < LED_COUNT; ++i) { - if (!buffer.unserialize8((uint8_t *)m_patternMap + i)) { + if (!ByteStream_unserialize8(buffer, (uint8_t *)self->m_patternMap + i)) { return false; } } return true; } -ColorsetMap::ColorsetMap() : - m_colorsetMap() +// =================== +// ColorsetMap + +void ColorsetMap_make(ColorsetMap *self) { + memset(self, 0, sizeof(*self)); } -ColorsetMap::ColorsetMap(const Colorset &colorset, LedMap positions) : - ColorsetMap() +void ColorsetMap_makeWith(ColorsetMap *self, const Colorset *colorset, LedMap positions) { - setColorsetAt(colorset, positions); + ColorsetMap_make(self); + ColorsetMap_setColorsetAt(self, colorset, positions); } -// set a pattern at each position in the LedMap -void ColorsetMap::setColorsetAt(const Colorset &colorset, LedMap positions) +// set a colorset at each position in the LedMap +void ColorsetMap_setColorsetAt(ColorsetMap *self, const Colorset *colorset, LedMap positions) { for (LedPos pos = LED_FIRST; pos < LED_COUNT; ++pos) { if (ledmapCheckLed(positions, pos)) { - m_colorsetMap[pos] = colorset; + self->m_colorsetMap[pos] = *colorset; } } } -const Colorset &ColorsetMap::operator[](LedPos index) const + +const Colorset *ColorsetMap_at(const ColorsetMap *self, LedPos index) { - return m_colorsetMap[index]; + return &self->m_colorsetMap[index]; } -bool ColorsetMap::serialize(ByteStream &buffer) const +bool ColorsetMap_serialize(const ColorsetMap *self, ByteStream *buffer) { for (LedPos i = LED_FIRST; i < LED_COUNT; ++i) { - if (!m_colorsetMap[i].serialize(buffer)) { + if (!Colorset_serialize(&self->m_colorsetMap[i], buffer)) { return false; } } return true; } -bool ColorsetMap::unserialize(ByteStream &buffer) +bool ColorsetMap_unserialize(ColorsetMap *self, ByteStream *buffer) { for (LedPos i = LED_FIRST; i < LED_COUNT; ++i) { - if (!m_colorsetMap[i].unserialize(buffer)) { + if (!Colorset_unserialize(&self->m_colorsetMap[i], buffer)) { return false; } } return true; } -// Make an array of sequence steps to create a sequenced pattern -SequenceStep::SequenceStep() : - m_duration(0), m_patternMap(), m_colorsetMap() -{ -} -SequenceStep::SequenceStep(uint16_t duration, const PatternMap &patternMap, const ColorsetMap &colorsetMap) : - m_duration(duration), m_patternMap(patternMap), m_colorsetMap(colorsetMap) +// =================== +// SequenceStep + +void SequenceStep_make(SequenceStep *self) { + self->m_duration = 0; + PatternMap_make(&self->m_patternMap); + ColorsetMap_make(&self->m_colorsetMap); } -SequenceStep::SequenceStep(const SequenceStep &other) : - SequenceStep(other.m_duration, other.m_patternMap, other.m_colorsetMap) + +void SequenceStep_makeWith(SequenceStep *self, uint16_t duration, const PatternMap *patternMap, const ColorsetMap *colorsetMap) { + self->m_duration = duration; + self->m_patternMap = *patternMap; + if (colorsetMap) { + self->m_colorsetMap = *colorsetMap; + } else { + ColorsetMap_make(&self->m_colorsetMap); + } } -bool SequenceStep::serialize(ByteStream &buffer) const +bool SequenceStep_serialize(const SequenceStep *self, ByteStream *buffer) { - if (!buffer.serialize16(m_duration)) { + if (!ByteStream_serialize16(buffer, self->m_duration)) { return false; } - if (!m_patternMap.serialize(buffer)) { + if (!PatternMap_serialize(&self->m_patternMap, buffer)) { return false; } - if (!m_colorsetMap.serialize(buffer)) { + if (!ColorsetMap_serialize(&self->m_colorsetMap, buffer)) { return false; } return true; } -bool SequenceStep::unserialize(ByteStream &buffer) +bool SequenceStep_unserialize(SequenceStep *self, ByteStream *buffer) { - if (!buffer.unserialize16(&m_duration)) { + if (!ByteStream_unserialize16(buffer, &self->m_duration)) { return false; } - if (!m_patternMap.unserialize(buffer)) { + if (!PatternMap_unserialize(&self->m_patternMap, buffer)) { return false; } - if (!m_colorsetMap.unserialize(buffer)) { + if (!ColorsetMap_unserialize(&self->m_colorsetMap, buffer)) { return false; } return true; } -Sequence::Sequence() : - m_sequenceSteps(nullptr), - m_numSteps(0) -{ -} - -Sequence::~Sequence() -{ - clear(); -} +// =================== +// Sequence -Sequence::Sequence(const Sequence &other) : - Sequence() +void Sequence_make(Sequence *self) { - // invoke = operator - *this = other; + self->m_sequenceSteps = NULL; + self->m_numSteps = 0; } -Sequence::Sequence(Sequence &&other) noexcept : - m_sequenceSteps(other.m_sequenceSteps), - m_numSteps(other.m_numSteps) +void Sequence_destroy(Sequence *self) { - other.m_sequenceSteps = nullptr; - other.m_numSteps = 0; + Sequence_clear(self); } -void Sequence::operator=(const Sequence &other) +void Sequence_copy(Sequence *self, const Sequence *other) { - clear(); - initSteps(other.m_numSteps); - for (uint8_t i = 0; i < other.m_numSteps; ++i) { - m_sequenceSteps[i] = other.m_sequenceSteps[i]; + Sequence_clear(self); + Sequence_initSteps(self, other->m_numSteps); + for (uint8_t i = 0; i < other->m_numSteps; ++i) { + self->m_sequenceSteps[i] = other->m_sequenceSteps[i]; } } -bool Sequence::operator==(const Sequence &other) const +bool Sequence_equals(const Sequence *self, const Sequence *other) { // only compare the palettes for equality - return (m_numSteps == other.m_numSteps) && - (memcmp(m_sequenceSteps, other.m_sequenceSteps, m_numSteps * sizeof(SequenceStep)) == 0); -} - -bool Sequence::operator!=(const Sequence &other) const -{ - return !operator==(other); + return (self->m_numSteps == other->m_numSteps) && + (memcmp(self->m_sequenceSteps, other->m_sequenceSteps, self->m_numSteps * sizeof(SequenceStep)) == 0); } -void Sequence::initSteps(uint8_t numSteps) +void Sequence_initSteps(Sequence *self, uint8_t numSteps) { - if (m_sequenceSteps) { - delete[] m_sequenceSteps; + if (self->m_sequenceSteps) { + vfree(self->m_sequenceSteps); } - m_sequenceSteps = new SequenceStep[numSteps]; - if (!m_sequenceSteps) { + self->m_sequenceSteps = (SequenceStep *)vmalloc(sizeof(SequenceStep) * numSteps); + if (!self->m_sequenceSteps) { ERROR_OUT_OF_MEMORY(); return; } - m_numSteps = numSteps; + self->m_numSteps = numSteps; } -uint8_t Sequence::addStep(const SequenceStep &step) +bool Sequence_addStep(Sequence *self, const SequenceStep *step) { - if (m_numSteps >= MAX_SEQUENCE_STEPS) { + if (self->m_numSteps >= MAX_SEQUENCE_STEPS) { return false; } // allocate a new palette one larger than before - SequenceStep *temp = new SequenceStep[m_numSteps + 1]; + SequenceStep *temp = (SequenceStep *)vmalloc(sizeof(SequenceStep) * (self->m_numSteps + 1)); if (!temp) { return false; } // if there is already some colors in the palette - if (m_numSteps && m_sequenceSteps) { + if (self->m_numSteps && self->m_sequenceSteps) { // copy over existing colors - for (uint8_t i = 0; i < m_numSteps; ++i) { - temp[i] = m_sequenceSteps[i]; + for (uint8_t i = 0; i < self->m_numSteps; ++i) { + temp[i] = self->m_sequenceSteps[i]; } - // and delete the existing palette - delete[] m_sequenceSteps; + // and free the existing palette + vfree(self->m_sequenceSteps); } // reassign new palette - m_sequenceSteps = temp; + self->m_sequenceSteps = temp; // insert new color and increment number of colors - m_sequenceSteps[m_numSteps] = step; - m_numSteps++; + self->m_sequenceSteps[self->m_numSteps] = *step; + self->m_numSteps++; return true; } -uint8_t Sequence::addStep(uint16_t duration, const PatternMap &patternMap, const ColorsetMap &colorsetMap) +bool Sequence_addStepWith(Sequence *self, uint16_t duration, const PatternMap *patternMap, const ColorsetMap *colorsetMap) { - return addStep(SequenceStep(duration, patternMap, colorsetMap)); + SequenceStep step; + SequenceStep_makeWith(&step, duration, patternMap, colorsetMap); + bool ok = Sequence_addStep(self, &step); + return ok; } -void Sequence::clear() +void Sequence_clear(Sequence *self) { - if (m_sequenceSteps) { - delete[] m_sequenceSteps; - m_sequenceSteps = nullptr; + if (self->m_sequenceSteps) { + vfree(self->m_sequenceSteps); + self->m_sequenceSteps = NULL; } - m_numSteps = 0; + self->m_numSteps = 0; } -bool Sequence::serialize(ByteStream &buffer) const +bool Sequence_serialize(const Sequence *self, ByteStream *buffer) { - if (!buffer.serialize8(m_numSteps)) { + if (!ByteStream_serialize8(buffer, self->m_numSteps)) { return false; } - for (uint8_t i = 0; i < m_numSteps; ++i) { - if (!m_sequenceSteps[i].serialize(buffer)) { + for (uint8_t i = 0; i < self->m_numSteps; ++i) { + if (!SequenceStep_serialize(&self->m_sequenceSteps[i], buffer)) { return false; } } return true; } -bool Sequence::unserialize(ByteStream &buffer) +bool Sequence_unserialize(Sequence *self, ByteStream *buffer) { - if (!buffer.unserialize8(&m_numSteps)) { + if (!ByteStream_unserialize8(buffer, &self->m_numSteps)) { return false; } - for (uint8_t i = 0; i < m_numSteps; ++i) { - if (!m_sequenceSteps[i].unserialize(buffer)) { + for (uint8_t i = 0; i < self->m_numSteps; ++i) { + if (!SequenceStep_unserialize(&self->m_sequenceSteps[i], buffer)) { return false; } } return true; } -uint8_t Sequence::numSteps() const -{ - return m_numSteps; -} - -const SequenceStep &Sequence::operator[](uint8_t index) const +const SequenceStep *Sequence_at(const Sequence *self, uint8_t index) { - return m_sequenceSteps[index]; + return &self->m_sequenceSteps[index]; } diff --git a/VortexEngine/src/Patterns/Multi/Sequencer/Sequence.h b/VortexEngine/src/Patterns/Multi/Sequencer/Sequence.h index 37ddf75911..ce0e354bba 100644 --- a/VortexEngine/src/Patterns/Multi/Sequencer/Sequence.h +++ b/VortexEngine/src/Patterns/Multi/Sequencer/Sequence.h @@ -1,100 +1,86 @@ #ifndef SEQUENCER_H #define SEQUENCER_H +#include +#include #include "../../Patterns.h" #include "../../../Leds/LedTypes.h" #include "../../../Colors/Colorset.h" #include "../../../Time/Timer.h" -class SequencedPattern; -class SingleLedPattern; -class ByteStream; +typedef struct ByteStream ByteStream; +typedef struct Sequence Sequence; +typedef struct SequenceStep SequenceStep; // a map of leds to pattern ids -class PatternMap -{ -public: - PatternMap(); - PatternMap(PatternID pattern, LedMap positions = MAP_LED_ALL); - - // set a pattern at each position in the LedMap - void setPatternAt(PatternID pattern, LedMap positions); - PatternID operator[](LedPos index) const; - - // serialize and unserialize a pattern map - bool serialize(ByteStream &buffer) const; - bool unserialize(ByteStream &buffer); - +typedef struct PatternMap { // public list of pattern IDs for each led PatternID m_patternMap[LED_COUNT]; -}; - -// A map of leds to colorsets -class ColorsetMap -{ -public: - ColorsetMap(); - ColorsetMap(const Colorset &colorset, LedMap positions = MAP_LED_ALL); - - // set a pattern at each position in the LedMap - void setColorsetAt(const Colorset &colorset, LedMap positions); - const Colorset &operator[](LedPos index) const; - - // serialize and unserialize a colorset map - bool serialize(ByteStream &buffer) const; - bool unserialize(ByteStream &buffer); - - // public list of pattern IDs for each led +} PatternMap; + +void PatternMap_make(PatternMap *self); +void PatternMap_makeWith(PatternMap *self, PatternID pattern, LedMap positions); +// set a pattern at each position in the LedMap +void PatternMap_setPatternAt(PatternMap *self, PatternID pattern, LedMap positions); +PatternID PatternMap_at(const PatternMap *self, LedPos index); +// serialize and unserialize a pattern map +bool PatternMap_serialize(const PatternMap *self, ByteStream *buffer); +bool PatternMap_unserialize(PatternMap *self, ByteStream *buffer); + +// a map of leds to colorsets +typedef struct ColorsetMap { + // public list of colorsets for each led Colorset m_colorsetMap[LED_COUNT]; -}; - -// A single step in a sequence -class SequenceStep -{ -public: - SequenceStep(); - SequenceStep(uint16_t duration, const PatternMap &patternMap, const ColorsetMap &colorsetMap = Colorset()); - SequenceStep(const SequenceStep &other); - - // serialize and unserialize a step in the sequencer - bool serialize(ByteStream &buffer) const; - bool unserialize(ByteStream &buffer); - +} ColorsetMap; + +void ColorsetMap_make(ColorsetMap *self); +void ColorsetMap_makeWith(ColorsetMap *self, const Colorset *colorset, LedMap positions); +// set a colorset at each position in the LedMap +void ColorsetMap_setColorsetAt(ColorsetMap *self, const Colorset *colorset, LedMap positions); +const Colorset *ColorsetMap_at(const ColorsetMap *self, LedPos index); +// serialize and unserialize a colorset map +bool ColorsetMap_serialize(const ColorsetMap *self, ByteStream *buffer); +bool ColorsetMap_unserialize(ColorsetMap *self, ByteStream *buffer); + +// a single step in a sequence +struct SequenceStep { // public members to allow for easy initialization of an array of SequenceSteps uint16_t m_duration; PatternMap m_patternMap; ColorsetMap m_colorsetMap; }; -// A sequence which can contain multiple SequenceSteps -class Sequence -{ -public: - Sequence(); - ~Sequence(); +void SequenceStep_make(SequenceStep *self); +void SequenceStep_makeWith(SequenceStep *self, uint16_t duration, const PatternMap *patternMap, const ColorsetMap *colorsetMap); +// serialize and unserialize a step in the sequencer +bool SequenceStep_serialize(const SequenceStep *self, ByteStream *buffer); +bool SequenceStep_unserialize(SequenceStep *self, ByteStream *buffer); - Sequence(const Sequence &other); - Sequence(Sequence &&other) noexcept; +// a sequence which can contain multiple SequenceSteps +struct Sequence { + // static data + SequenceStep *m_sequenceSteps; + uint8_t m_numSteps; +}; - void operator=(const Sequence &other); - bool operator==(const Sequence &other) const; - bool operator!=(const Sequence &other) const; +void Sequence_make(Sequence *self); +void Sequence_destroy(Sequence *self); +void Sequence_copy(Sequence *self, const Sequence *other); +bool Sequence_equals(const Sequence *self, const Sequence *other); - void initSteps(uint8_t numSteps); - uint8_t addStep(const SequenceStep &step); - uint8_t addStep(uint16_t duration, const PatternMap &patternMap, const ColorsetMap &colorsetMap = Colorset()); - void clear(); +void Sequence_initSteps(Sequence *self, uint8_t numSteps); +bool Sequence_addStep(Sequence *self, const SequenceStep *step); +bool Sequence_addStepWith(Sequence *self, uint16_t duration, const PatternMap *patternMap, const ColorsetMap *colorsetMap); +void Sequence_clear(Sequence *self); - bool serialize(ByteStream &buffer) const; - bool unserialize(ByteStream &buffer); +bool Sequence_serialize(const Sequence *self, ByteStream *buffer); +bool Sequence_unserialize(Sequence *self, ByteStream *buffer); - uint8_t numSteps() const; - const SequenceStep &operator[](uint8_t index) const; +static inline uint8_t Sequence_numSteps(const Sequence *self) +{ + return self->m_numSteps; +} -private: - // static data - SequenceStep *m_sequenceSteps; - uint8_t m_numSteps; -}; +const SequenceStep *Sequence_at(const Sequence *self, uint8_t index); #endif diff --git a/VortexEngine/src/Patterns/Multi/Sequencer/SequencedPattern.cpp b/VortexEngine/src/Patterns/Multi/Sequencer/SequencedPattern.cpp index b3e3639dba..5addbea57f 100644 --- a/VortexEngine/src/Patterns/Multi/Sequencer/SequencedPattern.cpp +++ b/VortexEngine/src/Patterns/Multi/Sequencer/SequencedPattern.cpp @@ -6,107 +6,122 @@ #include "../../../Serial/ByteStream.h" #include "../../../Leds/Leds.h" #include "../../../Log/Log.h" +#include "../../../Memory/Memory.h" #include -SequencedPattern::SequencedPattern(const PatternArgs &args) : - CompoundPattern(args), - m_sequence(), - m_curSequence(0), - m_timer() +static void playSequenceStep(SequencedPattern *self, const SequenceStep *step); + +const PatternVtable SequencedPattern_vtable = { + .destroy = SequencedPattern_destroy, + .bind = MultiLedPattern_bind, + .init = SequencedPattern_initImpl, + .play = SequencedPattern_play, +}; + +void SequencedPattern_init(SequencedPattern *self, const PatternArgs *args, const PatternVtable *vtable) { + CompoundPattern_init(&self->base, args, vtable); + Sequence_make(&self->m_sequence); + self->m_curSequence = 0; + Timer_make(&self->m_timer); // SequencedPattern is an abstract class it cannot be directly // instantiated so we do not need to assign a pattern id } -SequencedPattern::SequencedPattern(const PatternArgs &args, const Sequence &sequence) : - CompoundPattern(args), - m_sequence(sequence), - m_curSequence(0), - m_timer() +void SequencedPattern_initWithSequence(SequencedPattern *self, const PatternArgs *args, const Sequence *sequence, const PatternVtable *vtable) { - // SequencedPattern is an abstract class it cannot be directly - // instantiated so we do not need to assign a pattern id + SequencedPattern_init(self, args, vtable); + Sequence_copy(&self->m_sequence, sequence); } -SequencedPattern::~SequencedPattern() +void SequencedPattern_destroy(Pattern *pat) { + SequencedPattern *self = (SequencedPattern *)pat; + Sequence_destroy(&self->m_sequence); + CompoundPattern_destroy(pat); } // init the pattern to initial state -void SequencedPattern::init() +void SequencedPattern_initImpl(Pattern *pat) { - CompoundPattern::init(); + SequencedPattern *self = (SequencedPattern *)pat; + CompoundPattern_initImpl(pat); // start the sequence at 0 - m_curSequence = 0; - m_timer.reset(); + self->m_curSequence = 0; + Timer_reset(&self->m_timer); // create an alarm for each duration in the sequence - for (uint8_t i = 0; i < m_sequence.numSteps(); ++i) { - m_timer.addAlarm(m_sequence[i].m_duration); + for (uint8_t i = 0; i < Sequence_numSteps(&self->m_sequence); ++i) { + Timer_addAlarm(&self->m_timer, self->m_sequence.m_sequenceSteps[i].m_duration); } - m_timer.start(); + Timer_start0(&self->m_timer); // Play first sequence step in init, if there is one - if (m_sequence.numSteps() > 0) { - playSequenceStep(m_sequence[0]); + if (Sequence_numSteps(&self->m_sequence) > 0) { + playSequenceStep(self, &self->m_sequence.m_sequenceSteps[0]); } } -// pure virtual must the play function -void SequencedPattern::play() +// pure virtual must override the play function +void SequencedPattern_play(Pattern *pat) { - if (m_timer.alarm() != -1) { - m_curSequence = (m_curSequence + 1) % m_sequence.numSteps(); + SequencedPattern *self = (SequencedPattern *)pat; + if (Timer_alarm(&self->m_timer) != -1) { + self->m_curSequence = (self->m_curSequence + 1) % Sequence_numSteps(&self->m_sequence); } // only index the sequence if the current sequence index is valid - if (m_curSequence < m_sequence.numSteps()) { + if (self->m_curSequence < Sequence_numSteps(&self->m_sequence)) { // play the sequence step - playSequenceStep(m_sequence[m_curSequence]); + playSequenceStep(self, &self->m_sequence.m_sequenceSteps[self->m_curSequence]); } } -void SequencedPattern::playSequenceStep(const SequenceStep &step) +static void playSequenceStep(SequencedPattern *self, const SequenceStep *step) { for (LedPos pos = LED_FIRST; pos < LED_COUNT; ++pos) { // the current initialized pattern for this LED - SingleLedPattern *curPat = m_ledPatterns[pos]; + SingleLedPattern *curPat = self->base.m_ledPatterns[pos]; // the intended pattern ID for this LED on this step - PatternID stepPattern = step.m_patternMap[pos]; + PatternID stepPattern = PatternMap_at(&step->m_patternMap, pos); // the actual colorset being used might be the pattern's colorset - const Colorset *curSet = &m_colorset; - if (step.m_colorsetMap[pos].numColors() > 0) { + const Colorset *curSet = &self->base.base.base.m_colorset; + if (Colorset_numColors(&step->m_colorsetMap.m_colorsetMap[pos]) > 0) { // or it might be the steps' colorset if it's not empty - curSet = &step.m_colorsetMap[pos]; + curSet = ColorsetMap_at(&step->m_colorsetMap, pos); } // if there's no pattern, or pattern is wrong colorset or pattern ID - if (!curPat || !curPat->getColorset().equals(curSet) || curPat->getPatternID() != stepPattern) { - // delete any existing pattern and re-create it - delete m_ledPatterns[pos]; + if (!curPat || !Colorset_equals(&curPat->m_colorset, curSet) || Pattern_getPatternID(curPat) != stepPattern) { + // destroy any existing pattern and re-create it + if (curPat) { + Pattern_destroy(curPat); + PatternBuilder_freePattern(curPat); + self->base.m_ledPatterns[pos] = NULL; + } // create whichever pattern this step is (maybe PATTERN_NONE) - curPat = m_ledPatterns[pos] = PatternBuilder::makeSingle(stepPattern); + curPat = self->base.m_ledPatterns[pos] = PatternBuilder_makeSingle(stepPattern, NULL); // if a pattern was created then bind and init it if (curPat) { - curPat->bind(pos); - curPat->setColorset(*curSet); - curPat->init(); + Pattern_bind(curPat, pos); + Pattern_setColorset(curPat, curSet); + Pattern_init(curPat); } } // if there's no pattern set, or the intended pattern of this step is NONE if (!curPat || stepPattern == PATTERN_NONE) { // clear the LED and don't play the pattern - Leds::clearIndex(pos); + Leds_clearIndex(pos); continue; } // otherwise play the pattern on this index - curPat->play(); + Pattern_play(curPat); } } // apply a sequence to the pattern -void SequencedPattern::bindSequence(const Sequence &sequence) +void SequencedPattern_bindSequence(SequencedPattern *self, const Sequence *sequence) { - m_sequence = sequence; + Sequence_copy(&self->m_sequence, sequence); } diff --git a/VortexEngine/src/Patterns/Multi/Sequencer/SequencedPattern.h b/VortexEngine/src/Patterns/Multi/Sequencer/SequencedPattern.h index d1f6786ce3..d8e1cb21f8 100644 --- a/VortexEngine/src/Patterns/Multi/Sequencer/SequencedPattern.h +++ b/VortexEngine/src/Patterns/Multi/Sequencer/SequencedPattern.h @@ -1,6 +1,7 @@ #ifndef SEQUENCED_PATTERN_H #define SEQUENCED_PATTERN_H +#include #include "../CompoundPattern.h" #include "../../../Leds/LedTypes.h" @@ -8,31 +9,11 @@ #include "Sequence.h" -class ByteStream; - -class SequencedPattern : public CompoundPattern -{ -protected: - // SequencedPattern is an abstract class it cannot be directly - SequencedPattern(const PatternArgs &args); - SequencedPattern(const PatternArgs &args, const Sequence &sequence); - -public: - // initialize a sequence pattern with a list of pointers to sequencesteps and the - // number of entries in that list - virtual ~SequencedPattern(); - - // init the pattern to initial state - virtual void init() override; - - // pure virtual must override the play function - virtual void play() override; - - // change the sequence of the pattern - void bindSequence(const Sequence &sequence); - -protected: - void playSequenceStep(const SequenceStep &step); +// SequencedPattern is abstract, it drives a CompoundPattern through an +// ordered list of steps, each of which assigns a pattern to every led. +typedef struct SequencedPattern { + // must remain the first member + CompoundPattern base; // static data Sequence m_sequence; @@ -40,6 +21,19 @@ class SequencedPattern : public CompoundPattern // runtime data uint8_t m_curSequence; Timer m_timer; -}; +} SequencedPattern; + +// the vtable shared by every SequencedPattern +extern const PatternVtable SequencedPattern_vtable; + +void SequencedPattern_init(SequencedPattern *self, const PatternArgs *args, const PatternVtable *vtable); +void SequencedPattern_initWithSequence(SequencedPattern *self, const PatternArgs *args, const Sequence *sequence, const PatternVtable *vtable); + +void SequencedPattern_destroy(Pattern *self); +void SequencedPattern_initImpl(Pattern *self); +void SequencedPattern_play(Pattern *self); + +// change the sequence of the pattern +void SequencedPattern_bindSequence(SequencedPattern *self, const Sequence *sequence); #endif diff --git a/VortexEngine/src/Patterns/Multi/SnowballPattern.cpp b/VortexEngine/src/Patterns/Multi/SnowballPattern.cpp index 32d7121c17..048ef9b117 100644 --- a/VortexEngine/src/Patterns/Multi/SnowballPattern.cpp +++ b/VortexEngine/src/Patterns/Multi/SnowballPattern.cpp @@ -1,51 +1,71 @@ #include "SnowballPattern.h" +#include #include "../../Leds/Leds.h" #define WORM_SIZE 6 -SnowballPattern::SnowballPattern(const PatternArgs &args) : - BlinkStepPattern(args), - m_progress() +#include "../../Memory/Memory.h" +#include "../Pattern.h" + +const BlinkStepVtable SnowballPattern_bvtable = { + .base = { + .destroy = SnowballPattern_destroy, + .bind = MultiLedPattern_bind, + .init = SnowballPattern_initImpl, + .play = BlinkStepPattern_play + }, + .blinkOn = SnowballPattern_blinkOn, + .blinkOff = BlinkStepPattern_blinkOff, + .prestep = BlinkStepPattern_prestep, + .poststep = SnowballPattern_poststep, +}; + +void SnowballPattern_init(SnowballPattern *self, const PatternArgs *args) { - m_patternID = PATTERN_SNOWBALL; - setArgs(args); + BlinkStepPattern_init(&self->base, args, &SnowballPattern_bvtable); + self->m_progress = 0; + Pattern_setPatternID((&self->base.base.base), PATTERN_SNOWBALL); + Pattern_setArgs((&self->base.base.base), args); } -SnowballPattern::~SnowballPattern() +void SnowballPattern_destroy(Pattern *pat) { + SnowballPattern *self = (SnowballPattern *)pat; + (void)self; } -// init the pattern to initial state -void SnowballPattern::init() +void SnowballPattern_initImpl(Pattern *pat) { - BlinkStepPattern::init(); - + SnowballPattern *self = (SnowballPattern *)pat; + BlinkStepPattern_initImpl(pat); // start at index 1 - m_colorset.setCurIndex(1); + Colorset_setCurIndex(&(&self->base.base.base)->m_colorset, 1); } -void SnowballPattern::blinkOn() +void SnowballPattern_blinkOn(BlinkStepPattern *bsp) { - Leds::setAll(m_colorset.get(0)); + SnowballPattern *self = (SnowballPattern *)bsp; + Leds_setAll(Colorset_get(&(&self->base.base.base)->m_colorset, 0)); for (int body = 0; body < WORM_SIZE; ++body) { - if (body + m_progress < LED_COUNT) { - Leds::setIndex((LedPos)(body + m_progress), m_colorset.cur()); + if (body + self->m_progress < LED_COUNT) { + Leds_setIndex((LedPos)(body + self->m_progress), Colorset_cur(&(&self->base.base.base)->m_colorset)); } else { - RGBColor col = m_colorset.peekNext(); - if (m_colorset.curIndex() == m_colorset.numColors() - 1) { - col = m_colorset.peek(2); + RGBColor col = Colorset_peekNext(&(&self->base.base.base)->m_colorset); + if (Colorset_curIndex(&(&self->base.base.base)->m_colorset) == Colorset_numColors(&(&self->base.base.base)->m_colorset) - 1) { + col = Colorset_peek(&(&self->base.base.base)->m_colorset, 2); } - Leds::setIndex((LedPos)((body + m_progress) % LED_COUNT), col); + Leds_setIndex((LedPos)((body + self->m_progress) % LED_COUNT), col); } } } -void SnowballPattern::poststep() +void SnowballPattern_poststep(BlinkStepPattern *bsp) { - m_colorset.skip(); - if (m_colorset.curIndex() == 0) { - m_progress = (m_progress + 1) % LED_COUNT; - m_colorset.skip(); + SnowballPattern *self = (SnowballPattern *)bsp; + Colorset_skip(&(&self->base.base.base)->m_colorset, 1); + if (Colorset_curIndex(&(&self->base.base.base)->m_colorset) == 0) { + self->m_progress = (self->m_progress + 1) % LED_COUNT; + Colorset_skip(&(&self->base.base.base)->m_colorset, 1); } } diff --git a/VortexEngine/src/Patterns/Multi/SnowballPattern.h b/VortexEngine/src/Patterns/Multi/SnowballPattern.h index fa003f2809..c211424978 100644 --- a/VortexEngine/src/Patterns/Multi/SnowballPattern.h +++ b/VortexEngine/src/Patterns/Multi/SnowballPattern.h @@ -1,21 +1,25 @@ -#include "BlinkStepPattern.h" +#ifndef SNOWBALL_PATTERN_H +#define SNOWBALL_PATTERN_H +#include +#include "BlinkStepPattern.h" #include "../../Time/Timings.h" -class SnowballPattern : public BlinkStepPattern -{ -public: - SnowballPattern(const PatternArgs &args); - virtual ~SnowballPattern(); +typedef struct SnowballPattern { + // must remain the first member + BlinkStepPattern base; + uint8_t m_progress; +} SnowballPattern; - // init the pattern to initial state - virtual void init() override; +// the blink/step vtable for this pattern +extern const BlinkStepVtable SnowballPattern_bvtable; -protected: - virtual void blinkOn() override; - virtual void poststep() override; +// initialize a new SnowballPattern +void SnowballPattern_init(SnowballPattern *self, const PatternArgs *args); +void SnowballPattern_destroy(Pattern *self); -private: - // how much the warp has progressed - uint8_t m_progress; -}; +void SnowballPattern_initImpl(Pattern *self); +void SnowballPattern_blinkOn(BlinkStepPattern *self); +void SnowballPattern_poststep(BlinkStepPattern *self); + +#endif diff --git a/VortexEngine/src/Patterns/Multi/SparkleTracePattern.cpp b/VortexEngine/src/Patterns/Multi/SparkleTracePattern.cpp index 2221850d58..acff83dfee 100644 --- a/VortexEngine/src/Patterns/Multi/SparkleTracePattern.cpp +++ b/VortexEngine/src/Patterns/Multi/SparkleTracePattern.cpp @@ -1,33 +1,53 @@ #include "SparkleTracePattern.h" +#include #include "../../Time/TimeControl.h" #include "../../Leds/Leds.h" #include "../../Log/Log.h" +#include "../../Memory/Memory.h" +#include "../Pattern.h" -SparkleTracePattern::SparkleTracePattern(const PatternArgs &args) : - BlinkStepPattern(args), - m_randCtx() +const BlinkStepVtable SparkleTracePattern_bvtable = { + .base = { + .destroy = SparkleTracePattern_destroy, + .bind = MultiLedPattern_bind, + .init = BlinkStepPattern_initImpl, + .play = BlinkStepPattern_play + }, + .blinkOn = SparkleTracePattern_blinkOn, + .blinkOff = BlinkStepPattern_blinkOff, + .prestep = BlinkStepPattern_prestep, + .poststep = SparkleTracePattern_poststep, +}; + +void SparkleTracePattern_init(SparkleTracePattern *self, const PatternArgs *args) { - m_patternID = PATTERN_SPARKLETRACE; - setArgs(args); + BlinkStepPattern_init(&self->base, args, &SparkleTracePattern_bvtable); + Random_make(&self->m_randCtx); + Pattern_setPatternID((&self->base.base.base), PATTERN_SPARKLETRACE); + Pattern_setArgs((&self->base.base.base), args); } -SparkleTracePattern::~SparkleTracePattern() +void SparkleTracePattern_destroy(Pattern *pat) { + SparkleTracePattern *self = (SparkleTracePattern *)pat; + (void)self; } -void SparkleTracePattern::blinkOn() +void SparkleTracePattern_blinkOn(BlinkStepPattern *bsp) { - Leds::setAll(m_colorset.get(0)); + SparkleTracePattern *self = (SparkleTracePattern *)bsp; + Leds_setAll(Colorset_get(&(&self->base.base.base)->m_colorset, 0)); } -void SparkleTracePattern::poststep() +void SparkleTracePattern_poststep(BlinkStepPattern *bsp) { + SparkleTracePattern *self = (SparkleTracePattern *)bsp; for (uint8_t dot = 0; dot < 4; ++dot) { - Leds::setPair((Pair)m_randCtx.next8(PAIR_FIRST, PAIR_LAST), m_colorset.cur()); + Leds_setPair((Pair)Random_next8(&self->m_randCtx, PAIR_FIRST, PAIR_LAST), Colorset_cur(&(&self->base.base.base)->m_colorset)); } - m_colorset.skip(); - if (m_colorset.curIndex() == 0) { - m_colorset.skip(); + Colorset_skip(&(&self->base.base.base)->m_colorset, 1); + if (Colorset_curIndex(&(&self->base.base.base)->m_colorset) == 0) { + Colorset_skip(&(&self->base.base.base)->m_colorset, 1); } } diff --git a/VortexEngine/src/Patterns/Multi/SparkleTracePattern.h b/VortexEngine/src/Patterns/Multi/SparkleTracePattern.h index ef03a71827..c460f4ddb4 100644 --- a/VortexEngine/src/Patterns/Multi/SparkleTracePattern.h +++ b/VortexEngine/src/Patterns/Multi/SparkleTracePattern.h @@ -1,20 +1,24 @@ #ifndef SPARKLETRACE_PATTERN_H #define SPARKLETRACE_PATTERN_H +#include #include "BlinkStepPattern.h" - #include "../../Random/Random.h" -class SparkleTracePattern : public BlinkStepPattern -{ -public: - SparkleTracePattern(const PatternArgs &args); - virtual ~SparkleTracePattern(); +typedef struct SparkleTracePattern { + // must remain the first member + BlinkStepPattern base; + Random m_randCtx; +} SparkleTracePattern; -protected: - virtual void blinkOn() override; - virtual void poststep() override; +// the blink/step vtable for this pattern +extern const BlinkStepVtable SparkleTracePattern_bvtable; - Random m_randCtx; -}; -#endif \ No newline at end of file +// initialize a new SparkleTracePattern +void SparkleTracePattern_init(SparkleTracePattern *self, const PatternArgs *args); +void SparkleTracePattern_destroy(Pattern *self); + +void SparkleTracePattern_blinkOn(BlinkStepPattern *self); +void SparkleTracePattern_poststep(BlinkStepPattern *self); + +#endif diff --git a/VortexEngine/src/Patterns/Multi/TheaterChasePattern.cpp b/VortexEngine/src/Patterns/Multi/TheaterChasePattern.cpp index fbd527c7db..458ef685b9 100644 --- a/VortexEngine/src/Patterns/Multi/TheaterChasePattern.cpp +++ b/VortexEngine/src/Patterns/Multi/TheaterChasePattern.cpp @@ -1,44 +1,65 @@ #include "TheaterChasePattern.h" +#include #include "../../Leds/Leds.h" #define THEATER_CHASE_STEPS 10 -TheaterChasePattern::TheaterChasePattern(const PatternArgs &args) : - BlinkStepPattern(args), - m_ledPositions(0), - m_stepCounter(0) +#include "../../Memory/Memory.h" +#include "../Pattern.h" + +const BlinkStepVtable TheaterChasePattern_bvtable = { + .base = { + .destroy = TheaterChasePattern_destroy, + .bind = MultiLedPattern_bind, + .init = TheaterChasePattern_initImpl, + .play = BlinkStepPattern_play + }, + .blinkOn = TheaterChasePattern_blinkOn, + .blinkOff = BlinkStepPattern_blinkOff, + .prestep = BlinkStepPattern_prestep, + .poststep = TheaterChasePattern_poststep, +}; + +void TheaterChasePattern_init(TheaterChasePattern *self, const PatternArgs *args) { - m_patternID = PATTERN_THEATER_CHASE; - setArgs(args); + BlinkStepPattern_init(&self->base, args, &TheaterChasePattern_bvtable); + self->m_ledPositions = 0; + Pattern_setPatternID((&self->base.base.base), PATTERN_THEATER_CHASE); + Pattern_setArgs((&self->base.base.base), args); } -TheaterChasePattern::~TheaterChasePattern() +void TheaterChasePattern_destroy(Pattern *pat) { + TheaterChasePattern *self = (TheaterChasePattern *)pat; + (void)self; } -void TheaterChasePattern::init() +void TheaterChasePattern_initImpl(Pattern *pat) { - BlinkStepPattern::init(); + TheaterChasePattern *self = (TheaterChasePattern *)pat; + BlinkStepPattern_initImpl(pat); // starts on odd evens - m_ledPositions = MAP_PAIR_ODD_EVENS; - m_stepCounter = 0; + self->m_ledPositions = MAP_PAIR_ODD_EVENS; + self->m_stepCounter = 0; } -void TheaterChasePattern::blinkOn() +void TheaterChasePattern_blinkOn(BlinkStepPattern *bsp) { - Leds::setMap(m_ledPositions, m_colorset.getNext()); + TheaterChasePattern *self = (TheaterChasePattern *)bsp; + Leds_setMap(self->m_ledPositions, Colorset_getNext(&(&self->base.base.base)->m_colorset)); } -void TheaterChasePattern::poststep() +void TheaterChasePattern_poststep(BlinkStepPattern *bsp) { + TheaterChasePattern *self = (TheaterChasePattern *)bsp; // the first 5 steps are odd evens/odds alternating each step - if (m_stepCounter < 5) { - m_ledPositions = (m_stepCounter % 2) ? MAP_PAIR_ODD_ODDS : MAP_PAIR_ODD_EVENS; + if (self->m_stepCounter < 5) { + self->m_ledPositions = (self->m_stepCounter % 2) ? MAP_PAIR_ODD_ODDS : MAP_PAIR_ODD_EVENS; } else { // the end 5 steps are even evens/odds alternating each step - m_ledPositions = (m_stepCounter % 2) ? MAP_PAIR_EVEN_ODDS : MAP_PAIR_EVEN_EVENS; + self->m_ledPositions = (self->m_stepCounter % 2) ? MAP_PAIR_EVEN_ODDS : MAP_PAIR_EVEN_EVENS; } // increment step counter - m_stepCounter = (m_stepCounter + 1) % THEATER_CHASE_STEPS; + self->m_stepCounter = (self->m_stepCounter + 1) % THEATER_CHASE_STEPS; } diff --git a/VortexEngine/src/Patterns/Multi/TheaterChasePattern.h b/VortexEngine/src/Patterns/Multi/TheaterChasePattern.h index 983550d1f5..9a933686b0 100644 --- a/VortexEngine/src/Patterns/Multi/TheaterChasePattern.h +++ b/VortexEngine/src/Patterns/Multi/TheaterChasePattern.h @@ -1,24 +1,26 @@ #ifndef THEATER_CHASE_PATTERN_H #define THEATER_CHASE_PATTERN_H +#include #include "BlinkStepPattern.h" - #include "../../Time/Timings.h" -class TheaterChasePattern : public BlinkStepPattern -{ -public: - TheaterChasePattern(const PatternArgs &args); - virtual ~TheaterChasePattern(); +typedef struct TheaterChasePattern { + // must remain the first member + BlinkStepPattern base; + LedMap m_ledPositions; + uint8_t m_stepCounter; +} TheaterChasePattern; - virtual void init() override; +// the blink/step vtable for this pattern +extern const BlinkStepVtable TheaterChasePattern_bvtable; -protected: - virtual void blinkOn() override; - virtual void poststep() override; +// initialize a new TheaterChasePattern +void TheaterChasePattern_init(TheaterChasePattern *self, const PatternArgs *args); +void TheaterChasePattern_destroy(Pattern *self); - LedMap m_ledPositions; - uint8_t m_stepCounter; -}; +void TheaterChasePattern_initImpl(Pattern *self); +void TheaterChasePattern_blinkOn(BlinkStepPattern *self); +void TheaterChasePattern_poststep(BlinkStepPattern *self); #endif diff --git a/VortexEngine/src/Patterns/Multi/VortexPattern.cpp b/VortexEngine/src/Patterns/Multi/VortexPattern.cpp index b879687b11..c24c6fc383 100644 --- a/VortexEngine/src/Patterns/Multi/VortexPattern.cpp +++ b/VortexEngine/src/Patterns/Multi/VortexPattern.cpp @@ -1,48 +1,66 @@ #include "VortexPattern.h" +#include #include "../../Serial/ByteStream.h" #include "../../Time/TimeControl.h" #include "../../Leds/Leds.h" #include "../../Log/Log.h" -// add 1 to prevent the middle point from being led 0 #define MIDDLE_POINT ((LED_COUNT + 1) / 2) -VortexPattern::VortexPattern(const PatternArgs& args) : - BlinkStepPattern(args), - m_progress(0) +#include "../../Memory/Memory.h" +#include "../Pattern.h" + +const BlinkStepVtable VortexPattern_bvtable = { + .base = { + .destroy = VortexPattern_destroy, + .bind = MultiLedPattern_bind, + .init = VortexPattern_initImpl, + .play = BlinkStepPattern_play + }, + .blinkOn = VortexPattern_blinkOn, + .blinkOff = BlinkStepPattern_blinkOff, + .prestep = BlinkStepPattern_prestep, + .poststep = VortexPattern_poststep, +}; + +void VortexPattern_init(VortexPattern *self, const PatternArgs *args) { - m_patternID = PATTERN_VORTEXWIPE; - setArgs(args); + BlinkStepPattern_init(&self->base, args, &VortexPattern_bvtable); + } -VortexPattern::~VortexPattern() +void VortexPattern_destroy(Pattern *pat) { + VortexPattern *self = (VortexPattern *)pat; + (void)self; } -// init the pattern to initial state -void VortexPattern::init() +void VortexPattern_initImpl(Pattern *pat) { - BlinkStepPattern::init(); + VortexPattern *self = (VortexPattern *)pat; + BlinkStepPattern_initImpl(pat); // reset progress - m_progress = 0; + self->m_progress = 0; // start colorset at index 0 so cur() works - m_colorset.setCurIndex(0); + Colorset_setCurIndex(&(&self->base.base.base)->m_colorset, 0); } -void VortexPattern::blinkOn() +void VortexPattern_blinkOn(BlinkStepPattern *bsp) { + VortexPattern *self = (VortexPattern *)bsp; // Sets an LED at opposite ends of the strip and progresses towards the center - Leds::setIndex((LedPos)m_progress, m_colorset.peekNext()); - Leds::setIndex((LedPos)(LED_LAST - m_progress), m_colorset.peekNext()); + Leds_setIndex((LedPos)self->m_progress, Colorset_peekNext(&(&self->base.base.base)->m_colorset)); + Leds_setIndex((LedPos)(LED_LAST - self->m_progress), Colorset_peekNext(&(&self->base.base.base)->m_colorset)); } -void VortexPattern::poststep() +void VortexPattern_poststep(BlinkStepPattern *bsp) { + VortexPattern *self = (VortexPattern *)bsp; // step till the middle point - m_progress = (m_progress + 1) % MIDDLE_POINT; + self->m_progress = (self->m_progress + 1) % MIDDLE_POINT; // each cycle progress to the next color - if (m_progress == 0) { - m_colorset.getNext(); + if (self->m_progress == 0) { + Colorset_getNext(&(&self->base.base.base)->m_colorset); } } diff --git a/VortexEngine/src/Patterns/Multi/VortexPattern.h b/VortexEngine/src/Patterns/Multi/VortexPattern.h index 38b261c8ab..3b9cc67ee5 100644 --- a/VortexEngine/src/Patterns/Multi/VortexPattern.h +++ b/VortexEngine/src/Patterns/Multi/VortexPattern.h @@ -1,26 +1,25 @@ #ifndef VORTEX_PATTERN_H #define VORTEX_PATTERN_H +#include #include "BlinkStepPattern.h" - #include "../../Time/Timings.h" -class VortexPattern : public BlinkStepPattern -{ -public: - VortexPattern(const PatternArgs& args); - virtual ~VortexPattern(); +typedef struct VortexPattern { + // must remain the first member + BlinkStepPattern base; + uint8_t m_progress; +} VortexPattern; - // init the pattern to initial state - virtual void init() override; +// the blink/step vtable for this pattern +extern const BlinkStepVtable VortexPattern_bvtable; -protected: - virtual void blinkOn() override; - virtual void poststep() override; +// initialize a new VortexPattern +void VortexPattern_init(VortexPattern *self, const PatternArgs *args); +void VortexPattern_destroy(Pattern *self); -private: - // how much the fill has progressed - uint8_t m_progress; -}; +void VortexPattern_initImpl(Pattern *self); +void VortexPattern_blinkOn(BlinkStepPattern *self); +void VortexPattern_poststep(BlinkStepPattern *self); #endif diff --git a/VortexEngine/src/Patterns/Multi/VortexWipePattern.cpp b/VortexEngine/src/Patterns/Multi/VortexWipePattern.cpp index 1a2b19b185..58b8271ae5 100644 --- a/VortexEngine/src/Patterns/Multi/VortexWipePattern.cpp +++ b/VortexEngine/src/Patterns/Multi/VortexWipePattern.cpp @@ -1,11 +1,34 @@ #include "VortexWipePattern.h" +#include #include "../../Serial/ByteStream.h" #include "../../Time/TimeControl.h" #include "../../Leds/Leds.h" #include "../../Log/Log.h" +#include "../../Memory/Memory.h" +#include "../Pattern.h" -const LedPos VortexWipePattern::ledStepPositions[] = { +const BlinkStepVtable VortexWipePattern_bvtable = { + .base = { + .destroy = VortexWipePattern_destroy, + .bind = MultiLedPattern_bind, + .init = VortexWipePattern_initImpl, + .play = BlinkStepPattern_play + }, + .blinkOn = VortexWipePattern_blinkOn, + .blinkOff = BlinkStepPattern_blinkOff, + .prestep = BlinkStepPattern_prestep, + .poststep = VortexWipePattern_poststep, +}; + +void VortexWipePattern_init(VortexWipePattern *self, const PatternArgs *args) +{ + BlinkStepPattern_init(&self->base, args, &VortexWipePattern_bvtable); + Pattern_setPatternID((&self->base.base.base), PATTERN_VORTEXWIPE); + Pattern_setArgs((&self->base.base.base), args); +} + +const LedPos VortexWipePattern_ledStepPositions[] = { LED_9, LED_7, LED_5, @@ -19,42 +42,38 @@ const LedPos VortexWipePattern::ledStepPositions[] = { LED_8 }; -VortexWipePattern::VortexWipePattern(const PatternArgs &args) : - BlinkStepPattern(args), - m_progress(0) -{ - m_patternID = PATTERN_VORTEXWIPE; - setArgs(args); -} - -VortexWipePattern::~VortexWipePattern() +void VortexWipePattern_destroy(Pattern *pat) { + VortexWipePattern *self = (VortexWipePattern *)pat; + (void)self; } -// init the pattern to initial state -void VortexWipePattern::init() +void VortexWipePattern_initImpl(Pattern *pat) { - BlinkStepPattern::init(); + VortexWipePattern *self = (VortexWipePattern *)pat; + BlinkStepPattern_initImpl(pat); // reset progress - m_progress = 0; + self->m_progress = 0; // start colorset at index 0 so cur() works - m_colorset.setCurIndex(0); + Colorset_setCurIndex(&(&self->base.base.base)->m_colorset, 0); } -void VortexWipePattern::blinkOn() +void VortexWipePattern_blinkOn(BlinkStepPattern *bsp) { - for (int index = 0; index < m_progress; ++index) { - Leds::setIndex(ledStepPositions[index], m_colorset.peekNext()); + VortexWipePattern *self = (VortexWipePattern *)bsp; + for (int index = 0; index < self->m_progress; ++index) { + Leds_setIndex(VortexWipePattern_ledStepPositions[index], Colorset_peekNext(&(&self->base.base.base)->m_colorset)); } - for (int index = m_progress; index < LED_COUNT; ++index) { - Leds::setIndex(ledStepPositions[index], m_colorset.cur()); + for (int index = self->m_progress; index < LED_COUNT; ++index) { + Leds_setIndex(VortexWipePattern_ledStepPositions[index], Colorset_cur(&(&self->base.base.base)->m_colorset)); } } -void VortexWipePattern::poststep() +void VortexWipePattern_poststep(BlinkStepPattern *bsp) { - m_progress = (m_progress + 1) % LED_COUNT; - if (m_progress == 0) { - m_colorset.getNext(); + VortexWipePattern *self = (VortexWipePattern *)bsp; + self->m_progress = (self->m_progress + 1) % LED_COUNT; + if (self->m_progress == 0) { + Colorset_getNext(&(&self->base.base.base)->m_colorset); } } diff --git a/VortexEngine/src/Patterns/Multi/VortexWipePattern.h b/VortexEngine/src/Patterns/Multi/VortexWipePattern.h index 17030f3f6d..e02eff4d07 100644 --- a/VortexEngine/src/Patterns/Multi/VortexWipePattern.h +++ b/VortexEngine/src/Patterns/Multi/VortexWipePattern.h @@ -1,29 +1,28 @@ #ifndef VORTEXWIPE_PATTERN_H #define VORTEXWIPE_PATTERN_H +#include #include "BlinkStepPattern.h" - #include "../../Time/Timings.h" -class VortexWipePattern : public BlinkStepPattern -{ -public: - VortexWipePattern(const PatternArgs &args); - virtual ~VortexWipePattern(); +typedef struct VortexWipePattern { + // must remain the first member + BlinkStepPattern base; + static const LedPos ledStepPositions; + uint8_t m_progress; +} VortexWipePattern; - // init the pattern to initial state - virtual void init() override; +extern const LedPos VortexWipePattern_ledStepPositions[]; -protected: - virtual void blinkOn() override; - virtual void poststep() override; +// the blink/step vtable for this pattern +extern const BlinkStepVtable VortexWipePattern_bvtable; -private: - // path for leds to take, index this with m_step up to LED_COUNT steps - static const LedPos ledStepPositions[]; +// initialize a new VortexWipePattern +void VortexWipePattern_init(VortexWipePattern *self, const PatternArgs *args); +void VortexWipePattern_destroy(Pattern *self); - // how much the fill has progressed - uint8_t m_progress; -}; +void VortexWipePattern_initImpl(Pattern *self); +void VortexWipePattern_blinkOn(BlinkStepPattern *self); +void VortexWipePattern_poststep(BlinkStepPattern *self); #endif diff --git a/VortexEngine/src/Patterns/Multi/WarpPattern.cpp b/VortexEngine/src/Patterns/Multi/WarpPattern.cpp index 24770e4328..8f51eedeac 100644 --- a/VortexEngine/src/Patterns/Multi/WarpPattern.cpp +++ b/VortexEngine/src/Patterns/Multi/WarpPattern.cpp @@ -1,42 +1,61 @@ #include "WarpPattern.h" +#include #include "../../Serial/ByteStream.h" #include "../../Time/TimeControl.h" #include "../../Leds/Leds.h" #include "../../Log/Log.h" +#include "../../Memory/Memory.h" +#include "../Pattern.h" -WarpPattern::WarpPattern(const PatternArgs &args) : - BlinkStepPattern(args), - m_progress(0) +const BlinkStepVtable WarpPattern_bvtable = { + .base = { + .destroy = WarpPattern_destroy, + .bind = MultiLedPattern_bind, + .init = WarpPattern_initImpl, + .play = BlinkStepPattern_play + }, + .blinkOn = WarpPattern_blinkOn, + .blinkOff = BlinkStepPattern_blinkOff, + .prestep = BlinkStepPattern_prestep, + .poststep = WarpPattern_poststep, +}; + +void WarpPattern_init(WarpPattern *self, const PatternArgs *args) { - m_patternID = PATTERN_WARP; - setArgs(args); + BlinkStepPattern_init(&self->base, args, &WarpPattern_bvtable); + Pattern_setPatternID((&self->base.base.base), PATTERN_WARP); + Pattern_setArgs((&self->base.base.base), args); } -WarpPattern::~WarpPattern() +void WarpPattern_destroy(Pattern *pat) { + WarpPattern *self = (WarpPattern *)pat; + (void)self; } -// init the pattern to initial state -void WarpPattern::init() +void WarpPattern_initImpl(Pattern *pat) { - BlinkStepPattern::init(); + WarpPattern *self = (WarpPattern *)pat; + BlinkStepPattern_initImpl(pat); // reset progress - m_progress = 0; + self->m_progress = 0; // start colorset at index 0 so cur() works - m_colorset.setCurIndex(0); + Colorset_setCurIndex(&(&self->base.base.base)->m_colorset, 0); } -void WarpPattern::blinkOn() +void WarpPattern_blinkOn(BlinkStepPattern *bsp) { - Leds::setAll(m_colorset.cur()); - Leds::setPair((Pair)m_progress, m_colorset.peekNext()); + WarpPattern *self = (WarpPattern *)bsp; + Leds_setAll(Colorset_cur(&(&self->base.base.base)->m_colorset)); + Leds_setPair((Pair)self->m_progress, Colorset_peekNext(&(&self->base.base.base)->m_colorset)); } -void WarpPattern::poststep() +void WarpPattern_poststep(BlinkStepPattern *bsp) { - m_progress = (m_progress + 1) % PAIR_COUNT; - if (m_progress == 0) { - m_colorset.getNext(); + WarpPattern *self = (WarpPattern *)bsp; + self->m_progress = (self->m_progress + 1) % PAIR_COUNT; + if (self->m_progress == 0) { + Colorset_getNext(&(&self->base.base.base)->m_colorset); } } diff --git a/VortexEngine/src/Patterns/Multi/WarpPattern.h b/VortexEngine/src/Patterns/Multi/WarpPattern.h index 236de16347..88f04413ab 100644 --- a/VortexEngine/src/Patterns/Multi/WarpPattern.h +++ b/VortexEngine/src/Patterns/Multi/WarpPattern.h @@ -1,22 +1,25 @@ -#include "BlinkStepPattern.h" +#ifndef WARP_PATTERN_H +#define WARP_PATTERN_H +#include +#include "BlinkStepPattern.h" #include "../../Time/Timings.h" -class WarpPattern : public BlinkStepPattern -{ -public: - WarpPattern(const PatternArgs &args); - virtual ~WarpPattern(); +typedef struct WarpPattern { + // must remain the first member + BlinkStepPattern base; + uint8_t m_progress; +} WarpPattern; - // init the pattern to initial state - virtual void init() override; +// the blink/step vtable for this pattern +extern const BlinkStepVtable WarpPattern_bvtable; -protected: - virtual void blinkOn() override; - virtual void poststep() override; +// initialize a new WarpPattern +void WarpPattern_init(WarpPattern *self, const PatternArgs *args); +void WarpPattern_destroy(Pattern *self); -private: - // how much the warp has progressed - uint8_t m_progress; -}; +void WarpPattern_initImpl(Pattern *self); +void WarpPattern_blinkOn(BlinkStepPattern *self); +void WarpPattern_poststep(BlinkStepPattern *self); +#endif diff --git a/VortexEngine/src/Patterns/Multi/WarpWormPattern.cpp b/VortexEngine/src/Patterns/Multi/WarpWormPattern.cpp index b6311ad339..df90acc121 100644 --- a/VortexEngine/src/Patterns/Multi/WarpWormPattern.cpp +++ b/VortexEngine/src/Patterns/Multi/WarpWormPattern.cpp @@ -1,56 +1,75 @@ #include "WarpWormPattern.h" +#include #include "../../Serial/ByteStream.h" #include "../../Time/TimeControl.h" #include "../../Leds/Leds.h" #include "../../Log/Log.h" +#include "../../Memory/Memory.h" +#include "../Pattern.h" -WarpWormPattern::WarpWormPattern(const PatternArgs &args) : - BlinkStepPattern(args), - m_progress(0) +const BlinkStepVtable WarpWormPattern_bvtable = { + .base = { + .destroy = WarpWormPattern_destroy, + .bind = MultiLedPattern_bind, + .init = WarpWormPattern_initImpl, + .play = BlinkStepPattern_play + }, + .blinkOn = WarpWormPattern_blinkOn, + .blinkOff = BlinkStepPattern_blinkOff, + .prestep = BlinkStepPattern_prestep, + .poststep = WarpWormPattern_poststep, +}; + +void WarpWormPattern_init(WarpWormPattern *self, const PatternArgs *args) { - m_patternID = PATTERN_WARPWORM; - setArgs(args); + BlinkStepPattern_init(&self->base, args, &WarpWormPattern_bvtable); + Pattern_setPatternID((&self->base.base.base), PATTERN_WARPWORM); + Pattern_setArgs((&self->base.base.base), args); } -WarpWormPattern::~WarpWormPattern() +void WarpWormPattern_destroy(Pattern *pat) { + WarpWormPattern *self = (WarpWormPattern *)pat; + (void)self; } -// init the pattern to initial state -void WarpWormPattern::init() +void WarpWormPattern_initImpl(Pattern *pat) { - BlinkStepPattern::init(); + WarpWormPattern *self = (WarpWormPattern *)pat; + BlinkStepPattern_initImpl(pat); // reset progress - m_progress = 0; + self->m_progress = 0; // start colorset at index 0 so cur() works - m_colorset.setCurIndex(1); + Colorset_setCurIndex(&(&self->base.base.base)->m_colorset, 1); } -void WarpWormPattern::blinkOn() +void WarpWormPattern_blinkOn(BlinkStepPattern *bsp) { + WarpWormPattern *self = (WarpWormPattern *)bsp; int wormSize = 6; - Leds::setAll(m_colorset.get(0)); + Leds_setAll(Colorset_get(&(&self->base.base.base)->m_colorset, 0)); for (int body = 0; body < wormSize; ++body) { - if (body + m_progress < LED_COUNT) { - Leds::setIndex((LedPos)(body + m_progress), m_colorset.cur()); + if (body + self->m_progress < LED_COUNT) { + Leds_setIndex((LedPos)(body + self->m_progress), Colorset_cur(&(&self->base.base.base)->m_colorset)); } else { - RGBColor col = m_colorset.peekNext(); - if (m_colorset.curIndex() == m_colorset.numColors() - 1) { - col = m_colorset.peek(2); + RGBColor col = Colorset_peekNext(&(&self->base.base.base)->m_colorset); + if (Colorset_curIndex(&(&self->base.base.base)->m_colorset) == Colorset_numColors(&(&self->base.base.base)->m_colorset) - 1) { + col = Colorset_peek(&(&self->base.base.base)->m_colorset, 2); } - Leds::setIndex((LedPos)((body + m_progress) % LED_COUNT), col); + Leds_setIndex((LedPos)((body + self->m_progress) % LED_COUNT), col); } } } -void WarpWormPattern::poststep() +void WarpWormPattern_poststep(BlinkStepPattern *bsp) { - m_progress = (m_progress + 1) % LED_COUNT; - if (m_progress == 0) { - m_colorset.getNext(); - if (m_colorset.curIndex() == 0) { - m_colorset.getNext(); + WarpWormPattern *self = (WarpWormPattern *)bsp; + self->m_progress = (self->m_progress + 1) % LED_COUNT; + if (self->m_progress == 0) { + Colorset_getNext(&(&self->base.base.base)->m_colorset); + if (Colorset_curIndex(&(&self->base.base.base)->m_colorset) == 0) { + Colorset_getNext(&(&self->base.base.base)->m_colorset); } } } diff --git a/VortexEngine/src/Patterns/Multi/WarpWormPattern.h b/VortexEngine/src/Patterns/Multi/WarpWormPattern.h index a8a65eb532..498b85ba10 100644 --- a/VortexEngine/src/Patterns/Multi/WarpWormPattern.h +++ b/VortexEngine/src/Patterns/Multi/WarpWormPattern.h @@ -1,22 +1,25 @@ -#include "BlinkStepPattern.h" +#ifndef WARP_WORM_PATTERN_H +#define WARP_WORM_PATTERN_H +#include +#include "BlinkStepPattern.h" #include "../../Time/Timings.h" -class WarpWormPattern : public BlinkStepPattern -{ -public: - WarpWormPattern(const PatternArgs &args); - virtual ~WarpWormPattern(); +typedef struct WarpWormPattern { + // must remain the first member + BlinkStepPattern base; + uint8_t m_progress; +} WarpWormPattern; - // init the pattern to initial state - virtual void init() override; +// the blink/step vtable for this pattern +extern const BlinkStepVtable WarpWormPattern_bvtable; -protected: - virtual void blinkOn() override; - virtual void poststep() override; +// initialize a new WarpWormPattern +void WarpWormPattern_init(WarpWormPattern *self, const PatternArgs *args); +void WarpWormPattern_destroy(Pattern *self); -private: - // how much the warp has progressed - uint8_t m_progress; -}; +void WarpWormPattern_initImpl(Pattern *self); +void WarpWormPattern_blinkOn(BlinkStepPattern *self); +void WarpWormPattern_poststep(BlinkStepPattern *self); +#endif diff --git a/VortexEngine/src/Patterns/Multi/ZigzagPattern.cpp b/VortexEngine/src/Patterns/Multi/ZigzagPattern.cpp index 02062917eb..753e88faf1 100644 --- a/VortexEngine/src/Patterns/Multi/ZigzagPattern.cpp +++ b/VortexEngine/src/Patterns/Multi/ZigzagPattern.cpp @@ -1,5 +1,7 @@ #include "ZigzagPattern.h" +#include + #include "../../Time/TimeControl.h" #include "../../Leds/Leds.h" #include "../../Log/Log.h" @@ -7,7 +9,7 @@ // Mapping of LED positions to steps. // The lights runs across evens, then back across odds. // Index this array with m_step in order to get correct LedPos -const LedPos ZigzagPattern::ledStepPositions[] = { +const LedPos ZigzagPattern_ledStepPositions[] = { LED_1, LED_3, LED_5, @@ -22,116 +24,129 @@ const LedPos ZigzagPattern::ledStepPositions[] = { }; // There just happens to be LED_COUNT steps in the pattern -#define NUM_ZIGZAG_STEPS (sizeof(ledStepPositions) / sizeof(ledStepPositions[0])) +#define NUM_ZIGZAG_STEPS (sizeof(ZigzagPattern_ledStepPositions) / sizeof(ZigzagPattern_ledStepPositions[0])) #define HALF_ZIGZAG_STEPS (NUM_ZIGZAG_STEPS / 2) -ZigzagPattern::ZigzagPattern(const PatternArgs &args) : - MultiLedPattern(args), - m_onDuration(0), - m_offDuration(0), - m_stepDuration(0), - m_snakeSize(0), - m_fadeAmount(0), - m_stepTimer(), - m_snake1(), - m_snake2() +const PatternVtable ZigzagPattern_vtable = { + .destroy = ZigzagPattern_destroy, + .bind = MultiLedPattern_bind, + .init = ZigzagPattern_initImpl, + .play = ZigzagPattern_play, +}; + +void ZigzagPattern_init(ZigzagPattern *self, const PatternArgs *args) { - m_patternID = PATTERN_ZIGZAG; - REGISTER_ARG(m_onDuration); - REGISTER_ARG(m_offDuration); - REGISTER_ARG(m_stepDuration); - REGISTER_ARG(m_snakeSize); - REGISTER_ARG(m_fadeAmount); - setArgs(args); + MultiLedPattern_init(&self->base, args, &ZigzagPattern_vtable); + self->m_onDuration = 0; + self->m_offDuration = 0; + self->m_stepDuration = 0; + self->m_snakeSize = 0; + self->m_fadeAmount = 0; + Timer_make(&self->m_stepTimer); + ZigzagSnake_make(&self->m_snake1); + ZigzagSnake_make(&self->m_snake2); + Pattern_setPatternID((&self->base.base), PATTERN_ZIGZAG); + REGISTER_ARG((&self->base.base), ZigzagPattern, m_onDuration); + REGISTER_ARG((&self->base.base), ZigzagPattern, m_offDuration); + REGISTER_ARG((&self->base.base), ZigzagPattern, m_stepDuration); + REGISTER_ARG((&self->base.base), ZigzagPattern, m_snakeSize); + REGISTER_ARG((&self->base.base), ZigzagPattern, m_fadeAmount); + Pattern_setArgs((&self->base.base), args); } -ZigzagPattern::~ZigzagPattern() +void ZigzagPattern_destroy(Pattern *pat) { + ZigzagPattern *self = (ZigzagPattern *)pat; + (void)self; } // init the pattern to initial state -void ZigzagPattern::init() +void ZigzagPattern_initImpl(Pattern *pat) { - MultiLedPattern::init(); + ZigzagPattern *self = (ZigzagPattern *)pat; + MultiLedPattern_doInit(pat); // reset and add alarm - m_stepTimer.reset(); - m_stepTimer.addAlarm(m_stepDuration); - m_stepTimer.start(); + Timer_reset(&self->m_stepTimer); + Timer_addAlarm(&self->m_stepTimer, self->m_stepDuration); + Timer_start0(&self->m_stepTimer); // initialize the snakes with dops timing - m_snake1.init(m_onDuration, m_offDuration, m_colorset, 0, 0, m_snakeSize, m_fadeAmount, 3); - m_snake2.init(m_onDuration, m_offDuration, m_colorset, 1, HALF_ZIGZAG_STEPS, m_snakeSize, m_fadeAmount, 8); + ZigzagSnake_init(&self->m_snake1, self->m_onDuration, self->m_offDuration, &self->base.base.m_colorset, + 0, 0, self->m_snakeSize, self->m_fadeAmount, 3); + ZigzagSnake_init(&self->m_snake2, self->m_onDuration, self->m_offDuration, &self->base.base.m_colorset, + 1, HALF_ZIGZAG_STEPS, self->m_snakeSize, self->m_fadeAmount, 8); } // pure virtual must override the play function -void ZigzagPattern::play() +void ZigzagPattern_play(Pattern *pat) { + ZigzagPattern *self = (ZigzagPattern *)pat; // when the step timer triggers - if (m_stepTimer.alarm() == 0) { - m_snake1.step(); - m_snake2.step(); + if (Timer_alarm(&self->m_stepTimer) == 0) { + ZigzagSnake_step(&self->m_snake1); + ZigzagSnake_step(&self->m_snake2); } - m_snake1.draw(); - m_snake2.draw(); + ZigzagSnake_draw(&self->m_snake1); + ZigzagSnake_draw(&self->m_snake2); } // =================== // Snake code -ZigzagPattern::Snake::Snake() : - m_blinkTimer(), - m_colorset(), - m_step(0), - m_snakeSize(1), - m_fadeAmount(55), - m_changeBoundary(3) +void ZigzagSnake_make(ZigzagSnake *self) { + memset(self, 0, sizeof(*self)); + Timer_make(&self->m_blinkTimer); + self->m_step = 0; + self->m_snakeSize = 1; + self->m_fadeAmount = 55; + self->m_changeBoundary = 3; } -void ZigzagPattern::Snake::init(uint8_t onDuration, uint8_t offDuration, const Colorset &colorset, uint32_t colorOffset, - uint8_t step, uint8_t snakeSize, uint8_t fadeAmount, uint8_t changeBoundary) +void ZigzagSnake_init(ZigzagSnake *self, uint8_t onDuration, uint8_t offDuration, const Colorset *colorset, + uint32_t colorOffset, uint8_t step, uint8_t snakeSize, uint8_t fadeAmount, uint8_t changeBoundary) { // reset the blink timer entirely - m_blinkTimer.reset(); + Timer_reset(&self->m_blinkTimer); // dops timing - m_blinkTimer.addAlarm(onDuration); - m_blinkTimer.addAlarm(offDuration); + Timer_addAlarm(&self->m_blinkTimer, onDuration); + Timer_addAlarm(&self->m_blinkTimer, offDuration); // start the blink timer from the next frame - m_blinkTimer.start(); + Timer_start0(&self->m_blinkTimer); // start on the first color so that cur() works immediately - m_colorset = colorset; - m_colorset.setCurIndex(colorOffset); + self->m_colorset = *colorset; + Colorset_setCurIndex(&self->m_colorset, colorOffset); // set the starting step - m_step = step; + self->m_step = step; // set the snake size - m_snakeSize = snakeSize; + self->m_snakeSize = snakeSize; // set fade amount and change boundary - m_fadeAmount = fadeAmount; - m_changeBoundary = changeBoundary; + self->m_fadeAmount = fadeAmount; + self->m_changeBoundary = changeBoundary; } -void ZigzagPattern::Snake::step() +void ZigzagSnake_step(ZigzagSnake *self) { // increment to the next step - m_step = (m_step + 1) % NUM_ZIGZAG_STEPS; + self->m_step = (self->m_step + 1) % NUM_ZIGZAG_STEPS; // if the step is on a change boundary then increment the snake color - if (m_step == m_changeBoundary) { - m_colorset.getNext(); + if (self->m_step == self->m_changeBoundary) { + Colorset_getNext(&self->m_colorset); } } -void ZigzagPattern::Snake::draw() +void ZigzagSnake_draw(ZigzagSnake *self) { // when the blinkTimer triggers - switch (m_blinkTimer.alarm()) { + switch (Timer_alarm(&self->m_blinkTimer)) { case 0: // blinking on // draw two snakes, one on the current step, one on the opposite side - drawSnake(); + ZigzagSnake_drawSnake(self); break; case 1: // blinking off - Leds::clearAll(); + Leds_clearAll(); break; default: case -1: @@ -140,32 +155,31 @@ void ZigzagPattern::Snake::draw() } } -void ZigzagPattern::Snake::drawSnake() +void ZigzagSnake_drawSnake(ZigzagSnake *self) { uint8_t segment_position; int32_t colIndex = 0; - for (uint8_t segment = 0; segment < m_snakeSize; ++segment) { + for (uint8_t segment = 0; segment < self->m_snakeSize; ++segment) { // calculate the position of this segment, if the segment being rendered is // greater than the head position it means the snake head is at an index that // won't support it's entire tail without wrapping below 0 - if (segment > m_step) { - segment_position = NUM_ZIGZAG_STEPS - (segment - m_step); + if (segment > self->m_step) { + segment_position = NUM_ZIGZAG_STEPS - (segment - self->m_step); } else { - segment_position = m_step - segment; + segment_position = self->m_step - segment; } // adjust the brightness of the color before setting - RGBColor col = m_colorset.peek(colIndex); - if (segment < m_snakeSize) { - col.adjustBrightness(m_fadeAmount * segment); + RGBColor col = Colorset_peek(&self->m_colorset, colIndex); + if (segment < self->m_snakeSize) { + RGBColor_adjustBrightness(&col, self->m_fadeAmount * segment); } // lookup the target in the step positions array and turn it on with given color/brightness - Leds::setIndex(ledStepPositions[segment_position], col); + Leds_setIndex(ZigzagPattern_ledStepPositions[segment_position], col); // if this segment is on the step where the color changes - if (segment_position == m_changeBoundary) { + if (segment_position == self->m_changeBoundary) { // then decrement the color index for the rest of the snake so that the // tail of the snake has the old color till it reaches the change point colIndex--; } } } - diff --git a/VortexEngine/src/Patterns/Multi/ZigzagPattern.h b/VortexEngine/src/Patterns/Multi/ZigzagPattern.h index e4e0859daa..e753e05f9b 100644 --- a/VortexEngine/src/Patterns/Multi/ZigzagPattern.h +++ b/VortexEngine/src/Patterns/Multi/ZigzagPattern.h @@ -1,47 +1,28 @@ #ifndef ZIGZAG_PATTERN_H #define ZIGZAG_PATTERN_H +#include #include "MultiLedPattern.h" -#include "../../Time/Timings.h" #include "../../Time/Timer.h" +#include "../../Colors/Colorset.h" -class ZigzagPattern : public MultiLedPattern -{ -public: - ZigzagPattern(const PatternArgs &args); - virtual ~ZigzagPattern(); - - // init the pattern to initial state - virtual void init() override; - - // pure virtual must override the play function - virtual void play() override; - -private: - - class Snake { - public: - Snake(); - - void init(uint8_t onDuration, uint8_t offDuration, const Colorset &colorset, uint32_t colorOffset, - uint8_t step = 0, uint8_t snakeSize = 1, uint8_t fadeAmount = 55, uint8_t changeBoundary = 3); - void step(); - void draw(); - - private: - void drawSnake(); - - Timer m_blinkTimer; - Colorset m_colorset; - uint8_t m_step; - uint8_t m_snakeSize; - uint8_t m_fadeAmount; - uint8_t m_changeBoundary; - }; +// a single snake, it owns a blink timer and its own colorset. +// Defined first so ZigzagPattern can embed it by value. +typedef struct ZigzagSnake { + Timer m_blinkTimer; + Colorset m_colorset; + uint8_t m_step; + uint8_t m_snakeSize; + uint8_t m_fadeAmount; + uint8_t m_changeBoundary; +} ZigzagSnake; - // path for leds to take, index this with m_step up to LED_COUNT steps - static const LedPos ledStepPositions[]; +// ZigzagPattern runs two independent snakes across the led map, one heading +// over the evens and one over the odds. +typedef struct ZigzagPattern { + // must remain the first member + MultiLedPattern base; // blink on duration uint8_t m_onDuration; @@ -58,8 +39,27 @@ class ZigzagPattern : public MultiLedPattern Timer m_stepTimer; // the two snakes that zigzag - Snake m_snake1; - Snake m_snake2; -}; + ZigzagSnake m_snake1; + ZigzagSnake m_snake2; +} ZigzagPattern; + +// the led positions the snakes travel over, index with the snake step +extern const LedPos ZigzagPattern_ledStepPositions[]; + +// the vtable for this pattern +extern const PatternVtable ZigzagPattern_vtable; + +void ZigzagPattern_init(ZigzagPattern *self, const PatternArgs *args); +void ZigzagPattern_destroy(Pattern *self); +void ZigzagPattern_initImpl(Pattern *self); +void ZigzagPattern_play(Pattern *self); + +// snake entry points +void ZigzagSnake_make(ZigzagSnake *self); +void ZigzagSnake_init(ZigzagSnake *self, uint8_t onDuration, uint8_t offDuration, const Colorset *colorset, + uint32_t colorOffset, uint8_t step, uint8_t snakeSize, uint8_t fadeAmount, uint8_t changeBoundary); +void ZigzagSnake_step(ZigzagSnake *self); +void ZigzagSnake_draw(ZigzagSnake *self); +void ZigzagSnake_drawSnake(ZigzagSnake *self); #endif diff --git a/VortexEngine/src/Patterns/Pattern.cpp b/VortexEngine/src/Patterns/Pattern.cpp index bcdf645fc9..bee2f82779 100644 --- a/VortexEngine/src/Patterns/Pattern.cpp +++ b/VortexEngine/src/Patterns/Pattern.cpp @@ -1,3 +1,4 @@ +#include #include "Pattern.h" #include "../Patterns/PatternBuilder.h" @@ -9,62 +10,93 @@ #include "../VortexConfig.h" -Pattern::Pattern() : - m_patternID(PATTERN_FIRST), - m_patternFlags(0), - m_colorset(), - m_ledPos(LED_FIRST), - m_numArgs(0), - m_argList() +const PatternVtable Pattern_baseVtable = { + .destroy = Pattern_destroy, + .bind = Pattern_baseBind, + .init = Pattern_baseInit, + .play = Pattern_basePlay +}; + +void Pattern_initBase(Pattern *self, PatternID id, const PatternVtable *vtable) +{ + self->vtable = vtable; + self->m_patternID = id; + self->m_patternFlags = 0; + Colorset_make(&self->m_colorset); + self->m_ledPos = LED_FIRST; + self->m_numArgs = 0; + memset((void *)self->m_argList, 0, sizeof(self->m_argList)); #ifdef VORTEX_LIB - ,m_argNameList() + memset((void *)self->m_argNameList, 0, sizeof(self->m_argNameList)); #endif +} + +void Pattern_destroy(Pattern *self) +{ + if (!self) { + return; + } + // run the derived destructor hook; this does *not* free the object, + // the caller is responsible for vfree()ing it after this returns + if (self->vtable && self->vtable->destroy && self->vtable->destroy != Pattern_destroy) { + self->vtable->destroy(self); + } +} + +void Pattern_bind(Pattern *self, LedPos pos) +{ + self->vtable->bind(self, pos); +} + +void Pattern_init(Pattern *self) { + self->vtable->init(self); } -Pattern::Pattern(const PatternArgs &args) : - Pattern() +void Pattern_play(Pattern *self) { + self->vtable->play(self); } -Pattern::~Pattern() +void Pattern_baseBind(Pattern *self, LedPos pos) { + self->m_ledPos = pos; } -void Pattern::bind(LedPos pos) +void Pattern_baseInit(Pattern *self) { - m_ledPos = pos; + Colorset_resetIndex(&self->m_colorset); } -void Pattern::init() +void Pattern_basePlay(Pattern *self) { - m_colorset.resetIndex(); } #ifdef VORTEX_LIB -void Pattern::skip(uint32_t ticks) +void Pattern_skip(Pattern *self, uint32_t ticks) { - Time::startSimulation(); + Time_startSimulation(); for (uint32_t i = 0; i < ticks; ++i) { - play(); // simulate playing the pattern - Time::tickSimulation(); + Pattern_play(self); // simulate playing the pattern + Time_tickSimulation(); } - Time::endSimulation(); + Time_endSimulation(); } #endif // must override the serialize routine to save the pattern -bool Pattern::serialize(ByteStream &buffer) const +bool Pattern_serialize(const Pattern *self, ByteStream *buffer) { - if (!buffer.serialize8((uint8_t)m_patternID)) { + if (!ByteStream_serialize8(buffer, (uint8_t)self->m_patternID)) { return false; } - if (!m_colorset.serialize(buffer)) { + if (!Colorset_serialize(&self->m_colorset, buffer)) { return false; } PatternArgs args; - getArgs(args); - PatternArgs defaults = PatternBuilder::getDefaultArgs(m_patternID); + Pattern_getArgs(self, &args); + PatternArgs defaults; + PatternBuilder_getDefaultArgs(self->m_patternID, &defaults); // generate a bitmap of which args are defaulted uint8_t argmap = ARG_NONE; for (uint32_t i = 0; i < MAX_ARGS; ++i) { @@ -72,88 +104,89 @@ bool Pattern::serialize(ByteStream &buffer) const ARGMAP_SET(argmap, i); } } - if (!args.serialize(buffer, argmap)) { + if (!PatternArgs_serialize(&args, buffer, argmap)) { return false; } return true; } // must override unserialize to load patterns -bool Pattern::unserialize(ByteStream &buffer) +bool Pattern_unserialize(Pattern *self, ByteStream *buffer) { // don't unserialize the pattern ID because it is already // unserialized by the pattern builder to decide which pattern // to instantiate, instead only unserialize the colorset - if (!m_colorset.unserialize(buffer)) { + if (!Colorset_unserialize(&self->m_colorset, buffer)) { return false; } // start with the default args for this pattern - PatternArgs args = PatternBuilder::getDefaultArgs(m_patternID); + PatternArgs args; + PatternBuilder_getDefaultArgs(self->m_patternID, &args); // then unserialize any different args overtop of the defaults - args.unserialize(buffer); - setArgs(args); + PatternArgs_unserialize(&args, buffer); + Pattern_setArgs(self, &args); return true; } -void Pattern::setArg(uint8_t index, uint8_t value) +void Pattern_setArg(Pattern *self, uint8_t index, uint8_t value) { - if (index >= m_numArgs) { + if (index >= self->m_numArgs) { return; } - *((uint8_t *)this + m_argList[index]) = value; + *((uint8_t *)self + self->m_argList[index]) = value; } -uint8_t Pattern::getArg(uint8_t index) const +uint8_t Pattern_getArg(const Pattern *self, uint8_t index) { - if (index >= m_numArgs) { + if (index >= self->m_numArgs) { return 0; } - return *((uint8_t *)this + m_argList[index]); + return *((uint8_t *)self + self->m_argList[index]); } -uint8_t &Pattern::argRef(uint8_t index) +uint8_t *Pattern_argRef(Pattern *self, uint8_t index) { - if (index >= m_numArgs) { + if (index >= self->m_numArgs) { index = 0; } - return *((uint8_t *)this + m_argList[index]); + return ((uint8_t *)self + self->m_argList[index]); } -void Pattern::setArgs(const PatternArgs &args) +void Pattern_setArgs(Pattern *self, const PatternArgs *args) { - for (uint32_t i = 0; i < m_numArgs; ++i) { - *((uint8_t *)this + m_argList[i]) = args.args[i]; + for (uint32_t i = 0; i < self->m_numArgs; ++i) { + *((uint8_t *)self + self->m_argList[i]) = args->args[i]; } } -void Pattern::getArgs(PatternArgs &args) const +void Pattern_getArgs(const Pattern *self, PatternArgs *args) { - args.init(); - for (uint32_t i = 0; i < m_numArgs; ++i) { - args.addArgs(*((uint8_t *)this + m_argList[i])); + PatternArgs_init0(args); + for (uint32_t i = 0; i < self->m_numArgs; ++i) { + PatternArgs_addArgs1(args, *((uint8_t *)self + self->m_argList[i])); } } -bool Pattern::equals(const Pattern *other) +bool Pattern_equals(Pattern *self, const Pattern *other) { if (!other) { return false; } // compare pattern id - if (m_patternID != other->m_patternID) { + if (self->m_patternID != other->m_patternID) { return false; } // then colorset - if (!m_colorset.equals(&other->m_colorset)) { + if (!Colorset_equalsPtr(&self->m_colorset, &other->m_colorset)) { return false; } // number of args - if (m_numArgs != other->m_numArgs) { + if (self->m_numArgs != other->m_numArgs) { return false; } // compare each arg - for (uint8_t i = 0; i < m_numArgs; ++i) { - if (getArg(i) != other->getArg(i)) { + for (uint8_t i = 0; i < self->m_numArgs; ++i) { + if (Pattern_getArg(self, i) != Pattern_getArg(other, i)) { return false; } } @@ -161,28 +194,28 @@ bool Pattern::equals(const Pattern *other) } // change the colorset -void Pattern::setColorset(const Colorset &set) +void Pattern_setColorset(Pattern *self, const Colorset *set) { - m_colorset = set; + Colorset_set(&self->m_colorset, set); } -void Pattern::clearColorset() +void Pattern_clearColorset(Pattern *self) { - m_colorset.clear(); + Colorset_clear(&self->m_colorset); } #ifdef VORTEX_LIB -void Pattern::registerArg(const char *name, arg_offset_t argOffset) +void Pattern_registerArg(Pattern *self, const char *name, arg_offset_t argOffset) #else -void Pattern::registerArg(arg_offset_t argOffset) +void Pattern_registerArg(Pattern *self, arg_offset_t argOffset) #endif { - if (m_numArgs >= MAX_PATTERN_ARGS) { + if (self->m_numArgs >= MAX_PATTERN_ARGS) { ERROR_LOG("too many args"); return; } #ifdef VORTEX_LIB - m_argNameList[m_numArgs] = name; + self->m_argNameList[self->m_numArgs] = name; #endif - m_argList[m_numArgs++] = argOffset; + self->m_argList[self->m_numArgs++] = argOffset; } diff --git a/VortexEngine/src/Patterns/Pattern.h b/VortexEngine/src/Patterns/Pattern.h index 0a3454ce04..917f0db2d9 100644 --- a/VortexEngine/src/Patterns/Pattern.h +++ b/VortexEngine/src/Patterns/Pattern.h @@ -1,12 +1,15 @@ #ifndef PATTERN_H #define PATTERN_H +#include #include "../Leds/LedTypes.h" #include "../Colors/Colorset.h" #include "Patterns.h" #include "PatternArgs.h" +#include + #define MAX_PATTERN_ARGS 8 // The heirarchy of pattern currently looks like this: @@ -36,111 +39,167 @@ typedef uint8_t arg_offset_t; typedef uint16_t arg_offset_t; #endif -// macro to register args of a pattern +// macro to register args of a pattern. C has no 'this', so the containing +// type has to be named explicitly to compute the offset of the member. #ifdef VORTEX_LIB -#define REGISTER_ARG(arg) \ - registerArg(#arg, (arg_offset_t)(((uintptr_t)&arg - (uintptr_t)this))); +#define REGISTER_ARG(self, type, arg) \ + Pattern_registerArg(self, #arg, (arg_offset_t)offsetof(type, arg)); #else -#define REGISTER_ARG(arg) \ - registerArg((arg_offset_t)(((uintptr_t)&arg - (uintptr_t)this))); +#define REGISTER_ARG(self, type, arg) \ + Pattern_registerArg(self, (arg_offset_t)offsetof(type, arg)); #endif -class ByteStream; +typedef struct ByteStream ByteStream; +typedef struct Pattern Pattern; + +// the virtual method table for a pattern. 'self' is always the concrete +// pattern pointer, so embedded base structs are reachable by casting down. +typedef struct PatternVtable { + // destructor, so derived patterns can release their own allocations + void (*destroy)(Pattern *self); + // bind a colorset and position to the pattern + void (*bind)(Pattern *self, LedPos pos); + // init the pattern to initial state + void (*init)(Pattern *self); + // pure virtual in the original hierarchy, every concrete pattern must + // supply an implementation here + void (*play)(Pattern *self); +} PatternVtable; -class Pattern +struct Pattern { - // PatternBuilder can access the Pattern internals - friend class PatternBuilder; + // the vtable this pattern dispatches through + const PatternVtable *vtable; -protected: - // Pattern is an abstract class - Pattern(); + // the ID of this pattern (set by pattern builder) + PatternID m_patternID; + // any flags the pattern has + uint8_t m_patternFlags; + // a copy of the colorset that this pattern is initialized with + Colorset m_colorset; + // the Led the pattern is running on + LedPos m_ledPos; - Pattern(const PatternArgs &args); + uint8_t m_numArgs; + arg_offset_t m_argList[MAX_PATTERN_ARGS]; -public: - virtual ~Pattern(); +#ifdef VORTEX_LIB + const char *m_argNameList[MAX_PATTERN_ARGS]; +#endif +}; - // bind a colorset and position to the pattern - virtual void bind(LedPos pos); +// the base vtable, provides the default behaviour of the non-pure virtuals +extern const PatternVtable Pattern_baseVtable; - // init the pattern to initial state - virtual void init(); +// initialize the base portion of a pattern and select the vtable to +// dispatch through. derived initializers call this first, then populate +// their own members and register their args. +void Pattern_initBase(Pattern *self, PatternID id, const PatternVtable *vtable); + +// virtual entry points, these dispatch through the vtable +void Pattern_destroy(Pattern *self); +void Pattern_bind(Pattern *self, LedPos pos); +void Pattern_init(Pattern *self); +void Pattern_play(Pattern *self); - // pure virtual must override the play function - virtual void play() = 0; +// the base implementations, used as the default vtable entries and +// called directly by derived patterns in place of Pattern::bind() etc +void Pattern_baseBind(Pattern *self, LedPos pos); +void Pattern_baseInit(Pattern *self); +void Pattern_basePlay(Pattern *self); #ifdef VORTEX_LIB - // skip the pattern ahead some ticks - void skip(uint32_t ticks); +// skip the pattern ahead some ticks +void Pattern_skip(Pattern *self, uint32_t ticks); #endif - // serialize and unserialize a pattern to a bytestream - bool serialize(ByteStream &buffer) const; - bool unserialize(ByteStream &buffer); +// serialize and unserialize a pattern to a bytestream +bool Pattern_serialize(const Pattern *self, ByteStream *buffer); +bool Pattern_unserialize(Pattern *self, ByteStream *buffer); - // get or set a single arg - void setArg(uint8_t index, uint8_t value); - uint8_t getArg(uint8_t index) const; +// get or set a single arg +void Pattern_setArg(Pattern *self, uint8_t index, uint8_t value); +uint8_t Pattern_getArg(const Pattern *self, uint8_t index); - uint8_t &argRef(uint8_t index); +uint8_t *Pattern_argRef(Pattern *self, uint8_t index); #ifdef VORTEX_LIB - // get the name of an arg - const char *getArgName(uint8_t index) const { return index >= m_numArgs ? "" : m_argNameList[index]; } +// get the name of an arg +static inline const char *Pattern_getArgName(const Pattern *self, uint8_t index) +{ + return index >= self->m_numArgs ? "" : self->m_argNameList[index]; +} #endif - // get or set the entire list of pattern args - void setArgs(const PatternArgs &args); - void getArgs(PatternArgs &args) const; +// get or set the entire list of pattern args +void Pattern_setArgs(Pattern *self, const PatternArgs *args); +void Pattern_getArgs(const Pattern *self, PatternArgs *args); - // number of args the pattern has - uint8_t getNumArgs() const { return m_numArgs; } +// number of args the pattern has +static inline uint8_t Pattern_getNumArgs(const Pattern *self) +{ + return self->m_numArgs; +} - // comparison to other pattern - // NOTE: That may cause problems because the parameter is still a Pattern * - // which means comparison would need to cast the other upwards first - // NOTE2: Removing virtual because this probably shouldn't be overridden - bool equals(const Pattern *other); +// comparison to other pattern +// NOTE: That may cause problems because the parameter is still a Pattern * +// which means comparison would need to cast the other upwards first +// NOTE2: Removing virtual because this probably shouldn't be overridden +bool Pattern_equals(Pattern *self, const Pattern *other); - // change the colorset - const Colorset getColorset() const { return m_colorset; } - Colorset getColorset() { return m_colorset; } - void setColorset(const Colorset &set); - void clearColorset(); +// change the colorset +static inline Colorset Pattern_getColorset(const Pattern *self) +{ + return self->m_colorset; +} - // change the led position - void setLedPos(LedPos pos) { m_ledPos = pos; } +void Pattern_setColorset(Pattern *self, const Colorset *set); +void Pattern_clearColorset(Pattern *self); - // get/set the ID of the pattern (set by mode builder) - PatternID getPatternID() const { return m_patternID; } +// change the led position +static inline void Pattern_setLedPos(Pattern *self, LedPos pos) +{ + self->m_ledPos = pos; +} - // get a pointer to the colorset that is bound to the pattern - LedPos getLedPos() const { return m_ledPos; } +// get/set the ID of the pattern (set by mode builder) +static inline PatternID Pattern_getPatternID(const Pattern *self) +{ + return self->m_patternID; +} - // get the pattern flags - uint32_t getFlags() const { return m_patternFlags; } - bool hasFlags(uint32_t flags) const { return (m_patternFlags & flags) != 0; } +static inline void Pattern_setPatternID(Pattern *self, PatternID id) +{ + self->m_patternID = id; +} -protected: - // the ID of this pattern (set by pattern builder) - PatternID m_patternID; - // any flags the pattern has - uint8_t m_patternFlags; - // a copy of the colorset that this pattern is initialized with - Colorset m_colorset; - // the Led the pattern is running on - LedPos m_ledPos; +// get a pointer to the colorset that is bound to the pattern +static inline LedPos Pattern_getLedPos(const Pattern *self) +{ + return self->m_ledPos; +} - uint8_t m_numArgs; - arg_offset_t m_argList[MAX_PATTERN_ARGS]; +// get the pattern flags +static inline uint32_t Pattern_getFlags(const Pattern *self) +{ + return self->m_patternFlags; +} +static inline void Pattern_setFlags(Pattern *self, uint32_t flags) +{ + self->m_patternFlags = (uint8_t)flags; +} + +static inline bool Pattern_hasFlags(const Pattern *self, uint32_t flags) +{ + return (self->m_patternFlags & flags) != 0; +} + +// register an arg, called via the REGISTER_ARG macro #ifdef VORTEX_LIB - void registerArg(const char *name, arg_offset_t argOffset); - const char *m_argNameList[MAX_PATTERN_ARGS]; +void Pattern_registerArg(Pattern *self, const char *name, arg_offset_t argOffset); #else - void registerArg(arg_offset_t argOffset); +void Pattern_registerArg(Pattern *self, arg_offset_t argOffset); #endif -}; #endif diff --git a/VortexEngine/src/Patterns/PatternArgs.cpp b/VortexEngine/src/Patterns/PatternArgs.cpp index 8ea98aa93d..75d389cbc4 100644 --- a/VortexEngine/src/Patterns/PatternArgs.cpp +++ b/VortexEngine/src/Patterns/PatternArgs.cpp @@ -1,184 +1,245 @@ #include "PatternArgs.h" - #include "../Serial/ByteStream.h" - -PatternArgs::PatternArgs() : - arg1(0), arg2(0), arg3(0), arg4(0), arg5(0), arg6(0), arg7(0), arg8(0), numArgs(0) +void PatternArgs_make8(PatternArgs *self, uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6, uint8_t a7, uint8_t a8) { + PatternArgs_init8(self, a1, a2, a3, a4, a5, a6, a7, a8); } -PatternArgs::PatternArgs(uint8_t a1) : - arg1(a1), arg2(0), arg3(0), arg4(0), arg5(0), arg6(0), arg7(0), arg8(0), numArgs(1) +void PatternArgs_init8(PatternArgs *self, uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6, uint8_t a7, uint8_t a8) { + self->arg1 = a1; + self->arg2 = a2; + self->arg3 = a3; + self->arg4 = a4; + self->arg5 = a5; + self->arg6 = a6; + self->arg7 = a7; + self->arg8 = a8; + self->numArgs = 8; } -PatternArgs::PatternArgs(uint8_t a1, uint8_t a2) : - arg1(a1), arg2(a2), arg3(0), arg4(0), arg5(0), arg6(0), arg7(0), arg8(0), numArgs(2) +void PatternArgs_make0(PatternArgs *self) { + PatternArgs_init0(self); } -PatternArgs::PatternArgs(uint8_t a1, uint8_t a2, uint8_t a3) : - arg1(a1), arg2(a2), arg3(a3), arg4(0), arg5(0), arg6(0), arg7(0), arg8(0), numArgs(3) +void PatternArgs_init0(PatternArgs *self) { + self->arg1 = 0; + self->arg2 = 0; + self->arg3 = 0; + self->arg4 = 0; + self->arg5 = 0; + self->arg6 = 0; + self->arg7 = 0; + self->arg8 = 0; + self->numArgs = 0; } -PatternArgs::PatternArgs(uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4) : - arg1(a1), arg2(a2), arg3(a3), arg4(a4), arg5(0), arg6(0), arg7(0), arg8(0), numArgs(4) +void PatternArgs_make1(PatternArgs *self, uint8_t a1) { + PatternArgs_init1(self, a1); } -PatternArgs::PatternArgs(uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5) : - arg1(a1), arg2(a2), arg3(a3), arg4(a4), arg5(a5), arg6(0), arg7(0), arg8(0), numArgs(5) +void PatternArgs_init1(PatternArgs *self, uint8_t a1) { + self->arg1 = a1; + self->arg2 = 0; + self->arg3 = 0; + self->arg4 = 0; + self->arg5 = 0; + self->arg6 = 0; + self->arg7 = 0; + self->arg8 = 0; + self->numArgs = 1; } -PatternArgs::PatternArgs(uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6) : - arg1(a1), arg2(a2), arg3(a3), arg4(a4), arg5(a5), arg6(a6), arg7(0), arg8(0), numArgs(6) +void PatternArgs_make2(PatternArgs *self, uint8_t a1, uint8_t a2) { + PatternArgs_init2(self, a1, a2); } -PatternArgs::PatternArgs(uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6, uint8_t a7) : - arg1(a1), arg2(a2), arg3(a3), arg4(a4), arg5(a5), arg6(a6), arg7(a7), arg8(0), numArgs(7) +void PatternArgs_init2(PatternArgs *self, uint8_t a1, uint8_t a2) { + self->arg1 = a1; + self->arg2 = a2; + self->arg3 = 0; + self->arg4 = 0; + self->arg5 = 0; + self->arg6 = 0; + self->arg7 = 0; + self->arg8 = 0; + self->numArgs = 2; } -PatternArgs::PatternArgs(uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6, uint8_t a7, uint8_t a8) : - arg1(a1), arg2(a2), arg3(a3), arg4(a4), arg5(a5), arg6(a6), arg7(a7), arg8(a8), numArgs(8) +void PatternArgs_make3(PatternArgs *self, uint8_t a1, uint8_t a2, uint8_t a3) { + PatternArgs_init3(self, a1, a2, a3); } - -void PatternArgs::init() +void PatternArgs_init3(PatternArgs *self, uint8_t a1, uint8_t a2, uint8_t a3) { - *this = PatternArgs(); + self->arg1 = a1; + self->arg2 = a2; + self->arg3 = a3; + self->arg4 = 0; + self->arg5 = 0; + self->arg6 = 0; + self->arg7 = 0; + self->arg8 = 0; + self->numArgs = 3; } -void PatternArgs::init(uint8_t a1) +void PatternArgs_make4(PatternArgs *self, uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4) { - *this = PatternArgs(a1); + PatternArgs_init4(self, a1, a2, a3, a4); } -void PatternArgs::init(uint8_t a1, uint8_t a2) +void PatternArgs_init4(PatternArgs *self, uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4) { - *this = PatternArgs(a1, a2); + self->arg1 = a1; + self->arg2 = a2; + self->arg3 = a3; + self->arg4 = a4; + self->arg5 = 0; + self->arg6 = 0; + self->arg7 = 0; + self->arg8 = 0; + self->numArgs = 4; } -void PatternArgs::init(uint8_t a1, uint8_t a2, uint8_t a3) +void PatternArgs_make5(PatternArgs *self, uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5) { - *this = PatternArgs(a1, a2, a3); + PatternArgs_init5(self, a1, a2, a3, a4, a5); } -void PatternArgs::init(uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4) +void PatternArgs_init5(PatternArgs *self, uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5) { - *this = PatternArgs(a1, a2, a3, a4); + self->arg1 = a1; + self->arg2 = a2; + self->arg3 = a3; + self->arg4 = a4; + self->arg5 = a5; + self->arg6 = 0; + self->arg7 = 0; + self->arg8 = 0; + self->numArgs = 5; } -void PatternArgs::init(uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5) +void PatternArgs_make6(PatternArgs *self, uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6) { - *this = PatternArgs(a1, a2, a3, a4, a5); + PatternArgs_init6(self, a1, a2, a3, a4, a5, a6); } -void PatternArgs::init(uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6) +void PatternArgs_init6(PatternArgs *self, uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6) { - *this = PatternArgs(a1, a2, a3, a4, a5, a6); + self->arg1 = a1; + self->arg2 = a2; + self->arg3 = a3; + self->arg4 = a4; + self->arg5 = a5; + self->arg6 = a6; + self->arg7 = 0; + self->arg8 = 0; + self->numArgs = 6; } -void PatternArgs::init(uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6, uint8_t a7) +void PatternArgs_make7(PatternArgs *self, uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6, uint8_t a7) { - *this = PatternArgs(a1, a2, a3, a4, a5, a6, a7); + PatternArgs_init7(self, a1, a2, a3, a4, a5, a6, a7); } -void PatternArgs::init(uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6, uint8_t a7, uint8_t a8) +void PatternArgs_init7(PatternArgs *self, uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6, uint8_t a7) { - *this = PatternArgs(a1, a2, a3, a4, a5, a6, a7, a8); + self->arg1 = a1; + self->arg2 = a2; + self->arg3 = a3; + self->arg4 = a4; + self->arg5 = a5; + self->arg6 = a6; + self->arg7 = a7; + self->arg8 = 0; + self->numArgs = 7; } - -void PatternArgs::addArgs(uint8_t a1) +void PatternArgs_addArgs1(PatternArgs *self, uint8_t a1) { - args[numArgs++] = a1; + self->args[self->numArgs++] = a1; } - -void PatternArgs::addArgs(uint8_t a1, uint8_t a2) +void PatternArgs_addArgs2(PatternArgs *self, uint8_t a1, uint8_t a2) { - args[numArgs++] = a1; - args[numArgs++] = a2; + self->args[self->numArgs++] = a1; + self->args[self->numArgs++] = a2; } - -void PatternArgs::addArgs(uint8_t a1, uint8_t a2, uint8_t a3) +void PatternArgs_addArgs3(PatternArgs *self, uint8_t a1, uint8_t a2, uint8_t a3) { - args[numArgs++] = a1; - args[numArgs++] = a2; - args[numArgs++] = a3; + self->args[self->numArgs++] = a1; + self->args[self->numArgs++] = a2; + self->args[self->numArgs++] = a3; } - -void PatternArgs::addArgs(uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4) +void PatternArgs_addArgs4(PatternArgs *self, uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4) { - args[numArgs++] = a1; - args[numArgs++] = a2; - args[numArgs++] = a3; - args[numArgs++] = a4; + self->args[self->numArgs++] = a1; + self->args[self->numArgs++] = a2; + self->args[self->numArgs++] = a3; + self->args[self->numArgs++] = a4; } - -void PatternArgs::addArgs(uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5) +void PatternArgs_addArgs5(PatternArgs *self, uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5) { - args[numArgs++] = a1; - args[numArgs++] = a2; - args[numArgs++] = a3; - args[numArgs++] = a4; - args[numArgs++] = a5; + self->args[self->numArgs++] = a1; + self->args[self->numArgs++] = a2; + self->args[self->numArgs++] = a3; + self->args[self->numArgs++] = a4; + self->args[self->numArgs++] = a5; } - -void PatternArgs::addArgs(uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6) +void PatternArgs_addArgs6(PatternArgs *self, uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6) { - args[numArgs++] = a1; - args[numArgs++] = a2; - args[numArgs++] = a3; - args[numArgs++] = a4; - args[numArgs++] = a5; - args[numArgs++] = a6; + self->args[self->numArgs++] = a1; + self->args[self->numArgs++] = a2; + self->args[self->numArgs++] = a3; + self->args[self->numArgs++] = a4; + self->args[self->numArgs++] = a5; + self->args[self->numArgs++] = a6; } - -void PatternArgs::addArgs(uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6, uint8_t a7) +void PatternArgs_addArgs7(PatternArgs *self, uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6, uint8_t a7) { - args[numArgs++] = a1; - args[numArgs++] = a2; - args[numArgs++] = a3; - args[numArgs++] = a4; - args[numArgs++] = a5; - args[numArgs++] = a6; - args[numArgs++] = a7; + self->args[self->numArgs++] = a1; + self->args[self->numArgs++] = a2; + self->args[self->numArgs++] = a3; + self->args[self->numArgs++] = a4; + self->args[self->numArgs++] = a5; + self->args[self->numArgs++] = a6; + self->args[self->numArgs++] = a7; } - -void PatternArgs::addArgs(uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6, uint8_t a7, uint8_t a8) +void PatternArgs_addArgs8(PatternArgs *self, uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6, uint8_t a7, uint8_t a8) { - args[numArgs++] = a1; - args[numArgs++] = a2; - args[numArgs++] = a3; - args[numArgs++] = a4; - args[numArgs++] = a5; - args[numArgs++] = a6; - args[numArgs++] = a7; - args[numArgs++] = a7; + self->args[self->numArgs++] = a1; + self->args[self->numArgs++] = a2; + self->args[self->numArgs++] = a3; + self->args[self->numArgs++] = a4; + self->args[self->numArgs++] = a5; + self->args[self->numArgs++] = a6; + self->args[self->numArgs++] = a7; + self->args[self->numArgs++] = a7; } - -bool PatternArgs::operator!=(const PatternArgs &rhs) +bool PatternArgs_notEqual(const PatternArgs *self, const PatternArgs *rhs) { - return !(*this == rhs); + return !PatternArgs_equal(self, rhs); } -bool PatternArgs::operator==(const PatternArgs &rhs) + +bool PatternArgs_equal(const PatternArgs *self, const PatternArgs *rhs) { // just check number of args first - if (rhs.numArgs != numArgs) { + if (rhs->numArgs != self->numArgs) { return false; } // compare num args - for (uint8_t i = 0; i < numArgs; ++i) { - if (args[i] != rhs.args[i]) { + for (uint8_t i = 0; i < self->numArgs; ++i) { + if (self->args[i] != rhs->args[i]) { return false; } } return true; } -uint8_t PatternArgs::operator[](int index) const +uint8_t PatternArgs_at(const PatternArgs *self, int index) { if (index > 7) { return 0; } - return args[index]; + return self->args[index]; } -bool PatternArgs::serialize(ByteStream &buffer, ArgMap argmap) const +bool PatternArgs_serialize(const PatternArgs *self, ByteStream *buffer, ArgMap argmap) { - if (!buffer.serialize8(argmap)) { + if (!ByteStream_serialize8(buffer, argmap)) { return false; } for (uint8_t i = 0; i < MAX_ARGS; ++i) { if (ARGMAP_ISSET(argmap, i)) { - if (!buffer.serialize8(args[i])) { + if (!ByteStream_serialize8(buffer, self->args[i])) { return false; } } @@ -186,15 +247,20 @@ bool PatternArgs::serialize(ByteStream &buffer, ArgMap argmap) const return true; } -ArgMap PatternArgs::unserialize(ByteStream &buffer) +bool PatternArgs_serializeAll(const PatternArgs *self, ByteStream *buffer) +{ + return PatternArgs_serialize(self, buffer, ARG_ALL); +} + +ArgMap PatternArgs_unserialize(PatternArgs *self, ByteStream *buffer) { ArgMap argmap = ARG_NONE; - if (!buffer.unserialize8(&argmap)) { + if (!ByteStream_unserialize8(buffer, &argmap)) { return ARG_NONE; } for (uint8_t i = 0; i < MAX_ARGS; ++i) { if (ARGMAP_ISSET(argmap, i)) { - if (!buffer.unserialize8(args + i)) { + if (!ByteStream_unserialize8(buffer, self->args + i)) { break; } } diff --git a/VortexEngine/src/Patterns/PatternArgs.h b/VortexEngine/src/Patterns/PatternArgs.h index 67759ad682..c7338caea3 100644 --- a/VortexEngine/src/Patterns/PatternArgs.h +++ b/VortexEngine/src/Patterns/PatternArgs.h @@ -1,13 +1,15 @@ #ifndef PATTERN_ARGS_H #define PATTERN_ARGS_H +#include #include +#include // maximum number of args, this isn't really configurable so I'm // not putting it in VortexConfig.h #define MAX_ARGS 8 -class ByteStream; +typedef struct ByteStream ByteStream; // a bit map of arguments, this is used to indicate which arguments // are present in a pattern when saved to storage @@ -33,49 +35,7 @@ typedef uint8_t ArgMap; // a structured method for passing arguments to patterns, there's definitely more dynamic // ways to approach this but I'm aiming for simple and lightweight here -class PatternArgs -{ -public: - // constructor for each number of arguments, automatically fills the 'numArgs' field accordingly - PatternArgs(); - PatternArgs(uint8_t a1); - PatternArgs(uint8_t a1, uint8_t a2); - PatternArgs(uint8_t a1, uint8_t a2, uint8_t a3); - PatternArgs(uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4); - PatternArgs(uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5); - PatternArgs(uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6); - PatternArgs(uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6, uint8_t a7); - PatternArgs(uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6, uint8_t a7, uint8_t a8); - - void init(); - void init(uint8_t a1); - void init(uint8_t a1, uint8_t a2); - void init(uint8_t a1, uint8_t a2, uint8_t a3); - void init(uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4); - void init(uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5); - void init(uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6); - void init(uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6, uint8_t a7); - void init(uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6, uint8_t a7, uint8_t a8); - - void addArgs(uint8_t a1); - void addArgs(uint8_t a1, uint8_t a2); - void addArgs(uint8_t a1, uint8_t a2, uint8_t a3); - void addArgs(uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4); - void addArgs(uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5); - void addArgs(uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6); - void addArgs(uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6, uint8_t a7); - void addArgs(uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6, uint8_t a7, uint8_t a8); - - bool operator!=(const PatternArgs &rhs); - bool operator==(const PatternArgs &rhs); - uint8_t operator[](int index) const; - - // serialize the pattern args with a specific mapping of which args to store - bool serialize(ByteStream &buffer, ArgMap argmap = ARG_ALL) const; - // unserialize the pattern args and return the argmap of which args were loaded - // NOTE: You should start with an instance of the default args before you unserialize - ArgMap unserialize(ByteStream &buffer); - +typedef struct PatternArgs { // public access to args union { uint8_t args[8]; @@ -119,6 +79,78 @@ class PatternArgs }; // indicates how many of the above args are filled uint8_t numArgs; -}; +} PatternArgs; + +// zero-arg and full-width overloads +void PatternArgs_make0(PatternArgs *self); +void PatternArgs_make8(PatternArgs *self, uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, + uint8_t a5, uint8_t a6, uint8_t a7, uint8_t a8); +void PatternArgs_init0(PatternArgs *self); +void PatternArgs_init8(PatternArgs *self, uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, + uint8_t a5, uint8_t a6, uint8_t a7, uint8_t a8); + +// the arg count of the supplied argument list, used to pick the right +// _makeN / _initN / _addArgsN overload at compile time. the indirection +// through the trailing _(...) macros is required so that PATTERN_ARGS_COUNT +// expands before being pasted with ##. +#define PATTERN_ARGS_NARG(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N +#define PATTERN_ARGS_COUNT(...) \ + PATTERN_ARGS_NARG(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1) + +#define PATTERN_ARGS_CAT_(a, b) a##b +#define PATTERN_ARGS_MAKE_(N, ...) PATTERN_ARGS_CAT_(PatternArgs_make, N)(__VA_ARGS__) +#define PATTERN_ARGS_INIT_(N, ...) PATTERN_ARGS_CAT_(PatternArgs_init, N)(__VA_ARGS__) +#define PATTERN_ARGS_ADD_(N, ...) PATTERN_ARGS_CAT_(PatternArgs_addArgs, N)(__VA_ARGS__) + +// PatternArgs_make(self, ...) / PatternArgs_init(self, ...) for any number of args 1-8. +// self is a named parameter rather than part of __VA_ARGS__ so that it is +// excluded from the argument count. +#define PatternArgs_make(self, ...) \ + PATTERN_ARGS_MAKE_(PATTERN_ARGS_COUNT(__VA_ARGS__), self, __VA_ARGS__) +#define PatternArgs_init(self, ...) \ + PATTERN_ARGS_INIT_(PATTERN_ARGS_COUNT(__VA_ARGS__), self, __VA_ARGS__) + +void PatternArgs_make1(PatternArgs *self, uint8_t a1); +void PatternArgs_make2(PatternArgs *self, uint8_t a1, uint8_t a2); +void PatternArgs_make3(PatternArgs *self, uint8_t a1, uint8_t a2, uint8_t a3); +void PatternArgs_make4(PatternArgs *self, uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4); +void PatternArgs_make5(PatternArgs *self, uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5); +void PatternArgs_make6(PatternArgs *self, uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6); +void PatternArgs_make7(PatternArgs *self, uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6, uint8_t a7); + +void PatternArgs_init1(PatternArgs *self, uint8_t a1); +void PatternArgs_init2(PatternArgs *self, uint8_t a1, uint8_t a2); +void PatternArgs_init3(PatternArgs *self, uint8_t a1, uint8_t a2, uint8_t a3); +void PatternArgs_init4(PatternArgs *self, uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4); +void PatternArgs_init5(PatternArgs *self, uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5); +void PatternArgs_init6(PatternArgs *self, uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6); +void PatternArgs_init7(PatternArgs *self, uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6, uint8_t a7); + +// append arguments one at a time +void PatternArgs_addArgs1(PatternArgs *self, uint8_t a1); +void PatternArgs_addArgs2(PatternArgs *self, uint8_t a1, uint8_t a2); +void PatternArgs_addArgs3(PatternArgs *self, uint8_t a1, uint8_t a2, uint8_t a3); +void PatternArgs_addArgs4(PatternArgs *self, uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4); +void PatternArgs_addArgs5(PatternArgs *self, uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5); +void PatternArgs_addArgs6(PatternArgs *self, uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6); +void PatternArgs_addArgs7(PatternArgs *self, uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6, uint8_t a7); +void PatternArgs_addArgs8(PatternArgs *self, uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6, uint8_t a7, uint8_t a8); + +#define PatternArgs_addArgsN(self, ...) \ + PATTERN_ARGS_ADD_(PATTERN_ARGS_COUNT(__VA_ARGS__), self, __VA_ARGS__) + +// comparison operators +bool PatternArgs_notEqual(const PatternArgs *self, const PatternArgs *rhs); +bool PatternArgs_equal(const PatternArgs *self, const PatternArgs *rhs); + +// index operator +uint8_t PatternArgs_at(const PatternArgs *self, int index); + +// serialize the pattern args with a specific mapping of which args to store +bool PatternArgs_serialize(const PatternArgs *self, ByteStream *buffer, ArgMap argmap); +bool PatternArgs_serializeAll(const PatternArgs *self, ByteStream *buffer); +// unserialize the pattern args and return the argmap of which args were loaded +// NOTE: You should start with an instance of the default args before you unserialize +ArgMap PatternArgs_unserialize(PatternArgs *self, ByteStream *buffer); #endif diff --git a/VortexEngine/src/Patterns/PatternBuilder.cpp b/VortexEngine/src/Patterns/PatternBuilder.cpp index 2d965664b7..bbf300ed5d 100644 --- a/VortexEngine/src/Patterns/PatternBuilder.cpp +++ b/VortexEngine/src/Patterns/PatternBuilder.cpp @@ -4,6 +4,7 @@ #include "../Time/TimeControl.h" #include "../Time/Timings.h" #include "../Log/Log.h" +#include "../Memory/Memory.h" #include "Multi/Sequencer/SequencedPattern.h" #include "Multi/Sequencer/ChaserPattern.h" @@ -34,7 +35,66 @@ #include "Single/BlendPattern.h" #include "Single/SolidPattern.h" -Pattern *PatternBuilder::make(PatternID id, const PatternArgs *args) +// every pattern type that can be instantiated, the pattern pool blocks +// are sized to fit the largest of them +typedef union AnyPattern { + BlinkPattern blink; + SolidPattern solid; +#if VORTEX_SLIM == 0 + HueShiftPattern hueShift; + TheaterChasePattern theaterChase; + ChaserPattern chaser; + ZigzagPattern zigzag; + DripPattern drip; + DripMorphPattern dripMorph; + CrossDopsPattern crossDops; + DoubleStrobePattern doubleStrobe; + MeteorPattern meteor; + SparkleTracePattern sparkleTrace; + VortexWipePattern vortexWipe; + WarpPattern warp; + WarpWormPattern warpWorm; + SnowballPattern snowball; + LighthousePattern lighthouse; + PulsishPattern pulsish; + BouncePattern bounce; + BackStrobePattern backStrobe; + VortexPattern vortex; +#endif +} AnyPattern; + +// the pool all patterns are allocated from +static MemPool s_patternPool; +static MEMPOOL_STORAGE(s_patternStorage, sizeof(AnyPattern), PATTERN_POOL_SIZE); + +bool PatternBuilder_init() +{ + MemPool_init(&s_patternPool, s_patternStorage, sizeof(AnyPattern), PATTERN_POOL_SIZE); + return true; +} + +void *PatternBuilder_allocPattern() +{ + return MemPool_alloc(&s_patternPool); +} + +void PatternBuilder_freePattern(Pattern *pat) +{ + MemPool_free(&s_patternPool, pat); +} + +// allocate + construct a multi led pattern with the given args +#define MAKE_MULTI_PATTERN(CLASSNAME) \ + do { \ + CLASSNAME *p = (CLASSNAME *)PatternBuilder_allocPattern(); \ + if (p) { \ + CLASSNAME##_init(p, &args); \ + return (Pattern *)p; \ + } \ + return NULL; \ + } while (0) + +Pattern *PatternBuilder_make(PatternID id, const PatternArgs *args) { #if VORTEX_SLIM == 0 if (id > PATTERN_LAST) { @@ -46,85 +106,87 @@ Pattern *PatternBuilder::make(PatternID id, const PatternArgs *args) } #if VORTEX_SLIM == 0 if (isMultiLedPatternID(id)) { - return makeMulti(id, args); + return (Pattern *)PatternBuilder_makeMulti(id, args); } #endif - return makeSingle(id, args); + return PatternBuilder_makeSingle(id, args); } -Pattern *PatternBuilder::dupe(const Pattern *pat) +Pattern *PatternBuilder_dupe(const Pattern *pat) { if (!pat) { - return nullptr; + return NULL; } PatternArgs args; - pat->getArgs(args); - Pattern *newPat = make(pat->getPatternID(), &args); + Pattern_getArgs(pat, &args); + Pattern *newPat = PatternBuilder_make(pat->m_patternID, &args); if (!newPat) { - return nullptr; + return NULL; } - newPat->setColorset(pat->getColorset()); - newPat->bind(pat->getLedPos()); + Colorset cs = Pattern_getColorset(pat); + Pattern_setColorset(newPat, &cs); + newPat->vtable->bind(newPat, pat->m_ledPos); return newPat; } -// generate a single LED pattern (nullptr if patternid is not single LED) -SingleLedPattern *PatternBuilder::makeSingle(PatternID id, const PatternArgs *args) +// generate a single LED pattern (NULL if patternid is not single LED) +SingleLedPattern *PatternBuilder_makeSingle(PatternID id, const PatternArgs *args) { if (!isSingleLedPatternID(id)) { - return nullptr; + return NULL; } - Pattern *pat = makeInternal(id, args); + Pattern *pat = PatternBuilder_makeInternal(id, args); if (!pat) { - return nullptr; + return NULL; } // don't set any flags on single pattersn return (SingleLedPattern *)pat; } -// generate a multi LED pattern (nullptr if patternid is not multi LED) -MultiLedPattern *PatternBuilder::makeMulti(PatternID id, const PatternArgs *args) +// generate a multi LED pattern (NULL if patternid is not multi LED) +MultiLedPattern *PatternBuilder_makeMulti(PatternID id, const PatternArgs *args) { if (!isMultiLedPatternID(id)) { - return nullptr; + return NULL; } - Pattern *pat = makeInternal(id, args); + Pattern *pat = PatternBuilder_makeInternal(id, args); if (!pat) { - return nullptr; + return NULL; } return (MultiLedPattern *)pat; } -Pattern *PatternBuilder::unserialize(ByteStream &buffer) +Pattern *PatternBuilder_unserialize(ByteStream *buffer) { PatternID id = PATTERN_NONE; - if (!buffer.unserialize8((uint8_t *)&id)) { - return nullptr; + if (!ByteStream_unserialize8(buffer, (uint8_t *)&id)) { + return NULL; } - Pattern *pat = make(id); + Pattern *pat = PatternBuilder_make(id, NULL); if (!pat) { - return nullptr; + return NULL; } - if (!pat->unserialize(buffer)) { - delete pat; - return nullptr; + if (!Pattern_unserialize(pat, buffer)) { + Pattern_destroy(pat); + PatternBuilder_freePattern(pat); + return NULL; } return pat; } -Pattern *PatternBuilder::makeInternal(PatternID id, const PatternArgs *args) +Pattern *PatternBuilder_makeInternal(PatternID id, const PatternArgs *args) { if (id == PATTERN_NONE) { // no error - return nullptr; + return NULL; } - Pattern *pat = generate(id, args); + Pattern *pat = PatternBuilder_generate(id, args); if (!pat) { ERROR_OUT_OF_MEMORY(); - return nullptr; + return NULL; } - // set private pattern ID via friend class relationship - pat->m_patternID = id; + // set the pattern ID on the instance + Pattern_setPatternID(pat, id); return pat; } @@ -132,95 +194,101 @@ Pattern *PatternBuilder::makeInternal(PatternID id, const PatternArgs *args) // it will *not* indicate the true amount of arguments a pattern has // Some pattern ids are purely defined by their unique arguments // to a class, so calling them 'default' might be misleading -PatternArgs PatternBuilder::getDefaultArgs(PatternID id) +void PatternBuilder_getDefaultArgs(PatternID id, PatternArgs *out) { switch (id) { // ===================== // Single Led Patterns: - case PATTERN_STROBE: return PatternArgs(STROBE_ON_DURATION, STROBE_OFF_DURATION); - case PATTERN_HYPERSTROBE: return PatternArgs(HYPERSTROBE_ON_DURATION, HYPERSTROBE_OFF_DURATION); - case PATTERN_PICOSTROBE: return PatternArgs(PICOSTROBE_ON_DURATION, PICOSTROBE_OFF_DURATION); - case PATTERN_STROBIE: return PatternArgs(STROBIE_ON_DURATION, STROBIE_OFF_DURATION); - case PATTERN_DOPS: return PatternArgs(DOPS_ON_DURATION, DOPS_OFF_DURATION); - case PATTERN_ULTRADOPS: return PatternArgs(ULTRADOPS_ON_DURATION, ULTRADOPS_OFF_DURATION); - case PATTERN_STROBEGAP: return PatternArgs(STROBE_ON_DURATION, STROBE_OFF_DURATION, 25); - case PATTERN_HYPERGAP: return PatternArgs(HYPERSTROBE_ON_DURATION, HYPERSTROBE_OFF_DURATION, 218); - case PATTERN_PICOGAP: return PatternArgs(PICOSTROBE_ON_DURATION, PICOSTROBE_OFF_DURATION, 90); - case PATTERN_STROBIEGAP: return PatternArgs(STROBIE_ON_DURATION, STROBIE_OFF_DURATION, 100); - case PATTERN_DOPSGAP: return PatternArgs(DOPS_ON_DURATION, DOPS_OFF_DURATION, 40); - case PATTERN_ULTRAGAP: return PatternArgs(ULTRADOPS_ON_DURATION, ULTRADOPS_OFF_DURATION, 55); - case PATTERN_BLINKIE: return PatternArgs(3, 1, 65); - case PATTERN_GHOSTCRUSH: return PatternArgs(3, 1, 18); - case PATTERN_DOUBLEDOPS: return PatternArgs(ULTRADOPS_ON_DURATION, ULTRADOPS_OFF_DURATION, DOPS_OFF_DURATION, 0, 2); - case PATTERN_CHOPPER: return PatternArgs(ULTRADOPS_ON_DURATION, STROBIE_OFF_DURATION, 0, STROBE_ON_DURATION); - case PATTERN_DASHGAP: return PatternArgs(ULTRADOPS_ON_DURATION, ULTRADOPS_OFF_DURATION, 20, 20); - case PATTERN_DASHDOPS: return PatternArgs(DOPS_ON_DURATION, DOPS_OFF_DURATION, DOPS_OFF_DURATION, 18); - case PATTERN_DASHCRUSH: return PatternArgs(4, 1, 10, 18); - case PATTERN_ULTRADASH: return PatternArgs(ULTRADOPS_ON_DURATION, ULTRADOPS_OFF_DURATION, ULTRADOPS_OFF_DURATION, 14); - case PATTERN_GAPCYCLE: return PatternArgs(STROBIE_ON_DURATION, STROBE_OFF_DURATION, 12, 25, 2); - case PATTERN_DASHCYCLE: return PatternArgs(ULTRADOPS_ON_DURATION, ULTRADOPS_OFF_DURATION, ULTRADOPS_OFF_DURATION, 30, 2); - case PATTERN_TRACER: return PatternArgs(3, 0, 0, 20, 1); - case PATTERN_RIBBON: return PatternArgs(RIBBON_DURATION); - case PATTERN_MINIRIBBON: return PatternArgs(1); - case PATTERN_BLEND: return PatternArgs(BLEND_ON_DURATION, BLEND_OFF_DURATION, 0, 0, 0, 5, 0); - case PATTERN_BLENDSTROBE: return PatternArgs(STROBE_ON_DURATION, STROBE_OFF_DURATION, 0, 0, 0, 10, 0); - case PATTERN_BLENDSTROBEGAP: return PatternArgs(STROBE_ON_DURATION, STROBE_OFF_DURATION, 25, 0, 0, 10, 0); - case PATTERN_COMPLEMENTARY_BLEND: return PatternArgs(BLEND_ON_DURATION, BLEND_OFF_DURATION, 0, 0, 0, 5, 1); - case PATTERN_COMPLEMENTARY_BLENDSTROBE: return PatternArgs(STROBE_ON_DURATION, STROBE_OFF_DURATION, 0, 0, 0, 10, 1); - case PATTERN_COMPLEMENTARY_BLENDSTROBEGAP: return PatternArgs(STROBE_ON_DURATION, STROBE_OFF_DURATION, 25, 0, 0, 10, 1); - case PATTERN_SOLID: return PatternArgs(250); + case PATTERN_STROBE: PatternArgs_make(out, STROBE_ON_DURATION, STROBE_OFF_DURATION); return; + case PATTERN_HYPERSTROBE: PatternArgs_make(out, HYPERSTROBE_ON_DURATION, HYPERSTROBE_OFF_DURATION); return; + case PATTERN_PICOSTROBE: PatternArgs_make(out, PICOSTROBE_ON_DURATION, PICOSTROBE_OFF_DURATION); return; + case PATTERN_STROBIE: PatternArgs_make(out, STROBIE_ON_DURATION, STROBIE_OFF_DURATION); return; + case PATTERN_DOPS: PatternArgs_make(out, DOPS_ON_DURATION, DOPS_OFF_DURATION); return; + case PATTERN_ULTRADOPS: PatternArgs_make(out, ULTRADOPS_ON_DURATION, ULTRADOPS_OFF_DURATION); return; + case PATTERN_STROBEGAP: PatternArgs_make(out, STROBE_ON_DURATION, STROBE_OFF_DURATION, 25); return; + case PATTERN_HYPERGAP: PatternArgs_make(out, HYPERSTROBE_ON_DURATION, HYPERSTROBE_OFF_DURATION, 218); return; + case PATTERN_PICOGAP: PatternArgs_make(out, PICOSTROBE_ON_DURATION, PICOSTROBE_OFF_DURATION, 90); return; + case PATTERN_STROBIEGAP: PatternArgs_make(out, STROBIE_ON_DURATION, STROBIE_OFF_DURATION, 100); return; + case PATTERN_DOPSGAP: PatternArgs_make(out, DOPS_ON_DURATION, DOPS_OFF_DURATION, 40); return; + case PATTERN_ULTRAGAP: PatternArgs_make(out, ULTRADOPS_ON_DURATION, ULTRADOPS_OFF_DURATION, 55); return; + case PATTERN_BLINKIE: PatternArgs_make(out, 3, 1, 65); return; + case PATTERN_GHOSTCRUSH: PatternArgs_make(out, 3, 1, 18); return; + case PATTERN_DOUBLEDOPS: PatternArgs_make(out, ULTRADOPS_ON_DURATION, ULTRADOPS_OFF_DURATION, DOPS_OFF_DURATION, 0, 2); return; + case PATTERN_CHOPPER: PatternArgs_make(out, ULTRADOPS_ON_DURATION, STROBIE_OFF_DURATION, 0, STROBE_ON_DURATION); return; + case PATTERN_DASHGAP: PatternArgs_make(out, ULTRADOPS_ON_DURATION, ULTRADOPS_OFF_DURATION, 20, 20); return; + case PATTERN_DASHDOPS: PatternArgs_make(out, DOPS_ON_DURATION, DOPS_OFF_DURATION, DOPS_OFF_DURATION, 18); return; + case PATTERN_DASHCRUSH: PatternArgs_make(out, 4, 1, 10, 18); return; + case PATTERN_ULTRADASH: PatternArgs_make(out, ULTRADOPS_ON_DURATION, ULTRADOPS_OFF_DURATION, ULTRADOPS_OFF_DURATION, 14); return; + case PATTERN_GAPCYCLE: PatternArgs_make(out, STROBIE_ON_DURATION, STROBE_OFF_DURATION, 12, 25, 2); return; + case PATTERN_DASHCYCLE: PatternArgs_make(out, ULTRADOPS_ON_DURATION, ULTRADOPS_OFF_DURATION, ULTRADOPS_OFF_DURATION, 30, 2); return; + case PATTERN_TRACER: PatternArgs_make(out, 3, 0, 0, 20, 1); return; + case PATTERN_RIBBON: PatternArgs_make(out, RIBBON_DURATION); return; + case PATTERN_MINIRIBBON: PatternArgs_make(out, 1); return; + case PATTERN_BLEND: PatternArgs_make(out, BLEND_ON_DURATION, BLEND_OFF_DURATION, 0, 0, 0, 5, 0); return; + case PATTERN_BLENDSTROBE: PatternArgs_make(out, STROBE_ON_DURATION, STROBE_OFF_DURATION, 0, 0, 0, 10, 0); return; + case PATTERN_BLENDSTROBEGAP: PatternArgs_make(out, STROBE_ON_DURATION, STROBE_OFF_DURATION, 25, 0, 0, 10, 0); return; + case PATTERN_COMPLEMENTARY_BLEND: PatternArgs_make(out, BLEND_ON_DURATION, BLEND_OFF_DURATION, 0, 0, 0, 5, 1); return; + case PATTERN_COMPLEMENTARY_BLENDSTROBE: PatternArgs_make(out, STROBE_ON_DURATION, STROBE_OFF_DURATION, 0, 0, 0, 10, 1); return; + case PATTERN_COMPLEMENTARY_BLENDSTROBEGAP: PatternArgs_make(out, STROBE_ON_DURATION, STROBE_OFF_DURATION, 25, 0, 0, 10, 1); return; + case PATTERN_SOLID: PatternArgs_make(out, 250); return; // ===================== // Multi Led Patterns: #if VORTEX_SLIM == 0 - case PATTERN_HUE_SCROLL: return PatternArgs(1, 1, 10); - case PATTERN_THEATER_CHASE: return PatternArgs(DOPS_ON_DURATION, DOPS_OFF_DURATION, 28); - case PATTERN_CHASER: return PatternArgs(); - case PATTERN_ZIGZAG: return PatternArgs(DOPS_ON_DURATION, 3, 3, 5, 55); - case PATTERN_ZIPFADE: return PatternArgs(DOPS_ON_DURATION, 2, 75, 9, 230); - case PATTERN_DRIP: return PatternArgs(STROBE_ON_DURATION, STROBE_OFF_DURATION, 150); - case PATTERN_DRIPMORPH: return PatternArgs(STROBE_ON_DURATION, STROBE_OFF_DURATION, 1); - case PATTERN_CROSSDOPS: return PatternArgs(DOPS_ON_DURATION, 2, 25); - case PATTERN_DOUBLESTROBE: return PatternArgs(DOPS_ON_DURATION, DOPS_OFF_DURATION, 115); - case PATTERN_METEOR: return PatternArgs(1, 1, 20, 130); - case PATTERN_SPARKLETRACE: return PatternArgs(1, 5, 3); - case PATTERN_VORTEXWIPE: return PatternArgs(DOPS_ON_DURATION, 3, 80); - case PATTERN_WARP: return PatternArgs(3, DOPS_OFF_DURATION, 50); - case PATTERN_WARPWORM: return PatternArgs(DOPS_ON_DURATION, 10, 100); - case PATTERN_SNOWBALL: return PatternArgs(3, 3, 33); - case PATTERN_LIGHTHOUSE: return PatternArgs(DOPS_ON_DURATION, 5, 22, 3, 3); - case PATTERN_PULSISH: return PatternArgs(DOPS_ON_DURATION, 6, 5, 1, 100); - case PATTERN_FILL: return PatternArgs(DOPS_ON_DURATION, 5, 50); - case PATTERN_BOUNCE: return PatternArgs(10, 5, 50, 10); - case PATTERN_SPLITSTROBIE: return PatternArgs(DOPS_ON_DURATION, DOPS_OFF_DURATION, 0, 16, 3, 10, PATTERN_DOPS, PATTERN_STROBIE); - case PATTERN_BACKSTROBE: return PatternArgs(DOPS_ON_DURATION, DOPS_OFF_DURATION, 0, HYPERSTROBE_ON_DURATION, HYPERSTROBE_OFF_DURATION, 10, PATTERN_DOPS, PATTERN_HYPERSTROBE); - case PATTERN_VORTEX: return PatternArgs(1, 1, 130); + case PATTERN_HUE_SCROLL: PatternArgs_make(out, 1, 1, 10); return; + case PATTERN_THEATER_CHASE: PatternArgs_make(out, DOPS_ON_DURATION, DOPS_OFF_DURATION, 28); return; + case PATTERN_CHASER: PatternArgs_make0(out); return; + case PATTERN_ZIGZAG: PatternArgs_make(out, DOPS_ON_DURATION, 3, 3, 5, 55); return; + case PATTERN_ZIPFADE: PatternArgs_make(out, DOPS_ON_DURATION, 2, 75, 9, 230); return; + case PATTERN_DRIP: PatternArgs_make(out, STROBE_ON_DURATION, STROBE_OFF_DURATION, 150); return; + case PATTERN_DRIPMORPH: PatternArgs_make(out, STROBE_ON_DURATION, STROBE_OFF_DURATION, 1); return; + case PATTERN_CROSSDOPS: PatternArgs_make(out, DOPS_ON_DURATION, 2, 25); return; + case PATTERN_DOUBLESTROBE: PatternArgs_make(out, DOPS_ON_DURATION, DOPS_OFF_DURATION, 115); return; + case PATTERN_METEOR: PatternArgs_make(out, 1, 1, 20, 130); return; + case PATTERN_SPARKLETRACE: PatternArgs_make(out, 1, 5, 3); return; + case PATTERN_VORTEXWIPE: PatternArgs_make(out, DOPS_ON_DURATION, 3, 80); return; + case PATTERN_WARP: PatternArgs_make(out, 3, DOPS_OFF_DURATION, 50); return; + case PATTERN_WARPWORM: PatternArgs_make(out, DOPS_ON_DURATION, 10, 100); return; + case PATTERN_SNOWBALL: PatternArgs_make(out, 3, 3, 33); return; + case PATTERN_LIGHTHOUSE: PatternArgs_make(out, DOPS_ON_DURATION, 5, 22, 3, 3); return; + case PATTERN_PULSISH: PatternArgs_make(out, DOPS_ON_DURATION, 6, 5, 1, 100); return; + case PATTERN_FILL: PatternArgs_make(out, DOPS_ON_DURATION, 5, 50); return; + case PATTERN_BOUNCE: PatternArgs_make(out, 10, 5, 50, 10); return; + case PATTERN_SPLITSTROBIE: PatternArgs_make(out, DOPS_ON_DURATION, DOPS_OFF_DURATION, 0, 16, 3, 10, PATTERN_DOPS, PATTERN_STROBIE); return; + case PATTERN_BACKSTROBE: PatternArgs_make(out, DOPS_ON_DURATION, DOPS_OFF_DURATION, 0, HYPERSTROBE_ON_DURATION, HYPERSTROBE_OFF_DURATION, 10, PATTERN_DOPS, PATTERN_HYPERSTROBE); return; + case PATTERN_VORTEX: PatternArgs_make(out, 1, 1, 130); return; case PATTERN_NONE: break; default: break; #else // in vortex slim just use DOPS for all mult-led case PATTERN_NONE: break; - default: return PatternArgs(DOPS_ON_DURATION, DOPS_OFF_DURATION); + default: PatternArgs_make(out, DOPS_ON_DURATION, DOPS_OFF_DURATION); return; #endif } - return PatternArgs(); + PatternArgs_make0(out); } -uint8_t PatternBuilder::numDefaultArgs(PatternID id) +uint8_t PatternBuilder_numDefaultArgs(PatternID id) { - Pattern *pat = make(id); + Pattern *pat = PatternBuilder_make(id, NULL); if (!pat) { return 0; } - uint8_t numArgs = pat->getNumArgs(); - delete pat; + uint8_t numArgs = Pattern_getNumArgs(pat); + Pattern_destroy(pat); + PatternBuilder_freePattern(pat); return numArgs; } // This is responsible for instantiating the basic core patterns -Pattern *PatternBuilder::generate(PatternID id, const PatternArgs *userArgs) +Pattern *PatternBuilder_generate(PatternID id, const PatternArgs *userArgs) { - PatternArgs args = userArgs ? *userArgs : getDefaultArgs(id); + PatternArgs args; + if (userArgs) { + args = *userArgs; + } else { + PatternBuilder_getDefaultArgs(id, &args); + } switch (id) { // ===================== // Single Led Patterns: @@ -249,46 +317,48 @@ Pattern *PatternBuilder::generate(PatternID id, const PatternArgs *userArgs) case PATTERN_DASHCYCLE: case PATTERN_TRACER: case PATTERN_RIBBON: - case PATTERN_MINIRIBBON: return new BasicPattern(args); + // blink, dash, gap and blend patterns are all served by the single + // merged BlinkPattern, the id decides which behaviour and which args + case PATTERN_MINIRIBBON: case PATTERN_BLEND: case PATTERN_BLENDSTROBE: case PATTERN_BLENDSTROBEGAP: case PATTERN_COMPLEMENTARY_BLEND: case PATTERN_COMPLEMENTARY_BLENDSTROBE: - case PATTERN_COMPLEMENTARY_BLENDSTROBEGAP: return new BlendPattern(args); - case PATTERN_SOLID: return new SolidPattern(args); + case PATTERN_COMPLEMENTARY_BLENDSTROBEGAP: return (Pattern *)BlinkPattern_make(id, &args); + case PATTERN_SOLID: return (Pattern *)SolidPattern_make(id, &args); // ===================== // Multi Led Patterns: #if VORTEX_SLIM == 0 - case PATTERN_HUE_SCROLL: return new HueShiftPattern(args); - case PATTERN_THEATER_CHASE: return new TheaterChasePattern(args); - case PATTERN_CHASER: return new ChaserPattern(args); + case PATTERN_HUE_SCROLL: MAKE_MULTI_PATTERN(HueShiftPattern); + case PATTERN_THEATER_CHASE: MAKE_MULTI_PATTERN(TheaterChasePattern); + case PATTERN_CHASER: MAKE_MULTI_PATTERN(ChaserPattern); case PATTERN_ZIGZAG: - case PATTERN_ZIPFADE: return new ZigzagPattern(args); - case PATTERN_DRIP: return new DripPattern(args); - case PATTERN_DRIPMORPH: return new DripMorphPattern(args); - case PATTERN_CROSSDOPS: return new CrossDopsPattern(args); - case PATTERN_DOUBLESTROBE: return new DoubleStrobePattern(args); - case PATTERN_METEOR: return new MeteorPattern(args); - case PATTERN_SPARKLETRACE: return new SparkleTracePattern(args); - case PATTERN_VORTEXWIPE: return new VortexWipePattern(args); - case PATTERN_WARP: return new WarpPattern(args); - case PATTERN_WARPWORM: return new WarpWormPattern(args); - case PATTERN_SNOWBALL: return new SnowballPattern(args); - case PATTERN_LIGHTHOUSE: return new LighthousePattern(args); - case PATTERN_PULSISH: return new PulsishPattern(args); - case PATTERN_FILL: return new FillPattern(args); - case PATTERN_BOUNCE: return new BouncePattern(args); + case PATTERN_ZIPFADE: MAKE_MULTI_PATTERN(ZigzagPattern); + case PATTERN_DRIP: MAKE_MULTI_PATTERN(DripPattern); + case PATTERN_DRIPMORPH: MAKE_MULTI_PATTERN(DripMorphPattern); + case PATTERN_CROSSDOPS: MAKE_MULTI_PATTERN(CrossDopsPattern); + case PATTERN_DOUBLESTROBE: MAKE_MULTI_PATTERN(DoubleStrobePattern); + case PATTERN_METEOR: MAKE_MULTI_PATTERN(MeteorPattern); + case PATTERN_SPARKLETRACE: MAKE_MULTI_PATTERN(SparkleTracePattern); + case PATTERN_VORTEXWIPE: MAKE_MULTI_PATTERN(VortexWipePattern); + case PATTERN_WARP: MAKE_MULTI_PATTERN(WarpPattern); + case PATTERN_WARPWORM: MAKE_MULTI_PATTERN(WarpWormPattern); + case PATTERN_SNOWBALL: MAKE_MULTI_PATTERN(SnowballPattern); + case PATTERN_LIGHTHOUSE: MAKE_MULTI_PATTERN(LighthousePattern); + case PATTERN_PULSISH: MAKE_MULTI_PATTERN(PulsishPattern); + case PATTERN_FILL: MAKE_MULTI_PATTERN(FillPattern); + case PATTERN_BOUNCE: MAKE_MULTI_PATTERN(BouncePattern); case PATTERN_SPLITSTROBIE: - case PATTERN_BACKSTROBE: return new BackStrobePattern(args); - case PATTERN_VORTEX: return new VortexPattern(args); - case PATTERN_NONE: return nullptr; + case PATTERN_BACKSTROBE: MAKE_MULTI_PATTERN(BackStrobePattern); + case PATTERN_VORTEX: MAKE_MULTI_PATTERN(VortexPattern); + case PATTERN_NONE: return NULL; #else // in vortex slim just use basic pattern for all multi led - case PATTERN_NONE: return nullptr; + case PATTERN_NONE: return NULL; #endif } DEBUG_LOGF("Unknown pattern id: %u", id); - return nullptr; + return NULL; } diff --git a/VortexEngine/src/Patterns/PatternBuilder.h b/VortexEngine/src/Patterns/PatternBuilder.h index 7467f58888..18accf336f 100644 --- a/VortexEngine/src/Patterns/PatternBuilder.h +++ b/VortexEngine/src/Patterns/PatternBuilder.h @@ -2,41 +2,43 @@ #define PATTERN_BUILDER_H #include "Patterns.h" +#include "Pattern.h" +#include "PatternArgs.h" +#include "Multi/MultiLedPattern.h" +#include "Single/SingleLedPattern.h" -class Pattern; -class PatternArgs; -class ByteStream; -class MultiLedPattern; -class SingleLedPattern; - -class PatternBuilder -{ -public: - // generic make any pattern - static Pattern *make(PatternID id, const PatternArgs *args = nullptr); - - // make a copy of an existing pattern - static Pattern *dupe(const Pattern *pat); - - // generate a single LED pattern (nullptr if patternid is not single LED) - static SingleLedPattern *makeSingle(PatternID id, const PatternArgs *args = nullptr); - - // generate a multi LED pattern (nullptr if patternid is not multi LED) - static MultiLedPattern *makeMulti(PatternID id, const PatternArgs *args = nullptr); - - // unserialize a buffer into a pattern - static Pattern *unserialize(ByteStream &buffer); - - // This is just the default arguments for any given pattern id - // it will *not* indicate the true amount of arguments a pattern has - static PatternArgs getDefaultArgs(PatternID id); - // this will give you actual amount of default args - static uint8_t numDefaultArgs(PatternID id); - -private: - // helper routines - static Pattern *makeInternal(PatternID id, const PatternArgs *args = nullptr); - static Pattern *generate(PatternID id, const PatternArgs *args = nullptr); -}; +typedef struct ByteStream ByteStream; + +// allocate the pattern pool, all patterns are allocated from this pool +bool PatternBuilder_init(void); +// grab a block from the pattern pool to construct a pattern in +void *PatternBuilder_allocPattern(void); +// return a pattern's block to the pool, call Pattern_destroy first +void PatternBuilder_freePattern(Pattern *pat); + +// generic make any pattern +Pattern *PatternBuilder_make(PatternID id, const PatternArgs *args); + +// make a copy of an existing pattern +Pattern *PatternBuilder_dupe(const Pattern *pat); + +// generate a single LED pattern (NULL if patternid is not single LED) +SingleLedPattern *PatternBuilder_makeSingle(PatternID id, const PatternArgs *args); + +// generate a multi LED pattern (NULL if patternid is not multi LED) +MultiLedPattern *PatternBuilder_makeMulti(PatternID id, const PatternArgs *args); + +// unserialize a buffer into a pattern +Pattern *PatternBuilder_unserialize(ByteStream *buffer); + +// This is just the default arguments for any given pattern id +// it will *not* indicate the true amount of arguments a pattern has +void PatternBuilder_getDefaultArgs(PatternID id, PatternArgs *out); +// this will give you actual amount of default args +uint8_t PatternBuilder_numDefaultArgs(PatternID id); + +// helper routines +Pattern *PatternBuilder_makeInternal(PatternID id, const PatternArgs *args); +Pattern *PatternBuilder_generate(PatternID id, const PatternArgs *args); #endif diff --git a/VortexEngine/src/Patterns/Patterns.h b/VortexEngine/src/Patterns/Patterns.h index 65f3b90bb7..4879e4870c 100644 --- a/VortexEngine/src/Patterns/Patterns.h +++ b/VortexEngine/src/Patterns/Patterns.h @@ -1,6 +1,7 @@ #ifndef PATTERNS_H #define PATTERNS_H +#include #include // List of patterns that can be built, both single and multi-led patterns are found in this list. @@ -8,11 +9,13 @@ // with a class, and there are 'shell' patterns which are simply wrapperns around another pattern // with different parameters passed to the constructor. There is no way to know which patterns // are 'core' patterns, except by looking at PatternBuilder::generate to see which classes exist -enum PatternID : int8_t +typedef int8_t PatternID; + +enum { // no pattern at all, use this sparingly and default to // PATTERN_FIRST when possible - PATTERN_NONE = (PatternID)-1, + PATTERN_NONE = -1, // first pattern of all PATTERN_FIRST = 0, @@ -109,25 +112,4 @@ inline bool isSingleLedPatternID(PatternID id) { return id < PATTERN_MULTI_FIRST; } -// PatternID operators -inline PatternID &operator++(PatternID &c) -{ - c = PatternID(((uint16_t)c) + 1); - return c; -} -inline PatternID operator++(PatternID &c, int) -{ - PatternID temp = c; - ++c; - return temp; -} -inline PatternID operator+(PatternID &c, int b) -{ - return (PatternID)((uint16_t)c + b); -} -inline PatternID operator-(PatternID &c, int b) -{ - return (PatternID)((uint16_t)c - b); -} - #endif diff --git a/VortexEngine/src/Patterns/Single/BasicPattern.cpp b/VortexEngine/src/Patterns/Single/BasicPattern.cpp index 5a8f67dae5..c055341261 100644 --- a/VortexEngine/src/Patterns/Single/BasicPattern.cpp +++ b/VortexEngine/src/Patterns/Single/BasicPattern.cpp @@ -3,6 +3,8 @@ #include "../../Time/TimeControl.h" #include "../../Colors/Colorset.h" #include "../../Leds/Leds.h" +#include "../../Memory/Memory.h" +#include "../PatternBuilder.h" // uncomment me to print debug labels on the pattern states, this is useful if you // are debugging a pattern strip from the command line and want to see what state @@ -12,12 +14,12 @@ #ifdef DEBUG_BASIC_PATTERN #include // print out the current state of the pattern -#define PRINT_STATE(state) printState(state) -static void printState(PatternState state) +#define PRINT_STATE(state) printState(self, state) +static void printState(const BlinkPattern *self, BlinkPatternState state) { static uint64_t lastPrint = 0; - if (lastPrint == Time::getCurtime()) return; - switch (m_state) { + if (lastPrint == Time_getCurtime()) return; + switch (state) { case STATE_ON: printf("on "); break; case STATE_OFF: printf("off "); break; case STATE_IN_GAP: printf("gap1"); break; @@ -25,167 +27,315 @@ static void printState(PatternState state) case STATE_IN_GAP2: printf("gap2"); break; default: return; } - lastPrint = Time::getCurtime(); + lastPrint = Time_getCurtime(); } #else #define PRINT_STATE(state) // do nothing #endif -BasicPattern::BasicPattern(const PatternArgs &args) : - SingleLedPattern(args), - m_onDuration(0), - m_offDuration(0), - m_gapDuration(0), - m_dashDuration(0), - m_groupSize(0), - m_groupCounter(0), - m_state(STATE_BLINK_ON), - m_blinkTimer() +// forward declarations so the vtable can reference every hook below +static void BlinkPattern_onBlinkOnBasic(BlinkPattern *self); +static void BlinkPattern_onBlinkOnBlend(BlinkPattern *self); +void BlinkPattern_onBlinkOn(BlinkPattern *self); +void BlinkPattern_onBlinkOff(BlinkPattern *self); +void BlinkPattern_beginGap(BlinkPattern *self); +void BlinkPattern_beginDash(BlinkPattern *self); +void BlinkPattern_destroy(Pattern *self); +void BlinkPattern_bind(Pattern *self, LedPos pos); +void BlinkPattern_doInit(Pattern *self); +void BlinkPattern_play(Pattern *self); + +// the pattern ids that use the colour blending behaviour +bool BlinkPattern_isBlendID(PatternID id) +{ + switch (id) { + case PATTERN_BLEND: + case PATTERN_BLENDSTROBE: + case PATTERN_BLENDSTROBEGAP: + case PATTERN_COMPLEMENTARY_BLEND: + case PATTERN_COMPLEMENTARY_BLENDSTROBE: + case PATTERN_COMPLEMENTARY_BLENDSTROBEGAP: + return true; + default: + return false; + } +} + +void BlinkPattern_init(BlinkPattern *self, PatternID id, const PatternArgs *args) +{ + BlinkPattern_initWithVtable(self, id, args, &BlinkPattern_bvtable); +} + +void BlinkPattern_initWithVtable(BlinkPattern *self, PatternID id, const PatternArgs *args, const BlinkPatternVtable *vtable) +{ + Pattern_initBase(&self->base, id, &vtable->base); + self->bvtable = vtable; + + self->m_onDuration = 0; + self->m_offDuration = 0; + self->m_gapDuration = 0; + self->m_dashDuration = 0; + self->m_groupSize = 0; + self->m_blendSpeed = 0; + self->m_numFlips = 0; + self->m_groupCounter = 0; + self->m_state = STATE_BLINK_ON; + self->m_isBlend = BlinkPattern_isBlendID(id); + Timer_make(&self->m_blinkTimer); + self->m_cur = RGBColor_make(); + self->m_next = RGBColor_make(); + self->m_flip = 0; + + REGISTER_ARG((Pattern *)self, BlinkPattern, m_onDuration); + REGISTER_ARG((Pattern *)self, BlinkPattern, m_offDuration); + REGISTER_ARG((Pattern *)self, BlinkPattern, m_gapDuration); + REGISTER_ARG((Pattern *)self, BlinkPattern, m_dashDuration); + REGISTER_ARG((Pattern *)self, BlinkPattern, m_groupSize); + // the blend args only exist for the blend ids, which keeps the argument + // count (and therefore the serialized form and the editor layout) the + // same as when blend was a separate subclass + if (self->m_isBlend) { + REGISTER_ARG((Pattern *)self, BlinkPattern, m_blendSpeed); + REGISTER_ARG((Pattern *)self, BlinkPattern, m_numFlips); + } + + Pattern_setArgs(&self->base, args); +} + +BlinkPattern *BlinkPattern_make(PatternID id, const PatternArgs *args) +{ + BlinkPattern *self = (BlinkPattern *)PatternBuilder_allocPattern(); + if (!self) { + return NULL; + } + BlinkPattern_init(self, id, args); + return self; +} + +void BlinkPattern_destroy(Pattern *self) { - m_patternID = PATTERN_STROBE; - REGISTER_ARG(m_onDuration); - REGISTER_ARG(m_offDuration); - REGISTER_ARG(m_gapDuration); - REGISTER_ARG(m_dashDuration); - REGISTER_ARG(m_groupSize); - setArgs(args); + Timer_destroy(&((BlinkPattern *)self)->m_blinkTimer); } -BasicPattern::~BasicPattern() +void BlinkPattern_bind(Pattern *self, LedPos pos) { + Pattern_baseBind(self, pos); } -void BasicPattern::init() +void BlinkPattern_doInit(Pattern *pat) { + BlinkPattern *self = (BlinkPattern *)pat; + // run base pattern init logic - Pattern::init(); + Pattern_baseInit(pat); // the default state to begin with - m_state = STATE_BLINK_ON; + self->m_state = STATE_BLINK_ON; // if a dash is present then always start with the dash because // it consumes the first color in the colorset - if (m_dashDuration > 0) { - m_state = STATE_BEGIN_DASH; + if (self->m_dashDuration > 0) { + self->m_state = STATE_BEGIN_DASH; } // if there's no on duration or dash duration the led is just disabled - if ((!m_onDuration && !m_dashDuration) || !m_colorset.numColors()) { - m_state = STATE_DISABLED; + if ((!self->m_onDuration && !self->m_dashDuration) || !Colorset_numColors(&self->base.m_colorset)) { + self->m_state = STATE_DISABLED; + } + self->m_groupCounter = self->m_groupSize ? self->m_groupSize : (Colorset_numColors(&self->base.m_colorset) - (self->m_dashDuration != 0)); + + // blend patterns additionally seed the current/target colors and reset + // the flip counter, this is what BlendPattern::init used to do + if (self->m_isBlend) { + self->m_cur = Colorset_getNext(&self->base.m_colorset); + self->m_next = Colorset_getNext(&self->base.m_colorset); + self->m_flip = 0; + } +} + +static void BlinkPattern_nextState(BlinkPattern *self, uint8_t timing) +{ + Timer_initN(&self->m_blinkTimer, TIMER_1_ALARM | TIMER_START, timing); + self->m_state = (BlinkPatternState)(self->m_state + 1); +} + +// the merged hook: blend ids blend, every other id does the plain blink. +// keeping both behaviours behind one vtable slot is what lets the former +// BlendPattern subclass disappear without changing any output. +void BlinkPattern_onBlinkOn(BlinkPattern *self) +{ + if (self->m_isBlend) { + BlinkPattern_onBlinkOnBlend(self); + } else { + BlinkPattern_onBlinkOnBasic(self); + } +} + +void BlinkPattern_onBlinkOff(BlinkPattern *self) +{ + PRINT_STATE(STATE_OFF); + Leds_clearIndex(self->base.m_ledPos); +} + +void BlinkPattern_beginGap(BlinkPattern *self) +{ + PRINT_STATE(STATE_IN_GAP); + Leds_clearIndex(self->base.m_ledPos); +} + +void BlinkPattern_beginDash(BlinkPattern *self) +{ + PRINT_STATE(STATE_IN_DASH); + Leds_setIndex(self->base.m_ledPos, Colorset_getNext(&self->base.m_colorset)); +} + +// step a single channel towards the target by at most m_blendSpeed +static void BlinkPattern_interpolate(const BlinkPattern *self, uint8_t *current, uint8_t next) +{ + if (*current < next) { + uint8_t step = (next - *current) > self->m_blendSpeed ? self->m_blendSpeed : (next - *current); + *current += step; + } else if (*current > next) { + uint8_t step = (*current - next) > self->m_blendSpeed ? self->m_blendSpeed : (*current - next); + *current -= step; } - m_groupCounter = m_groupSize ? m_groupSize : (m_colorset.numColors() - (m_dashDuration != 0)); } -void BasicPattern::nextState(uint8_t timing) +// the plain blink behaviour, this is what BasicPattern::onBlinkOn did +static void BlinkPattern_onBlinkOnBasic(BlinkPattern *self) { - m_blinkTimer.init(TIMER_1_ALARM | TIMER_START, timing); - m_state = (PatternState)(m_state + 1); + PRINT_STATE(STATE_ON); + Leds_setIndex(self->base.m_ledPos, Colorset_getNext(&self->base.m_colorset)); +} + +// the colour blending behaviour, this is what BlendPattern::onBlinkOn did +static void BlinkPattern_onBlinkOnBlend(BlinkPattern *self) +{ + if (RGBColor_equals(&self->m_cur, &self->m_next)) { + self->m_next = Colorset_getNext(&self->base.m_colorset); + } + BlinkPattern_interpolate(self, &self->m_cur.red, self->m_next.red); + BlinkPattern_interpolate(self, &self->m_cur.green, self->m_next.green); + BlinkPattern_interpolate(self, &self->m_cur.blue, self->m_next.blue); + RGBColor col = self->m_cur; + // it should be impossible for m_numFlips to be 0 and m_flip to be 1 + // unless the pattern arg is changed mid-play which could happen with + // and editor or something, so check both for 0 + if (self->m_flip && self->m_numFlips) { + // convert to hsv + HSVColor hsvCol = rgb_to_hsv_generic(&self->m_cur); + // shift the hue by a flip size + hsvCol.hue += (self->m_flip * (127 / self->m_numFlips)); + // convert the hsv color back to RGB + col = RGBColor_fromHSV(&hsvCol); + } + // set the color + Leds_setIndex(self->base.m_ledPos, col); + // increment the flip count + self->m_flip++; + // modulate the flip count DO NOT USE MODULO OPERATOR BECAUSE + // THE FLIP COUNT COULD BE 0 THAT WILL DIVIDE BY ZERO + if (self->m_flip > self->m_numFlips) { + self->m_flip = 0; + } } -void BasicPattern::play() +// the single vtable shared by every blink and blend pattern +const BlinkPatternVtable BlinkPattern_bvtable = { + .base = { + .destroy = BlinkPattern_destroy, + .bind = BlinkPattern_bind, + .init = BlinkPattern_doInit, + .play = BlinkPattern_play + }, + .onBlinkOn = BlinkPattern_onBlinkOn, + .onBlinkOff = BlinkPattern_onBlinkOff, + .beginGap = BlinkPattern_beginGap, + .beginDash = BlinkPattern_beginDash +}; + +void BlinkPattern_play(Pattern *pat) { + BlinkPattern *self = (BlinkPattern *)pat; + // Sometimes the pattern needs to cycle multiple states in a single frame so // instead of using a loop or recursion I have just used a simple goto -replay: + replay: // its kinda evolving as i go - switch (m_state) { + switch (self->m_state) { case STATE_DISABLED: return; case STATE_BLINK_ON: - if (m_onDuration > 0) { - onBlinkOn(); - --m_groupCounter; - nextState(m_onDuration); + if (self->m_onDuration > 0) { + self->bvtable->onBlinkOn(self); + --self->m_groupCounter; + BlinkPattern_nextState(self, self->m_onDuration); return; } - m_state = STATE_BLINK_OFF; + self->m_state = STATE_BLINK_OFF; case STATE_BLINK_OFF: // the whole 'should blink off' situation is tricky because we might need // to go back to blinking on if our colorset isn't at the end yet - if (m_groupCounter > 0 || (!m_gapDuration && !m_dashDuration)) { - if (m_offDuration > 0) { - onBlinkOff(); - nextState(m_offDuration); + if (self->m_groupCounter > 0 || (!self->m_gapDuration && !self->m_dashDuration)) { + if (self->m_offDuration > 0) { + self->bvtable->onBlinkOff(self); + BlinkPattern_nextState(self, self->m_offDuration); return; } - if (m_groupCounter > 0 && m_onDuration > 0) { - m_state = STATE_BLINK_ON; + if (self->m_groupCounter > 0 && self->m_onDuration > 0) { + self->m_state = STATE_BLINK_ON; goto replay; } } - m_state = STATE_BEGIN_GAP; + self->m_state = STATE_BEGIN_GAP; case STATE_BEGIN_GAP: - m_groupCounter = m_groupSize ? m_groupSize : (m_colorset.numColors() - (m_dashDuration != 0)); - if (m_gapDuration > 0) { - beginGap(); - nextState(m_gapDuration); + self->m_groupCounter = self->m_groupSize ? self->m_groupSize : (Colorset_numColors(&self->base.m_colorset) - (self->m_dashDuration != 0)); + if (self->m_gapDuration > 0) { + self->bvtable->beginGap(self); + BlinkPattern_nextState(self, self->m_gapDuration); return; } - m_state = STATE_BEGIN_DASH; + self->m_state = STATE_BEGIN_DASH; case STATE_BEGIN_DASH: - if (m_dashDuration > 0) { - beginDash(); - nextState(m_dashDuration); + if (self->m_dashDuration > 0) { + self->bvtable->beginDash(self); + BlinkPattern_nextState(self, self->m_dashDuration); return; } - m_state = STATE_BEGIN_GAP2; + self->m_state = STATE_BEGIN_GAP2; case STATE_BEGIN_GAP2: - if (m_dashDuration > 0 && m_gapDuration > 0) { - beginGap(); - nextState(m_gapDuration); + if (self->m_dashDuration > 0 && self->m_gapDuration > 0) { + self->bvtable->beginGap(self); + BlinkPattern_nextState(self, self->m_gapDuration); return; } - m_state = STATE_BLINK_ON; + self->m_state = STATE_BLINK_ON; goto replay; default: break; } - if (m_blinkTimer.alarm() == -1) { + if (Timer_alarm(&self->m_blinkTimer) == ALARM_NONE) { // no alarm triggered just stay in current state, return and don't transition states - PRINT_STATE(m_state); + PRINT_STATE(self->m_state); return; } // this just transitions the state into the next state, with some edge conditions for // transitioning to different states under certain circumstances. Honestly this is // a nightmare to read now and idk how to fix it - if (m_state == STATE_IN_GAP2 || (m_state == STATE_OFF && m_groupCounter > 0)) { + if (self->m_state == STATE_IN_GAP2 || (self->m_state == STATE_OFF && self->m_groupCounter > 0)) { // this is an edge condition for when in the second gap or off in the non-last off blink // then the state actually needs to jump backwards rather than iterate - m_state = m_onDuration ? STATE_BLINK_ON : (m_dashDuration ? STATE_BEGIN_DASH : STATE_BEGIN_GAP); - } else if (m_state == STATE_OFF && (!m_groupCounter || m_colorset.numColors() == 1)) { + self->m_state = self->m_onDuration ? STATE_BLINK_ON : (self->m_dashDuration ? STATE_BEGIN_DASH : STATE_BEGIN_GAP); + } else if (self->m_state == STATE_OFF && (!self->m_groupCounter || Colorset_numColors(&self->base.m_colorset) == 1)) { // this is an edge condition when the state is off but this is the last off blink in the // group or there's literally only one color in the group then if there is more blinks // left in the group we need to cycle back to blink on instead of to the next state - m_state = (m_groupCounter > 0) ? STATE_BLINK_ON : STATE_BEGIN_GAP; + self->m_state = (self->m_groupCounter > 0) ? STATE_BLINK_ON : STATE_BEGIN_GAP; } else { // this is the standard case, iterate to the next state - m_state = (PatternState)(m_state + 1); + self->m_state = (BlinkPatternState)(self->m_state + 1); } // poor-mans recurse with the new state change (this transitions to a new state within the same tick) goto replay; } - -void BasicPattern::onBlinkOn() -{ - PRINT_STATE(STATE_ON); - Leds::setIndex(m_ledPos, m_colorset.getNext()); -} - -void BasicPattern::onBlinkOff() -{ - PRINT_STATE(STATE_OFF); - Leds::clearIndex(m_ledPos); -} - -void BasicPattern::beginGap() -{ - PRINT_STATE(STATE_IN_GAP); - Leds::clearIndex(m_ledPos); -} - -void BasicPattern::beginDash() -{ - PRINT_STATE(STATE_IN_DASH); - Leds::setIndex(m_ledPos, m_colorset.getNext()); -} diff --git a/VortexEngine/src/Patterns/Single/BasicPattern.h b/VortexEngine/src/Patterns/Single/BasicPattern.h index 39cc660bd4..e1e4b2f8d6 100644 --- a/VortexEngine/src/Patterns/Single/BasicPattern.h +++ b/VortexEngine/src/Patterns/Single/BasicPattern.h @@ -1,27 +1,71 @@ #ifndef BASIC_PATTERN_H #define BASIC_PATTERN_H -#include "SingleLedPattern.h" +#include +#include "../Pattern.h" #include "../../Time/Timer.h" -class BasicPattern : public SingleLedPattern -{ -public: - BasicPattern(const PatternArgs &args); - virtual ~BasicPattern(); +// the various different blinking states the pattern can be in +typedef uint8_t BlinkPatternState; +enum { + // the led is disabled (there is no on or dash) + STATE_DISABLED, - virtual void init() override; - virtual void play() override; + // the pattern is blinking on the next color in the set + STATE_BLINK_ON, + STATE_ON, -protected: - // when blinking off - virtual void onBlinkOn(); - virtual void onBlinkOff(); - virtual void beginGap(); - virtual void beginDash(); + // the pattern is blinking off + STATE_BLINK_OFF, + STATE_OFF, + + // the pattern is starting a gap after a colorset + STATE_BEGIN_GAP, + STATE_IN_GAP, + + // the pattern is beginning a dash after a colorset or gap + STATE_BEGIN_DASH, + STATE_IN_DASH, + + // the pattern is starting a gap after a dash + STATE_BEGIN_GAP2, + STATE_IN_GAP2, +}; - // iterate to next state - void nextState(uint8_t timing); +// forward declare so the vtable can name the type +typedef struct BlinkPattern BlinkPattern; + +// BlinkPatternVtable extends the base pattern vtable with the four blinking +// hooks. Blend and solid behaviour is selected by pointing these at a +// different implementation, which is how the former BlendPattern subclass +// and the SolidPattern subclass keep working without their own structs. +typedef struct BlinkPatternVtable { + // the leading PatternVtable, so a BlinkPatternVtable* can be used + // wherever a PatternVtable* is expected + PatternVtable base; + + // when blinking on + void (*onBlinkOn)(BlinkPattern *self); + // when blinking off + void (*onBlinkOff)(BlinkPattern *self); + // after a colorset + void (*beginGap)(BlinkPattern *self); + // after a colorset or gap + void (*beginDash)(BlinkPattern *self); +} BlinkPatternVtable; + +// BlinkPattern is the single implementation that covers every blink, dash, +// gap and blend pattern. The blink/dash behaviour and the colour blending +// behaviour live side by side in one struct; m_isBlend selects which of the +// two is active for a given pattern id. The blend arguments are only +// registered for blend pattern ids so that the argument count reported to +// the editor and to the savefile stays identical to before the merge. +struct BlinkPattern { + // must remain the first member, the base vtable dispatch relies on it + Pattern base; + + // the blinking hooks, points at a BlinkPatternVtable + const BlinkPatternVtable *bvtable; // the parameters of the pattern uint8_t m_onDuration; @@ -30,40 +74,60 @@ class BasicPattern : public SingleLedPattern uint8_t m_dashDuration; uint8_t m_groupSize; - uint8_t m_groupCounter; + // blend arguments, only registered for the blend pattern ids + uint8_t m_blendSpeed; + uint8_t m_numFlips; - // the various different blinking states the pattern can be in - enum PatternState : uint8_t - { - // the led is disabled (there is no on or dash) - STATE_DISABLED, - - // the pattern is blinking on the next color in the set - STATE_BLINK_ON, - STATE_ON, - - // the pattern is blinking off - STATE_BLINK_OFF, - STATE_OFF, - - // the pattern is starting a gap after a colorset - STATE_BEGIN_GAP, - STATE_IN_GAP, - - // the pattern is beginning a dash after a colorset or gap - STATE_BEGIN_DASH, - STATE_IN_DASH, + uint8_t m_groupCounter; - // the pattern is starting a gap after a dash - STATE_BEGIN_GAP2, - STATE_IN_GAP2, - }; + // whether this instance runs the colour blending behaviour + bool m_isBlend; // the state of the current pattern - PatternState m_state; + BlinkPatternState m_state; // the blink timer used to measure blink timings Timer m_blinkTimer; + + // current color and target blend color + RGBColor m_cur; + RGBColor m_next; + + // the current flip counter + uint8_t m_flip; }; +// the historical name is kept as an alias so existing includes and +// signatures referring to BasicPattern continue to resolve +typedef BlinkPattern BasicPattern; + +// the vtable every BlinkPattern dispatches through +extern const BlinkPatternVtable BlinkPattern_bvtable; +// the plain PatternVtable view of the above, for the base dispatch +#define BlinkPattern_vtable (BlinkPattern_bvtable.base) + +// initialize a BlinkPattern for the given pattern id, the id decides +// whether the blend arguments are registered and which behaviour runs +void BlinkPattern_init(BlinkPattern *self, PatternID id, const PatternArgs *args); +void BlinkPattern_initWithVtable(BlinkPattern *self, PatternID id, const PatternArgs *args, const BlinkPatternVtable *vtable); + +// allocate and initialize a BlinkPattern for the given pattern id +BlinkPattern *BlinkPattern_make(PatternID id, const PatternArgs *args); + +// vtable entry points +void BlinkPattern_destroy(Pattern *self); +void BlinkPattern_bind(Pattern *self, LedPos pos); +void BlinkPattern_doInit(Pattern *self); +void BlinkPattern_play(Pattern *self); + +// the blinking hooks, these are dispatched through the vtable so that +// subclasses can replace them without needing their own play loop +void BlinkPattern_onBlinkOn(BlinkPattern *self); +void BlinkPattern_onBlinkOff(BlinkPattern *self); +void BlinkPattern_beginGap(BlinkPattern *self); +void BlinkPattern_beginDash(BlinkPattern *self); + +// whether the given pattern id uses the colour blending behaviour +bool BlinkPattern_isBlendID(PatternID id); + #endif diff --git a/VortexEngine/src/Patterns/Single/BlendPattern.cpp b/VortexEngine/src/Patterns/Single/BlendPattern.cpp index bb9d95f138..a99d0be862 100644 --- a/VortexEngine/src/Patterns/Single/BlendPattern.cpp +++ b/VortexEngine/src/Patterns/Single/BlendPattern.cpp @@ -1,76 +1,5 @@ #include "BlendPattern.h" -#include "../../Time/TimeControl.h" -#include "../../Colors/Colorset.h" -#include "../../Leds/Leds.h" - -BlendPattern::BlendPattern(const PatternArgs &args) : - BasicPattern(args), - m_blendSpeed(0), - m_numFlips(0), - m_cur(), - m_next(), - m_flip(0) -{ - m_patternID = PATTERN_BLEND; - REGISTER_ARG(m_blendSpeed); - REGISTER_ARG(m_numFlips); - setArgs(args); -} - -BlendPattern::~BlendPattern() -{ -} - -void BlendPattern::init() -{ - // run basic pattern init logic - BasicPattern::init(); - // convert current/next colors to HSV - m_cur = m_colorset.getNext(); - m_next = m_colorset.getNext(); - // reset the flip count - m_flip = 0; -} - -void BlendPattern::onBlinkOn() -{ - if (m_cur == m_next) { - m_next = m_colorset.getNext(); - } - interpolate(m_cur.red, m_next.red); - interpolate(m_cur.green, m_next.green); - interpolate(m_cur.blue, m_next.blue); - RGBColor col = m_cur; - // it should be impossible for m_numFlips to be 0 and m_flip to be 1 - // unless the pattern arg is changed mid-play which could happen with - // and editor or something, so check both for 0 - if (m_flip && m_numFlips) { - // convert to hsv - HSVColor hsvCol = m_cur; - // shift the hue by a flip size - hsvCol.hue += (m_flip * (127 / m_numFlips)); - // convert the hsv color back to RGB - col = hsvCol; - } - // set the color - Leds::setIndex(m_ledPos, col); - // increment the flip count - m_flip++; - // modulate the flip count DO NOT USE MODULO OPERATOR BECAUSE - // THE FLIP COUNT COULD BE 0 THAT WILL DIVIDE BY ZERO - if (m_flip > m_numFlips) { - m_flip = 0; - } -} - -void BlendPattern::interpolate(uint8_t ¤t, const uint8_t next) -{ - if (current < next) { - uint8_t step = (next - current) > m_blendSpeed ? m_blendSpeed : (next - current); - current += step; - } else if (current > next) { - uint8_t step = (current - next) > m_blendSpeed ? m_blendSpeed : (current - next); - current -= step; - } -} \ No newline at end of file +// The blend behaviour is implemented by BlinkPattern in BasicPattern.cpp, +// see BasicPattern.h. This translation unit is intentionally empty so that +// the existing BlendPattern.cpp build entry keeps working. diff --git a/VortexEngine/src/Patterns/Single/BlendPattern.h b/VortexEngine/src/Patterns/Single/BlendPattern.h index bff3c1ff06..462ba1d755 100644 --- a/VortexEngine/src/Patterns/Single/BlendPattern.h +++ b/VortexEngine/src/Patterns/Single/BlendPattern.h @@ -1,34 +1,11 @@ #ifndef BLEND_PATTERN_H #define BLEND_PATTERN_H +// The blend behaviour was merged into BlinkPattern so that blink and blend +// share a single implementation. BlendPattern is kept as an alias so that +// existing includes of this header keep working. #include "BasicPattern.h" -#include "../../Time/Timings.h" - -class BlendPattern : public BasicPattern -{ -public: - BlendPattern(const PatternArgs &args); - virtual ~BlendPattern(); - - virtual void init() override; - -protected: - // only override the onBlinkOn so we can control the color it blinks - virtual void onBlinkOn() override; - void interpolate(uint8_t ¤t, const uint8_t next); - - // offset of starting hue and number of flips - uint8_t m_blendSpeed; - uint8_t m_numFlips; - - // current color and target blend color - RGBColor m_cur; - RGBColor m_next; - - // the current flip counter - uint8_t m_flip; -}; +typedef BlinkPattern BlendPattern; #endif - diff --git a/VortexEngine/src/Patterns/Single/SingleLedPattern.cpp b/VortexEngine/src/Patterns/Single/SingleLedPattern.cpp index e47e605ff9..3bbacdfe5b 100644 --- a/VortexEngine/src/Patterns/Single/SingleLedPattern.cpp +++ b/VortexEngine/src/Patterns/Single/SingleLedPattern.cpp @@ -1,19 +1,5 @@ #include "SingleLedPattern.h" -#include "../../Time/TimeControl.h" - -SingleLedPattern::SingleLedPattern(const PatternArgs &args) : - Pattern(args) -{ - // SingleLed is an abstract class it cannot be directly - // instantiated so we do not need to assign a pattern id -} - -SingleLedPattern::~SingleLedPattern() -{ -} - -void SingleLedPattern::init() -{ - Pattern::init(); -} \ No newline at end of file +// SingleLedPattern is now an alias for Pattern, see SingleLedPattern.h. +// This translation unit is intentionally empty so the existing build entry +// keeps working. diff --git a/VortexEngine/src/Patterns/Single/SingleLedPattern.h b/VortexEngine/src/Patterns/Single/SingleLedPattern.h index 443045253c..1990b7ec5f 100644 --- a/VortexEngine/src/Patterns/Single/SingleLedPattern.h +++ b/VortexEngine/src/Patterns/Single/SingleLedPattern.h @@ -3,19 +3,10 @@ #include "../Pattern.h" -class SingleLedPattern : public Pattern -{ -protected: - // SingleLedPattern is an abstract class - SingleLedPattern(const PatternArgs &args); - -public: - virtual ~SingleLedPattern(); - - virtual void init() override; - -private: - -}; +// SingleLedPattern used to be a thin abstract subclass of Pattern whose only +// behaviour was to call Pattern::init(). In C that distinction disappears, +// so it is simply an alias for the base and existing references keep working. +// The concrete single-led patterns derive from BlinkPattern directly. +typedef Pattern SingleLedPattern; #endif diff --git a/VortexEngine/src/Patterns/Single/SolidPattern.cpp b/VortexEngine/src/Patterns/Single/SolidPattern.cpp index ef59cf7894..4e254297a5 100644 --- a/VortexEngine/src/Patterns/Single/SolidPattern.cpp +++ b/VortexEngine/src/Patterns/Single/SolidPattern.cpp @@ -1,21 +1,59 @@ #include "SolidPattern.h" -SolidPattern::SolidPattern(const PatternArgs &args) : - BasicPattern(args), - m_colIndex(0) +#include "../../Colors/Colorset.h" +#include "../../Memory/Memory.h" +#include "../PatternBuilder.h" + +// the solid vtable, only onBlinkOn differs from the blink vtable +const BlinkPatternVtable SolidPattern_bvtable = { + .base = { + .destroy = BlinkPattern_destroy, + .bind = BlinkPattern_bind, + .init = BlinkPattern_doInit, + .play = BlinkPattern_play + }, + .onBlinkOn = SolidPattern_onBlinkOn, + .onBlinkOff = BlinkPattern_onBlinkOff, + .beginGap = BlinkPattern_beginGap, + .beginDash = BlinkPattern_beginDash +}; + +// the only hook SolidPattern changes, it positions the colorset before +// handing off to the regular blink behaviour +void SolidPattern_onBlinkOn(BlinkPattern *bpat) { - m_patternID = PATTERN_SOLID; - REGISTER_ARG(m_colIndex); - setArgs(args); + SolidPattern *self = (SolidPattern *)bpat; + if (self->m_colIndex == 0) { + Colorset_resetIndex(&bpat->base.m_colorset); + } else { + Colorset_setCurIndex(&bpat->base.m_colorset, self->m_colIndex - 1); + } + // the original called the *base* (non blend) implementation here, and + // because m_isBlend is false for PATTERN_SOLID the dispatch lands there + BlinkPattern_onBlinkOn(bpat); } -// callbacks for blinking on/off, can be overridden by derived classes -void SolidPattern::onBlinkOn() +void SolidPattern_init(SolidPattern *self, PatternID id, const PatternArgs *args) { - if (m_colIndex == 0) { - m_colorset.resetIndex(); - } else { - m_colorset.setCurIndex(m_colIndex - 1); + // this registers the five blink arguments, matching the order the old + // SolidPattern constructor produced by calling the base constructor first + BlinkPattern_initWithVtable(&self->base, id, args, &SolidPattern_bvtable); + + self->m_colIndex = 0; + // the colIndex argument stays registered as the sixth and last one so + // the serialized form and the editor layout are unchanged + REGISTER_ARG((Pattern *)&self->base.base, SolidPattern, m_colIndex); + + // re-apply the arguments now that the extra one is registered + Pattern_setArgs(&self->base.base, args); +} + +SolidPattern *SolidPattern_make(PatternID id, const PatternArgs *args) +{ + SolidPattern *self = (SolidPattern *)PatternBuilder_allocPattern(); + if (!self) { + return NULL; } - BasicPattern::onBlinkOn(); + SolidPattern_init(self, id, args); + return self; } diff --git a/VortexEngine/src/Patterns/Single/SolidPattern.h b/VortexEngine/src/Patterns/Single/SolidPattern.h index ac50b71da9..83df23338a 100644 --- a/VortexEngine/src/Patterns/Single/SolidPattern.h +++ b/VortexEngine/src/Patterns/Single/SolidPattern.h @@ -3,18 +3,25 @@ #include "BasicPattern.h" -#include "../../Time/Timings.h" +// SolidPattern is a BlinkPattern that always shows a single colour, chosen by +// the m_colIndex argument. It only replaces the onBlinkOn hook, the rest of +// the blinking state machine is reused unchanged. +typedef struct SolidPattern { + // must remain the first member + BlinkPattern base; -class SolidPattern : public BasicPattern -{ -public: - SolidPattern(const PatternArgs &args); + // the index into the colorset to display, 0 means use the current one + uint8_t m_colIndex; +} SolidPattern; -protected: - // callbacks for blinking on/off, can be overridden by derived classes - virtual void onBlinkOn() override; +// the vtable for SolidPattern, identical to the blink one except that +// onBlinkOn is replaced +extern const BlinkPatternVtable SolidPattern_bvtable; - uint8_t m_colIndex; -}; +void SolidPattern_init(SolidPattern *self, PatternID id, const PatternArgs *args); +SolidPattern *SolidPattern_make(PatternID id, const PatternArgs *args); + +// the replaced blinking hook +void SolidPattern_onBlinkOn(BlinkPattern *self); #endif diff --git a/VortexEngine/src/Random/Random.cpp b/VortexEngine/src/Random/Random.cpp index fb10f20105..74a6705a8c 100644 --- a/VortexEngine/src/Random/Random.cpp +++ b/VortexEngine/src/Random/Random.cpp @@ -1,45 +1,45 @@ #include "Random.h" -Random::Random() : - m_seed(0) +void Random_make(Random *self) { + self->m_seed = 0; } -Random::Random(uint32_t newseed) : - Random() +void Random_makeSeed(Random *self, uint32_t newseed) { - seed(newseed); + Random_make(self); + Random_seed(self, newseed); } -Random::~Random() +void Random_destroy(Random *self) { } -void Random::seed(uint32_t newseed) +void Random_seed(Random *self, uint32_t newseed) { if (!newseed) { - m_seed = 42; + self->m_seed = 42; } - m_seed = newseed; + self->m_seed = newseed; } -uint16_t Random::next16(uint16_t minValue, uint16_t maxValue) +uint16_t Random_next16(Random *self, uint16_t minValue, uint16_t maxValue) { // walk the LCG forward to the next step - m_seed = (m_seed * 1103515245 + 12345) & 0x7FFFFFFF; + self->m_seed = (self->m_seed * 1103515245 + 12345) & 0x7FFFFFFF; uint32_t range = maxValue - minValue; if (range != 0xFFFFFFFF) { // shift the seed 16 bits to the right because the lower 16 bits // of this LCG are apparently not uniform whatsoever, where as the // upper 16 bits appear to be quite uniform as per tests. We don't // really need 32bit random values so we offer max 16bits of entropy - return ((m_seed >> 16) % (range + 1)) + minValue; + return ((self->m_seed >> 16) % (range + 1)) + minValue; } - return (m_seed >> 16); + return (self->m_seed >> 16); } -uint8_t Random::next8(uint8_t minValue, uint8_t maxValue) +uint8_t Random_next8(Random *self, uint8_t minValue, uint8_t maxValue) { - uint32_t result = next16(minValue, maxValue); - return static_cast(result); + uint32_t result = Random_next16(self, minValue, maxValue); + return (uint8_t)result; } diff --git a/VortexEngine/src/Random/Random.h b/VortexEngine/src/Random/Random.h index b413b7210f..aa99c45303 100644 --- a/VortexEngine/src/Random/Random.h +++ b/VortexEngine/src/Random/Random.h @@ -2,18 +2,26 @@ #include -class Random -{ -public: - Random(); - Random(uint32_t newseed); - ~Random(); +typedef struct Random { + uint32_t m_seed; +} Random; - void seed(uint32_t newseed); +void Random_make(Random *self); +void Random_makeSeed(Random *self, uint32_t newseed); +void Random_destroy(Random *self); - uint8_t next8(uint8_t minValue = 0, uint8_t maxValue = 0xFF); - uint16_t next16(uint16_t minValue = 0, uint16_t maxValue = 0xFFFF); +void Random_seed(Random *self, uint32_t newseed); -private: - uint32_t m_seed; -}; +uint8_t Random_next8(Random *self, uint8_t minValue, uint8_t maxValue); +uint16_t Random_next16(Random *self, uint16_t minValue, uint16_t maxValue); + +// default-argument shorthands for the common cases +static inline uint8_t Random_next8Default(Random *self) +{ + return Random_next8(self, 0, 0xFF); +} + +static inline uint16_t Random_next16Default(Random *self) +{ + return Random_next16(self, 0, 0xFFFF); +} diff --git a/VortexEngine/src/Serial/BitStream.cpp b/VortexEngine/src/Serial/BitStream.cpp index 27aadeaac0..7fff37bbaf 100644 --- a/VortexEngine/src/Serial/BitStream.cpp +++ b/VortexEngine/src/Serial/BitStream.cpp @@ -5,124 +5,126 @@ #include -BitStream::BitStream() : - m_buf(nullptr), m_buf_size(0), - m_bit_pos(0), m_buf_eof(false), - m_allocated(false) +void BitStream_make(BitStream *self) { + self->m_buf = NULL; + self->m_buf_size = 0; + self->m_bit_pos = 0; + self->m_buf_eof = false; + self->m_allocated = false; } -BitStream::BitStream(uint8_t *buf, uint32_t size) : - BitStream() +void BitStream_makeBuf(BitStream *self, uint8_t *buf, uint32_t size) { - init(buf, size); + BitStream_make(self); + BitStream_initBuf(self, buf, size); } -BitStream::BitStream(uint32_t size) : - BitStream() +void BitStream_makeSize(BitStream *self, uint32_t size) { - init(size); + BitStream_make(self); + BitStream_initSize(self, size); } -BitStream::~BitStream() +void BitStream_destroy(BitStream *self) { - if (m_allocated) { - vfree(m_buf); + if (self->m_allocated) { + vfree(self->m_buf); } } -bool BitStream::init(uint8_t *buf, uint32_t size) +bool BitStream_initBuf(BitStream *self, uint8_t *buf, uint32_t size) { - m_buf = buf; - m_buf_size = size; - resetPos(); + self->m_buf = buf; + self->m_buf_size = size; + BitStream_resetPos(self); return true; } -bool BitStream::init(uint32_t size) +bool BitStream_initSize(BitStream *self, uint32_t size) { - if (m_buf) { - vfree(m_buf); + if (self->m_buf) { + vfree(self->m_buf); } - m_buf = (uint8_t *)vcalloc(1, size); - if (!m_buf) { + self->m_buf = (uint8_t *)vcalloc(1, size); + if (!self->m_buf) { ERROR_OUT_OF_MEMORY(); return false; } - m_buf_size = size; - m_allocated = true; - resetPos(); + self->m_buf_size = size; + self->m_allocated = true; + BitStream_resetPos(self); return true; } -void BitStream::reset() +void BitStream_reset(BitStream *self) { - if (m_buf) { - memset(m_buf, 0, m_buf_size); + if (self->m_buf) { + memset(self->m_buf, 0, self->m_buf_size); } - resetPos(); + BitStream_resetPos(self); } -void BitStream::resetPos() +void BitStream_resetPos(BitStream *self) { - m_bit_pos = 0; - m_buf_eof = false; + self->m_bit_pos = 0; + self->m_buf_eof = false; } -uint8_t BitStream::read1Bit() +uint8_t BitStream_read1Bit(BitStream *self) { - if (m_buf_eof) { + if (self->m_buf_eof) { return 0; } - if (m_bit_pos >= (m_buf_size * 8)) { - m_buf_eof = true; + if (self->m_bit_pos >= (self->m_buf_size * 8)) { + self->m_buf_eof = true; return 0; } - uint32_t rv = (m_buf[m_bit_pos / 8] >> (7 - (m_bit_pos % 8))) & 1; - m_bit_pos++; - if (m_bit_pos >= (m_buf_size * 8)) { - m_buf_eof = true; + uint32_t rv = (self->m_buf[self->m_bit_pos / 8] >> (7 - (self->m_bit_pos % 8))) & 1; + self->m_bit_pos++; + if (self->m_bit_pos >= (self->m_buf_size * 8)) { + self->m_buf_eof = true; } return rv; } -void BitStream::write1Bit(bool bit) +void BitStream_write1Bit(BitStream *self, bool bit) { - if (m_buf_eof) { + if (self->m_buf_eof) { return; } - if (m_bit_pos >= (m_buf_size * 8)) { - m_buf_eof = true; + if (self->m_bit_pos >= (self->m_buf_size * 8)) { + self->m_buf_eof = true; return; } uint8_t bitVal = (uint8_t)bit & 1; - m_buf[m_bit_pos / 8] |= bitVal << (7 - (m_bit_pos % 8)); - m_bit_pos++; - if (m_bit_pos >= (m_buf_size * 8)) { - m_buf_eof = true; + self->m_buf[self->m_bit_pos / 8] |= bitVal << (7 - (self->m_bit_pos % 8)); + self->m_bit_pos++; + if (self->m_bit_pos >= (self->m_buf_size * 8)) { + self->m_buf_eof = true; } } -uint8_t BitStream::readBits(uint32_t numBits) +uint8_t BitStream_readBits(BitStream *self, uint32_t numBits) { uint32_t val = 0; - if (m_buf_eof) { + if (self->m_buf_eof) { return 0; } for (uint32_t i = 0; i < numBits; ++i) { - val = (val << 1) | read1Bit(); - if (m_buf_eof) { + val = (val << 1) | BitStream_read1Bit(self); + if (self->m_buf_eof) { break; } } - if (m_bit_pos >= (m_buf_size * 8)) { - m_buf_eof = true; + if (self->m_bit_pos >= (self->m_buf_size * 8)) { + self->m_buf_eof = true; } return val; } // writes numBits of the LSB in sequential order -void BitStream::writeBits(uint32_t numBits, uint32_t val) +void BitStream_writeBits(BitStream *self, uint32_t numBits, uint32_t val) { for (uint32_t i = 0; i < numBits; ++i) { // write each bit in order, for ex if numBits is 4: @@ -130,6 +132,6 @@ void BitStream::writeBits(uint32_t numBits, uint32_t val) // 00000000 01010101 // ^^^^ // write these four bits from left to right - write1Bit((val >> ((numBits - 1) - i)) & 1); + BitStream_write1Bit(self, (val >> ((numBits - 1) - i)) & 1); } } \ No newline at end of file diff --git a/VortexEngine/src/Serial/BitStream.h b/VortexEngine/src/Serial/BitStream.h index 87aba5d66e..80a0895430 100644 --- a/VortexEngine/src/Serial/BitStream.h +++ b/VortexEngine/src/Serial/BitStream.h @@ -2,50 +2,48 @@ #define BITSTREAM_H #include +#include -// A class to read/write a buffer of bits one bit at a time -class BitStream -{ -public: - BitStream(); - BitStream(uint32_t size); - BitStream(uint8_t *buf, uint32_t size); - ~BitStream(); - - // init the stream with a buffer - bool init(uint8_t *buf, uint32_t size); - // init the stream and allocate a buffer - bool init(uint32_t size); - - // clear the target buffer to 0 and reset position - void reset(); - // reset the reader/writer position - void resetPos(); - - // read/write a single bit in LSB - uint8_t read1Bit(); - void write1Bit(bool bit); - // read/write multiple bits from left to right at LSB - uint8_t readBits(uint32_t numBits); - void writeBits(uint32_t numBits, uint32_t val); - - // metainfo about the bit stream - bool eof() const { return m_buf_eof; } - bool allocated() const { return m_allocated; } - uint16_t size() const { return m_buf_size; } - const uint8_t *data() const { return m_buf; } - uint8_t peekData(uint8_t pos) const { return m_buf[pos]; } - const uint32_t *dwData() const { return (uint32_t *)m_buf; } - uint16_t dwordpos() const { return m_bit_pos / 32; } - uint16_t bytepos() const { return m_bit_pos / 8; } - uint16_t bitpos() const { return m_bit_pos; } - -private: +// A struct to read/write a buffer of bits one bit at a time +typedef struct BitStream { uint8_t *m_buf; uint16_t m_buf_size; uint16_t m_bit_pos; bool m_buf_eof; bool m_allocated; -}; +} BitStream; + +void BitStream_make(BitStream *self); +void BitStream_makeSize(BitStream *self, uint32_t size); +void BitStream_makeBuf(BitStream *self, uint8_t *buf, uint32_t size); +void BitStream_destroy(BitStream *self); + +// init the stream with a buffer +bool BitStream_initBuf(BitStream *self, uint8_t *buf, uint32_t size); +// init the stream and allocate a buffer +bool BitStream_initSize(BitStream *self, uint32_t size); + +// clear the target buffer to 0 and reset position +void BitStream_reset(BitStream *self); +// reset the reader/writer position +void BitStream_resetPos(BitStream *self); + +// read/write a single bit in LSB +uint8_t BitStream_read1Bit(BitStream *self); +void BitStream_write1Bit(BitStream *self, bool bit); +// read/write multiple bits from left to right at LSB +uint8_t BitStream_readBits(BitStream *self, uint32_t numBits); +void BitStream_writeBits(BitStream *self, uint32_t numBits, uint32_t val); + +// metainfo about the bit stream +static inline bool BitStream_eof(const BitStream *self) { return self->m_buf_eof; } +static inline bool BitStream_allocated(const BitStream *self) { return self->m_allocated; } +static inline uint16_t BitStream_size(const BitStream *self) { return self->m_buf_size; } +static inline const uint8_t *BitStream_data(const BitStream *self) { return self->m_buf; } +static inline uint8_t BitStream_peekData(const BitStream *self, uint8_t pos) { return self->m_buf[pos]; } +static inline const uint32_t *BitStream_dwData(const BitStream *self) { return (const uint32_t *)self->m_buf; } +static inline uint16_t BitStream_dwordpos(const BitStream *self) { return self->m_bit_pos / 32; } +static inline uint16_t BitStream_bytepos(const BitStream *self) { return self->m_bit_pos / 8; } +static inline uint16_t BitStream_bitpos(const BitStream *self) { return self->m_bit_pos; } #endif diff --git a/VortexEngine/src/Serial/ByteStream.cpp b/VortexEngine/src/Serial/ByteStream.cpp index 7c2264fa6e..d909ac8edf 100644 --- a/VortexEngine/src/Serial/ByteStream.cpp +++ b/VortexEngine/src/Serial/ByteStream.cpp @@ -6,262 +6,445 @@ #include +#ifdef VORTEX_EMBEDDED +#include +#endif + #include "Compression.h" // flags for saving buffer to disk #define BUFFER_FLAG_COMRPESSED (1<<0) // buffer is compressed #define BUFFER_FLAG_DIRTY (1<<1) // buffer crc is dirty -ByteStream::ByteStream(uint32_t size, const uint8_t *buf) : - m_pData(), - m_position(0), - m_capacity(0) +// =================================================================== +// ByteStream Arena +// +// The data of every ByteStream lives in a single arena. Each buffer is a +// chunk in the arena that records which +// ByteStream owns it, the chunks are always packed together at the front +// of the arena with all of the free space in one block at the end. When a +// chunk is released or resized the chunks after it are slid along to close +// or open the gap and their owners are updated to point at the new spot. +// This means the arena can never fragment, the only way an allocation can +// fail is if the total size of all live buffers exceeds the arena size. +// +// On the device the arena is all of the free ram between the end of the +// static data and the stack, exactly where the heap used to be. It grows +// up towards the stack and refuses to come within a margin of the stack +// pointer, the same way malloc does, so the stack and the ByteStreams share +// the free ram instead of each reserving their worst case. +// +// The catch is a ByteStream must not move in memory while it owns a buffer +// and it must be destroyed (or cleared) before it goes out of scope, and +// raw pointers into a buffer are only valid until another ByteStream is +// initialized, resized or cleared. + +#ifdef VORTEX_LIB +typedef uint32_t arena_size_t; +#else +typedef uint16_t arena_size_t; +#endif + +typedef struct ArenaChunk +{ + // the ByteStream that owns this chunk + ByteStream *owner; + // the total size of this chunk including this header + arena_size_t size; +} ArenaChunk; + +// chunks are rounded up to keep every chunk header and RawBuffer aligned +#define ARENA_ALIGN(size) ((((size) + sizeof(void *) - 1) / sizeof(void *)) * sizeof(void *)) +#define ARENA_CHUNK_HEADER ARENA_ALIGN(sizeof(ArenaChunk)) +// the total chunk size for a buffer of the given capacity +#define ARENA_CHUNK_SIZE(capacity) ARENA_ALIGN(ARENA_CHUNK_HEADER + sizeof(RawBuffer) + (capacity)) + +#ifdef VORTEX_EMBEDDED +// the end of the static data, provided by the linker +extern uint8_t __heap_start; +#else +// the storage for the arena, declared as pointers so it is pointer aligned +static void *s_arenaStorage[ARENA_ALIGN(BYTESTREAM_ARENA_SIZE) / sizeof(void *)]; +#endif +static uint8_t *s_arena = NULL; +static arena_size_t s_arenaUsed = 0; +static arena_size_t s_arenaHighWater = 0; + +static ArenaChunk *ByteStream_chunk(const RawBuffer *buf) { - init(size, buf); + return (ArenaChunk *)((uint8_t *)buf - ARENA_CHUNK_HEADER); } -ByteStream::~ByteStream() +static RawBuffer *ByteStream_chunkData(ArenaChunk *chunk) { - clear(); + return (RawBuffer *)((uint8_t *)chunk + ARENA_CHUNK_HEADER); } -ByteStream::ByteStream(const ByteStream &other) : - ByteStream() +bool ByteStream_initArena() { - init(other.capacity(), other.data()); - m_pData->flags = other.m_pData->flags; - m_pData->crc32 = other.m_pData->crc32; - m_pData->size = other.m_pData->size; + if (s_arena) { + // already initialized, buffers may still be in use so leave it be + return true; + } +#ifdef VORTEX_EMBEDDED + s_arena = &__heap_start; +#else + s_arena = (uint8_t *)s_arenaStorage; +#endif + s_arenaUsed = 0; + s_arenaHighWater = 0; + return true; +} + +uint32_t ByteStream_arenaUsed() +{ + return s_arenaUsed; } -void ByteStream::operator=(const ByteStream &other) +uint32_t ByteStream_arenaHighWater() { - init(other.capacity(), other.data()); - m_pData->flags = other.m_pData->flags; - m_pData->crc32 = other.m_pData->crc32; - m_pData->size = other.m_pData->size; + return s_arenaHighWater; } -void ByteStream::move(ByteStream *target) +// the amount of free space left in the arena +static arena_size_t ByteStream_arenaFree() +{ +#ifdef VORTEX_EMBEDDED + // the arena can grow up to the margin below the current stack pointer + // but never into the space reserved for the deepest the stack can go + uint8_t *limit = (uint8_t *)SP - BYTESTREAM_STACK_MARGIN; + uint8_t *reserve = (uint8_t *)(RAMEND + 1 - BYTESTREAM_STACK_RESERVE); + if (limit > reserve) { + limit = reserve; + } + uint8_t *end = s_arena + s_arenaUsed; + return (limit > end) ? (arena_size_t)(limit - end) : 0; +#else + return BYTESTREAM_ARENA_SIZE - s_arenaUsed; +#endif +} + +// resize the chunk to newSize by sliding every chunk after it, a newSize +// of 0 removes the chunk entirely, returns false if there isn't room +static bool ByteStream_resizeChunk(ArenaChunk *chunk, arena_size_t newSize) +{ + arena_size_t oldSize = chunk->size; + if (newSize > oldSize && (newSize - oldSize) > ByteStream_arenaFree()) { + ERROR_OUT_OF_MEMORY(); + return false; + } + uint8_t *end = (uint8_t *)chunk + oldSize; + arena_size_t tail = (arena_size_t)((s_arena + s_arenaUsed) - end); + // slide the following chunks to their new spot + memmove((uint8_t *)chunk + newSize, end, tail); + s_arenaUsed = s_arenaUsed - oldSize + newSize; + if (s_arenaUsed > s_arenaHighWater) { + s_arenaHighWater = s_arenaUsed; + } + uint8_t *next = (uint8_t *)chunk + newSize; + if (newSize) { + chunk->size = newSize; + } + // then point the owners of the chunks that moved at their new data + while (next < s_arena + s_arenaUsed) { + ArenaChunk *moved = (ArenaChunk *)next; + moved->owner->m_pData = ByteStream_chunkData(moved); + next += moved->size; + } + return true; +} + +// allocate a buffer from the end of the arena for the given owner +static RawBuffer *ByteStream_allocBuffer(ByteStream *owner, uint32_t capacity) +{ + arena_size_t size = ARENA_CHUNK_SIZE(capacity); + if (!s_arena || size > ByteStream_arenaFree()) { + ERROR_OUT_OF_MEMORY(); + return NULL; + } + ArenaChunk *chunk = (ArenaChunk *)(s_arena + s_arenaUsed); + chunk->owner = owner; + chunk->size = size; + s_arenaUsed += size; + if (s_arenaUsed > s_arenaHighWater) { + s_arenaHighWater = s_arenaUsed; + } + return ByteStream_chunkData(chunk); +} + +// release a buffer back to the arena, this compacts the arena +static void ByteStream_freeBuffer(RawBuffer *buf) +{ + ByteStream_resizeChunk(ByteStream_chunk(buf), 0); +} + +// resize a buffer in place to hold the given capacity, this may move +// any buffer in the arena (including this one) so the owners are updated +static bool ByteStream_resizeBuffer(ByteStream *owner, uint32_t capacity) +{ + return ByteStream_resizeChunk(ByteStream_chunk(owner->m_pData), ARENA_CHUNK_SIZE(capacity)); +} + +void ByteStream_make(ByteStream *self) +{ + self->m_pData = NULL; + self->m_position = 0; + self->m_capacity = 0; + ByteStream_init(self, 0); +} + +void ByteStream_makeBuf(ByteStream *self, uint32_t size, const uint8_t *buf) +{ + self->m_pData = NULL; + self->m_position = 0; + self->m_capacity = 0; + ByteStream_initBuf(self, size, buf); +} + +void ByteStream_destroy(ByteStream *self) +{ + ByteStream_clear(self); +} + +// copy and assignment operators +void ByteStream_set(ByteStream *self, const ByteStream *other) +{ + // initialize first then copy, initializing may move the other buffer + // in the arena so a pointer to its data cannot be taken beforehand + if (!ByteStream_init(self, ByteStream_capacity(other)) || !self->m_pData) { + return; + } + memcpy(self->m_pData->buf, other->m_pData->buf, ByteStream_capacity(other)); + self->m_pData->flags = other->m_pData->flags; + self->m_pData->crc32 = other->m_pData->crc32; + self->m_pData->size = other->m_pData->size; +} + +// move operator since std::move not always available +void ByteStream_move(ByteStream *self, ByteStream *target) { if (!target) { return; } - target->clear(); - target->m_pData = m_pData; - target->m_position = m_position; - target->m_capacity = m_capacity; - m_pData = nullptr; - m_position = 0; - m_capacity = 0; + ByteStream_clear(target); + target->m_pData = self->m_pData; + target->m_position = self->m_position; + target->m_capacity = self->m_capacity; + if (target->m_pData) { + // the target now owns the chunk in the arena + ByteStream_chunk(target->m_pData)->owner = target; + } + self->m_pData = NULL; + self->m_position = 0; + self->m_capacity = 0; } -bool ByteStream::rawInit(const uint8_t *rawdata, uint32_t size) + +bool ByteStream_rawInit(ByteStream *self, const uint8_t *rawdata, uint32_t size) { if (!rawdata || size < sizeof(RawBuffer)) { DEBUG_LOGF("Cannot rawInit: %p %u", rawdata, size); return false; } + // release any existing buffer first so it doesn't linger in the arena + ByteStream_clear(self); // round up to nearest 4 - m_capacity = (size + 4) - (size % 4); - m_pData = (RawBuffer *)vcalloc(1, m_capacity + sizeof(RawBuffer)); - if (!m_pData) { - m_capacity = 0; - ERROR_OUT_OF_MEMORY(); + self->m_capacity = (size + 4) - (size % 4); + self->m_pData = ByteStream_allocBuffer(self, self->m_capacity); + if (!self->m_pData) { + self->m_capacity = 0; return false; } + memset(self->m_pData, 0, self->m_capacity + sizeof(RawBuffer)); // copy in the actual data from the serial buffer, this will fill the // members: size, flags, and crc32 of m_pData as well as the buf - memcpy(m_pData, rawdata, size); + memcpy(self->m_pData, rawdata, size); // ensure the internal size doesn't exceed outer size - sanity(); + ByteStream_sanity(self); return true; } // reset the buffer -bool ByteStream::init(uint32_t size, const uint8_t *buf) +bool ByteStream_init(ByteStream *self, uint32_t capacity) { - clear(); + return ByteStream_initBuf(self, capacity, NULL); +} + +bool ByteStream_initBuf(ByteStream *self, uint32_t size, const uint8_t *buf) +{ + ByteStream_clear(self); if (size) { // round up to nearest 4 - m_capacity = (size + 4) - (size % 4); - m_pData = (RawBuffer *)vcalloc(1, m_capacity + sizeof(RawBuffer)); - if (!m_pData) { - m_capacity = 0; - ERROR_OUT_OF_MEMORY(); + self->m_capacity = (size + 4) - (size % 4); + self->m_pData = ByteStream_allocBuffer(self, self->m_capacity); + if (!self->m_pData) { + self->m_capacity = 0; return false; } - m_pData->size = 0; - m_pData->flags = 0; - m_pData->crc32 = 0; - memset(m_pData->buf, 0, m_capacity); + self->m_pData->size = 0; + self->m_pData->flags = 0; + self->m_pData->crc32 = 0; + memset(self->m_pData->buf, 0, self->m_capacity); } - if (buf && m_pData) { - memcpy(m_pData->buf, buf, size); - m_pData->size = size; - m_pData->recalcCRC(); + if (buf && self->m_pData) { + memcpy(self->m_pData->buf, buf, size); + self->m_pData->size = size; + RawBuffer_recalcCRC(self->m_pData); } return true; } -void ByteStream::clear() +void ByteStream_clear(ByteStream *self) { - if (m_pData) { - vfree(m_pData); - m_pData = nullptr; + if (self->m_pData) { + ByteStream_freeBuffer(self->m_pData); + self->m_pData = NULL; } - m_capacity = 0; - m_position = 0; + self->m_capacity = 0; + self->m_position = 0; } -bool ByteStream::shrink() +bool ByteStream_shrink(ByteStream *self) { - if (!m_pData) { + if (!self->m_pData) { return false; } - if (m_pData->size == m_capacity) { + if (self->m_pData->size == self->m_capacity) { return true; } - m_capacity = m_pData->size; - RawBuffer *temp = (RawBuffer *)vrealloc(m_pData, m_pData->size + sizeof(RawBuffer)); - if (!temp) { - ERROR_OUT_OF_MEMORY(); - return false; - } - m_pData = temp; - return true; + self->m_capacity = self->m_pData->size; + return ByteStream_resizeBuffer(self, self->m_capacity); } // append another buffer -bool ByteStream::append(const ByteStream &other) +bool ByteStream_append(ByteStream *self, const ByteStream *other) { - if (!other.m_pData) { + if (!other->m_pData) { // nothing to append return true; } - if (!m_pData || other.size() > (m_capacity - size())) { - if (!extend(other.size())) { + if (!self->m_pData || ByteStream_size(other) > (self->m_capacity - ByteStream_size(self))) { + if (!ByteStream_extend(self, ByteStream_size(other))) { return false; } } - memcpy(frontSerializer(), other.data(), other.size()); - m_pData->size += other.size(); + memcpy(ByteStream_frontSerializer(self), ByteStream_data(other), ByteStream_size(other)); + self->m_pData->size += ByteStream_size(other); return true; } // trim some amount of bytes off the end of the stream -void ByteStream::trim(uint32_t bytes) +void ByteStream_trim(ByteStream *self, uint32_t bytes) { - if (!m_pData) { + if (!self->m_pData) { return; } // just adjust the data size no need to reallocate, this // will put the extra bytes into 'extra space' and shrink() // could be called to reallocate the buffer without them - if (m_pData->size <= bytes) { - m_pData->size = 0; + if (self->m_pData->size <= bytes) { + self->m_pData->size = 0; } else { - m_pData->size -= bytes; + self->m_pData->size -= bytes; } // move the unserializer back if necessary - if (m_position >= m_pData->size) { - m_position = m_pData->size; + if (self->m_position >= self->m_pData->size) { + self->m_position = self->m_pData->size; } } // extend the storage without changing the size of the data -bool ByteStream::extend(uint32_t size) +bool ByteStream_extend(ByteStream *self, uint32_t size) { if (!size) { // ??? return true; } // if the buffer is empty just initialize a new buffer - if (!m_capacity) { - return init(size); + if (!self->m_capacity) { + return ByteStream_init(self, size); } // new size is current plus extension amount - uint32_t buffer_size = m_capacity + size + 3; + uint32_t buffer_size = self->m_capacity + size + 3; // round size up to nearest 4 and add the size of the base raw buffer buffer_size -= buffer_size % 4; - // the size of the actual new block of memory - uint32_t new_size = buffer_size + sizeof(RawBuffer); - RawBuffer *temp = (RawBuffer *)vrealloc(m_pData, new_size); - if (!temp) { - ERROR_OUT_OF_MEMORY(); + // grow the buffer in the arena, this updates m_pData if it moves + if (!ByteStream_resizeBuffer(self, buffer_size)) { return false; } - // calc the region that was newly allocated by realloc - uint8_t *new_mem = ((uint8_t *)temp) + sizeof(RawBuffer) + m_capacity; - size_t new_mem_size = buffer_size - m_capacity; + // calc the region that was newly added to the buffer + uint8_t *new_mem = ((uint8_t *)self->m_pData) + sizeof(RawBuffer) + self->m_capacity; + size_t new_mem_size = buffer_size - self->m_capacity; // zero out the new memory memset(new_mem, 0, new_mem_size); - // update the pointer and size of the serial buffer - m_pData = temp; - m_capacity = buffer_size; + // update the size of the serial buffer + self->m_capacity = buffer_size; return true; } -bool ByteStream::compress() +bool ByteStream_compress(ByteStream *self) { // only compress if we have valid data - if (!m_pData) { + if (!self->m_pData) { DEBUG_LOG("No data to compress"); return false; } // recalculate the CRC even if we don't compress, this ensures the // CRC is always up to date as long as compress() is called - recalcCRC(); + ByteStream_recalcCRC(self, false); // don't actually perform any compression if VORTEX SLIM is enabled #if VORTEX_SLIM == 0 // check to see if the buffer is already compressed - if (is_compressed()) { + if (ByteStream_is_compressed(self)) { // already compressed return true; } // use LZ4_compressBound for the output buffer size, it may be larger // but it will allow for faster compression then we can shrink it after - ByteStream compressedBuffer(compress_size(m_pData->size)); + ByteStream compressedBuffer; + ByteStream_makeBuf(&compressedBuffer, compress_size(self->m_pData->size), NULL); // compress the data - int compressedSize = compress_buffer(m_pData->buf, compressedBuffer.m_pData->buf, - m_pData->size, compressedBuffer.m_capacity); + int compressedSize = compress_buffer(self->m_pData->buf, compressedBuffer.m_pData->buf, + self->m_pData->size, compressedBuffer.m_capacity); // check for compression error if (compressedSize < 0) { DEBUG_LOGF("Failed to compress, error: %d", compressedSize); + ByteStream_destroy(&compressedBuffer); return false; } // check for useless compression - if (!compressedSize || (uint32_t)compressedSize >= m_pData->size) { + if (!compressedSize || (uint32_t)compressedSize >= self->m_pData->size) { // cannot compress, or compressed no smaller than original + ByteStream_destroy(&compressedBuffer); return true; } // update the buffer size compressedBuffer.m_pData->size = compressedSize; // buffer is now compressed - compressedBuffer.m_pData->flags = (m_pData->flags | BUFFER_FLAG_COMRPESSED); + compressedBuffer.m_pData->flags = (self->m_pData->flags | BUFFER_FLAG_COMRPESSED); // recalc the crc on the data buffer - compressedBuffer.m_pData->recalcCRC(); + RawBuffer_recalcCRC(compressedBuffer.m_pData); // shrink buffer capacity to match size - compressedBuffer.shrink(); + ByteStream_shrink(&compressedBuffer); // move into self - compressedBuffer.move(this); + ByteStream_move(&compressedBuffer, self); #endif return true; } -bool ByteStream::decompress() +bool ByteStream_decompress(ByteStream *self) { // ensure the internal buffer is sane after reading it out, this // prevents segfaults if the internal size reports larger than capacity - sanity(); + ByteStream_sanity(self); // only decompress if we have valid data - if (!m_pData || !m_pData->verify()) { + if (!self->m_pData || !RawBuffer_verify(self->m_pData)) { DEBUG_LOG("Cannot verify crc, not decompressing"); return false; } // don't actually perform any compression if VORTEX SLIM is enabled #if VORTEX_SLIM == 0 - if (!is_compressed()) { + if (!ByteStream_is_compressed(self)) { // already decompressed return true; } @@ -269,310 +452,312 @@ bool ByteStream::decompress() uint32_t multiple = 1; int decompressedSize = 0; ByteStream decompressedBuffer; + ByteStream_make(&decompressedBuffer); do { // double the amount each time multiple *= 2; // create buffer to receive decompressed data - decompressedBuffer.init(m_pData->size * multiple); + ByteStream_init(&decompressedBuffer, self->m_pData->size * multiple); // decompress the data - decompressedSize = decompress_buffer(m_pData->buf, decompressedBuffer.m_pData->buf, - m_pData->size, decompressedBuffer.m_capacity); + decompressedSize = decompress_buffer(self->m_pData->buf, decompressedBuffer.m_pData->buf, + self->m_pData->size, decompressedBuffer.m_capacity); } while (decompressedSize < 0 && multiple < 512); // still error if (decompressedSize < 0) { ERROR_LOGF("Failed to decompress: %d", decompressedSize); + ByteStream_destroy(&decompressedBuffer); return false; } // size changed decompressedBuffer.m_pData->size = decompressedSize; // data is no longer compressed - decompressedBuffer.m_pData->flags = (m_pData->flags & ~BUFFER_FLAG_COMRPESSED); + decompressedBuffer.m_pData->flags = (self->m_pData->flags & ~BUFFER_FLAG_COMRPESSED); // recalc crc of buffer - decompressedBuffer.m_pData->recalcCRC(); - DEBUG_LOGF("Decompressed %u to %u bytes", m_pData->size, decompressedBuffer.m_pData->size); + RawBuffer_recalcCRC(decompressedBuffer.m_pData); + DEBUG_LOGF("Decompressed %u to %u bytes", self->m_pData->size, decompressedBuffer.m_pData->size); // shrink buffer capacity to match size - decompressedBuffer.shrink(); + ByteStream_shrink(&decompressedBuffer); // move into self - decompressedBuffer.move(this); + ByteStream_move(&decompressedBuffer, self); #endif return true; } -uint32_t ByteStream::recalcCRC(bool force) +uint32_t ByteStream_recalcCRC(ByteStream *self, bool force) { - if (!m_pData || !m_pData->size) { + if (!self->m_pData || !self->m_pData->size) { return 0; } // is the crc dirty? // or is the recalc being forced anyway? - if (!force && !(m_pData->flags & BUFFER_FLAG_DIRTY)) { + if (!force && !(self->m_pData->flags & BUFFER_FLAG_DIRTY)) { return 0; } // re-calculate the CRC on the buffer - m_pData->recalcCRC(); + RawBuffer_recalcCRC(self->m_pData); // unset dirty flag - m_pData->flags &= ~BUFFER_FLAG_DIRTY; + self->m_pData->flags &= ~BUFFER_FLAG_DIRTY; // return the newly calculated crc - return m_pData->crc32; + return self->m_pData->crc32; } -void ByteStream::sanity() +void ByteStream_sanity(ByteStream *self) { // to ensure size never exceeds the buffer capacity - if (m_pData && m_pData->size > m_capacity) { - m_pData->size = m_capacity; + if (self->m_pData && self->m_pData->size > self->m_capacity) { + self->m_pData->size = self->m_capacity; } } -bool ByteStream::checkCRC() const +bool ByteStream_checkCRC(const ByteStream *self) { // don't check if dirty - if (!m_pData || isCRCDirty()) { + if (!self->m_pData || ByteStream_isCRCDirty(self)) { return false; } - return m_pData->verify(); + return RawBuffer_verify(self->m_pData); } -bool ByteStream::isCRCDirty() const +bool ByteStream_isCRCDirty(const ByteStream *self) { - return (m_pData && (m_pData->flags & BUFFER_FLAG_DIRTY) != 0); + return (self->m_pData && (self->m_pData->flags & BUFFER_FLAG_DIRTY) != 0); } -void ByteStream::setCRCDirty() +void ByteStream_setCRCDirty(ByteStream *self) { - if (!m_pData) { + if (!self->m_pData) { return; } // set the dirty flag - m_pData->flags |= BUFFER_FLAG_DIRTY; + self->m_pData->flags |= BUFFER_FLAG_DIRTY; } -bool ByteStream::serialize8(uint8_t byte) +bool ByteStream_serialize8(ByteStream *self, uint8_t byte) { //DEBUG_LOGF("Serialize8(): %u", byte); - if (!m_pData || (m_pData->size + sizeof(uint8_t)) > m_capacity) { - if (!extend(sizeof(uint32_t))) { + if (!self->m_pData || (self->m_pData->size + sizeof(uint8_t)) > self->m_capacity) { + if (!ByteStream_extend(self, sizeof(uint32_t))) { return false; } } - memcpy(m_pData->buf + m_pData->size, &byte, sizeof(uint8_t)); + memcpy(self->m_pData->buf + self->m_pData->size, &byte, sizeof(uint8_t)); // walk forward - m_pData->size += sizeof(uint8_t); + self->m_pData->size += sizeof(uint8_t); // dirty the crc - setCRCDirty(); + ByteStream_setCRCDirty(self); return true; } -bool ByteStream::serialize16(uint16_t bytes) +bool ByteStream_serialize16(ByteStream *self, uint16_t bytes) { //DEBUG_LOGF("Serialize16(): %u", bytes); - if (!m_pData || (m_pData->size + sizeof(uint16_t)) > m_capacity) { - if (!extend(sizeof(uint32_t))) { + if (!self->m_pData || (self->m_pData->size + sizeof(uint16_t)) > self->m_capacity) { + if (!ByteStream_extend(self, sizeof(uint32_t))) { return false; } } - memcpy(m_pData->buf + m_pData->size, &bytes, sizeof(uint16_t)); + memcpy(self->m_pData->buf + self->m_pData->size, &bytes, sizeof(uint16_t)); // walk forward - m_pData->size += sizeof(uint16_t); + self->m_pData->size += sizeof(uint16_t); // dirty the crc - setCRCDirty(); + ByteStream_setCRCDirty(self); return true; } -bool ByteStream::serialize32(uint32_t bytes) +bool ByteStream_serialize32(ByteStream *self, uint32_t bytes) { //DEBUG_LOGF("Serialize32(): %u", bytes); - if (!m_pData || (m_pData->size + sizeof(uint32_t)) > m_capacity) { - if (!extend(sizeof(uint32_t))) { + if (!self->m_pData || (self->m_pData->size + sizeof(uint32_t)) > self->m_capacity) { + if (!ByteStream_extend(self, sizeof(uint32_t))) { return false; } } - memcpy(m_pData->buf + m_pData->size, &bytes, sizeof(uint32_t)); + memcpy(self->m_pData->buf + self->m_pData->size, &bytes, sizeof(uint32_t)); // walk forward - m_pData->size += sizeof(uint32_t); + self->m_pData->size += sizeof(uint32_t); // dirty the crc - setCRCDirty(); + ByteStream_setCRCDirty(self); return true; } // reset the unserializer index so that unserialization will // begin from the start of the buffer -void ByteStream::resetUnserializer() +void ByteStream_resetUnserializer(ByteStream *self) { - moveUnserializer(0); + ByteStream_moveUnserializer(self, 0); } // move the unserializer index manually -void ByteStream::moveUnserializer(uint32_t idx) +void ByteStream_moveUnserializer(ByteStream *self, uint32_t idx) { - if (!m_pData) { + if (!self->m_pData) { return; } - if (idx >= m_pData->size) { + if (idx >= self->m_pData->size) { idx = 0; } - m_position = idx; + self->m_position = idx; } -bool ByteStream::unserializerAtEnd() const +bool ByteStream_unserializerAtEnd(const ByteStream *self) { // the serializer always points to the end of the buffer, // if the unserializer points there too then there's nothing // more for the unserializer to read - return m_pData && m_position == m_pData->size; + return self->m_pData && self->m_position == self->m_pData->size; } // unserialize data and walk the buffer that many bytes -bool ByteStream::unserialize8(uint8_t *byte) +bool ByteStream_unserialize8(ByteStream *self, uint8_t *byte) { - if (!m_pData || m_position >= m_pData->size || (m_pData->size - m_position) < sizeof(uint8_t)) { + if (!self->m_pData || self->m_position >= self->m_pData->size || (self->m_pData->size - self->m_position) < sizeof(uint8_t)) { return false; } - memcpy(byte, m_pData->buf + m_position, sizeof(uint8_t)); + memcpy(byte, self->m_pData->buf + self->m_position, sizeof(uint8_t)); //DEBUG_LOGF("Unserialize8(): %u", *byte); - m_position += sizeof(uint8_t); + self->m_position += sizeof(uint8_t); return true; } -bool ByteStream::unserialize16(uint16_t *bytes) +bool ByteStream_unserialize16(ByteStream *self, uint16_t *bytes) { - if (!m_pData || m_position >= m_pData->size || (m_pData->size - m_position) < sizeof(uint16_t)) { + if (!self->m_pData || self->m_position >= self->m_pData->size || (self->m_pData->size - self->m_position) < sizeof(uint16_t)) { return false; } - memcpy(bytes, m_pData->buf + m_position, sizeof(uint16_t)); + memcpy(bytes, self->m_pData->buf + self->m_position, sizeof(uint16_t)); //DEBUG_LOGF("Unserialize16(): %u", *bytes); - m_position += sizeof(uint16_t); + self->m_position += sizeof(uint16_t); return true; } -bool ByteStream::unserialize32(uint32_t *bytes) +bool ByteStream_unserialize32(ByteStream *self, uint32_t *bytes) { - if (!m_pData || m_position >= m_pData->size || (m_pData->size - m_position) < sizeof(uint32_t)) { + if (!self->m_pData || self->m_position >= self->m_pData->size || (self->m_pData->size - self->m_position) < sizeof(uint32_t)) { return false; } - memcpy(bytes, m_pData->buf + m_position, sizeof(uint32_t)); + memcpy(bytes, self->m_pData->buf + self->m_position, sizeof(uint32_t)); //DEBUG_LOGF("Unserialize32(): %u", *bytes); - m_position += sizeof(uint32_t); + self->m_position += sizeof(uint32_t); return true; } // unserialize data and erase it from the buffer -bool ByteStream::consume8(uint8_t *bytes) +bool ByteStream_consume8(ByteStream *self, uint8_t *bytes) { - if (!m_pData || m_pData->size < sizeof(uint8_t)) { + if (!self->m_pData || self->m_pData->size < sizeof(uint8_t)) { return false; } if (bytes) { // copy out the data - memcpy(bytes, m_pData->buf, sizeof(uint8_t)); + memcpy(bytes, self->m_pData->buf, sizeof(uint8_t)); } // adjust the size - m_pData->size -= sizeof(uint8_t); + self->m_pData->size -= sizeof(uint8_t); // shift the data - memmove(m_pData->buf, m_pData->buf + sizeof(uint8_t), m_pData->size); + memmove(self->m_pData->buf, self->m_pData->buf + sizeof(uint8_t), self->m_pData->size); // dirty the crc - setCRCDirty(); + ByteStream_setCRCDirty(self); return true; } -bool ByteStream::consume16(uint16_t *bytes) +bool ByteStream_consume16(ByteStream *self, uint16_t *bytes) { - if (!m_pData || m_pData->size < sizeof(uint16_t)) { + if (!self->m_pData || self->m_pData->size < sizeof(uint16_t)) { return false; } if (bytes) { // copy out the data - memcpy(bytes, m_pData->buf, sizeof(uint16_t)); + memcpy(bytes, self->m_pData->buf, sizeof(uint16_t)); } // adjust the size - m_pData->size -= sizeof(uint16_t); + self->m_pData->size -= sizeof(uint16_t); // shift the data - memmove(m_pData->buf, m_pData->buf + sizeof(uint16_t), m_pData->size); + memmove(self->m_pData->buf, self->m_pData->buf + sizeof(uint16_t), self->m_pData->size); // dirty the crc - setCRCDirty(); + ByteStream_setCRCDirty(self); return true; } -bool ByteStream::consume32(uint32_t *bytes) +bool ByteStream_consume32(ByteStream *self, uint32_t *bytes) { - if (!m_pData || m_pData->size < sizeof(uint32_t)) { + if (!self->m_pData || self->m_pData->size < sizeof(uint32_t)) { return false; } if (bytes) { // copy out the data - memcpy(bytes, m_pData->buf, sizeof(uint32_t)); + memcpy(bytes, self->m_pData->buf, sizeof(uint32_t)); } // adjust the size - m_pData->size -= sizeof(uint32_t); + self->m_pData->size -= sizeof(uint32_t); // shift the data - memmove(m_pData->buf, m_pData->buf + sizeof(uint32_t), m_pData->size); + memmove(self->m_pData->buf, self->m_pData->buf + sizeof(uint32_t), self->m_pData->size); // dirty the crc - setCRCDirty(); + ByteStream_setCRCDirty(self); return true; } -bool ByteStream::consume(uint32_t size, void *bytes) +bool ByteStream_consume(ByteStream *self, uint32_t size, void *bytes) { - if (!m_pData || m_position >= m_pData->size || (m_pData->size - m_position) < size) { + if (!self->m_pData || self->m_position >= self->m_pData->size || (self->m_pData->size - self->m_position) < size) { return false; } if (bytes) { // copy out the data - memcpy(bytes, m_pData->buf, size); + memcpy(bytes, self->m_pData->buf, size); } // adjust the size - m_pData->size -= size; + self->m_pData->size -= size; // shift the data - memmove(m_pData->buf, m_pData->buf + size, m_pData->size); + memmove(self->m_pData->buf, self->m_pData->buf + size, self->m_pData->size); // dirty the crc - setCRCDirty(); + ByteStream_setCRCDirty(self); return true; } -uint8_t ByteStream::peek8() const +uint8_t ByteStream_peek8(const ByteStream *self) { - if (!m_pData) { + if (!self->m_pData) { return 0; } - return *(uint8_t *)frontUnserializer(); + return *(uint8_t *)ByteStream_frontUnserializer(self); } -uint16_t ByteStream::peek16() const +uint16_t ByteStream_peek16(const ByteStream *self) { - if (!m_pData) { + if (!self->m_pData) { return 0; } - return *(uint16_t *)frontUnserializer(); + return *(uint16_t *)ByteStream_frontUnserializer(self); } -uint32_t ByteStream::peek32() const +uint32_t ByteStream_peek32(const ByteStream *self) { - if (!m_pData) { + if (!self->m_pData) { return 0; } - return *(uint32_t *)frontUnserializer(); + return *(uint32_t *)ByteStream_frontUnserializer(self); } -bool ByteStream::is_compressed() const +bool ByteStream_is_compressed(const ByteStream *self) { - if (!m_pData) { + if (!self->m_pData) { return false; } - return (m_pData->flags & BUFFER_FLAG_COMRPESSED) != 0; + return (self->m_pData->flags & BUFFER_FLAG_COMRPESSED) != 0; } -bool ByteStream::largeEnough(uint32_t amount) const +bool ByteStream_largeEnough(const ByteStream *self, uint32_t amount) { - if (!m_pData) { + if (!self->m_pData) { return false; } - return ((m_pData->size + amount) <= m_capacity); + return ((self->m_pData->size + amount) <= self->m_capacity); } // get width of bits that makes up value // ex: 3 -> 2 bits wide (11) // 9 -> 4 bits wide (1001) -uint32_t ByteStream::getWidth(uint32_t value) const +uint32_t ByteStream_getWidth(const ByteStream *self, uint32_t value) { if (!value) { return 0; diff --git a/VortexEngine/src/Serial/ByteStream.h b/VortexEngine/src/Serial/ByteStream.h index 722aa81a08..c1a4fa8639 100644 --- a/VortexEngine/src/Serial/ByteStream.h +++ b/VortexEngine/src/Serial/ByteStream.h @@ -2,6 +2,8 @@ #define BYTE_STREAM_H #include +#include +#include #include "../Log/Log.h" // disable warning about using flexible array at end of structure @@ -12,175 +14,59 @@ #pragma warning(disable : 4200) #endif -class FlashClass; +typedef struct FlashClass FlashClass; +typedef struct ByteStream ByteStream; + +// the structure of raw data that's written to storage +typedef struct RawBuffer { + // the size of the buf of data + uint32_t size; + // any special flags about the data (compression etc) + uint32_t flags; + // the crc32 for the data + uint32_t crc32; + // the start of the buf of data + uint8_t buf[]; +} RawBuffer; + +// hash the raw buffer into crc +static inline uint32_t RawBuffer_hash(const RawBuffer *self) +{ + uint32_t hash = 5381; + for (uint32_t i = 0; i < self->size; ++i) { + hash = ((hash << 5) + hash) + self->buf[i]; + } + return hash; +} + +// accumulate the data into the hash +static inline void RawBuffer_accumulate(RawBuffer *self, uint32_t val) +{ + self->crc32 = ((self->crc32 << 5) + self->crc32) + val; +} -class ByteStream +// veryify the crc +static inline bool RawBuffer_verify(const RawBuffer *self) +{ + // if the buffer is empty then 'verify' should just return true + if (!self->size) { + return true; + } + if (RawBuffer_hash(self) != self->crc32) { + DEBUG_LOGF("CRC mismatch: %x should be %x", RawBuffer_hash(self), self->crc32); + return false; + } + return true; +} + +// re-calculate the crc +static inline void RawBuffer_recalcCRC(RawBuffer *self) { - friend class Storage; - -public: - ByteStream(uint32_t size = 0, const uint8_t *buf = nullptr); - ~ByteStream(); - - // copy and assignment operators - ByteStream(const ByteStream &other); - void operator=(const ByteStream &other); - - // move operator since std::move not always available - void move(ByteStream *target); - - // used to initialize the full RawBuffer with a chunk of data, - // this includes the size, flags, crc, and data - bool rawInit(const uint8_t *rawdata, uint32_t size); - - // reset the buffer to a specific capcity - bool init(uint32_t capacity = 0, const uint8_t *buf = nullptr); - - // clear the buffer - void clear(); - - // shrink capacity down to size, to free unused space - bool shrink(); - - // append another buffer - bool append(const ByteStream &other); - - // extend the storage without changing the size of the data - bool extend(uint32_t size); - - // trim the storage and erase some bytes from the end - void trim(uint32_t bytes); - - // badly written in-place compression and decompression - // which just uses a table of values then compresses the - // data into indexes in the table packed into a buffer. - // Unfortunately sometimes compression does not yield a - // smaller buffer size, incases like this compress returns - // true but the data isn't compressed. Similarly, if that - // data is passed to decompress it will return true. - bool compress(); - bool decompress(); - - // re-calculate the built-in CRC on the bytestream if needed - // if the crc is up to date this will do nothing. Optionally - // force a re-calculation even if the dirty flag is missing. - // Will return the new CRC value. - uint32_t recalcCRC(bool force = false); - - // helper func to ensure the internal buffer is sane, use this - // after reading into the rawdata of the buffer - void sanity(); - // check the CRC without re-calculating, note, if the CRC is - // dirty then this will simply return false - bool checkCRC() const; - // check whether the data in the buffer has changed since the - // crc has been re-calculated, if it is dirty, call recalcCRC - bool isCRCDirty() const; - // mark the CRC as dirty (manually modified the buffer or something) - void setCRCDirty(); - - // serialize a byte into the buffer - bool serialize8(uint8_t byte); - bool serialize16(uint16_t bytes); - bool serialize32(uint32_t bytes); - - // reset the unserializer index - void resetUnserializer(); - // move the unserializer index manually - void moveUnserializer(uint32_t idx); - // check if the unserializer is at the end - bool unserializerAtEnd() const; - - // unserialize data out of the buffer - bool unserialize8(uint8_t *byte); - bool unserialize16(uint16_t *bytes); - bool unserialize32(uint32_t *bytes); - - // unserialize data and erase it from the buffer - bool consume8(uint8_t *byte = nullptr); - bool consume16(uint16_t *bytes = nullptr); - bool consume32(uint32_t *bytes = nullptr); - - // unserialize and consume a whole chunk - bool consume(uint32_t size, void *bytes = nullptr); - - uint8_t peek8() const; - uint16_t peek16() const; - uint32_t peek32() const; - - // overload [] for array access (no bounds check lol) - uint8_t &operator[](uint32_t index) { return m_pData->buf[index]; } - // overload (uint8_t *) for cast to buffer - operator uint8_t *() const { return m_pData ? m_pData->buf : nullptr; } - operator const uint8_t *() const { return m_pData ? m_pData->buf : nullptr; } - - // return the members - const uint8_t *data() const { return m_pData ? m_pData->buf : nullptr; } - void *rawData() const { return m_pData; } - uint16_t rawSize() const { return m_pData ? (uint16_t)m_pData->size + sizeof(RawBuffer) : 0; } - uint32_t size() const { return m_pData ? m_pData->size : 0; } - uint32_t capacity() const { return m_capacity; } - bool is_compressed() const; - uint32_t CRC() const { return m_pData ? m_pData->crc32 : 0; } - - uint8_t *frontUnserializer() const { return m_pData ? m_pData->buf + m_position : nullptr; } - -private: - // fetch pointers that walk the buffer to serialize/unserialize - //uint8_t *frontUnserializer() const { return m_pData ? m_pData->buf + m_position : nullptr; } - uint8_t *frontSerializer() const { return m_pData ? m_pData->buf + m_pData->size : nullptr; } - // check if the buffer is large enough for the amount - bool largeEnough(uint32_t amount) const; - // helper to get width of bits that makes up value - uint32_t getWidth(uint32_t value) const; - - // the structure of raw data that's written to storage - struct RawBuffer - { - RawBuffer() : size(0), flags(0), crc32(5381) {} - // hash the raw buffer into crc - uint32_t hash() const - { - uint32_t hash = 5381; - for (uint32_t i = 0; i < size; ++i) { - hash = ((hash << 5) + hash) + buf[i]; - } - return hash; - } - // accumulate the data into the hash - void accumulate(uint32_t val) - { - crc32 = ((crc32 << 5) + crc32) + val; - } - // veryify the crc - bool verify() const - { - // if the buffer is empty then 'verify' should just return true - if (!size) { - return true; - } - if (hash() != crc32) { - DEBUG_LOGF("CRC mismatch: %x should be %x", hash(), crc32); - return false; - } - return true; - } - // re-calculate the crc - void recalcCRC() - { - crc32 = hash(); - } - // the size of the buf of data - uint32_t size; - // any special flags about the data (compression etc) - uint32_t flags; - // the crc32 for the data - uint32_t crc32; - // the start of the buf of data - uint8_t buf[]; - }; - - // The raw buffer of data along with size and flags + self->crc32 = RawBuffer_hash(self); +} + +// The raw buffer of data along with size and flags +struct ByteStream { RawBuffer *m_pData; // the index in the raw buffer for unserialization uint16_t m_position; @@ -188,4 +74,153 @@ class ByteStream uint16_t m_capacity; }; +// setup the arena that all ByteStream buffers live in, this is +// done once at startup +bool ByteStream_initArena(void); +// the bytes of the arena currently used and the most ever used +uint32_t ByteStream_arenaUsed(void); +uint32_t ByteStream_arenaHighWater(void); + +void ByteStream_make(ByteStream *self); +void ByteStream_makeBuf(ByteStream *self, uint32_t size, const uint8_t *buf); +void ByteStream_destroy(ByteStream *self); + +// copy and assignment operators +void ByteStream_set(ByteStream *self, const ByteStream *other); + +// move operator since std::move not always available +void ByteStream_move(ByteStream *self, ByteStream *target); + +// used to initialize the full RawBuffer with a chunk of data, +// this includes the size, flags, crc, and data +bool ByteStream_rawInit(ByteStream *self, const uint8_t *rawdata, uint32_t size); + +// reset the buffer to a specific capcity +bool ByteStream_init(ByteStream *self, uint32_t capacity); +bool ByteStream_initBuf(ByteStream *self, uint32_t capacity, const uint8_t *buf); + +// clear the buffer +void ByteStream_clear(ByteStream *self); + +// shrink capacity down to size, to free unused space +bool ByteStream_shrink(ByteStream *self); + +// append another buffer +bool ByteStream_append(ByteStream *self, const ByteStream *other); + +// extend the storage without changing the size of the data +bool ByteStream_extend(ByteStream *self, uint32_t size); + +// trim the storage and erase some bytes from the end +void ByteStream_trim(ByteStream *self, uint32_t bytes); + +// badly written in-place compression and decompression +// which just uses a table of values then compresses the +// data into indexes in the table packed into a buffer. +// Unfortunately sometimes compression does not yield a +// smaller buffer size, incases like this compress returns +// true but the data isn't compressed. Similarly, if that +// data is passed to decompress it will return true. +bool ByteStream_compress(ByteStream *self); +bool ByteStream_decompress(ByteStream *self); + +// re-calculate the built-in CRC on the bytestream if needed +// if the crc is up to date this will do nothing. Optionally +// force a re-calculation even if the dirty flag is missing. +// Will return the new CRC value. +uint32_t ByteStream_recalcCRC(ByteStream *self, bool force); + +// helper func to ensure the internal buffer is sane, use this +// after reading into the rawdata of the buffer +void ByteStream_sanity(ByteStream *self); +// check the CRC without re-calculating, note, if the CRC is +// dirty then this will simply return false +bool ByteStream_checkCRC(const ByteStream *self); +// check whether the data in the buffer has changed since the +// crc has been re-calculated, if it is dirty, call recalcCRC +bool ByteStream_isCRCDirty(const ByteStream *self); +// mark the CRC as dirty (manually modified the buffer or something) +void ByteStream_setCRCDirty(ByteStream *self); + +// serialize a byte into the buffer +bool ByteStream_serialize8(ByteStream *self, uint8_t byte); +bool ByteStream_serialize16(ByteStream *self, uint16_t bytes); +bool ByteStream_serialize32(ByteStream *self, uint32_t bytes); + +// reset the unserializer index +void ByteStream_resetUnserializer(ByteStream *self); +// move the unserializer index manually +void ByteStream_moveUnserializer(ByteStream *self, uint32_t idx); +// check if the unserializer is at the end +bool ByteStream_unserializerAtEnd(const ByteStream *self); + +// unserialize data out of the buffer +bool ByteStream_unserialize8(ByteStream *self, uint8_t *byte); +bool ByteStream_unserialize16(ByteStream *self, uint16_t *bytes); +bool ByteStream_unserialize32(ByteStream *self, uint32_t *bytes); + +// unserialize data and erase it from the buffer +bool ByteStream_consume8(ByteStream *self, uint8_t *byte); +bool ByteStream_consume16(ByteStream *self, uint16_t *bytes); +bool ByteStream_consume32(ByteStream *self, uint32_t *bytes); + +// unserialize and consume a whole chunk +bool ByteStream_consume(ByteStream *self, uint32_t size, void *bytes); + +uint8_t ByteStream_peek8(const ByteStream *self); +uint16_t ByteStream_peek16(const ByteStream *self); +uint32_t ByteStream_peek32(const ByteStream *self); + +// overload [] for array access (no bounds check lol) +uint8_t *ByteStream_at(ByteStream *self, uint32_t index); + +// return the members +static inline const uint8_t *ByteStream_data(const ByteStream *self) +{ + return self->m_pData ? self->m_pData->buf : NULL; +} + +static inline void *ByteStream_rawData(const ByteStream *self) +{ + return self->m_pData; +} + +static inline uint16_t ByteStream_rawSize(const ByteStream *self) +{ + return self->m_pData ? (uint16_t)self->m_pData->size + sizeof(RawBuffer) : 0; +} + +static inline uint32_t ByteStream_size(const ByteStream *self) +{ + return self->m_pData ? self->m_pData->size : 0; +} + +static inline uint32_t ByteStream_capacity(const ByteStream *self) +{ + return self->m_capacity; +} + +bool ByteStream_is_compressed(const ByteStream *self); + +static inline uint32_t ByteStream_CRC(const ByteStream *self) +{ + return self->m_pData ? self->m_pData->crc32 : 0; +} + +static inline uint8_t *ByteStream_frontUnserializer(const ByteStream *self) +{ + return self->m_pData ? self->m_pData->buf + self->m_position : NULL; +} + +// fetch pointers that walk the buffer to serialize/unserialize +static inline uint8_t *ByteStream_frontSerializer(const ByteStream *self) +{ + return self->m_pData ? self->m_pData->buf + self->m_pData->size : NULL; +} + +// check if the buffer is large enough for the amount +bool ByteStream_largeEnough(const ByteStream *self, uint32_t amount); +// helper to get width of bits that makes up value +uint32_t ByteStream_getWidth(const ByteStream *self, uint32_t value); + #endif diff --git a/VortexEngine/src/Serial/Serial.cpp b/VortexEngine/src/Serial/Serial.cpp index 5079e80455..1447eb74fb 100644 --- a/VortexEngine/src/Serial/Serial.cpp +++ b/VortexEngine/src/Serial/Serial.cpp @@ -7,46 +7,48 @@ #include "../VortexEngine.h" +#include +#include + #ifdef VORTEX_LIB #include "VortexLib.h" -#include #endif -bool SerialComs::m_serialConnected = false; -uint32_t SerialComs::m_lastCheck = 0; +static bool s_serialConnected = false; +static uint32_t s_lastCheck = 0; // init serial -bool SerialComs::init() +bool SerialComs_init() { // Try connecting serial ? //checkSerial(); return true; } -void SerialComs::cleanup() +void SerialComs_cleanup() { } -bool SerialComs::isConnected() +bool SerialComs_isConnected() { - return m_serialConnected; + return s_serialConnected; } // check for any serial connection or messages -bool SerialComs::checkSerial() +bool SerialComs_checkSerial() { #if VORTEX_SLIM == 0 - if (isConnected()) { + if (SerialComs_isConnected()) { // already connected return true; } - uint32_t now = Time::getCurtime(); + uint32_t now = Time_getCurtime(); // don't check for serial too fast - if (m_lastCheck && (now - m_lastCheck) < MAX_SERIAL_CHECK_INTERVAL) { + if (s_lastCheck && (now - s_lastCheck) < MAX_SERIAL_CHECK_INTERVAL) { // can't check yet too soon return false; } - m_lastCheck = now; + s_lastCheck = now; #ifdef VORTEX_LIB if (!Vortex::vcallbacks()->serialCheck()) { return false; @@ -63,14 +65,14 @@ bool SerialComs::checkSerial() #endif #endif // serial is now connected - m_serialConnected = true; + s_serialConnected = true; return true; } -void SerialComs::write(const char *msg, ...) +void SerialComs_write(const char *msg, ...) { #if VORTEX_SLIM == 0 - if (!isConnected()) { + if (!SerialComs_isConnected()) { return; } va_list list; @@ -87,29 +89,29 @@ void SerialComs::write(const char *msg, ...) #endif } -void SerialComs::write(ByteStream &byteStream) +void SerialComs_writeStream(ByteStream *byteStream) { #if VORTEX_SLIM == 0 - if (!isConnected()) { + if (!SerialComs_isConnected()) { return; } - byteStream.recalcCRC(); - uint32_t size = byteStream.rawSize(); + ByteStream_recalcCRC(byteStream, false); + uint32_t size = ByteStream_rawSize(byteStream); #ifdef VORTEX_LIB Vortex::vcallbacks()->serialWrite((const uint8_t *)&size, sizeof(size)); - Vortex::vcallbacks()->serialWrite((const uint8_t *)byteStream.rawData(), byteStream.rawSize()); + Vortex::vcallbacks()->serialWrite((const uint8_t *)ByteStream_rawData(byteStream), ByteStream_rawSize(byteStream)); #else Serial.write((const uint8_t *)&size, sizeof(size)); - Serial.write((const uint8_t *)byteStream.rawData(), byteStream.rawSize()); + Serial.write((const uint8_t *)ByteStream_rawData(byteStream), ByteStream_rawSize(byteStream)); Serial.flush(); #endif #endif } -void SerialComs::read(ByteStream &byteStream) +void SerialComs_read(ByteStream *byteStream) { #if VORTEX_SLIM == 0 - if (!isConnected()) { + if (!SerialComs_isConnected()) { return; } #ifdef VORTEX_LIB @@ -129,15 +131,15 @@ void SerialComs::read(ByteStream &byteStream) #else byte = Serial.read(); #endif - byteStream.serialize8(byte); + ByteStream_serialize8(byteStream, byte); } while (--amt > 0); #endif } -bool SerialComs::dataReady() +bool SerialComs_dataReady() { #if VORTEX_SLIM == 0 - if (!isConnected()) { + if (!SerialComs_isConnected()) { return false; } #ifdef VORTEX_LIB @@ -148,4 +150,4 @@ bool SerialComs::dataReady() #else return false; #endif -} +} \ No newline at end of file diff --git a/VortexEngine/src/Serial/Serial.h b/VortexEngine/src/Serial/Serial.h index 0121b5bbb0..0702398773 100644 --- a/VortexEngine/src/Serial/Serial.h +++ b/VortexEngine/src/Serial/Serial.h @@ -1,41 +1,31 @@ #ifndef SERIAL_H #define SERIAL_H +#include #include -class ByteStream; +typedef struct ByteStream ByteStream; -// Really wish I could name this Serial but arduino ruined that for me -class SerialComs -{ -public: +// init serial +bool SerialComs_init(); +void SerialComs_cleanup(); - // init serial - static bool init(); - static void cleanup(); +// whether serial is initialized +bool SerialComs_isConnected(); - // whether serial is initialized - static bool isConnected(); +// check for any serial connection or messages +bool SerialComs_checkSerial(); - // check for any serial connection or messages - static bool checkSerial(); +// write a formatted message to serial +void SerialComs_write(const char *msg, ...); - // write a message to serial - static void write(const char *msg, ...); +// write raw data +void SerialComs_writeStream(ByteStream *byteStream); - // write raw data - static void write(ByteStream &byteStream); +// read a message from serial +void SerialComs_read(ByteStream *byteStream); - // read a message from serial - static void read(ByteStream &byteStream); +// data in the socket ready to read +bool SerialComs_dataReady(); - // data in the socket ready to read - static bool dataReady(); - -private: - // whether serial communications are initialized - static bool m_serialConnected; - static uint32_t m_lastCheck; -}; - -#endif +#endif \ No newline at end of file diff --git a/VortexEngine/src/Storage/Storage.cpp b/VortexEngine/src/Storage/Storage.cpp index 778234e047..4f9c23da50 100644 --- a/VortexEngine/src/Storage/Storage.cpp +++ b/VortexEngine/src/Storage/Storage.cpp @@ -1,5 +1,7 @@ #include "Storage.h" +#include "../Serial/ByteStream.h" + #include #include @@ -31,8 +33,8 @@ #define DEFAULT_STORAGE_FILENAME "FlashStorage.flash" #ifdef VORTEX_LIB -std::string Storage::m_storageFilename; -#define STORAGE_FILENAME m_storageFilename.c_str() +std::string Storage_m_storageFilename; +#define STORAGE_FILENAME Storage_m_storageFilename.c_str() #else #define STORAGE_FILENAME DEFAULT_STORAGE_FILENAME #endif @@ -54,28 +56,24 @@ std::string Storage::m_storageFilename; // The full header size includes the 12 bytes for the serialbuffer header #define STORAGE_HEADER_SIZE (HEADER_SIZE + 12) -uint32_t Storage::m_lastSaveSize = 0; +uint32_t Storage_m_lastSaveSize = 0; -Storage::Storage() -{ -} - -bool Storage::init() +bool Storage_init() { #ifdef VORTEX_LIB - if (!m_storageFilename.length() && Vortex::storageEnabled()) { - m_storageFilename = DEFAULT_STORAGE_FILENAME; + if (!Storage_m_storageFilename.length() && Vortex::storageEnabled()) { + Storage_m_storageFilename = DEFAULT_STORAGE_FILENAME; } #endif return true; } -void Storage::cleanup() +void Storage_cleanup() { } // store a serial buffer to storage -bool Storage::write(uint8_t slot, ByteStream &buffer) +bool Storage_write(uint8_t slot, ByteStream *buffer) { #ifdef VORTEX_LIB if (!Vortex::storageEnabled()) { @@ -84,8 +82,8 @@ bool Storage::write(uint8_t slot, ByteStream &buffer) } #endif // check size - uint16_t size = buffer.rawSize(); - if (buffer.rawSize() > MAX_MODE_SIZE) { + uint16_t size = ByteStream_rawSize(buffer); + if (ByteStream_rawSize(buffer) > MAX_MODE_SIZE) { ERROR_LOG("Buffer too big for storage space"); return false; } @@ -93,9 +91,9 @@ bool Storage::write(uint8_t slot, ByteStream &buffer) return false; } // just in case - buffer.recalcCRC(); + ByteStream_recalcCRC(buffer, false); #ifdef VORTEX_EMBEDDED - const uint8_t *buf = (const uint8_t *)buffer.rawData(); + const uint8_t *buf = (const uint8_t *)ByteStream_rawData(buffer); // the header is slot 0 and it gets 17 bytes in the start of the eeprom then // the next 3 modes are 76 bytes each taking up 228 bytes for a total of 245 if (slot < 4) { @@ -108,7 +106,7 @@ bool Storage::write(uint8_t slot, ByteStream &buffer) uint8_t slotSize = (slot == 0) ? STORAGE_HEADER_SIZE : MAX_MODE_SIZE; for (uint8_t i = 0; i < slotSize; ++i) { uint8_t b = (i < size) ? buf[i] : 0x00; - eepromWriteByte(eepSlot + i, b); + Storage_eepromWriteByte(eepSlot + i, b); } } else { // Then flash storage is 0x200 or 512 bytes which allows for 6 x 76 byte modes @@ -161,7 +159,7 @@ bool Storage::write(uint8_t slot, ByteStream &buffer) SetFilePointer(hFile, offset, NULL, FILE_BEGIN); uint8_t modeBuffer[MAX_MODE_SIZE] = {0}; // copy the mode data into a temp buffer - memcpy(modeBuffer, buffer.rawData(), size); + memcpy(modeBuffer, ByteStream_rawData(buffer), size); // then copy the full size of the temp buffer in if (!WriteFile(hFile, modeBuffer, MAX_MODE_SIZE, &written, NULL)) { // error @@ -177,18 +175,18 @@ bool Storage::write(uint8_t slot, ByteStream &buffer) fseek(f, offset, SEEK_SET); uint8_t modeBuffer[MAX_MODE_SIZE] = {0}; // copy the mode data into a temp buffer - memcpy(modeBuffer, buffer.rawData(), size); + memcpy(modeBuffer, ByteStream_rawData(buffer), size); if (!fwrite(modeBuffer, sizeof(char), MAX_MODE_SIZE, f)) { return false; } fclose(f); #endif // VORTEX_EMBEDDED - DEBUG_LOGF("Wrote %u bytes to storage (max: %u)", m_lastSaveSize, STORAGE_SIZE); + DEBUG_LOGF("Wrote %u bytes to storage (max: %u)", Storage_m_lastSaveSize, STORAGE_SIZE); return true; } // read a serial buffer from storage -bool Storage::read(uint8_t slot, ByteStream &buffer) +bool Storage_read(uint8_t slot, ByteStream *buffer) { #ifdef VORTEX_LIB if (!Vortex::storageEnabled()) { @@ -198,14 +196,14 @@ bool Storage::read(uint8_t slot, ByteStream &buffer) } #endif uint32_t size = MAX_MODE_SIZE; - if (size > STORAGE_SIZE || size < sizeof(ByteStream::RawBuffer) + 4 || slot >= NUM_MODE_SLOTS) { + if (size > STORAGE_SIZE || size < sizeof(RawBuffer) + 4 || slot >= NUM_MODE_SLOTS) { return false; } - if (!buffer.init(size)) { + if (!ByteStream_init(buffer, size)) { return false; } #ifdef VORTEX_EMBEDDED - uint8_t *buf = (uint8_t *)buffer.rawData(); + uint8_t *buf = (uint8_t *)ByteStream_rawData(buffer); volatile uint8_t *src; if (slot == 0) { // slot 0 is header eeprom src = (volatile uint8_t *)MAPPED_EEPROM_START; @@ -227,7 +225,7 @@ bool Storage::read(uint8_t slot, ByteStream &buffer) DWORD bytesRead = 0; DWORD offset = slot * MAX_MODE_SIZE; SetFilePointer(hFile, offset, NULL, FILE_BEGIN); - if (!ReadFile(hFile, buffer.rawData(), MAX_MODE_SIZE, &bytesRead, NULL)) { + if (!ReadFile(hFile, ByteStream_rawData(buffer), MAX_MODE_SIZE, &bytesRead, NULL)) { // error return false; } @@ -239,36 +237,48 @@ bool Storage::read(uint8_t slot, ByteStream &buffer) } long offset = slot * MAX_MODE_SIZE; fseek(f, offset, SEEK_SET); - if (!fread(buffer.rawData(), sizeof(char), MAX_MODE_SIZE, f)) { + if (!fread(ByteStream_rawData(buffer), sizeof(char), MAX_MODE_SIZE, f)) { return false; } fclose(f); #endif // ensure the internal buffer is sane after reading it out, this // prevents segfaults if the internal size reports larger than capacity - buffer.sanity(); + ByteStream_sanity(buffer); // check crc immediately since we read into raw data copying the // array could be dangerous - if (!buffer.checkCRC()) { - buffer.clear(); + if (!ByteStream_checkCRC(buffer)) { + ByteStream_clear(buffer); ERROR_LOG("Could not verify buffer"); return false; } - m_lastSaveSize = size; - DEBUG_LOGF("Loaded savedata (Size: %u)", buffer.size()); + Storage_m_lastSaveSize = size; + DEBUG_LOGF("Loaded savedata (Size: %u)", ByteStream_size(buffer)); return true; } -uint32_t Storage::lastSaveSize() +uint32_t Storage_lastSaveSize() { - return m_lastSaveSize; + return Storage_m_lastSaveSize; } +#ifdef VORTEX_LIB +void Storage_setStorageFilename(const std::string &name) +{ + Storage_m_storageFilename = name; +} + +std::string Storage_getStorageFilename() +{ + return Storage_m_storageFilename; +} +#endif + #ifdef VORTEX_EMBEDDED // write out the eeprom byte -void Storage::eepromWriteByte(uint16_t index, uint8_t in) +void Storage_eepromWriteByte(uint16_t index, uint8_t in) { - if (eepromReadByte(index) == in) { + if (Storage_eepromReadByte(index) == in) { return; } // The first two pages of the data goes into the eeprom and then the last page goes @@ -292,7 +302,7 @@ void Storage::eepromWriteByte(uint16_t index, uint8_t in) while (!(NVMCTRL.STATUS & NVMCTRL_EEBUSY_bm)); } -uint8_t Storage::eepromReadByte(uint16_t index) +uint8_t Storage_eepromReadByte(uint16_t index) { return *(volatile uint8_t *)(MAPPED_EEPROM_START + index); } diff --git a/VortexEngine/src/Storage/Storage.h b/VortexEngine/src/Storage/Storage.h index 80da3edcb9..8c43ff8577 100644 --- a/VortexEngine/src/Storage/Storage.h +++ b/VortexEngine/src/Storage/Storage.h @@ -1,47 +1,42 @@ #ifndef STORAGE_H #define STORAGE_H +#include #include #ifdef VORTEX_LIB #include #endif -class ByteStream; +typedef struct ByteStream ByteStream; -class Storage -{ - Storage(); -public: +// init storage +bool Storage_init(void); +void Storage_cleanup(void); - // init storage - static bool init(); - static void cleanup(); +// store a serial buffer to storage +bool Storage_write(uint8_t slot, ByteStream *buffer); +// read a serial buffer from storage +bool Storage_read(uint8_t slot, ByteStream *buffer); - // store a serial buffer to storage - static bool write(uint8_t slot, ByteStream &buffer); - // read a serial buffer from storage - static bool read(uint8_t slot, ByteStream &buffer); +// the last save size (use STORAGE_SIZE For total space) +uint32_t Storage_lastSaveSize(void); - // the last save size (use STORAGE_SIZE For total space) - static uint32_t lastSaveSize(); +// write out a byte to the eeprom +void Storage_eepromWriteByte(uint16_t index, uint8_t in); +// read a byte from the eeprom +uint8_t Storage_eepromReadByte(uint16_t index); #ifdef VORTEX_LIB - static void setStorageFilename(const std::string &name) { m_storageFilename = name; } - static std::string getStorageFilename() { return m_storageFilename; } +void Storage_setStorageFilename(const std::string &name); +std::string Storage_getStorageFilename(void); #endif -private: - // write out a single byte to the eeprom - static void eepromWriteByte(uint16_t index, uint8_t in); - static uint8_t eepromReadByte(uint16_t index); - #ifdef VORTEX_LIB - static std::string m_storageFilename; +extern std::string Storage_m_storageFilename; #endif - // the size of the last save - static uint32_t m_lastSaveSize; -}; +// the size of the last save +extern uint32_t Storage_m_lastSaveSize; -#endif +#endif \ No newline at end of file diff --git a/VortexEngine/src/Time/TimeControl.cpp b/VortexEngine/src/Time/TimeControl.cpp index 76afc8c816..65ecf4bde1 100644 --- a/VortexEngine/src/Time/TimeControl.cpp +++ b/VortexEngine/src/Time/TimeControl.cpp @@ -1,3 +1,6 @@ +#define _DEFAULT_SOURCE +#define _XOPEN_SOURCE 700 +#define _POSIX_C_SOURCE 200809L #include "TimeControl.h" #include @@ -27,17 +30,17 @@ static LARGE_INTEGER tps; static LARGE_INTEGER start; #endif -// static members +// file scope state, previously static class members #if VARIABLE_TICKRATE == 1 -uint32_t Time::m_tickrate = DEFAULT_TICKRATE; +static uint32_t m_tickrate = DEFAULT_TICKRATE; #endif -uint32_t Time::m_curTick = 0; +static uint32_t m_curTick = 0; #ifdef VORTEX_LIB -uint32_t Time::m_prevTime = 0; -uint32_t Time::m_firstTime = 0; -uint32_t Time::m_simulationTick = 0; -bool Time::m_isSimulation = false; -bool Time::m_instantTimestep = false; +static uint32_t m_prevTime = 0; +static uint32_t m_firstTime = 0; +static uint32_t m_simulationTick = 0; +static bool m_isSimulation = false; +static bool m_instantTimestep = false; #endif // Within this file TICKRATE may refer to the variable member @@ -48,7 +51,7 @@ bool Time::m_instantTimestep = false; #define TICKRATE DEFAULT_TICKRATE #endif -bool Time::init() +bool Time_init() { #ifdef VORTEX_EMBEDDED // initialize main clock @@ -70,13 +73,13 @@ bool Time::init() m_tickrate = DEFAULT_TICKRATE; #endif #ifdef VORTEX_LIB - m_firstTime = m_prevTime = microseconds(); + m_firstTime = m_prevTime = Time_microseconds(); m_simulationTick = 0; m_isSimulation = false; m_instantTimestep = false; #endif #if !defined(_WIN32) || defined(WASM) - start = microseconds(); + start = Time_microseconds(); #else QueryPerformanceFrequency(&tps); QueryPerformanceCounter(&start); @@ -84,11 +87,11 @@ bool Time::init() return true; } -void Time::cleanup() +void Time_cleanup() { } -void Time::tickClock() +void Time_tickClock() { // tick clock forward m_curTick++; @@ -110,7 +113,7 @@ void Time::tickClock() uint32_t elapsed_us; uint32_t us; do { - us = microseconds(); + us = Time_microseconds(); // detect rollover of microsecond counter if (us < m_prevTime) { // calculate wrapped around difference @@ -131,7 +134,7 @@ void Time::tickClock() // up being more accurate to poll QPF + QPC via microseconds() sleepTime = required - elapsed_us; } - Time::delayMicroseconds(sleepTime); + Time_delayMicroseconds(sleepTime); break; #endif // 1000us per ms, divided by tickrate gives @@ -139,24 +142,24 @@ void Time::tickClock() } while (elapsed_us < (1000000 / TICKRATE)); // store current time - m_prevTime = microseconds(); + m_prevTime = Time_microseconds(); #endif } // the real current time, bypass simulations, used by timers -uint32_t Time::getRealCurtime() +uint32_t Time_getRealCurtime() { return m_curTick; } -uint32_t Time::getTickrate() +uint32_t Time_getTickrate() { return TICKRATE; } // Set tickrate in Ticks Per Second (TPS) // The valid range for this is 1 <= x <= 1000000 -void Time::setTickrate(uint32_t tickrate) +void Time_setTickrate(uint32_t tickrate) { #if VARIABLE_TICKRATE == 1 if (!tickrate) { @@ -172,7 +175,7 @@ void Time::setTickrate(uint32_t tickrate) } #if VARIABLE_TICKRATE == 1 -uint32_t Time::millisecondsToTicks(uint32_t ms) +uint32_t Time_millisecondsToTicks(uint32_t ms) { // 0ms = 0 ticks if (!ms) { @@ -189,7 +192,7 @@ uint32_t Time::millisecondsToTicks(uint32_t ms) } #endif -uint32_t Time::microseconds() +uint32_t Time_microseconds() { #ifndef VORTEX_LIB // Embedded avr devices uint32_t ticks; @@ -215,7 +218,7 @@ uint32_t Time::microseconds() #ifdef VORTEX_EMBEDDED __attribute__ ((noinline)) #endif -void Time::delayMicroseconds(uint32_t us) +void Time_delayMicroseconds(uint32_t us) { #ifdef VORTEX_EMBEDDED #if F_CPU >= 20000000L @@ -257,12 +260,12 @@ void Time::delayMicroseconds(uint32_t us) #endif } -void Time::delayMilliseconds(uint32_t ms) +void Time_delayMilliseconds(uint32_t ms) { #ifdef VORTEX_EMBEDDED // not very accurate for (uint16_t i = 0; i < ms; ++i) { - delayMicroseconds(1000); + Time_delayMicroseconds(1000); } #elif defined(_WIN32) Sleep(ms); @@ -276,53 +279,64 @@ void Time::delayMilliseconds(uint32_t ms) // Start a time simulation, while the simulation is active you can // increment the 'current time' with tickSimulation() then when you // call endSimulation() the currentTime will be restored -uint32_t Time::startSimulation() +uint32_t Time_startSimulation() { m_simulationTick = 0; m_isSimulation = true; - return (uint32_t)getCurtime(); + return (uint32_t)Time_getCurtime(); } // Tick a time simulation forward, returning the next tick -uint32_t Time::tickSimulation() +uint32_t Time_tickSimulation() { return ++m_simulationTick; } // whether running time simulation -bool Time::isSimulation() +bool Time_isSimulation() { return m_isSimulation; } // get the current tick in the simulation -uint32_t Time::getSimulationTick() +uint32_t Time_getSimulationTick() { return m_simulationTick; } // Finish a time simulation -uint32_t Time::endSimulation() +uint32_t Time_endSimulation() { - uint32_t endTick = (uint32_t)getCurtime(); + uint32_t endTick = (uint32_t)Time_getCurtime(); m_simulationTick = 0; m_isSimulation = false; return endTick; } +// Toggle instant step +void Time_setInstantTimestep(bool instant) +{ + m_instantTimestep = instant; +} + +bool Time_isInstantStepping() +{ + return m_instantTimestep; +} + #endif #if TIMER_TEST == 1 #include -void Time::test() +void Time_test() { DEBUG_LOG("Starting Time class tests..."); // just return immediately when testing the time system, // this prevents the actual delay from occurring and // allows us to tick forward as fast as we want - setInstantTimestep(true); + Time_setInstantTimestep(true); // Test init function assert(init()); @@ -336,9 +350,9 @@ void Time::test() DEBUG_LOG("tickClock test passed"); // Test msToTicks function - assert(msToTicks(1000) == TICKRATE); - assert(msToTicks(500) == TICKRATE / 2); - assert(msToTicks(0) == 0); + assert(MS_TO_TICKS(1000) == TICKRATE); + assert(MS_TO_TICKS(500) == TICKRATE / 2); + assert(MS_TO_TICKS(0) == 0); DEBUG_LOG("msToTicks test passed"); // Test getRealCurtime function diff --git a/VortexEngine/src/Time/TimeControl.h b/VortexEngine/src/Time/TimeControl.h index 7c7a061984..1f61acb413 100644 --- a/VortexEngine/src/Time/TimeControl.h +++ b/VortexEngine/src/Time/TimeControl.h @@ -1,141 +1,121 @@ #ifndef TIME_CONTROL_H #define TIME_CONTROL_H +#include #include +#include #include "../VortexConfig.h" #include "../Leds/LedTypes.h" #if VARIABLE_TICKRATE == 1 -#define MS_TO_TICKS(ms) Time::millisecondsToTicks(ms) -#define SEC_TO_TICKS(s) Time::secondsToTicks(s) +#define MS_TO_TICKS(ms) Time_millisecondsToTicks(ms) +#define SEC_TO_TICKS(s) Time_secondsToTicks(s) #else #define MS_TO_TICKS(ms) (uint32_t)(((uint32_t)(ms) * DEFAULT_TICKRATE) / 1000) #define SEC_TO_TICKS(s) (uint32_t)((uint32_t)(s) * DEFAULT_TICKRATE) #endif #ifdef VORTEX_LIB -#define SIMULATION_TICK getSimulationTick() +#define SIMULATION_TICK Time_getSimulationTick() #else #define SIMULATION_TICK 0 #endif -class Time -{ - // private unimplemented constructor - Time(); - -public: - // opting for static class here because there should only ever be one - // Settings control object and I don't like singletons - static bool init(); - static void cleanup(); - - // tick the clock forward to millis() - static void tickClock(); - - // get the current tick, offset by any active simulation (simulation only exists in vortexlib) - // Exposing this in the header seems to save on space a non negligible amount, it is used a lot - // and exposing in the header probably allows the compiler to optimize away repititive calls - static uint32_t getCurtime() { return m_curTick + SIMULATION_TICK; } - - // this ignore simulation time, it's used by timers to make simulations work - static uint32_t getRealCurtime(); - - // Set tickrate in Ticks Per Second (TPS) - // The valid range for this is 1 <= x <= 1000000 - // - // Setting a value of 0 will restore the default - // - // NOTE: Patterns will not change when this changes, you - // must re-create the pattern for the change to take - // effect. This is done by design to allow the test - // framework to control the speed of patterns. - // See PatternBuilder.cpp for more info. - static void setTickrate(uint32_t tickrate = 0); - - // The current tickrate - static uint32_t getTickrate(); - - // these apis are only present if the tickrate can change +// Time is a stateless namespace of free functions, there should only ever +// be one instance of it and the tick state lives in TimeControl.cpp + +// opting for static class here because there should only ever be one +// Settings control object and I don't like singletons +bool Time_init(void); +void Time_cleanup(void); + +// tick the clock forward to millis() +void Time_tickClock(void); + +// get the current tick, offset by any active simulation (simulation only exists in vortexlib) +// Exposing this in the header seems to save on space a non negligible amount, it is used a lot +// and exposing in the header probably allows the compiler to optimize away repititive calls +static inline uint32_t Time_getCurtime(void); + +// this ignore simulation time, it's used by timers to make simulations work +uint32_t Time_getRealCurtime(void); + +// Set tickrate in Ticks Per Second (TPS) +// The valid range for this is 1 <= x <= 1000000 +// +// Setting a value of 0 will restore the default +// +// NOTE: Patterns will not change when this changes, you +// must re-create the pattern for the change to take +// effect. This is done by design to allow the test +// framework to control the speed of patterns. +// See PatternBuilder.cpp for more info. +void Time_setTickrate(uint32_t tickrate); + +// The current tickrate +uint32_t Time_getTickrate(void); + +// these apis are only present if the tickrate can change #if VARIABLE_TICKRATE == 1 - // convert milliseconds to a tickcount based on tickrate - static uint32_t millisecondsToTicks(uint32_t ms); +// convert milliseconds to a tickcount based on tickrate +uint32_t Time_millisecondsToTicks(uint32_t ms); - // convert seconds to a tickcount based on tickrate - static uint32_t secondsToTicks(uint32_t sec) { return millisecondsToTicks(sec * 1000); } +// convert seconds to a tickcount based on tickrate +static inline uint32_t Time_secondsToTicks(uint32_t sec) +{ + return Time_millisecondsToTicks(sec * 1000); +} #endif - // Current microseconds since startup, only use this for things like measuring rapid data transfer timings. - // If you just need to perform regular time checks for a pattern or some logic then use getCurtime() and measure - // time in ticks, use the SEC_TO_TICKS() or MS_TO_TICKS() macros to convert timings to measures of ticks for - // purpose of comparing against getCurtime() - static uint32_t microseconds(); +// Current microseconds since startup, only use this for things like measuring rapid data transfer timings. +// If you just need to perform regular time checks for a pattern or some logic then use Time_getCurtime() and measure +// time in ticks, use the SEC_TO_TICKS() or MS_TO_TICKS() macros to convert timings to measures of ticks for +// purpose of comparing against Time_getCurtime() +uint32_t Time_microseconds(void); - // delay for some number of microseconds or milliseconds, these are bad - static void delayMicroseconds(uint32_t us); - static void delayMilliseconds(uint32_t ms); +// delay for some number of microseconds or milliseconds, these are bad +void Time_delayMicroseconds(uint32_t us); +void Time_delayMilliseconds(uint32_t ms); #ifdef VORTEX_LIB - // Start a time simulation, while the simulation is active you can - // increment the 'current time' with tickSimulation() and all calls - // to Time::getCurtime will reflect the changes, then when you call - // endSimulation() the currentTime will be restored - static uint32_t startSimulation(); +// Start a time simulation, while the simulation is active you can +// increment the 'current time' with Time_tickSimulation() and all calls +// to Time_getCurtime will reflect the changes, then when you call +// endSimulation() the currentTime will be restored +uint32_t Time_startSimulation(void); - // Tick a time simulation forward, returning the next tick - static uint32_t tickSimulation(); +// Tick a time simulation forward, returning the next tick +uint32_t Time_tickSimulation(void); - // Whether running a time simulation - static bool isSimulation(); +// Whether running a time simulation +bool Time_isSimulation(void); - // get the current tick in the simulation - static uint32_t getSimulationTick(); +// get the current tick in the simulation +uint32_t Time_getSimulationTick(void); - // Finish a time simulation - static uint32_t endSimulation(); +// Finish a time simulation +uint32_t Time_endSimulation(void); - // Toggle instant step - static void setInstantTimestep(bool instant) { m_instantTimestep = instant; } - static bool isInstantStepping() { return m_instantTimestep; } +// Toggle instant step +void Time_setInstantTimestep(bool instant); +bool Time_isInstantStepping(void); #endif #if TIMER_TEST == 1 - static void test(); -#endif - -private: -#ifdef VORTEX_EMBEDDED - static void initMCUTime(); +void Time_test(void); #endif -#if VARIABLE_TICKRATE == 1 - // the number of ticks per second - static uint32_t m_tickrate; -#endif - - // global tick counter - static uint32_t m_curTick; - #ifdef VORTEX_LIB - // the last frame timestamp - static uint32_t m_prevTime; - - // the first timestamp - static uint32_t m_firstTime; - - // the current simulation offset, simulations are - // used to fastforward patterns and colorsets by - // simulating tick changes and running pattern logic - // multiple times in a single frame - static uint32_t m_simulationTick; - - // whether the timer is running a simulation - static bool m_isSimulation; - - // whether to disable timestep and instantly tick - static bool m_instantTimestep; +static inline uint32_t Time_getCurtime(void) +{ + return Time_getRealCurtime() + SIMULATION_TICK; +} +#else +static inline uint32_t Time_getCurtime(void) +{ + return Time_getRealCurtime(); +} #endif -}; #endif diff --git a/VortexEngine/src/Time/Timer.cpp b/VortexEngine/src/Time/Timer.cpp index e9f3072d45..3966c1e03b 100644 --- a/VortexEngine/src/Time/Timer.cpp +++ b/VortexEngine/src/Time/Timer.cpp @@ -5,97 +5,99 @@ #include "../Memory/Memory.h" #include "../Log/Log.h" -Timer::Timer() : - m_alarms(nullptr), - m_numAlarms(0), - m_curAlarm(0), - m_startTime(0) +void Timer_make(Timer *self) +{ + self->m_numAlarms = 0; + self->m_curAlarm = 0; + self->m_startTime = 0; #ifdef VORTEX_LIB - , m_simStartTime(0) + self->m_simStartTime = 0; #endif -{ } -Timer::~Timer() +void Timer_destroy(Timer *self) { - if (m_alarms) { - vfree(m_alarms); - m_alarms = nullptr; - } + // the alarms are stored inline, nothing to release + (void)self; } -void Timer::init(uint8_t flags, uint8_t alarm1, uint8_t alarm2, uint8_t alarm3, uint8_t alarm4) +void Timer_init5(Timer *self, uint8_t flags, uint8_t alarm1, uint8_t alarm2, uint8_t alarm3, uint8_t alarm4) { - reset(); - if ((flags & TIMER_ALARM_MASK) >= TIMER_1_ALARM) addAlarm(alarm1); - if ((flags & TIMER_ALARM_MASK) >= TIMER_2_ALARMS) addAlarm(alarm2); - if ((flags & TIMER_ALARM_MASK) >= TIMER_3_ALARMS) addAlarm(alarm3); - if ((flags & TIMER_ALARM_MASK) >= TIMER_4_ALARMS) addAlarm(alarm4); - if ((flags & TIMER_START) != 0) start(); + Timer_reset(self); + if ((flags & TIMER_ALARM_MASK) >= TIMER_1_ALARM) Timer_addAlarm1(self, alarm1); + if ((flags & TIMER_ALARM_MASK) >= TIMER_2_ALARMS) Timer_addAlarm1(self, alarm2); + if ((flags & TIMER_ALARM_MASK) >= TIMER_3_ALARMS) Timer_addAlarm1(self, alarm3); + if ((flags & TIMER_ALARM_MASK) >= TIMER_4_ALARMS) Timer_addAlarm1(self, alarm4); + if ((flags & TIMER_START) != 0) Timer_start0(self); } -AlarmID Timer::addAlarm(uint32_t interval) + +// arity shims for the optional trailing arguments +void Timer_init1(Timer *self, uint8_t flags) { Timer_init5(self, flags, 0, 0, 0, 0); } +void Timer_init2(Timer *self, uint8_t flags, uint8_t alarm1) { Timer_init5(self, flags, alarm1, 0, 0, 0); } +void Timer_init3(Timer *self, uint8_t flags, uint8_t alarm1, uint8_t alarm2) { Timer_init5(self, flags, alarm1, alarm2, 0, 0); } +void Timer_init4(Timer *self, uint8_t flags, uint8_t alarm1, uint8_t alarm2, uint8_t alarm3) { Timer_init5(self, flags, alarm1, alarm2, alarm3, 0); } +void Timer_restart0(Timer *self) { Timer_restart(self, 0); } +void Timer_start0(Timer *self) { Timer_start(self, 0); } + +AlarmID Timer_addAlarm1(Timer *self, uint32_t interval) { - void *temp = vrealloc(m_alarms, sizeof(uint32_t) * (m_numAlarms + 1)); - if (!temp) { - ERROR_OUT_OF_MEMORY(); - return false; + if (self->m_numAlarms >= TIMER_MAX_ALARMS) { + ERROR_LOG("Too many alarms"); + return ALARM_NONE; } - m_alarms = (uint32_t *)temp; - m_alarms[m_numAlarms] = interval; - return m_numAlarms++; + self->m_alarms[self->m_numAlarms] = interval; + return self->m_numAlarms++; } -void Timer::restart(uint32_t offset) +void Timer_restart(Timer *self, uint32_t offset) { - start(offset); + Timer_start(self, offset); // reset the current alarm - m_curAlarm = 0; + self->m_curAlarm = 0; } -void Timer::start(uint32_t offset) +void Timer_start(Timer *self, uint32_t offset) { // reset the start time - m_startTime = Time::getCurtime() + offset; + self->m_startTime = Time_getCurtime() + offset; #ifdef VORTEX_LIB - m_simStartTime = m_startTime; + self->m_simStartTime = self->m_startTime; #endif } -void Timer::reset() +void Timer_reset(Timer *self) { - vfree(m_alarms); - m_alarms = nullptr; - m_numAlarms = 0; - m_curAlarm = 0; - m_startTime = 0; + self->m_numAlarms = 0; + self->m_curAlarm = 0; + self->m_startTime = 0; #ifdef VORTEX_LIB - m_simStartTime = 0; + self->m_simStartTime = 0; #endif } -bool Timer::onStart() const +bool Timer_onStart(const Timer *self) { - if (Time::getCurtime() == getStartTime() && m_curAlarm == 0) { + if (Time_getCurtime() == Timer_getStartTime(self) && self->m_curAlarm == 0) { return true; } return false; } -bool Timer::onEnd() const +bool Timer_onEnd(const Timer *self) { - if (!m_alarms || !m_numAlarms) { + if (!self->m_numAlarms) { return false; } // the tick for when this alarm triggers - uint32_t alarmTime = m_alarms[m_curAlarm]; + uint32_t alarmTime = self->m_alarms[self->m_curAlarm]; if (alarmTime <= 1) { return true; } // grab curTime - uint32_t now = Time::getCurtime(); + uint32_t now = Time_getCurtime(); // get the start time of the timer - uint32_t startTime = getStartTime(); + uint32_t startTime = Timer_getStartTime(self); // time since start (forward or backwards) int32_t timeDiff = (int32_t)(int64_t)(now - startTime); // if no time since start then this definitely isn't the end @@ -106,14 +108,14 @@ bool Timer::onEnd() const return ((timeDiff % (alarmTime - 1)) == 0); } -AlarmID Timer::alarm() +AlarmID Timer_alarm(Timer *self) { - if (!m_numAlarms || !m_alarms || m_curAlarm == ALARM_NONE) { + if (!self->m_numAlarms || self->m_curAlarm == ALARM_NONE) { return ALARM_NONE; } - uint32_t now = Time::getCurtime(); + uint32_t now = Time_getCurtime(); // time since start (forward or backwards) - int32_t timeDiff = (int32_t)(int64_t)(now - getStartTime()); + int32_t timeDiff = (int32_t)(int64_t)(now - Timer_getStartTime(self)); if (timeDiff < 0) { return ALARM_NONE; } @@ -121,51 +123,51 @@ AlarmID Timer::alarm() if (timeDiff == 0) { return 0; } - uint32_t alarmTime = m_alarms[m_curAlarm]; + uint32_t alarmTime = self->m_alarms[self->m_curAlarm]; // if the current alarm duration is not a multiple of the current tick if (alarmTime && (timeDiff % alarmTime) != 0) { // then the alarm was not hit return ALARM_NONE; } // update the start time of the timer - setStartTime(now); + Timer_setStartTime(self, now); // increment current alarm then return that id - m_curAlarm = (AlarmID)((m_curAlarm + 1) % m_numAlarms); - return m_curAlarm; + self->m_curAlarm = (AlarmID)((self->m_curAlarm + 1) % self->m_numAlarms); + return self->m_curAlarm; } -uint32_t Timer::getStartTime() const +uint32_t Timer_getStartTime(const Timer *self) { #ifdef VORTEX_LIB // timers use a different 'start time' tracker in simulations so that // the startTime remains unchanged when the simulation ends - if (Time::isSimulation() && m_simStartTime) { - return m_simStartTime; + if (Time_isSimulation() && self->m_simStartTime) { + return self->m_simStartTime; } #endif - return m_startTime; + return self->m_startTime; } -void Timer::setStartTime(uint32_t tick) +void Timer_setStartTime(Timer *self, uint32_t tick) { #ifdef VORTEX_LIB // if this timer is running in a simulation then don't actually update // the starttime, instead update the simulation start time so that when // the simulation ends the startTime will not have changed at all - if (Time::isSimulation()) { + if (Time_isSimulation()) { // move the sim start time - m_simStartTime = tick; + self->m_simStartTime = tick; return; } #endif // move the start time forward - m_startTime = tick; + self->m_startTime = tick; } #if TIMER_TEST == 1 #include -void Timer::test() +void Timer_test(void) { DEBUG_LOG("Starting Timer class tests..."); Timer newTimer; @@ -204,7 +206,7 @@ void Timer::test() } else { assert(alarm == ALARM_NONE); } - Time::tickClock(); + Time_tickClock(); } #ifdef VORTEX_LIB @@ -219,7 +221,7 @@ void Timer::test() newTimer.start(); assert(newTimer.alarm() == simAlarm0); - Time::startSimulation(); + Time_startSimulation(); for (uint32_t t = 0; t <= 35; ++t) { AlarmID alarm = newTimer.alarm(); if (t == 0) { // 0th tick timer 0 fires as it begins @@ -237,12 +239,17 @@ void Timer::test() } else { assert(alarm == ALARM_NONE); } - Time::tickSimulation(); + Time_tickSimulation(); } - Time::endSimulation(); + Time_endSimulation(); DEBUG_LOG("Time simulation tests passed."); #endif DEBUG_LOG("Timer class tests completed successfully."); } #endif + +AlarmID Timer_addAlarm(Timer *self, uint32_t interval) +{ + return Timer_addAlarm1(self, interval); +} diff --git a/VortexEngine/src/Time/Timer.h b/VortexEngine/src/Time/Timer.h index bb69ab2530..928dc95ef4 100644 --- a/VortexEngine/src/Time/Timer.h +++ b/VortexEngine/src/Time/Timer.h @@ -2,8 +2,10 @@ #define TIMER_H #include +#include #include "../Leds/LedTypes.h" +#include "../VortexConfig.h" #define ALARM_NONE -1 @@ -24,59 +26,19 @@ typedef int8_t AlarmID; // start the alarm #define TIMER_START (1 << 7) -class Timer -{ -public: - Timer(); - ~Timer(); - - // init a timer with a number of alarms and optionally start it - void init(uint8_t flags = TIMER_FLAGS_NONE, uint8_t alarm1 = 0, - uint8_t alarm2 = 0, uint8_t alarm3 = 0, uint8_t alarm4 = 0); - - // add alarms to the timer and returns it's ID - // Alarm IDs start at 0 and count upward - AlarmID addAlarm(uint32_t interval); - - // restart the timer entirely - void restart(uint32_t offset = 0); - - // start the timer but don't change current alarm, this shifts - // the timer startTime but does not reset it's alarm state - void start(uint32_t offset = 0); - - // delete all alarms from the timer and reset - void reset(); - - // returns true when the current timer is starting, used for synchronization - bool onStart() const; - // returns true when the current timer is ending, used for synchronization - bool onEnd() const; - - // Will return the alarmID of the alarm that triggered - // If no alarm hits it will return ALARM_NONE. - // Outputs an integer indicating the number of times the alarm has triggered - // since the start time or the last detection of that alarm ID - AlarmID alarm(); - - // the number of alarms - uint32_t numAlarms() const { return m_numAlarms; } - uint32_t curAlarm() const { return m_curAlarm; } - - // the start time of the timer - uint32_t startTime() const { return m_startTime; } - -#if TIMER_TEST == 1 - static void test(); +// the maximum number of alarms a timer can hold, the alarms are stored +// inline in the timer so that adding alarms never touches the heap. The +// slim build only uses single alarm timers (basic patterns), the full build +// needs enough for the multi-led patterns and their sequence steps +#if VORTEX_SLIM == 1 +#define TIMER_MAX_ALARMS 1 +#else +#define TIMER_MAX_ALARMS ((LED_COUNT > 4) ? LED_COUNT : 4) #endif -private: - // helpers to set/get start time - uint32_t getStartTime() const; - void setStartTime(uint32_t); - +typedef struct Timer { // the list of alarms and number of alarms - uint32_t *m_alarms; + uint32_t m_alarms[TIMER_MAX_ALARMS]; uint8_t m_numAlarms; // the current alarm being checked AlarmID m_curAlarm; @@ -88,6 +50,71 @@ class Timer // the simulation start time uint32_t m_simStartTime; #endif -}; +} Timer; + +void Timer_make(Timer *self); +void Timer_destroy(Timer *self); + +// init a timer with a number of alarms and optionally start it +void Timer_init(Timer *self, uint8_t flags, uint8_t alarm1, uint8_t alarm2, uint8_t alarm3, uint8_t alarm4); +void Timer_init1(Timer *self, uint8_t flags); +void Timer_init2(Timer *self, uint8_t flags, uint8_t alarm1); +void Timer_init3(Timer *self, uint8_t flags, uint8_t alarm1, uint8_t alarm2); +void Timer_init4(Timer *self, uint8_t flags, uint8_t alarm1, uint8_t alarm2, uint8_t alarm3); +void Timer_init5(Timer *self, uint8_t flags, uint8_t alarm1, uint8_t alarm2, uint8_t alarm3, uint8_t alarm4); + +#define TIMER_NARG(_1, _2, _3, _4, _5, N, ...) N +#define TIMER_COUNT(...) TIMER_NARG(__VA_ARGS__, 5, 4, 3, 2, 1) +#define TIMER_CAT_(a, b) a##b +#define TIMER_INIT_(N, ...) TIMER_CAT_(Timer_init, N)(__VA_ARGS__) + +#define Timer_initN(self, ...) TIMER_INIT_(TIMER_COUNT(__VA_ARGS__), self, __VA_ARGS__) + +// add alarms to the timer and returns it's ID +// Alarm IDs start at 0 and count upward +AlarmID Timer_addAlarm(Timer *self, uint32_t interval); +AlarmID Timer_addAlarm1(Timer *self, uint32_t interval); + +// helpers to set/get start time +uint32_t Timer_getStartTime(const Timer *self); +void Timer_setStartTime(Timer *self, uint32_t tick); + +// restart the timer entirely +void Timer_restart(Timer *self, uint32_t offset); +void Timer_restart0(Timer *self); + +// start the timer but don't change current alarm, this shifts +// the timer startTime but does not reset it's alarm state +void Timer_start(Timer *self, uint32_t offset); +void Timer_start0(Timer *self); + +#define TIMER_OFF_(fn, N, ...) TIMER_CAT_(fn, N)(__VA_ARGS__) +#define Timer_restartN(self, ...) TIMER_OFF_(Timer_restart, TIMER_COUNT(__VA_ARGS__), self, __VA_ARGS__) +#define Timer_startN(self, ...) TIMER_OFF_(Timer_start, TIMER_COUNT(__VA_ARGS__), self, __VA_ARGS__) + +// delete all alarms from the timer and reset +void Timer_reset(Timer *self); + +// returns true when the current timer is starting, used for synchronization +bool Timer_onStart(const Timer *self); +// returns true when the current timer is ending, used for synchronization +bool Timer_onEnd(const Timer *self); + +// Will return the alarmID of the alarm that triggered +// If no alarm hits it will return ALARM_NONE. +// Outputs an integer indicating the number of times the alarm has triggered +// since the start time or the last detection of that alarm ID +AlarmID Timer_alarm(Timer *self); + +// the number of alarms +static inline uint32_t Timer_numAlarms(const Timer *self) { return self->m_numAlarms; } +static inline uint32_t Timer_curAlarm(const Timer *self) { return self->m_curAlarm; } + +// the start time of the timer +static inline uint32_t Timer_startTime(const Timer *self) { return self->m_startTime; } -#endif \ No newline at end of file +#if TIMER_TEST == 1 +void Timer_test(void); +#endif + +#endif diff --git a/VortexEngine/src/VortexConfig.h b/VortexEngine/src/VortexConfig.h index b04345cb0a..133070df1d 100644 --- a/VortexEngine/src/VortexConfig.h +++ b/VortexEngine/src/VortexConfig.h @@ -238,6 +238,39 @@ // Mostly for catching leaks or high memory usage in development. #define MAX_MEMORY 8000 +// Memory Pools +// +// The engine never allocates memory while running, all of the objects +// that come and go at runtime are carved out of fixed block pools which +// are allocated once at startup. Fixed blocks can never fragment memory, +// the only failure is running out of blocks so these are the maximum +// number of each object that can exist at the same time. +// +// Patterns: two leds worth for the current mode and the menu preview +// mode, plus one spare for a temporary pattern +#define PATTERN_POOL_SIZE 5 +// Instantiated modes: the current mode and one more being serialized +#define MODE_POOL_SIZE 2 +// Mode links: one for each mode in the mode list +#define MODELINK_POOL_SIZE MAX_MODES +// Menus: only one menu is ever open at a time +#define MENU_POOL_SIZE 1 +// ByteStreams: the data of every ByteStream lives in one arena, this holds +// the serialized form of every mode plus any temporary buffers used for +// saving, loading and transferring modes. On the device the arena is all +// of the free ram between the static data and the stack (where the heap +// would be) and it always leaves this many bytes free below the stack +// pointer, this is the same as the default malloc margin. Elsewhere the +// arena is a fixed size buffer of BYTESTREAM_ARENA_SIZE bytes. +#define BYTESTREAM_STACK_MARGIN 32 +// The margin alone is not enough because an allocation made while the stack +// is shallow can be overrun when the stack later grows deeper (for example +// saving all modes after receiving one), so the arena also never grows into +// this many bytes at the top of ram. This must cover the deepest stack the +// engine ever reaches, which is about 260 bytes. +#define BYTESTREAM_STACK_RESERVE 300 +#define BYTESTREAM_ARENA_SIZE 640 + // Log Level // // Set the logging level to control info/error/debug logs accordingly @@ -422,6 +455,14 @@ // See the below header for all of the available color constants #include "Colors/ColorConstants.h" +// static_assert spelling that works in both C11 and C++11, the sources +// are being converted to C but headers are shared during the transition +#if defined(__cplusplus) +#define STATIC_ASSERT(cond, msg) static_assert(cond, msg) +#else +#define STATIC_ASSERT(cond, msg) _Static_assert(cond, msg) +#endif + // Randomizer Menu Color #define RGB_MENU_RANDOMIZER RGB_WHITE1 @@ -602,6 +643,19 @@ #undef VORTEX_SLIM #define VORTEX_SLIM 0 +// VortexLib has plenty of memory and the editor/test framework juggle far +// more modes and patterns at once so give the pools lots of room +#undef PATTERN_POOL_SIZE +#define PATTERN_POOL_SIZE 128 +#undef MODE_POOL_SIZE +#define MODE_POOL_SIZE 16 +#undef MODELINK_POOL_SIZE +#define MODELINK_POOL_SIZE 128 +#undef MENU_POOL_SIZE +#define MENU_POOL_SIZE 2 +#undef BYTESTREAM_ARENA_SIZE +#define BYTESTREAM_ARENA_SIZE (256 * 1024) + // The test framework needs brighter menu colors can't really see them on the screen #undef RGB_MENU_RANDOMIZER #undef RGB_MENU_MODE_SHARING diff --git a/VortexEngine/src/VortexEngine.cpp b/VortexEngine/src/VortexEngine.cpp index 8afc4f4165..1c32ca0398 100644 --- a/VortexEngine/src/VortexEngine.cpp +++ b/VortexEngine/src/VortexEngine.cpp @@ -11,7 +11,9 @@ #include "Time/TimeControl.h" #include "Time/Timings.h" #include "Serial/Serial.h" +#include "Serial/ByteStream.h" #include "Modes/Modes.h" +#include "Patterns/PatternBuilder.h" #include "Menus/Menus.h" #include "Modes/Mode.h" #include "Leds/Leds.h" @@ -25,85 +27,108 @@ #include "VortexLib.h" #endif -// bool in vortexlib to simulate sleeping -volatile bool VortexEngine::m_sleeping = false; +// the sleep state, lives here in the engine +static volatile bool VortexEngine_m_sleeping = false; // whether the 'force sleep' option is available (hold down for long time) -bool VortexEngine::m_forceSleepEnabled = true; +static bool VortexEngine_m_forceSleepEnabled = true; // auto cycling -bool VortexEngine::m_autoCycle = false; +static bool VortexEngine_m_autoCycle = false; + +bool VortexEngine_isSleeping() +{ + return VortexEngine_m_sleeping; +} + +void VortexEngine_toggleForceSleep(bool enabled) +{ + VortexEngine_m_forceSleepEnabled = enabled; +} + +void VortexEngine_setAutoCycle(bool enabled) +{ + VortexEngine_m_autoCycle = enabled; +} -bool VortexEngine::init() +bool VortexEngine_init() { #ifdef VORTEX_EMBEDDED // clear the output pins to initialize everything - clearOutputPins(); + VortexEngine_clearOutputPins(); #endif // all of the global controllers - if (!SerialComs::init()) { + if (!ByteStream_initArena()) { + DEBUG_LOG("ByteStream arena failed to initialize"); + return false; + } + if (!SerialComs_init()) { DEBUG_LOG("Serial failed to initialize"); return false; } - if (!Time::init()) { + if (!Time_init()) { DEBUG_LOG("Time failed to initialize"); return false; } - if (!Storage::init()) { + if (!Storage_init()) { DEBUG_LOG("Storage failed to initialize"); return false; } #if IR_ENABLE_RECEIVER == 1 - if (!IRReceiver::init()) { + if (!IRReceiver_init()) { DEBUG_LOG("IRReceiver failed to initialize"); return false; } #endif #if IR_ENABLE_SENDER == 1 - if (!IRSender::init()) { + if (!IRSender_init()) { DEBUG_LOG("IRSender failed to initialize"); return false; } #endif #if VL_ENABLE_RECEIVER == 1 - if (!VLReceiver::init()) { + if (!VLReceiver_init()) { DEBUG_LOG("VLReceiver failed to initialize"); return false; } #endif #if VL_ENABLE_SENDER == 1 - if (!VLSender::init()) { + if (!VLSender_init()) { DEBUG_LOG("VLSender failed to initialize"); return false; } #endif - if (!Leds::init()) { + if (!Leds_init()) { DEBUG_LOG("Leds failed to initialize"); return false; } - if (!Buttons::init()) { + if (!Buttons_init()) { DEBUG_LOG("Buttons failed to initialize"); return false; } - if (!Menus::init()) { + if (!PatternBuilder_init()) { + DEBUG_LOG("PatternBuilder failed to initialize"); + return false; + } + if (!Menus_init()) { DEBUG_LOG("Menus failed to initialize"); return false; } - if (!Modes::init()) { + if (!Modes_init()) { DEBUG_LOG("Settings failed to initialize"); return false; } #if COMPRESSION_TEST == 1 - compressionTest(); + VortexEngine_compressionTest(); #endif #if SERIALIZATION_TEST == 1 - serializationTest(); + VortexEngine_serializationTest(); #endif #if TIMER_TEST == 1 - timerTest(); + VortexEngine_timerTest(); #endif #ifdef VORTEX_EMBEDDED @@ -113,13 +138,13 @@ bool VortexEngine::init() // Set clock source to CPU divided by 1, Keep running in sleep mode, Enable TCB TCB0.CTRLA = TCB_CLKSEL_CLKDIV1_gc | TCB_RUNSTDBY_bm | TCB_ENABLE_bm; // set the state of the mosfet based on whether the chip is locked or not - enableMOSFET(!Modes::locked()); + VortexEngine_enableMOSFET(!Modes_locked()); // setup sleep mode for standby set_sleep_mode(SLEEP_MODE_STANDBY); // enable interrupts sei(); // standby indefinitely while the ISR runs VortexEngine::tick - while (!m_sleeping) { + while (!VortexEngine_m_sleeping) { sleep_mode(); } #endif @@ -127,90 +152,90 @@ bool VortexEngine::init() return true; } -void VortexEngine::cleanup() +void VortexEngine_cleanup() { // cleanup in reverse order // NOTE: the embedded doesn't actually cleanup, // but the test frameworks do #ifdef VORTEX_LIB - Modes::cleanup(); - Menus::cleanup(); - Buttons::cleanup(); - Leds::cleanup(); + Modes_cleanup(); + Menus_cleanup(); + Buttons_cleanup(); + Leds_cleanup(); #if VL_ENABLE_SENDER == 1 - VLSender::cleanup(); + VLSender_cleanup(); #endif #if VL_ENABLE_RECEIVER == 1 - VLReceiver::cleanup(); + VLReceiver_cleanup(); #endif #if IR_ENABLE_SENDER == 1 - IRSender::cleanup(); + IRSender_cleanup(); #endif #if IR_ENABLE_RECEIVER == 1 - IRReceiver::cleanup(); + IRReceiver_cleanup(); #endif - Storage::cleanup(); - Time::cleanup(); - SerialComs::cleanup(); + Storage_cleanup(); + Time_cleanup(); + SerialComs_cleanup(); #endif } -void VortexEngine::tick() +void VortexEngine_tick() { #ifdef VORTEX_LIB - if (m_sleeping) { + if (VortexEngine_m_sleeping) { // update the buttons to check for wake - Buttons::update(); + Buttons_update(); // several fast clicks will unlock the device - if (Modes::locked() && g_pButton->onConsecutivePresses(DEVICE_LOCK_CLICKS - 1)) { + if (Modes_locked() && Button_onConsecutivePresses(g_pButton, DEVICE_LOCK_CLICKS - 1)) { // turn off the lock flag and save it to disk - Modes::setLocked(false); + Modes_setLocked(false, true); } // check for any kind of press to wakeup - if (g_pButton->check() || g_pButton->onRelease() || !Vortex::sleepEnabled()) { - wakeup(); + if (Button_check(g_pButton) || Button_onRelease(g_pButton) || !Vortex::sleepEnabled()) { + VortexEngine_wakeup(true); } return; } #endif // tick the current time counter forward - Time::tickClock(); + Time_tickClock(); // poll the button(s) and update the button object states - Buttons::update(); + Buttons_update(); // run the main logic for the engine - runMainLogic(); + VortexEngine_runMainLogic(); // update the leds - Leds::update(); + Leds_update(); } // warning this function is quite heavy at this point, it could probably be split -void VortexEngine::runMainLogic() +void VortexEngine_runMainLogic() { // the current tick - uint32_t now = Time::getCurtime(); + uint32_t now = Time_getCurtime(); // if the device is locked then that takes priority over all, while locked the // device will only listen for clicks to wakeup momentarily then go back to sleep - if (Modes::locked()) { + if (Modes_locked()) { // several fast clicks will unlock the device - if (g_pButton->onConsecutivePresses(DEVICE_LOCK_CLICKS - 1)) { + if (Button_onConsecutivePresses(g_pButton, DEVICE_LOCK_CLICKS - 1)) { // turn off the lock flag and save it to disk - Modes::setLocked(false); + Modes_setLocked(false, true); #ifdef VORTEX_EMBEDDED // then enable the mosfet - enableMOSFET(true); + VortexEngine_enableMOSFET(true); #endif } else if (now > (CONSECUTIVE_WINDOW_TICKS * DEVICE_LOCK_CLICKS)) { // go back to sleep if they don't unlock in time, don't save - enterSleep(false); + VortexEngine_enterSleep(false); } // OPTIONAL: render a dim led during unlock window waiting for clicks? - //Leds::setIndex(LED_1, RGB_RED4); - Leds::clearAll(); + //Leds_setIndex(LED_1, RGB_RED4); + Leds_clearAll(); // don't do anything else while locked, just return return; } @@ -218,26 +243,26 @@ void VortexEngine::runMainLogic() // the device is not locked, proceed with regular logic // if the button hasn't been released since turning on then there is custom logic - if (g_pButton->releaseCount() == 0) { - if (!Modes::load()) { + if (Button_releaseCount(g_pButton) == 0) { + if (!Modes_load()) { return; } // if the button is held for 2 seconds from off, switch to on click mode on // the last mode shown before sleep - if (!Modes::keychainModeEnabled() && now == ONE_CLICK_THRESHOLD_TICKS && g_pButton->isPressed()) { + if (!Modes_keychainModeEnabled() && now == ONE_CLICK_THRESHOLD_TICKS && Button_isPressed(g_pButton)) { // whether oneclick mode is now enabled - bool isEnabledNow = !Modes::oneClickModeEnabled(); + bool isEnabledNow = !Modes_oneClickModeEnabled(); // toggle one click mode - Modes::setOneClickMode(isEnabledNow); + Modes_setOneClickMode(isEnabledNow, true); // if we turned it on then switch to that mode if (isEnabledNow) { - Modes::switchToStartupMode(); + Modes_switchToStartupMode(); } else { - Modes::setCurMode(0); + Modes_setCurMode(0); } // flash either low white or dim white2 to indicate // whether one-click mode has been turned on or off - Leds::holdAll(isEnabledNow ? RGB_WHITE0 : RGB_WHITE5); + Leds_holdAll(isEnabledNow ? RGB_WHITE0 : RGB_WHITE5); } return; } @@ -251,24 +276,24 @@ void VortexEngine::runMainLogic() if (now == 1) { // This check is for whether a new firmware was just flashed, if a new // firmware was flashed then write out a new save header - if (Modes::getFlag(MODES_FLAG_NEW_FIRMWARE)) { + if (Modes_getFlag(MODES_FLAG_NEW_FIRMWARE)) { // reset the flags back to normal - Modes::resetFlags(); + Modes_resetFlags(); // try to load the modes and hope it works, need the num modes to write a // new save header and they may have backed up and restored a custom number // of modes - Modes::load(); + Modes_load(); // then save the new header with current version and num modes etc - Modes::saveHeader(); + Modes_saveHeader(); } // then just gracefully go back to sleep to prevent the chip from turning // on randomly when it receives power - enterSleep(false); + VortexEngine_enterSleep(false); return; } #endif - if (!Modes::load()) { + if (!Modes_load()) { return; } @@ -276,53 +301,53 @@ void VortexEngine::runMainLogic() // just run the regular main logic of the system // re-enter keychain mode if it was never disabled - if (Modes::keychainModeEnabled() && !Menus::checkInMenu()) { + if (Modes_keychainModeEnabled() && !Menus_checkInMenu()) { // switch to the last mode we were on - Modes::switchToStartupMode(); + Modes_switchToStartupMode(); // enter keychain mode menu - Menus::openMenu(MENU_GLOBAL_BRIGHTNESS, true); + Menus_openMenu(MENU_GLOBAL_BRIGHTNESS, true); } // first look for the force-sleep and instant on/off toggle - const uint32_t holdTime = g_pButton->holdDuration(); + const uint32_t holdTime = Button_holdDuration(g_pButton); // force-sleep check takes precedence above all, but it does not run when keychain mode is enabled - if (m_forceSleepEnabled && holdTime >= FORCE_SLEEP_THRESHOLD_TICKS) { + if (VortexEngine_m_forceSleepEnabled && holdTime >= FORCE_SLEEP_THRESHOLD_TICKS) { // as long as they hold down past this threshold just turn off - if (g_pButton->isPressed()) { - Leds::clearAll(); + if (Button_isPressed(g_pButton)) { + Leds_clearAll(); return; } // but as soon as they actually release put the device to sleep - if (g_pButton->onRelease()) { + if (Button_onRelease(g_pButton)) { // do not save on force sleep - enterSleep(false); + VortexEngine_enterSleep(false); } return; } // run the menus to see if they are open and need to do anything - if (Menus::run()) { + if (Menus_run()) { // if they return true that means the menus are open and rendering so just return return; } // if the user releases the button after the sleep threshold and // we're still in menu state not open, then we can go to sleep - if (g_pButton->onRelease() && holdTime >= SLEEP_ENTER_THRESHOLD_TICKS) { + if (Button_onRelease(g_pButton) && holdTime >= SLEEP_ENTER_THRESHOLD_TICKS) { // enter sleep mode - enterSleep(); + VortexEngine_enterSleep(true); return; } // if the button is just held beyond the sleep threshold then - if (g_pButton->isPressed() && holdTime >= SLEEP_ENTER_THRESHOLD_TICKS) { + if (Button_isPressed(g_pButton) && holdTime >= SLEEP_ENTER_THRESHOLD_TICKS) { // clear all the leds for a short moment - Leds::clearAll(); + Leds_clearAll(); // then oncethe user holds past the sleep window threshold open up the menus if (holdTime >= (SLEEP_ENTER_THRESHOLD_TICKS + SLEEP_WINDOW_THRESHOLD_TICKS)) { // open the menu selection area DEBUG_LOG("Entering ring fill..."); - Menus::openMenuSelection(); + Menus_openMenuSelection(); } // don't play the modes because the user is going into menus return; @@ -331,47 +356,47 @@ void VortexEngine::runMainLogic() // lastly check if we are locking the device, which can only happen if they click the // button 5 times quickly when the device was off, so 4 times in the first x ticks because // the first click was used to wake the device and isn't counted in the consecutive clicks - if (now < (CONSECUTIVE_WINDOW_TICKS * DEVICE_LOCK_CLICKS) && g_pButton->onConsecutivePresses(DEVICE_LOCK_CLICKS - 1)) { + if (now < (CONSECUTIVE_WINDOW_TICKS * DEVICE_LOCK_CLICKS) && Button_onConsecutivePresses(g_pButton, DEVICE_LOCK_CLICKS - 1)) { #ifdef VORTEX_LIB if (!Vortex::lockEnabled()) { return; } #endif // lock and just go to sleep, don't need to reset consecutive press counter here - Modes::setLocked(true); - enterSleep(); + Modes_setLocked(true, true); + VortexEngine_enterSleep(true); return; } // toggle auto cycle mode with many clicks at main modes - if ((g_pButton->onRelease() && m_autoCycle) || g_pButton->onConsecutivePresses(AUTO_CYCLE_MODES_CLICKS)) { - m_autoCycle = !m_autoCycle; - Leds::holdAll(m_autoCycle ? RGB_GREEN : RGB_RED); + if ((Button_onRelease(g_pButton) && VortexEngine_m_autoCycle) || Button_onConsecutivePresses(g_pButton, AUTO_CYCLE_MODES_CLICKS)) { + VortexEngine_m_autoCycle = !VortexEngine_m_autoCycle; + Leds_holdAll(VortexEngine_m_autoCycle ? RGB_GREEN : RGB_RED); } // if auto cycle is enabled and the last switch was more than the delay ago - if (m_autoCycle && (Modes::lastSwitchTime() + AUTO_RANDOM_DELAY < now)) { + if (VortexEngine_m_autoCycle && (Modes_lastSwitchTime() + AUTO_RANDOM_DELAY < now)) { // then switch to the next mode automatically - Modes::nextMode(); + Modes_nextMode(); } // otherwise just play the modes - Modes::play(); + Modes_play(); } -bool VortexEngine::serializeVersion(ByteStream &stream) +bool VortexEngine_serializeVersion(ByteStream *stream) { // serialize the vortex version - if (!stream.serialize8((uint8_t)VORTEX_VERSION_MAJOR)) { + if (!ByteStream_serialize8(stream, (uint8_t)VORTEX_VERSION_MAJOR)) { return false; } - if (!stream.serialize8((uint8_t)VORTEX_VERSION_MINOR)) { + if (!ByteStream_serialize8(stream, (uint8_t)VORTEX_VERSION_MINOR)) { return false; } return true; } -bool VortexEngine::checkVersion(uint8_t major, uint8_t minor) +bool VortexEngine_checkVersion(uint8_t major, uint8_t minor) { if (major != VORTEX_VERSION_MAJOR) { return false; @@ -380,10 +405,10 @@ bool VortexEngine::checkVersion(uint8_t major, uint8_t minor) return true; } -Mode *VortexEngine::curMode() +Mode *VortexEngine_curMode() { #ifdef VORTEX_LIB - return Modes::curMode(); + return Modes_curMode(); #else // don't need this outside vortex lib return nullptr; @@ -391,71 +416,89 @@ Mode *VortexEngine::curMode() } #ifdef VORTEX_LIB -uint32_t VortexEngine::totalStorageSpace() +uint32_t VortexEngine_totalStorageSpace() { return STORAGE_SIZE; } -uint32_t VortexEngine::savefileSize() +uint32_t VortexEngine_savefileSize() { - return Storage::lastSaveSize(); + return Storage_lastSaveSize(); } #endif -void VortexEngine::enterSleep(bool save) +void VortexEngine_enterSleep(bool save) { DEBUG_LOG("Sleeping"); if (save) { // update the startup mode when going to sleep - Modes::setStartupMode(Modes::curModeIndex()); + Modes_setStartupMode(Modes_curModeIndex()); // save anything that hasn't been saved - Modes::saveStorage(); + Modes_saveStorage(); } // clear all the leds - Leds::clearAll(); - Leds::update(); + Leds_clearAll(); + Leds_update(); #ifdef VORTEX_EMBEDDED // init the output pins to prevent any floating pins - clearOutputPins(); + VortexEngine_clearOutputPins(); // close the mosfet so that power cannot flow to the leds - enableMOSFET(false); + VortexEngine_enableMOSFET(false); // delay for a bit to let the mosfet close and leds turn off - Time::delayMicroseconds(250); + Time_delayMicroseconds(250); // this is an ISR that runs in the timecontrol system to handle // micros, it will wake the device up periodically TCB0.INTCTRL = 0; TCB0.CTRLA = 0; // Enable wake on interrupt for the button - g_pButton->enableWake(); + Button_enableWake(g_pButton); // Set sleep mode to POWER DOWN mode set_sleep_mode(SLEEP_MODE_PWR_DOWN); // enable the sleep bool right before we enter sleep, this will allow // the main loop to break and return - m_sleeping = true; + VortexEngine_m_sleeping = true; // enter sleep sleep_mode(); #else // enable the sleep bool - m_sleeping = true; + VortexEngine_m_sleeping = true; #endif } -void VortexEngine::wakeup(bool reset) +#ifdef VORTEX_EMBEDDED +void VortexEngine_outOfMemory(void) +{ + // show red for a second so the failure is visible + Leds_setAll(RGB_RED); + Leds_update(); + for (uint16_t i = 0; i < 1000; ++i) { + Time_delayMicroseconds(1000); + } + // then turn off without saving, saving would need to allocate + VortexEngine_enterSleep(false); + // the button wakes the chip but this usually runs inside the tick + // interrupt so the wake interrupt can't run yet, just reset here instead + // of returning to whatever just failed to allocate + VortexEngine_wakeup(true); +} +#endif + +void VortexEngine_wakeup(bool reset) { DEBUG_LOG("Waking up"); #ifdef VORTEX_EMBEDDED // turn the LED mosfet back on - enableMOSFET(true); + VortexEngine_enableMOSFET(true); if (reset) { // just reset _PROTECTED_WRITE(RSTCTRL.SWRR, 1); } #else // need to fake the reset in vortexlib, lol this works I guess - m_sleeping = false; + VortexEngine_m_sleeping = false; if (reset) { - cleanup(); - init(); + VortexEngine_cleanup(); + VortexEngine_init(); } #endif } @@ -466,10 +509,10 @@ ISR(TCB0_INT_vect) { // Clear interrupt flag TCB0.INTFLAGS = TCB_CAPT_bm; - VortexEngine::tick(); + VortexEngine_tick(); } -void VortexEngine::clearOutputPins() +void VortexEngine_clearOutputPins() { // Set all pins to input with pull-ups PORTA.DIRCLR = 0xFF; @@ -499,7 +542,7 @@ void VortexEngine::clearOutputPins() PORTC.PIN5CTRL = PORT_ISC_INPUT_DISABLE_gc; } -void VortexEngine::enableMOSFET(bool enabled) +void VortexEngine_enableMOSFET(bool enabled) { PORTC.DIRSET |= PIN4_bm; if (enabled) { @@ -518,28 +561,28 @@ void VortexEngine::enableMOSFET(bool enabled) #include "Colors/Colorset.h" #include "Memory/Memory.h" #include "Random/Random.h" -void VortexEngine::compressionTest() +void VortexEngine_compressionTest() { // always use same seed Random rand(0xdeadbeef); ByteStream stream; - Modes::clearModes(); + Modes_clearModes(); for (uint32_t len = 1; len < 4096; ++len) { uint8_t *buf = (uint8_t *)vcalloc(1, len + 1); if (!buf) { continue; } ByteStream modeStream; - Modes::serialize(modeStream); + Modes_serialize(modeStream); while (modeStream.size() < len) { Mode tmpMode; tmpMode.setPattern((PatternID)(len % PATTERN_COUNT)); Colorset set; set.randomizeColorTheory(rand, 8); tmpMode.setColorset(set); - Modes::addMode(&tmpMode); + Modes_addMode(&tmpMode); modeStream.clear(); - Modes::serialize(modeStream); + Modes_serialize(modeStream); } stream = modeStream; #if 0 @@ -567,7 +610,7 @@ void VortexEngine::compressionTest() } free(buf); } - Modes::clearModes(); + Modes_clearModes(); DEBUG_LOG("Success testing compression"); } #endif @@ -580,7 +623,7 @@ void VortexEngine::compressionTest() #include "Patterns/PatternBuilder.h" #include "Random/Random.h" #include -void VortexEngine::serializationTest() +void VortexEngine_serializationTest() { Colorset bigSet; Random rand; @@ -589,7 +632,7 @@ void VortexEngine::serializationTest() } DEBUG_LOG("== Beginning Serialization Test =="); for (PatternID patternID = PATTERN_FIRST; patternID < PATTERN_COUNT; ++patternID) { - PatternArgs defaults = PatternBuilder::getDefaultArgs(patternID); + PatternArgs defaults = PatternBuilder_getDefaultArgs(patternID); PatternArgs args(0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x11, 0x22, 0x33); args.numArgs = defaults.numArgs; Mode tmpMode(patternID, &args, &bigSet); @@ -629,21 +672,21 @@ void VortexEngine::serializationTest() } DEBUG_LOG("All patterns serialized successfully..."); DEBUG_LOG("Attempting full serialization of all modes"); - Modes::clearModes(); + Modes_clearModes(); for (PatternID patternID = PATTERN_FIRST; patternID < PATTERN_COUNT; ++patternID) { - if (!Modes::addMode(patternID, nullptr, &bigSet)) { + if (!Modes_addMode(patternID, nullptr, &bigSet)) { ERROR_LOGF("ERROR!! Failed to add mode %u", patternID); return; } } ByteStream buffer; - Modes::serialize(buffer); + Modes_serialize(buffer); if (!buffer.size()) { ERROR_LOG("ERROR!! Buffer empty after modes serialize"); return; } - Modes::clearModes(); - Modes::unserialize(buffer); + Modes_clearModes(); + Modes_unserialize(buffer); if (!buffer.unserializerAtEnd()) { ERROR_LOG("ERROR!! Unserializer still has data after modes unserialize:"); uint8_t byte = 0; @@ -657,7 +700,7 @@ void VortexEngine::serializationTest() printf("\n"); return; } - Modes::clearModes(); + Modes_clearModes(); DEBUG_LOG("Success all modes serialized cleanly"); DEBUG_LOG("== SUCCESS RUNNING SERIALIZATION TEST =="); } @@ -666,11 +709,11 @@ void VortexEngine::serializationTest() #if TIMER_TEST == 1 #include "Time/TimeControl.h" #include "Time/timer.h" -void VortexEngine::timerTest() +void VortexEngine_timerTest() { DEBUG_LOG("== BEGINNING TIMER TESTS =="); - Time::test(); - Timer::test(); + Time_test(); + Timer_test(); DEBUG_LOG("== SUCCESS TIMER TESTS PASSED =="); } #endif diff --git a/VortexEngine/src/VortexEngine.h b/VortexEngine/src/VortexEngine.h index ae1b054840..2f0fd71f3a 100644 --- a/VortexEngine/src/VortexEngine.h +++ b/VortexEngine/src/VortexEngine.h @@ -2,77 +2,72 @@ #define VORTEX_ENGINE_H #include +#include + #include "VortexConfig.h" -class ByteStream; -class Mode; +typedef struct ByteStream ByteStream; +typedef struct Mode Mode; -class VortexEngine -{ - // private unimplemented constructor - VortexEngine(); +// VortexEngine is a stateless namespace of free functions, the state +// lives in VortexEngine.cpp -public: - // setup and initialization function - static bool init(); - // cleanup - static void cleanup(); +// setup and initialization function +bool VortexEngine_init(void); +// cleanup +void VortexEngine_cleanup(void); - // tick function for each loop - static void tick(); +// tick function for each loop +void VortexEngine_tick(void); - // run the main logic of the engine - static void runMainLogic(); +// run the main logic of the engine +void VortexEngine_runMainLogic(void); - // serialize the version number to a byte stream - static bool serializeVersion(ByteStream &stream); - static bool checkVersion(uint8_t major, uint8_t minor); +// serialize the version number to a byte stream +bool VortexEngine_serializeVersion(ByteStream *stream); +bool VortexEngine_checkVersion(uint8_t major, uint8_t minor); - // get the current mode - static Mode *curMode(); +// get the current mode +Mode *VortexEngine_curMode(void); #ifdef VORTEX_LIB - // the total available storage space - static uint32_t totalStorageSpace(); - // the size of the savefile - static uint32_t savefileSize(); - // whether sleeping or not - static bool isSleeping() { return m_sleeping; } +// the total available storage space +uint32_t VortexEngine_totalStorageSpace(void); +// the size of the savefile +uint32_t VortexEngine_savefileSize(void); +// whether sleeping or not +bool VortexEngine_isSleeping(void); #endif - // enter/leave sleep mode - static void enterSleep(bool save = true); - static void wakeup(bool reset = true); - - // toggle force sleep from working - static void toggleForceSleep(bool enabled) { m_forceSleepEnabled = enabled; } - // toggle auto cycle enabled - static void setAutoCycle(bool enabled) { m_autoCycle = enabled; } +// enter/leave sleep mode +void VortexEngine_enterSleep(bool save); +void VortexEngine_wakeup(bool reset); #ifdef VORTEX_EMBEDDED - // clear output pins - static void clearOutputPins(); - // enable/disable the mosfet - static void enableMOSFET(bool enabled); +// called when an allocation fails, shows red for a second then shuts off +void VortexEngine_outOfMemory(void); #endif -private: - // bool in vortexlib to simulate sleeping - static volatile bool m_sleeping; - static bool m_forceSleepEnabled; +// toggle force sleep from working +void VortexEngine_toggleForceSleep(bool enabled); +// toggle auto cycle enabled +void VortexEngine_setAutoCycle(bool enabled); - // whether auto cycle modes is turned on - static bool m_autoCycle; +#ifdef VORTEX_EMBEDDED +// clear output pins +void VortexEngine_clearOutputPins(void); +// enable/disable the mosfet +void VortexEngine_enableMOSFET(bool enabled); +#endif #if COMPRESSION_TEST == 1 - static void compressionTest(); +void VortexEngine_compressionTest(void); #endif #if SERIALIZATION_TEST == 1 - static void serializationTest(); +void VortexEngine_serializationTest(void); #endif #if TIMER_TEST == 1 - static void timerTest(); +void VortexEngine_timerTest(void); #endif -}; #endif diff --git a/VortexEngine/src/Wireless/IRReceiver.cpp b/VortexEngine/src/Wireless/IRReceiver.cpp index 47ec50ef4d..b140425671 100644 --- a/VortexEngine/src/Wireless/IRReceiver.cpp +++ b/VortexEngine/src/Wireless/IRReceiver.cpp @@ -9,30 +9,50 @@ #include "../Modes/Mode.h" #include "../Log/Log.h" -BitStream IRReceiver::m_irData; -IRReceiver::RecvState IRReceiver::m_recvState = WAITING_HEADER_MARK; -uint32_t IRReceiver::m_prevTime = 0; -uint8_t IRReceiver::m_pinState = 0; -uint32_t IRReceiver::m_previousBytes = 0; +// bit stream that IR data is fed to bit by bit +static BitStream IRReceiver_m_irData; + +// receive state used for the state machine in the PCI handler +typedef enum { + WAITING_HEADER_MARK, + WAITING_HEADER_SPACE, + READING_DATA_MARK, + READING_DATA_SPACE +} RecvState; + +// state information used by the PCI handler +static RecvState IRReceiver_m_recvState = WAITING_HEADER_MARK; +// used to track pin changes +static uint32_t IRReceiver_m_prevTime = 0; +static uint8_t IRReceiver_m_pinState = 0; + +// used to compare if received data has changed since last checking +static uint32_t IRReceiver_m_previousBytes = 0; + +uint16_t IRReceiver_bytesReceived() +{ + return BitStream_bytepos(&IRReceiver_m_irData); +} -bool IRReceiver::init() +bool IRReceiver_init() { - m_irData.init(IR_RECV_BUF_SIZE); - return true; + // receive into a static buffer so the receiver never touches the heap + static uint8_t s_irDataBuf[IR_RECV_BUF_SIZE]; + return BitStream_initBuf(&IRReceiver_m_irData, s_irDataBuf, IR_RECV_BUF_SIZE); } -void IRReceiver::cleanup() +void IRReceiver_cleanup() { } -bool IRReceiver::dataReady() +bool IRReceiver_dataReady() { // is the receiver actually receiving data? - if (!isReceiving()) { + if (!IRReceiver_isReceiving()) { return false; } - uint8_t blocks = m_irData.data()[0]; - uint8_t remainder = m_irData.data()[1]; + uint8_t blocks = BitStream_data(&IRReceiver_m_irData)[0]; + uint8_t remainder = BitStream_data(&IRReceiver_m_irData)[1]; uint32_t total = ((blocks - 1) * 32) + remainder; if (!total || total > IR_MAX_DATA_TRANSFER) { DEBUG_LOGF("Bad IR Data size: %u", total); @@ -41,76 +61,80 @@ bool IRReceiver::dataReady() // if there are size + 2 bytes in the IRData receiver // then a full message is ready, the + 2 is from the // two bytes for blocks + remainder that are sent first - return (m_irData.bytepos() >= (uint32_t)(total + 2)); + return (BitStream_bytepos(&IRReceiver_m_irData) >= (uint32_t)(total + 2)); } // whether actively receiving -bool IRReceiver::isReceiving() +bool IRReceiver_isReceiving() { // if there are at least 2 bytes in the data buffer then // the receiver is receiving a packet. If there is less // than 2 bytes then we're still waiting for the 'blocks' // and 'remainder' bytes which prefix a packet - return (m_irData.bytepos() > 2); + return (BitStream_bytepos(&IRReceiver_m_irData) > 2); } // the percent of data received -uint8_t IRReceiver::percentReceived() +uint8_t IRReceiver_percentReceived() { - if (!isReceiving()) { + if (!IRReceiver_isReceiving()) { return 0; } - uint8_t blocks = m_irData.data()[0]; - uint8_t remainder = m_irData.data()[1]; + uint8_t blocks = BitStream_data(&IRReceiver_m_irData)[0]; + uint8_t remainder = BitStream_data(&IRReceiver_m_irData)[1]; uint16_t total = ((blocks - 1) * 32) + remainder; // round by adding half of the total to the numerator - return (uint8_t)((uint16_t)((m_irData.bytepos() * 100 + (total / 2)) / total)); + return (uint8_t)((uint16_t)((BitStream_bytepos(&IRReceiver_m_irData) * 100 + (total / 2)) / total)); } -bool IRReceiver::receiveMode(Mode *pMode) +bool IRReceiver_receiveMode(Mode *pMode) { ByteStream buf; + ByteStream_make(&buf); // read from the receive buffer into the byte stream - if (!read(buf)) { + if (!IRReceiver_read(&buf)) { // no data to read right now, or an error DEBUG_LOG("No data available to read, or error reading"); + ByteStream_destroy(&buf); return false; } - DEBUG_LOGF("Received %u bytes", buf.rawSize()); + DEBUG_LOGF("Received %u bytes", ByteStream_rawSize(&buf)); // load the data into the target mode - return pMode->loadFromBuffer(buf); + bool rv = Mode_loadFromBuffer(pMode, &buf); + ByteStream_destroy(&buf); + return rv; } -bool IRReceiver::beginReceiving() +bool IRReceiver_beginReceiving() { - resetIRState(); + IRReceiver_resetIRState(); return true; } -bool IRReceiver::endReceiving() +bool IRReceiver_endReceiving() { - resetIRState(); + IRReceiver_resetIRState(); return true; } -bool IRReceiver::onNewData() +bool IRReceiver_onNewData() { - if (bytesReceived() == m_previousBytes) { + if (IRReceiver_bytesReceived() == IRReceiver_m_previousBytes) { return false; } - m_previousBytes = bytesReceived(); + IRReceiver_m_previousBytes = IRReceiver_bytesReceived(); return true; } -bool IRReceiver::read(ByteStream &data) +bool IRReceiver_read(ByteStream *data) { - if (!m_irData.bytepos() || m_irData.bytepos() > IR_MAX_DATA_TRANSFER) { + if (!BitStream_bytepos(&IRReceiver_m_irData) || BitStream_bytepos(&IRReceiver_m_irData) > IR_MAX_DATA_TRANSFER) { DEBUG_LOG("Nothing to read, or read too much"); return false; } // read the size out (blocks + remainder) - uint8_t blocks = m_irData.data()[0]; - uint8_t remainder = m_irData.data()[1]; + uint8_t blocks = BitStream_data(&IRReceiver_m_irData)[0]; + uint8_t remainder = BitStream_data(&IRReceiver_m_irData)[1]; // calculate size from blocks + remainder uint32_t size = ((blocks - 1) * 32) + remainder; if (!size || size > IR_MAX_DATA_TRANSFER) { @@ -118,86 +142,86 @@ bool IRReceiver::read(ByteStream &data) return false; } // the actual data starts 2 bytes later because of the size byte - const uint8_t *actualData = m_irData.data() + 2; - if (!data.rawInit(actualData, size)) { + const uint8_t *actualData = BitStream_data(&IRReceiver_m_irData) + 2; + if (!ByteStream_rawInit(data, actualData, size)) { DEBUG_LOG("Failed to init buffer for IR read"); return false; } // reset the IR state and receive buffer now - resetIRState(); + IRReceiver_resetIRState(); return true; } // The recv PCI handler is called every time the pin state changes -void IRReceiver::recvPCIHandler() +void IRReceiver_recvPCIHandler() { // toggle the tracked pin state no matter what - m_pinState = (uint8_t)!m_pinState; + IRReceiver_m_pinState = (uint8_t)!IRReceiver_m_pinState; // grab current time - uint32_t now = Time::microseconds(); + uint32_t now = Time_microseconds(); // check previous time for validity - if (!m_prevTime || m_prevTime > now) { - m_prevTime = now; + if (!IRReceiver_m_prevTime || IRReceiver_m_prevTime > now) { + IRReceiver_m_prevTime = now; DEBUG_LOG("Bad first time diff, resetting..."); - resetIRState(); + IRReceiver_resetIRState(); return; } // calc time difference between previous change and now - uint32_t diff = (uint32_t)(now - m_prevTime); + uint32_t diff = (uint32_t)(now - IRReceiver_m_prevTime); // and update the previous changetime for next loop - m_prevTime = now; + IRReceiver_m_prevTime = now; // handle the blink duration and process it - handleIRTiming(diff); + IRReceiver_handleIRTiming(diff); } // state machine that can be fed IR timings to parse them and interpret the intervals -void IRReceiver::handleIRTiming(uint32_t diff) +void IRReceiver_handleIRTiming(uint32_t diff) { // if the diff is too long or too short then it's not useful - if ((diff > IR_HEADER_MARK_MAX && m_recvState < READING_DATA_MARK) || diff < IR_TIMING_MIN) { + if ((diff > IR_HEADER_MARK_MAX && IRReceiver_m_recvState < READING_DATA_MARK) || diff < IR_TIMING_MIN) { DEBUG_LOGF("bad delay: %u, resetting...", diff); - resetIRState(); + IRReceiver_resetIRState(); return; } - switch (m_recvState) { + switch (IRReceiver_m_recvState) { case WAITING_HEADER_MARK: // initial state if (diff >= IR_HEADER_MARK_MIN && diff <= IR_HEADER_MARK_MAX) { - m_recvState = WAITING_HEADER_SPACE; + IRReceiver_m_recvState = WAITING_HEADER_SPACE; } else { DEBUG_LOGF("Bad header mark %u, resetting...", diff); - resetIRState(); + IRReceiver_resetIRState(); } break; case WAITING_HEADER_SPACE: if (diff >= IR_HEADER_SPACE_MIN && diff <= IR_HEADER_SPACE_MAX) { - m_recvState = READING_DATA_MARK; + IRReceiver_m_recvState = READING_DATA_MARK; } else { DEBUG_LOGF("Bad header space %u, resetting...", diff); - resetIRState(); + IRReceiver_resetIRState(); } break; case READING_DATA_MARK: // classify mark/space based on the timing and write into buffer - m_irData.write1Bit((diff > (IR_TIMING * 2)) ? 1 : 0); - m_recvState = READING_DATA_SPACE; + BitStream_write1Bit(&IRReceiver_m_irData, (diff > (IR_TIMING * 2)) ? true : false); + IRReceiver_m_recvState = READING_DATA_SPACE; break; case READING_DATA_SPACE: // the space could be just a regular space, or a gap in between blocks - m_recvState = READING_DATA_MARK; + IRReceiver_m_recvState = READING_DATA_MARK; break; default: // ?? - DEBUG_LOGF("Bad receive state: %u", m_recvState); + DEBUG_LOGF("Bad receive state: %u", IRReceiver_m_recvState); break; } } -void IRReceiver::resetIRState() +void IRReceiver_resetIRState() { - m_previousBytes = 0; - m_recvState = WAITING_HEADER_MARK; + IRReceiver_m_previousBytes = 0; + IRReceiver_m_recvState = WAITING_HEADER_MARK; // zero out the receive buffer and reset bit receiver position - m_irData.reset(); + BitStream_reset(&IRReceiver_m_irData); DEBUG_LOG("IR State Reset"); } -#endif +#endif \ No newline at end of file diff --git a/VortexEngine/src/Wireless/IRReceiver.h b/VortexEngine/src/Wireless/IRReceiver.h index 14dff9069f..450b01d5f3 100644 --- a/VortexEngine/src/Wireless/IRReceiver.h +++ b/VortexEngine/src/Wireless/IRReceiver.h @@ -1,6 +1,7 @@ #ifndef IR_RECEIVER_H #define IR_RECEIVER_H +#include #include #include "../Serial/BitStream.h" @@ -9,74 +10,41 @@ #if IR_ENABLE_RECEIVER == 1 -class ByteStream; -class Mode; +typedef struct ByteStream ByteStream; +typedef struct Mode Mode; + +// init and cleanup the receiver +bool IRReceiver_init(); +void IRReceiver_cleanup(); + +// check whether a full IR message is ready to read +bool IRReceiver_dataReady(); +// whether actively receiving +bool IRReceiver_isReceiving(); +// the percent of data received +uint8_t IRReceiver_percentReceived(); +uint16_t IRReceiver_bytesReceived(); + +// receive the IR message into a target mode +bool IRReceiver_receiveMode(Mode *pMode); + +// turn the receiver on/off +bool IRReceiver_beginReceiving(); +bool IRReceiver_endReceiving(); +// checks if there is new data since the last reset or check +bool IRReceiver_onNewData(); +// reset IR receiver buffer +void IRReceiver_resetIRState(); + +// The handler called when the IR receiver pin changes states +// this has to be public because it's called from a global ISR +// but technically it's an internal function for IRReceiver +void IRReceiver_recvPCIHandler(); + +// reading functions +bool IRReceiver_read(ByteStream *data); +void IRReceiver_handleIRTiming(uint32_t diff); -class IRReceiver -{ - IRReceiver(); - -public: - // init and cleanup the receiver - static bool init(); - static void cleanup(); - - // check whether a full IR message is ready to read - static bool dataReady(); - // whether actively receiving - static bool isReceiving(); - // the percent of data received - static uint8_t percentReceived(); - static uint16_t bytesReceived() { return m_irData.bytepos(); } - - // receive the IR message into a target mode - static bool receiveMode(Mode *pMode); - - // turn the receiver on/off - static bool beginReceiving(); - static bool endReceiving(); - // checks if there is new data since the last reset or check - static bool onNewData(); - // reset IR receiver buffer - static void resetIRState(); - -private: - - // reading functions - // PCI handler for when IR receiver pin changes states - static bool read(ByteStream &data); - static void recvPCIHandler(); - static void handleIRTiming(uint32_t diff); - - // =================== - // private data: - - // BitStream object that IR data is fed to bit by bit - static BitStream m_irData; - - // Receive state used for state machine in PCIhandler - enum RecvState : uint8_t - { - WAITING_HEADER_MARK, - WAITING_HEADER_SPACE, - READING_DATA_MARK, - READING_DATA_SPACE - }; - - // state information used by the PCIHandler - static RecvState m_recvState; - // used to track pin changes - static uint32_t m_prevTime; - static uint8_t m_pinState; - - // used to compare if received data has changed since last checking - static uint32_t m_previousBytes; - -#ifdef VORTEX_LIB - friend class Vortex; #endif -}; -#endif - -#endif +#endif \ No newline at end of file diff --git a/VortexEngine/src/Wireless/IRSender.cpp b/VortexEngine/src/Wireless/IRSender.cpp index a4119926dc..4442910870 100644 --- a/VortexEngine/src/Wireless/IRSender.cpp +++ b/VortexEngine/src/Wireless/IRSender.cpp @@ -12,166 +12,192 @@ #endif // the serial buffer for the data -ByteStream IRSender::m_serialBuf; +static ByteStream IRSender_m_serialBuf; // a bit walker for the serial data -BitStream IRSender::m_bitStream; +static BitStream IRSender_m_bitStream; // whether actively sending -bool IRSender::m_isSending = false; +static bool IRSender_m_isSending = false; // the time of the last sent chunk -uint32_t IRSender::m_lastSendTime = 0; +static uint32_t IRSender_m_lastSendTime = 0; + // some runtime meta info -uint32_t IRSender::m_size = 0; +static uint32_t IRSender_m_size = 0; // the number of blocks that will be sent -uint8_t IRSender::m_numBlocks = 0; +static uint8_t IRSender_m_numBlocks = 0; // the amount in the final block -uint8_t IRSender::m_remainder = 0; +static uint8_t IRSender_m_remainder = 0; + // configuration options for the sender -uint32_t IRSender::m_blockSize = 0; +static uint32_t IRSender_m_blockSize = 0; + // write total -uint32_t IRSender::m_writeCounter = 0; +static uint32_t IRSender_m_writeCounter = 0; #if defined(VORTEX_EMBEDDED) -// Timer used for PWM, is initialized in initpwm() +// Timer used for PWM, is initialized in initPWM() Tcc *IR_TCCx; #endif -bool IRSender::init() +// sender functions +static void IRSender_beginSend(); +// send a full 8 bits in a tight loop +static void IRSender_sendByte(uint8_t data); +// send a mark/space by turning PWM on/off +static void IRSender_sendMark(uint16_t time); +static void IRSender_sendSpace(uint16_t time); +// Pulse-Width Modulator (IR Transmitter) +static void IRSender_initPWM(); +// turn the IR transmitter on/off in realtime +static void IRSender_startPWM(); +static void IRSender_stopPWM(); + +bool IRSender_isSending() +{ + return IRSender_m_isSending; +} + +uint32_t IRSender_percentDone() +{ + return (uint32_t)(((float)IRSender_m_writeCounter / (float)IRSender_m_size) * 100.0); +} + +bool IRSender_init() { // initialize the IR device - initPWM(); + IRSender_initPWM(); return true; } -void IRSender::cleanup() +void IRSender_cleanup() { } -bool IRSender::loadMode(const Mode *targetMode) +bool IRSender_loadMode(const Mode *targetMode) { - m_serialBuf.clear(); + ByteStream_clear(&IRSender_m_serialBuf); // save the target mode to it's savefile buffer format - if (!targetMode->saveToBuffer(m_serialBuf)) { + if (!Mode_saveToBuffer(targetMode, &IRSender_m_serialBuf, 0)) { DEBUG_LOG("Failed to save mode to buffer"); return false; } // ensure the data isn't too big - if (m_serialBuf.rawSize() > IR_MAX_DATA_TRANSFER) { - DEBUG_LOGF("Cannot transfer that much data: %u bytes", m_serialBuf.rawSize()); + if (ByteStream_rawSize(&IRSender_m_serialBuf) > IR_MAX_DATA_TRANSFER) { + DEBUG_LOGF("Cannot transfer that much data: %u bytes", ByteStream_rawSize(&IRSender_m_serialBuf)); return false; } // point the bitstream at the serial buffer - m_bitStream.init((uint8_t *)m_serialBuf.rawData(), m_serialBuf.rawSize()); + BitStream_initBuf(&IRSender_m_bitStream, (uint8_t *)ByteStream_rawData(&IRSender_m_serialBuf), ByteStream_rawSize(&IRSender_m_serialBuf)); // the size of the packet - m_size = m_serialBuf.rawSize(); + IRSender_m_size = ByteStream_rawSize(&IRSender_m_serialBuf); // the number of blocks that will be sent (possibly just 1 with less than 32 bytes) - m_numBlocks = (m_size + (IR_DEFAULT_BLOCK_SIZE - 1)) / IR_DEFAULT_BLOCK_SIZE; + IRSender_m_numBlocks = (uint8_t)((IRSender_m_size + (IR_DEFAULT_BLOCK_SIZE - 1)) / IR_DEFAULT_BLOCK_SIZE); // the amount in the final block (possibly the only block) - m_remainder = m_size % (IR_DEFAULT_BLOCK_SIZE + 0); - DEBUG_LOGF("Num blocks: %u", m_numBlocks); - DEBUG_LOGF("Remainder: %u", m_remainder); - DEBUG_LOGF("Size: %u", m_size); + IRSender_m_remainder = (uint8_t)(IRSender_m_size % (IR_DEFAULT_BLOCK_SIZE + 0)); + DEBUG_LOGF("Num blocks: %u", IRSender_m_numBlocks); + DEBUG_LOGF("Remainder: %u", IRSender_m_remainder); + DEBUG_LOGF("Size: %u", IRSender_m_size); // reset write counter - m_writeCounter = 0; + IRSender_m_writeCounter = 0; return true; } -bool IRSender::send() +bool IRSender_send() { // must have at least one block to send - if (m_numBlocks < 1) { - m_isSending = false; - return m_isSending; + if (IRSender_m_numBlocks < 1) { + IRSender_m_isSending = false; + return IRSender_m_isSending; } - if (!m_isSending) { - beginSend(); + if (!IRSender_m_isSending) { + IRSender_beginSend(); } - if (m_lastSendTime > 0 && m_lastSendTime + IR_DEFAULT_BLOCK_SPACING > Time::getCurtime()) { + if (IRSender_m_lastSendTime > 0 && IRSender_m_lastSendTime + IR_DEFAULT_BLOCK_SPACING > Time_getCurtime()) { // don't send yet - return m_isSending; + return IRSender_m_isSending; } // blocksize is default unless it's the last block, then it's the remainder - uint32_t blocksize = (m_numBlocks == 1) ? m_remainder : IR_DEFAULT_BLOCK_SIZE; - DEBUG_LOGF("IRSender Sending block #%u", m_numBlocks); + uint32_t blocksize = (IRSender_m_numBlocks == 1) ? IRSender_m_remainder : IR_DEFAULT_BLOCK_SIZE; + DEBUG_LOGF("IRSender Sending block #%u", IRSender_m_numBlocks); // pointer to the data at the correct location - const uint8_t *buf_ptr = m_bitStream.data() + m_writeCounter; + const uint8_t *buf_ptr = BitStream_data(&IRSender_m_bitStream) + IRSender_m_writeCounter; // iterate each byte in the block and write it for (uint32_t i = 0; i < blocksize; ++i) { // write the byte - sendByte(*buf_ptr); + IRSender_sendByte(*buf_ptr); // increment to next byte in buffer buf_ptr++; // record the written byte - m_writeCounter++; + IRSender_m_writeCounter++; } // wrote one block - m_numBlocks--; + IRSender_m_numBlocks--; // still sending if we have more blocks - m_isSending = (m_numBlocks > 0); + IRSender_m_isSending = (IRSender_m_numBlocks > 0); // the curtime - m_lastSendTime = Time::getCurtime(); + IRSender_m_lastSendTime = Time_getCurtime(); // return whether still sending - return m_isSending; + return IRSender_m_isSending; } -void IRSender::beginSend() +static void IRSender_beginSend() { - m_isSending = true; + IRSender_m_isSending = true; DEBUG_LOGF("[%zu] Beginning send size %u (blocks: %u remainder: %u blocksize: %u)", - Time::microseconds(), m_size, m_numBlocks, m_remainder, m_blockSize); + Time_microseconds(), IRSender_m_size, IRSender_m_numBlocks, IRSender_m_remainder, IRSender_m_blockSize); // init sender before writing, is this necessary here? I think so - initPWM(); + IRSender_initPWM(); // wakeup the other receiver with a very quick mark/space - sendMark(50); - sendSpace(100); + IRSender_sendMark(50); + IRSender_sendSpace(100); // now send the header - sendMark(IR_HEADER_MARK); - sendSpace(IR_HEADER_SPACE); + IRSender_sendMark(IR_HEADER_MARK); + IRSender_sendSpace(IR_HEADER_SPACE); // reset writeCounter - m_writeCounter = 0; + IRSender_m_writeCounter = 0; // write the number of blocks being sent, most likely just 1 - sendByte(m_numBlocks); + IRSender_sendByte(IRSender_m_numBlocks); // now write the number of bytes in the last block - sendByte(m_remainder); + IRSender_sendByte(IRSender_m_remainder); } -void IRSender::sendByte(uint8_t data) +static void IRSender_sendByte(uint8_t data) { // Sends from left to right, MSB first for (int b = 0; b < 8; b++) { // grab the bit of data at the indexspace uint32_t bit = (data >> (7 - b)) & 1; // send 3x timing size for 1s and 1x timing for 0 - sendMark(IR_TIMING + (IR_TIMING * (2 * bit))); + IRSender_sendMark((uint16_t)(IR_TIMING + (IR_TIMING * (2 * bit)))); // send 1x timing size for space - sendSpace(IR_TIMING); + IRSender_sendSpace(IR_TIMING); } - DEBUG_LOGF("Sent byte[%u]: 0x%x", m_writeCounter, data); + DEBUG_LOGF("Sent byte[%u]: 0x%x", IRSender_m_writeCounter, data); } -void IRSender::sendMark(uint16_t time) +static void IRSender_sendMark(uint16_t time) { #ifdef VORTEX_LIB // send mark timing over socket Vortex::vcallbacks()->infraredWrite(true, time); #else - startPWM(); - Time::delayMicroseconds(time); + IRSender_startPWM(); + Time_delayMicroseconds(time); #endif } -void IRSender::sendSpace(uint16_t time) +static void IRSender_sendSpace(uint16_t time) { #ifdef VORTEX_LIB // send space timing over socket Vortex::vcallbacks()->infraredWrite(false, time); #else - stopPWM(); - Time::delayMicroseconds(time); + IRSender_stopPWM(); + Time_delayMicroseconds(time); #endif } // shamelessly stolen from IRLib2, thanks -void IRSender::initPWM() +static void IRSender_initPWM() { #if defined(VORTEX_EMBEDDED) // just in case @@ -181,7 +207,6 @@ void IRSender::initPWM() uint8_t pin = g_APinDescription[IR_SEND_PWM_PIN].ulPin; // 8 ETCChannel IR_TCC_Channel = TCC0_CH0; int8_t IR_PER_EorF = PORT_PMUX_PMUXE_E; - //println();Serial.print("Port:"); Serial.print(port,DEC); Serial.print(" Pin:"); Serial.println(pin,DEC); // Enable the port multiplexer for the PWM channel on pin PORT->Group[port].PINCFG[pin].bit.PMUXEN = 1; @@ -189,7 +214,6 @@ void IRSender::initPWM() // F & E peripherals specify the timers: TCC0, TCC1 and TCC2 PORT->Group[port].PMUX[pin >> 1].reg |= IR_PER_EorF; -// pinPeripheral (IR_SEND_PWM_PIN,PIO_TIMER_ALT); // Feed GCLK0 to TCC0 and TCC1 REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN | // Enable GCLK0 to TCC0 and TCC1 GCLK_CLKCTRL_GEN_GCLK0 | // Select GCLK0 @@ -212,7 +236,6 @@ void IRSender::initPWM() // Set the duty cycle of the PWM on TCC0 to 33% IR_TCCx->CC[GetTCChannelNumber(IR_TCC_Channel)].reg = cc/3; while (IR_TCCx->SYNCBUSY.reg & TCC_SYNCBUSY_MASK); - //while(IR_TCCx->SYNCBUSY.bit.CC3); // Enable IR_TCCx timer but do not turn on PWM yet. Will turn it on later. IR_TCCx->CTRLA.reg |= TCC_CTRLA_PRESCALER_DIV1; // Divide GCLK0 by 1 @@ -222,7 +245,7 @@ void IRSender::initPWM() #endif } -void IRSender::startPWM() +static void IRSender_startPWM() { #if defined(VORTEX_EMBEDDED) // start the PWM @@ -231,7 +254,7 @@ void IRSender::startPWM() #endif } -void IRSender::stopPWM() +static void IRSender_stopPWM() { #if defined(VORTEX_EMBEDDED) // stop the PWM @@ -240,4 +263,4 @@ void IRSender::stopPWM() #endif } -#endif +#endif \ No newline at end of file diff --git a/VortexEngine/src/Wireless/IRSender.h b/VortexEngine/src/Wireless/IRSender.h index d8e9df30eb..b18c544c13 100644 --- a/VortexEngine/src/Wireless/IRSender.h +++ b/VortexEngine/src/Wireless/IRSender.h @@ -1,6 +1,9 @@ #ifndef IR_SENDER_H #define IR_SENDER_H +#include +#include + #include "../Serial/ByteStream.h" #include "../Serial/BitStream.h" @@ -8,59 +11,19 @@ #if IR_ENABLE_SENDER == 1 -class Mode; - -class IRSender -{ - IRSender(); - -public: - static bool init(); - static void cleanup(); - - // initialize the IR sender with a mode to send - static bool loadMode(const Mode *targetMode); - static bool send(); - - static bool isSending() { return m_isSending; } +typedef struct Mode Mode; - static uint32_t percentDone() { return (uint32_t)(((float)m_writeCounter / (float)m_size) * 100.0); } +// init and cleanup the sender +bool IRSender_init(); +void IRSender_cleanup(); -private: - // sender functions - static void beginSend(); - // send a full 8 bits in a tight loop - static void sendByte(uint8_t data); - // send a mark/space by turning PWM on/off - static void sendMark(uint16_t time); - static void sendSpace(uint16_t time); - // Pulse-Width Modulator (IR Transmitter) - static void initPWM(); - // turn the IR transmitter on/off in realtime - static void startPWM(); - static void stopPWM(); +// initialize the IR sender with a mode to send +bool IRSender_loadMode(const Mode *targetMode); +bool IRSender_send(); - // the serial buffer for the data - static ByteStream m_serialBuf; - // a bit walker for the serial data - static BitStream m_bitStream; - static bool m_isSending; - static uint32_t m_lastSendTime; - - // some runtime meta info - static uint32_t m_size; - // the number of blocks that will be sent - static uint8_t m_numBlocks; - // the amount in the final block - static uint8_t m_remainder; - - // configuration options for the sender - static uint32_t m_blockSize; - - // write total - static uint32_t m_writeCounter; -}; +bool IRSender_isSending(); +uint32_t IRSender_percentDone(); #endif -#endif +#endif \ No newline at end of file diff --git a/VortexEngine/src/Wireless/VLConfig.h b/VortexEngine/src/Wireless/VLConfig.h index f10896810c..ac1a2482ff 100644 --- a/VortexEngine/src/Wireless/VLConfig.h +++ b/VortexEngine/src/Wireless/VLConfig.h @@ -18,8 +18,9 @@ #define VL_MAX_DWORDS_TRANSFER 128 #define VL_MAX_DATA_TRANSFER (VL_MAX_DWORDS_TRANSFER * sizeof(uint32_t)) -// the IR receiver buffer size in dwords -#define VL_RECV_BUF_SIZE VL_MAX_DATA_TRANSFER +// the VL receiver buffer size in bytes, a packet is a 2 byte prefix and a +// payload whose size is sent in a single byte so it can never be larger +#define VL_RECV_BUF_SIZE (2 + 255) #define VL_THRESHOLD 0.5 #define VL_THRES_UP (1 + VL_THRESHOLD) diff --git a/VortexEngine/src/Wireless/VLReceiver.cpp b/VortexEngine/src/Wireless/VLReceiver.cpp index 96e1d9972d..1808280fb5 100644 --- a/VortexEngine/src/Wireless/VLReceiver.cpp +++ b/VortexEngine/src/Wireless/VLReceiver.cpp @@ -15,20 +15,37 @@ #include #endif -BitStream VLReceiver::m_vlData; -VLReceiver::RecvState VLReceiver::m_recvState = WAITING_HEADER_MARK; -uint32_t VLReceiver::m_prevTime = 0; -uint8_t VLReceiver::m_pinState = 0; -uint16_t VLReceiver::m_previousBytes = 0; +// the receive state machine used for the PCI handler +typedef uint8_t RecvState; +enum { + WAITING_HEADER_MARK, + WAITING_HEADER_SPACE, + READING_BAUD_MARK, + READING_BAUD_SPACE, + READING_DATA_MARK, + READING_DATA_SPACE, + READING_DATA_PARITY_MARK, + READING_DATA_PARITY_SPACE +}; + +// BitStream object that VL data is fed to bit by bit +static BitStream s_vlData; +// state information used by the PCIHandler +static RecvState s_recvState = WAITING_HEADER_MARK; +// used to track pin changes +static uint32_t s_prevTime = 0; +static uint8_t s_pinState = 0; +// used to compare if received data has changed since last checking +static uint16_t s_previousBytes = 0; // the determined time based on sync -uint16_t VLReceiver::m_vlMarkThreshold = 0; -uint16_t VLReceiver::m_vlSpaceThreshold = 0; +static uint16_t s_vlMarkThreshold = 0; +static uint16_t s_vlSpaceThreshold = 0; // counter is reused for two purposes -uint8_t VLReceiver::m_counter = 0; +static uint8_t s_counter = 0; // parity is for each byte of data received -uint8_t VLReceiver::m_parityBit = 0; +static uint8_t s_parityBit = 0; // legacy is for old receiving method (sucks) -bool VLReceiver::m_legacy = false; +static bool s_legacy = false; #ifdef VORTEX_EMBEDDED #define MIN_THRESHOLD 200 @@ -44,7 +61,7 @@ bool VLReceiver::m_legacy = false; // 6 ACC64 64 results accumulated > doesn't work #define SAMPLE_COUNT 5 // the threshold needs to start high then it will be automatically pulled down -uint16_t threshold = THRESHOLD_BEGIN; +static uint16_t s_threshold = THRESHOLD_BEGIN; ISR(ADC0_WCOMP_vect) { // this will store the last known state @@ -54,13 +71,13 @@ ISR(ADC0_WCOMP_vect) // calculate a threshold by using the baseline minimum value that is above 0 // with a static offset, this ensures whatever the baseline light level and/or // hardware sensitivity is it will always pick a threshold just above the 'off' - if (val > MIN_THRESHOLD && val < (threshold + BASE_OFFSET)) { - threshold = val + BASE_OFFSET; + if (val > MIN_THRESHOLD && val < (s_threshold + BASE_OFFSET)) { + s_threshold = val + BASE_OFFSET; } // compare the current analog value to the light threshold - bool isAboveThreshold = (val > threshold); + bool isAboveThreshold = (val > s_threshold); if (wasAboveThreshold != isAboveThreshold) { - VLReceiver::recvPCIHandler(); + VLReceiver_recvPCIHandler(); wasAboveThreshold = isAboveThreshold; } // Clear the Window Comparator interrupt flag @@ -68,67 +85,73 @@ ISR(ADC0_WCOMP_vect) } #endif -bool VLReceiver::init() +bool VLReceiver_init() { #ifdef VORTEX_EMBEDDED // Disable digital input buffer on the pin to save power PORTB.PIN1CTRL &= ~PORT_ISC_gm; PORTB.PIN1CTRL |= PORT_ISC_INPUT_DISABLE_gc; #endif - return m_vlData.init(VL_RECV_BUF_SIZE); + // receive into a static buffer so the receiver never touches the heap + static uint8_t s_vlDataBuf[VL_RECV_BUF_SIZE]; + return BitStream_initBuf(&s_vlData, s_vlDataBuf, VL_RECV_BUF_SIZE); } -void VLReceiver::cleanup() +void VLReceiver_cleanup() { } -bool VLReceiver::dataReady() +bool VLReceiver_dataReady() { // is the receiver actually receiving data? - if (!isReceiving()) { + if (!VLReceiver_isReceiving()) { return false; } - uint8_t size = m_vlData.peekData(1); + uint8_t size = BitStream_peekData(&s_vlData, 1); // check if there are size + 1 bytes in the VLData receiver - return (m_vlData.bytepos() >= ((uint32_t)size + 2)); + return (BitStream_bytepos(&s_vlData) >= ((uint32_t)size + 2)); } // whether actively receiving -bool VLReceiver::isReceiving() +bool VLReceiver_isReceiving() { // if there are at least 2 bytes in the data buffer then // the receiver is receiving a packet. If there is less // than 2 bytes then we're still waiting for the 'blocks' // and 'remainder' bytes which prefix a packet - return (m_vlData.bytepos() > 2); + return (BitStream_bytepos(&s_vlData) > 2); } // the percent of data received -uint8_t VLReceiver::percentReceived() +uint8_t VLReceiver_percentReceived() { - if (!isReceiving()) { + if (!VLReceiver_isReceiving()) { return 0; } - uint8_t size = m_vlData.peekData(1); + uint8_t size = BitStream_peekData(&s_vlData, 1); // round by adding half of the total to the numerator - return (uint8_t)((uint16_t)((m_vlData.bytepos() * 100 + (size / 2)) / size)); + return (uint8_t)((uint16_t)((BitStream_bytepos(&s_vlData) * 100 + (size / 2)) / size)); } -bool VLReceiver::receiveMode(Mode *pMode) +bool VLReceiver_receiveMode(Mode *pMode) { ByteStream buf; + ByteStream_make(&buf); // read from the receive buffer into the byte stream - if (!read(buf)) { + if (!VLReceiver_read(&buf)) { // no data to read right now, or an error DEBUG_LOG("No data available to read, or error reading"); + ByteStream_destroy(&buf); return false; } - DEBUG_LOGF("Received %u bytes", buf.rawSize()); + DEBUG_LOGF("Received %u bytes", ByteStream_rawSize(&buf)); // load the data into the target mode - return pMode->loadFromBuffer(buf); + bool rv = Mode_loadFromBuffer(pMode, &buf); + ByteStream_destroy(&buf); + return rv; } -bool VLReceiver::beginReceiving() +bool VLReceiver_beginReceiving() { #ifdef VORTEX_EMBEDDED // Set up the ADC @@ -172,11 +195,11 @@ bool VLReceiver::beginReceiving() // start the first conversion ADC0.COMMAND = ADC_STCONV_bm; #endif - resetVLState(); + VLReceiver_resetVLState(); return true; } -bool VLReceiver::endReceiving() +bool VLReceiver_endReceiving() { #ifdef VORTEX_EMBEDDED // Stop conversions and disable the ADC @@ -186,66 +209,77 @@ bool VLReceiver::endReceiving() return true; } -bool VLReceiver::onNewData() +bool VLReceiver_onNewData() { - if (bytesReceived() == m_previousBytes) { + if (VLReceiver_bytesReceived() == s_previousBytes) { return false; } - m_previousBytes = bytesReceived(); + s_previousBytes = VLReceiver_bytesReceived(); return true; } -bool VLReceiver::read(ByteStream &data) +uint16_t VLReceiver_bytesReceived() +{ + return BitStream_bytepos(&s_vlData); +} + +bool VLReceiver_read(ByteStream *data) { - if (!m_vlData.bytepos() || m_vlData.bytepos() > VL_MAX_DATA_TRANSFER) { + if (!BitStream_bytepos(&s_vlData) || BitStream_bytepos(&s_vlData) > VL_MAX_DATA_TRANSFER) { DEBUG_LOG("Nothing to read, or read too much"); return false; } - uint8_t size = m_vlData.peekData(1); - const uint8_t *actualData = m_vlData.data() + 2; - if (!data.rawInit(actualData, size)) { + uint8_t size = BitStream_peekData(&s_vlData, 1); + const uint8_t *actualData = BitStream_data(&s_vlData) + 2; + if (!ByteStream_rawInit(data, actualData, size)) { DEBUG_LOG("Failed to init buffer for VL read"); return false; } // reset the VL state and receive buffer now - resetVLState(); + VLReceiver_resetVLState(); return true; } // The recv PCI handler is called every time the analog value passes the // 'threshold' which is dynamically established to be about half way between // 'light' and 'dark' in the current environment. So in other words, it is +// turn on/off the legacy receiver +void VLReceiver_setLegacyReceiver(bool legacy) +{ + s_legacy = legacy; +} + // called for every 'on' or 'off' blink -void VLReceiver::recvPCIHandler() +void VLReceiver_recvPCIHandler() { // toggle the tracked pin state no matter what - m_pinState = (uint8_t)!m_pinState; + s_pinState = (uint8_t)!s_pinState; // grab current time - uint32_t now = Time::microseconds(); + uint32_t now = Time_microseconds(); // calc time difference between previous change and now - uint32_t diff = (uint32_t)(now - m_prevTime); - m_prevTime = now; + uint32_t diff = (uint32_t)(now - s_prevTime); + s_prevTime = now; // filter out values much too large from being truncated if (diff > UINT16_MAX) { return; } // handle the blink duration and process it into data - if (m_legacy) { - handleVLTimingLegacy((uint16_t)diff); + if (s_legacy) { + VLReceiver_handleVLTimingLegacy((uint16_t)diff); } else { - handleVLTiming((uint16_t)diff); + VLReceiver_handleVLTiming((uint16_t)diff); } } // state machine that can be fed VL timings to parse them and interpret the intervals -void VLReceiver::handleVLTimingLegacy(uint16_t diff) +void VLReceiver_handleVLTimingLegacy(uint16_t diff) { - switch (m_recvState) { + switch (s_recvState) { case WAITING_HEADER_MARK: // just look for a big blink timing if (diff >= VL_HEADER_SPACE_MIN_LEGACY && diff <= VL_HEADER_MARK_MAX_LEGACY) { // go straight to the header space - m_recvState = WAITING_HEADER_SPACE; + s_recvState = WAITING_HEADER_SPACE; } break; case WAITING_HEADER_SPACE: @@ -253,129 +287,129 @@ void VLReceiver::handleVLTimingLegacy(uint16_t diff) // because being restrictive here isn't really beneficial if (diff >= VL_HEADER_SPACE_MIN_LEGACY && diff <= VL_HEADER_MARK_MAX_LEGACY) { // iterate to first data mark - m_recvState = READING_DATA_MARK; + s_recvState = READING_DATA_MARK; // estimate the data threshold based on the length of the space - m_vlMarkThreshold = (diff / 4); + s_vlMarkThreshold = (diff / 4); } break; case READING_DATA_MARK: // classify as 1 or 0 based on the mark threshold and write into buffer - m_vlData.write1Bit(diff > m_vlMarkThreshold); - m_recvState = READING_DATA_SPACE; + BitStream_write1Bit(&s_vlData, diff > s_vlMarkThreshold); + s_recvState = READING_DATA_SPACE; break; case READING_DATA_SPACE: // in the legacy transmission the spaces didn't carry data - m_recvState = READING_DATA_MARK; + s_recvState = READING_DATA_MARK; break; default: // ?? - DEBUG_LOGF("Bad receive state: %u", m_recvState); + DEBUG_LOGF("Bad receive state: %u", s_recvState); break; } } // state machine that can be fed VL timings to parse them and interpret the intervals -void VLReceiver::handleVLTiming(uint16_t diff) +void VLReceiver_handleVLTiming(uint16_t diff) { uint8_t bit; - switch (m_recvState) { + switch (s_recvState) { case WAITING_HEADER_MARK: case WAITING_HEADER_SPACE: // both cases are basically the same, just look for a big timing if (diff >= VL_HEADER_SPACE_MIN && diff <= VL_HEADER_MARK_MAX) { // iterate through first two states - m_recvState = (RecvState)(m_recvState + 1); + s_recvState = (RecvState)(s_recvState + 1); } break; case READING_BAUD_MARK: // accumulate the diff in the mark threshold for averaging later - m_vlMarkThreshold += diff; - m_recvState = READING_BAUD_SPACE; + s_vlMarkThreshold += diff; + s_recvState = READING_BAUD_SPACE; break; case READING_BAUD_SPACE: // couonter is used to count bauds till 4 - m_counter++; + s_counter++; // accumulate the diff in the space threshold for averaging later - m_vlSpaceThreshold += diff; + s_vlSpaceThreshold += diff; // if not at 4 bauds yet keep reading baud marks - if (m_counter < 4) { - m_recvState = READING_BAUD_MARK; + if (s_counter < 4) { + s_recvState = READING_BAUD_MARK; break; } // otherwise read all 4 bauds now proceed with processing them // average out the mark and space from the bauds - m_vlMarkThreshold /= 4; - m_vlSpaceThreshold /= 4; + s_vlMarkThreshold /= 4; + s_vlSpaceThreshold /= 4; // reset counter and parity bit - m_counter = 0; - m_parityBit = 0; + s_counter = 0; + s_parityBit = 0; // advanced state to first data mark - m_recvState = READING_DATA_MARK; + s_recvState = READING_DATA_MARK; break; case READING_DATA_MARK: // counter is now counting the marks for parity tracking - m_counter++; + s_counter++; // extract the bit of data based on the calculated mark threshold - bit = (diff > m_vlMarkThreshold) ? 1 : 0; + bit = (diff > s_vlMarkThreshold) ? 1 : 0; // accumulate the parity and write out the bit into the vldata - m_parityBit = (m_parityBit ^ bit) & 1; - m_vlData.write1Bit(bit); - m_recvState = READING_DATA_SPACE; + s_parityBit = (s_parityBit ^ bit) & 1; + BitStream_write1Bit(&s_vlData, bit); + s_recvState = READING_DATA_SPACE; break; case READING_DATA_SPACE: // the spaces also transmit data in their lengths - bit = (diff > m_vlSpaceThreshold) ? 1 : 0; + bit = (diff > s_vlSpaceThreshold) ? 1 : 0; // also accumulate this data into the parity - m_parityBit = (m_parityBit ^ bit) & 1; + s_parityBit = (s_parityBit ^ bit) & 1; // and then write out the bit into the vldata - m_vlData.write1Bit(bit); + BitStream_write1Bit(&s_vlData, bit); // when counter is a multiple of 4 (since it only counts marks) - if ((m_counter % 4) == 0) { + if ((s_counter % 4) == 0) { // then go to the parity processing for the previous 8 bits of data - m_recvState = READING_DATA_PARITY_MARK; + s_recvState = READING_DATA_PARITY_MARK; } else { // otherwise continue reading out data bits - m_recvState = READING_DATA_MARK; + s_recvState = READING_DATA_MARK; } break; case READING_DATA_PARITY_MARK: // the parity is also a bit of data using a mark - bit = (diff > m_vlMarkThreshold) ? 1 : 0; + bit = (diff > s_vlMarkThreshold) ? 1 : 0; // check the parity bit against the running accumulated parity - if ((m_parityBit & 1) != bit) { + if ((s_parityBit & 1) != bit) { // immediately reset the receiver if the parity doesn't match // could show a flash here but that would probably just make // receiving worse by introducing a delay - resetVLState(); + VLReceiver_resetVLState(); break; } // the parity space is just an empty space after the parity bit - m_recvState = READING_DATA_PARITY_SPACE; + s_recvState = READING_DATA_PARITY_SPACE; break; case READING_DATA_PARITY_SPACE: // after the parity it's back to regular mark - m_recvState = READING_DATA_MARK; + s_recvState = READING_DATA_MARK; break; default: // ?? - DEBUG_LOGF("Bad receive state: %u", m_recvState); + DEBUG_LOGF("Bad receive state: %u", s_recvState); break; } } -void VLReceiver::resetVLState() +void VLReceiver_resetVLState() { - m_counter = 0; - m_vlMarkThreshold = 0; - m_vlSpaceThreshold = 0; - m_previousBytes = 0; - m_parityBit = 0; - m_recvState = WAITING_HEADER_MARK; + s_counter = 0; + s_vlMarkThreshold = 0; + s_vlSpaceThreshold = 0; + s_previousBytes = 0; + s_parityBit = 0; + s_recvState = WAITING_HEADER_MARK; // zero out the receive buffer and reset bit receiver position - m_vlData.reset(); + BitStream_reset(&s_vlData); #ifdef VORTEX_EMBEDDED // reset the threshold to a high value so that it can be pulled down again - threshold = THRESHOLD_BEGIN; + s_threshold = THRESHOLD_BEGIN; #endif DEBUG_LOG("VL State Reset"); } -#endif +#endif \ No newline at end of file diff --git a/VortexEngine/src/Wireless/VLReceiver.h b/VortexEngine/src/Wireless/VLReceiver.h index 96482cea1f..f4e7660b5c 100644 --- a/VortexEngine/src/Wireless/VLReceiver.h +++ b/VortexEngine/src/Wireless/VLReceiver.h @@ -1,6 +1,7 @@ #ifndef VL_RECEIVER_H #define VL_RECEIVER_H +#include #include #include "../Serial/BitStream.h" @@ -9,97 +10,45 @@ #if VL_ENABLE_RECEIVER == 1 -class ByteStream; -class Mode; - -class VLReceiver -{ - VLReceiver(); - -public: - // init and cleanup the receiver - static bool init(); - static void cleanup(); - - // check whether a full VL message is ready to read - static bool dataReady(); - // whether actively receiving - static bool isReceiving(); - // the percent of data received - static uint8_t percentReceived(); - static uint16_t bytesReceived() { return m_vlData.bytepos(); } - - // receive the VL message into a target mode - static bool receiveMode(Mode *pMode); - - // turn the receiver on/off - static bool beginReceiving(); - static bool endReceiving(); - // checks if there is new data since the last reset or check - static bool onNewData(); - // reset VL receiver buffer - static void resetVLState(); - - // turn on/off the legacy receiver - static void setLegacyReceiver(bool legacy) { m_legacy = legacy; } - - // The handler called when the VL receiver flips between on/off - // this has to be public because it's called from a global ISR - // but technically it's an internal function for VLReceiver - static void recvPCIHandler(); -private: - // reading functions - // PCI handler for when VL receiver pin changes states - static bool read(ByteStream &data); - static void handleVLTiming(uint16_t diff); - static void handleVLTimingLegacy(uint16_t diff); - - // =================== - // private data: - - // BitStream object that VL data is fed to bit by bit - static BitStream m_vlData; - - // Receive state used for state machine in PCIhandler - enum RecvState : uint8_t - { - WAITING_HEADER_MARK, - WAITING_HEADER_SPACE, - READING_BAUD_MARK, - READING_BAUD_SPACE, - READING_DATA_MARK, - READING_DATA_SPACE, - READING_DATA_PARITY_MARK, - READING_DATA_PARITY_SPACE - }; - - // state information used by the PCIHandler - static RecvState m_recvState; - // used to track pin changes - static uint32_t m_prevTime; - static uint8_t m_pinState; - - // used to compare if received data has changed since last checking - static uint16_t m_previousBytes; - - // the determined time based on sync - static uint16_t m_vlMarkThreshold; - // the determined time based on sync - static uint16_t m_vlSpaceThreshold; - - // count of the sync bits (similar length starter bits) - static uint8_t m_counter; - - static uint8_t m_parityBit; - - // legacy mode - static bool m_legacy; - -#ifdef VORTEX_LIB - friend class Vortex; -#endif -}; +typedef struct ByteStream ByteStream; +typedef struct Mode Mode; + +// init and cleanup the receiver +bool VLReceiver_init(); +void VLReceiver_cleanup(); + +// check whether a full VL message is ready to read +bool VLReceiver_dataReady(); +// whether actively receiving +bool VLReceiver_isReceiving(); +// the percent of data received +uint8_t VLReceiver_percentReceived(); +uint16_t VLReceiver_bytesReceived(); + +// receive the VL message into a target mode +bool VLReceiver_receiveMode(Mode *pMode); + +// turn the receiver on/off +bool VLReceiver_beginReceiving(); +bool VLReceiver_endReceiving(); +// checks if there is new data since the last reset or check +bool VLReceiver_onNewData(); +// reset VL receiver buffer +void VLReceiver_resetVLState(); + +// turn on/off the legacy receiver +void VLReceiver_setLegacyReceiver(bool legacy); + +// The handler called when the VL receiver flips between on/off +// this has to be public because it's called from a global ISR +// but technically it's an internal function for VLReceiver +void VLReceiver_recvPCIHandler(); + +// reading function +bool VLReceiver_read(ByteStream *data); +void VLReceiver_handleVLTiming(uint16_t diff); +void VLReceiver_handleVLTimingLegacy(uint16_t diff); #endif -#endif +#endif \ No newline at end of file diff --git a/VortexEngine/src/Wireless/VLSender.cpp b/VortexEngine/src/Wireless/VLSender.cpp index 9abdb9d0f8..c9b60e439a 100644 --- a/VortexEngine/src/Wireless/VLSender.cpp +++ b/VortexEngine/src/Wireless/VLSender.cpp @@ -13,105 +13,105 @@ #endif // the serial buffer for the data -ByteStream VLSender::m_serialBuf; +static ByteStream s_serialBuf; // a bit walker for the serial data -BitStream VLSender::m_bitStream; +static BitStream s_bitStream; // some runtime meta info -uint8_t VLSender::m_size = 0; -uint8_t VLSender::m_parity = 0; +static uint8_t s_size = 0; +static uint8_t s_parity = 0; -bool VLSender::init() +bool VLSender_init() { return true; } -void VLSender::cleanup() +void VLSender_cleanup() { } -bool VLSender::loadMode(const Mode *targetMode) +bool VLSender_loadMode(const Mode *targetMode) { - m_serialBuf.clear(); + ByteStream_clear(&s_serialBuf); // save the target mode to it's savefile buffer format - if (!targetMode->saveToBuffer(m_serialBuf, 2)) { + if (!Mode_saveToBuffer(targetMode, &s_serialBuf, 2)) { DEBUG_LOG("Failed to save mode to buffer"); return false; } // decompress the mode on lifi, no compression here - if (!m_serialBuf.decompress()) { + if (!ByteStream_decompress(&s_serialBuf)) { DEBUG_LOG("Failed to decompress VL buf for sending"); return false; } // ensure the data isn't too big - if (m_serialBuf.rawSize() > VL_MAX_DATA_TRANSFER) { - DEBUG_LOGF("Cannot transfer that much data: %u bytes", m_serialBuf.rawSize()); + if (ByteStream_rawSize(&s_serialBuf) > VL_MAX_DATA_TRANSFER) { + DEBUG_LOGF("Cannot transfer that much data: %u bytes", ByteStream_rawSize(&s_serialBuf)); return false; } // point the bitstream at the serial buffer - m_bitStream.init((uint8_t *)m_serialBuf.rawData(), m_serialBuf.rawSize()); + BitStream_initBuf(&s_bitStream, (uint8_t *)ByteStream_rawData(&s_serialBuf), ByteStream_rawSize(&s_serialBuf)); // the size of the buf should never really exceed 256 - m_size = (uint8_t)m_serialBuf.rawSize(); + s_size = (uint8_t)ByteStream_rawSize(&s_serialBuf); // reset parity - m_parity = 0; - DEBUG_LOGF("Size: %u", m_size); + s_parity = 0; + DEBUG_LOGF("Size: %u", s_size); return true; } -void VLSender::send(const Mode *targetMode) +void VLSender_send(const Mode *targetMode) { - loadMode(targetMode); + VLSender_loadMode(targetMode); // now send the header - sendMarkSpace(VL_HEADER_MARK, VL_HEADER_SPACE); + VLSender_sendMarkSpace(VL_HEADER_MARK, VL_HEADER_SPACE); // send some sync bytes to let the receiver determine baudrate, this will // send: (zero mark - zero space - one mark - one space) x 2 // then the receiver will add up all the marks and all the spaces and divide // each by 4 to get the middle point between the one/zero for both marks and spaces for (uint8_t b = 0; b < 2; b++) { - sendMarkSpace(VL_TIMING_BIT_ZERO, VL_TIMING_BIT_ZERO); - sendMarkSpace(VL_TIMING_BIT_ONE, VL_TIMING_BIT_ONE); + VLSender_sendMarkSpace(VL_TIMING_BIT_ZERO, VL_TIMING_BIT_ZERO); + VLSender_sendMarkSpace(VL_TIMING_BIT_ONE, VL_TIMING_BIT_ONE); } // it ends up being way cheaper just to send this legacy byte than to // implement logic in the receiver to account for this byte being missing in // the new protocol, so we send it, originally it was the block count but // that always ended up being 1 anyway for the Duo because blocksize was 255 // and the duo mode size max was no more than ~76 bytes? (I forget right now) - sendByte(1); + VLSender_sendByte(1); // send the size of the data first (this was the blocksize) - sendByte(m_size); + VLSender_sendByte(s_size); // iterate each byte in the block and write it - for (uint8_t i = 0; i < m_size; ++i) { + for (uint8_t i = 0; i < s_size; ++i) { // write the byte - sendByte(m_bitStream.peekData(i)); + VLSender_sendByte(BitStream_peekData(&s_bitStream, i)); } // send one last blink because the previous data could end on a space // so the receiver needs a last blink to determine it's length - sendMarkSpace(VL_TIMING_BIT_ZERO, VL_TIMING_BIT_ZERO); + VLSender_sendMarkSpace(VL_TIMING_BIT_ZERO, VL_TIMING_BIT_ZERO); } -void VLSender::sendLegacy(const Mode *targetMode) +void VLSender_sendLegacy(const Mode *targetMode) { - loadMode(targetMode); + VLSender_loadMode(targetMode); // wakeup receiver - sendMarkSpace(50, 50); + VLSender_sendMarkSpace(50, 50); // now send the header - sendMarkSpace(VL_HEADER_MARK_LEGACY, VL_HEADER_SPACE_LEGACY); + VLSender_sendMarkSpace(VL_HEADER_MARK_LEGACY, VL_HEADER_SPACE_LEGACY); // the number of blocks is always 1, previously block size would be for big // transfers like IR but in reality that never happens on a duo so it is 1 - sendByteLegacy(1); + VLSender_sendByteLegacy(1); // the next byte is the size or 'remainder' in the last block, so 1 block // means the size in this byte is the size of that block. But again on duo // this protocol ended up being impractical and only 1 block is ever needed. // The purpose of separating blocks was to put a space between blocks for // improved sender/receiver synchronization instead of one long stream - sendByteLegacy(m_size); + VLSender_sendByteLegacy(s_size); // iterate each byte in the block and write it - for (uint8_t i = 0; i < m_size; ++i) { + for (uint8_t i = 0; i < s_size; ++i) { // write the byte - sendByteLegacy(m_bitStream.peekData(i)); + VLSender_sendByteLegacy(BitStream_peekData(&s_bitStream, i)); } } -void VLSender::sendByte(uint8_t data) +void VLSender_sendByte(uint8_t data) { // Loop through bits 7-0 two at a time for (uint8_t i = 0; i < 8; i += 2) { @@ -122,26 +122,26 @@ void VLSender::sendByte(uint8_t data) // Second bit (space) is the lower bit uint8_t space = pair & 1; // Send both bits as a mark-space pair - sendMarkSpace(VL_TIMING_BIT(mark), VL_TIMING_BIT(space)); + VLSender_sendMarkSpace(VL_TIMING_BIT(mark), VL_TIMING_BIT(space)); // Update parity by XOR'ing both bits - m_parity ^= mark ^ space; + s_parity ^= mark ^ space; } // Send final parity bit as mark, followed by a 0 space - sendMarkSpace(VL_TIMING_BIT(m_parity & 1), VL_TIMING_BIT(0)); + VLSender_sendMarkSpace(VL_TIMING_BIT(s_parity & 1), VL_TIMING_BIT(0)); } -void VLSender::sendByteLegacy(uint8_t data) +void VLSender_sendByteLegacy(uint8_t data) { // Sends from left to right, MSB first for (uint8_t b = 0; b < 8; b++) { // grab the bit of data at the index uint8_t bit = (data >> (7 - b)) & 1; // send 3x timing size for 1s and 1x timing for 0 - sendMarkSpace(VL_TIMING_BIT_LEGACY(bit), VL_TIMING); + VLSender_sendMarkSpace(VL_TIMING_BIT_LEGACY(bit), VL_TIMING); } } -void VLSender::sendMarkSpace(uint16_t markTime, uint16_t spaceTime) +void VLSender_sendMarkSpace(uint16_t markTime, uint16_t spaceTime) { #ifdef VORTEX_LIB // send mark timing over socket @@ -149,33 +149,33 @@ void VLSender::sendMarkSpace(uint16_t markTime, uint16_t spaceTime) // send space timing over socket Vortex::vcallbacks()->infraredWrite(false, spaceTime); #else - startPWM(); - Time::delayMicroseconds(markTime); - stopPWM(); - Time::delayMicroseconds(spaceTime); + VLSender_startPWM(); + Time_delayMicroseconds(markTime); + VLSender_stopPWM(); + Time_delayMicroseconds(spaceTime); #endif } -void VLSender::startPWM() +void VLSender_startPWM() { #if defined(VORTEX_EMBEDDED) // brightness backup - uint8_t oldBrightness = Leds::getBrightness(); + uint8_t oldBrightness = Leds_getBrightness(); // ensure max brightness - Leds::setBrightness(255); - Leds::setIndex(LED_0, RGB_WHITE); - Leds::update(); + Leds_setBrightness(255); + Leds_setIndex(LED_0, RGB_WHITE); + Leds_update(); // restore brightness - Leds::setBrightness(oldBrightness); + Leds_setBrightness(oldBrightness); #endif } -void VLSender::stopPWM() +void VLSender_stopPWM() { #if defined(VORTEX_EMBEDDED) - Leds::clearAll(); - Leds::update(); + Leds_clearAll(); + Leds_update(); #endif } -#endif +#endif \ No newline at end of file diff --git a/VortexEngine/src/Wireless/VLSender.h b/VortexEngine/src/Wireless/VLSender.h index a368201d99..391c52db2f 100644 --- a/VortexEngine/src/Wireless/VLSender.h +++ b/VortexEngine/src/Wireless/VLSender.h @@ -1,6 +1,7 @@ #ifndef VL_SENDER_H #define VL_SENDER_H +#include #include "../Serial/ByteStream.h" #include "../Serial/BitStream.h" @@ -8,42 +9,26 @@ #if VL_ENABLE_SENDER == 1 -class Mode; - -class VLSender -{ - VLSender(); - -public: - static bool init(); - static void cleanup(); - - // send a mode - static void send(const Mode *targetMode); - static void sendLegacy(const Mode *targetMode); - -private: - static bool loadMode(const Mode *targetMode); - // send a full 8 bits in a tight loop - static void sendByte(uint8_t data); - // send full 8 bits legacy protocol - static void sendByteLegacy(uint8_t data); - // send a mark/space by turning PWM on/off - static void sendMarkSpace(uint16_t markTime, uint16_t spaceTime); - // turn the VL transmitter on/off in realtime - static void startPWM(); - static void stopPWM(); - - // the serial buffer for the data - static ByteStream m_serialBuf; - // a bit walker for the serial data - static BitStream m_bitStream; - - // some runtime meta info - static uint8_t m_size; - static uint8_t m_parity; -}; +typedef struct Mode Mode; -#endif +bool VLSender_init(); +void VLSender_cleanup(); + +// send a mode +void VLSender_send(const Mode *targetMode); +void VLSender_sendLegacy(const Mode *targetMode); + +bool VLSender_loadMode(const Mode *targetMode); +// send a full 8 bits in a tight loop +void VLSender_sendByte(uint8_t data); +// send full 8 bits legacy protocol +void VLSender_sendByteLegacy(uint8_t data); +// send a mark/space by turning PWM on/off +void VLSender_sendMarkSpace(uint16_t markTime, uint16_t spaceTime); +// turn the VL transmitter on/off in realtime +void VLSender_startPWM(); +void VLSender_stopPWM(); #endif + +#endif \ No newline at end of file diff --git a/avrsize.sh b/avrsize.sh old mode 100644 new mode 100755