#include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace monoformat; constexpr static std::uint16_t const ScreenWidth = 120; constexpr static std::uint16_t const ScreenHeight = 60; static std::atomic g_ctrlCPressed; static void handleCtrlC(int) { g_ctrlCPressed.store(true, std::memory_order_relaxed); } template static Container readEntireFile(std::filesystem::path const& fileName) { std::ifstream fileReader(fileName, std::ios::binary | std::ios::ate | std::ios::in); if (!fileReader) { std::ostringstream buf; buf << "Could not open \"" << fileName << "\" for reading"; throw std::runtime_error(buf.str()); } auto fileSize = fileReader.tellg(); fileReader.seekg(std::ios::beg); typename Container::size_type scalarSize = sizeof(typename Container::value_type); Container content((static_cast(fileSize) + scalarSize - 1) / scalarSize, static_cast(0)); fileReader.read(reinterpret_cast(&content[0]), static_cast(fileSize)); return content; } static void clearScreen() { std::cout << "\x1b[1J\x1b[1;1H" << std::flush; } static void renderScreeen(monoformat::OneBitBufferInterface const& imageBuffer) { std::uint16_t const w = ScreenWidth; std::uint16_t const h = ScreenHeight / 2; std::ostringstream buf; buf << "\xE2\x94\x8C"; for (std::uint16_t i = 0; i < w; ++i) { buf << "\xE2\x94\x80"; } buf << "\xE2\x94\x90\n"; for (std::uint16_t y = 0; y < h; ++y) { buf << "\xE2\x94\x82"; for (std::uint16_t x = 0; x < w; ++x) { bool const pxUpperSet = imageBuffer.isPixelSet(x, 2 * y + 0); bool const pxLowerSet = imageBuffer.isPixelSet(x, 2 * y + 1); if (pxUpperSet && pxLowerSet) { buf << "\xE2\x96\x88"; } else if (pxUpperSet) { buf << "\xF0\x9F\xAC\x8E"; } else if (pxLowerSet) { buf << "\xE2\x96\x84"; } else { buf << " "; } } buf << "\xE2\x94\x82\n"; } if (ScreenHeight % 1) { buf << "\xE2\x94\x82"; for (std::uint16_t x = 0; x < w; ++x) { bool const pxUpperSet = imageBuffer.isPixelSet(x, ScreenHeight - 1); if (pxUpperSet) { buf << "\xF0\x9F\xAC\x8E"; } else { buf << " "; } } buf << "\xE2\x94\x82\n"; } buf << "\xE2\x94\x94"; for (std::uint16_t i = 0; i < w; ++i) { buf << "\xE2\x94\x80"; } buf << "\xE2\x94\x98\n"; std::cout << buf.str(); } static void showContents(monoformat::File const& file, std::size_t tick, std::int64_t timestamp, std::span customFonts) { monoformat::ImageElement imageBufferElement{0, 0, ScreenWidth, ScreenHeight}; auto imageBuffer = imageBufferElement.image(); for (auto const& section : file.sections) { if (auto sec = dynamic_cast(section.get())) { for (std::size_t i = 0; i < sec->elementCount(); ++i) { sec->elementAt(i)->drawTo(&imageBuffer, tick, timestamp, customFonts); } } else if (auto sec = dynamic_cast(section.get())) { for (std::size_t i = 0; i < sec->elementCount(); ++i) { sec->elementAt(i)->drawTo(&imageBuffer, tick, timestamp, customFonts); } } } renderScreeen(imageBuffer); } static void showContents2(std::span data, std::size_t tick, std::int64_t timestamp) { std::vector buffer; buffer.resize((ScreenWidth * ScreenHeight + 8 - 1) / 8); MemoryOneBitBuffer imageBuffer{buffer, ScreenWidth, ScreenHeight}; auto ret = parseAndApply(data, &imageBuffer, ScreenWidth, ScreenHeight, true, tick, timestamp); if (!ret) { std::cerr << "Parse & apply error\n"; } renderScreeen(imageBuffer); } int main(int argc, char** argv) { if (argc < 2 || !argv[1]) { std::ostringstream buf; buf << "Usage: " << argv[0] << " filename [timestamp]\n"; std::cerr << buf.str(); return 1; } if (std::string_view{argv[1]} == "--help") { std::ostringstream buf; buf << "Usage: " << argv[0] << " filename [timestamp]\n"; std::cout << buf.str() << std::flush; return 0; } std::int64_t timestamp = -1; if (argc > 2) { char* endptr = nullptr; timestamp = strtoll(argv[2], &endptr, 10); if (!endptr || *endptr) { std::ostringstream buf; buf << "Usage: " << argv[0] << " filename [timestamp]\n"; std::cerr << buf.str(); return 1; } } try { auto contents = readEntireFile>(argv[1]); auto file = monoformat::parseFile(contents); if (!file) { std::ostringstream buf; buf << "Parse error while processing the file \"" << argv[1] << "\""; throw std::runtime_error(buf.str()); } std::vector customFonts; for (auto const& section : file->sections) { if (auto fs = dynamic_cast(section.get())) { customFonts.push_back(fs); } } std::signal(SIGINT, &handleCtrlC); std::signal(SIGTERM, &handleCtrlC); std::size_t tick = 0; while (!g_ctrlCPressed.load(std::memory_order_relaxed)) { std::int64_t currentTime = timestamp == -1 ? time(nullptr) : timestamp; clearScreen(); //showContents(*file, tick, currentTime, customFonts); showContents2(contents, tick, currentTime); std::ostringstream buf; buf << "Animation Tick: " << tick << ", Timestamp = " << currentTime << "\n"; std::cout << buf.str() << std::flush; std::this_thread::sleep_for(std::chrono::milliseconds{40}); ++tick; } } catch (std::exception& e) { std::ostringstream buf; buf << argv[0] << ": " << e.what() << "\n"; std::cerr << buf.str(); return 2; } return 0; }