#ifndef ANSICOLOR_H #define ANSICOLOR_H #include #include #include #define CSI "\x1b[" /** * ANSI Color codes */ /** * @brief The colors available under ANSI-BBS */ enum class COLOR : std::int8_t { /// BLACK (0) BLACK, /// RED (1) RED, /// GREEN (2) GREEN, /// BROWN (3) BROWN, /// YELLOW (3) YELLOW = 3, /// BLUE (4) BLUE, /// MAGENTA (5) MAGENTA, /// CYAN (6) CYAN, /// WHITE (7) WHITE }; /** * @brief ANSI-BBS text attributes */ enum class ATTR : std::int8_t { /// RESET forces all attributes (and Colors) to be sent. RESET, /// BOLD is the same as BRIGHT. BOLD, /// BRIGHT is the same as BOLD. BRIGHT = 1, /// SLOW BLINK BLINK = 5, /// INVERSE is Background on Foreground. INVERSE = 7 }; // std::int32_t constexpr long strhash(const char *txt) { long result = 0; for (int x = 0; x < 3; ++x) { if (txt[x] == 0) break; char c = txt[x]; // toupper if ((c >= 'a') && (c <= 'z')) c &= ~0x20; result = (result << 8) | c; } return result; }; #define A_BOLD 0x01 #define A_BLINK 0x02 #define A_INVERSE 0x04 #define A_RESET 0x10 /** * @class ANSIColor * This holds foreground, background and ANSI-BBS attribute * information. * The special attribute RESET forces attribute and color * output always. * * @brief Foreground, Background and Attributes * */ class ANSIColor { /** Foreground color */ COLOR fg; /** Background color */ COLOR bg; int attr; public: ANSIColor(); ANSIColor(ATTR a); ANSIColor(COLOR f); ANSIColor(COLOR f, ATTR a); ANSIColor(COLOR f, ATTR a1, ATTR a2); ANSIColor(COLOR f, COLOR b); ANSIColor(COLOR f, COLOR b, ATTR a); ANSIColor(COLOR f, COLOR b, ATTR a1, ATTR a2); ANSIColor(std::initializer_list il); constexpr ANSIColor(const char *text) : fg(COLOR::WHITE), bg(COLOR::BLACK), attr(0) { const char *cp = text; bool use_on = false; while (*cp != 0) { long key = strhash(cp); switch (key) { case strhash("BRI"): attr |= A_BOLD; break; case strhash("BOL"): attr |= A_BOLD; break; case strhash("BLI"): attr |= A_BLINK; break; case strhash("INV"): attr |= A_INVERSE; break; case strhash("RES"): attr |= A_RESET; break; case strhash("ON "): use_on = true; break; case strhash("BLA"): if (use_on) bg = COLOR::BLACK; else fg = COLOR::BLACK; break; case strhash("RED"): if (use_on) bg = COLOR::RED; else fg = COLOR::RED; break; case strhash("GRE"): if (use_on) bg = COLOR::GREEN; else fg = COLOR::GREEN; break; case strhash("BRO"): if (use_on) bg = COLOR::BROWN; else fg = COLOR::BROWN; break; case strhash("YEL"): if (use_on) bg = COLOR::YELLOW; else { fg = COLOR::YELLOW; attr |= A_BOLD; } // if (use_on) throw error! break; case strhash("BLU"): if (use_on) bg = COLOR::BLUE; else fg = COLOR::BLUE; break; case strhash("MAG"): if (use_on) bg = COLOR::MAGENTA; else fg = COLOR::MAGENTA; break; case strhash("CYA"): if (use_on) bg = COLOR::CYAN; else fg = COLOR::CYAN; break; case strhash("WHI"): if (use_on) bg = COLOR::WHITE; else fg = COLOR::WHITE; break; default: throw std::range_error("Invalid/unknown color or attribute"); } // skip to the space character while ((*cp != ' ') && (*cp != 0)) ++cp; // skip past the space character while (*cp == ' ') ++cp; } }; /* ANSIColor(int c1); ANSIColor(int c1, int c2); */ ANSIColor &Attr(ATTR a); bool operator==(const ANSIColor &c) const; bool operator!=(const ANSIColor &c) const; void setFg(COLOR f); void setFg(COLOR f, ATTR a); void setBg(COLOR b); /** * Get the foreground color * @return COLOR */ COLOR getFg() { return fg; }; /** * Get the background color * @return COLOR */ COLOR getBg() { return bg; }; void setAttr(ATTR a); std::string output(void) const; std::string operator()(void) const; }; #ifdef NON_WORKING #include constexpr std::vector from_text(const char *text) { std::vector vi; const char *cp = text; bool use_on = false; while (*cp != 0) { int key = strhash(cp); switch (key) { case strhash("BRI"): vi.push_back(1); break; case strhash("BOL"): vi.push_back(1); break; case strhash("BLI"): vi.push_back(5); break; case strhash("INV"): vi.push_back(7); break; case strhash("ON "): use_on = true; break; case strhash("BLU"): if (use_on) ac.setBg(COLOR::BLUE); else ac.setFg(COLOR::BLUE); break; case strhash("RED"): if (use_on) ac.setBg(COLOR::RED); else ac.setFg(COLOR::RED); break; case strhash("GRE"): if (use_on) ac.setBg(COLOR::GREEN); else ac.setFg(COLOR::GREEN); break; case strhash("YEL"): // if (use_on) throw error! ac.setFg(COLOR::YELLOW); ac.Attr(ATTR::BOLD); break; case strhash("BRO"): if (use_on) ac.setBg(COLOR::BROWN); else ac.setFg(COLOR::BROWN); break; case strhash("CYA"): if (use_on) ac.setBg(COLOR::CYAN); else ac.setFg(COLOR::CYAN); break; case strhash("MAG"): if (use_on) ac.setBg(COLOR::MAGENTA); else ac.setFg(COLOR::MAGENTA); break; case strhash("BLA"): if (use_on) ac.setBg(COLOR::BLACK); else ac.setFg(COLOR::BLACK); break; case strhash("WHI"): if (use_on) ac.setBg(COLOR::WHITE); else ac.setFg(COLOR::WHITE); break; } // skip to the space character while ((*cp != ' ') && (*cp != 0)) ++cp; // skip past the space character while (*cp == ' ') ++cp; } return vi; } #endif /* Maybe make char, char, char (fg, bg, attr) and store those from the constexpr. This "works", in that it does construct an ANSIColor from text. However, it isn't stored as an ANSIColor, but calls the function at run-time. (I was hoping for an efficient way to construct ANSIColor from a string.) Maybe a template that constructs it from a initializer_list? */ constexpr ANSIColor ac_from_text(const char *text) { ANSIColor ac(nullptr); const char *cp = text; bool use_on = false; while (*cp != 0) { int key = strhash(cp); switch (key) { case strhash("BRI"): ac.Attr(ATTR::BRIGHT); break; case strhash("BOL"): ac.Attr(ATTR::BOLD); break; case strhash("BLI"): ac.Attr(ATTR::BLINK); break; case strhash("INV"): ac.Attr(ATTR::INVERSE); break; case strhash("ON "): use_on = true; break; case strhash("BLU"): if (use_on) ac.setBg(COLOR::BLUE); else ac.setFg(COLOR::BLUE); break; case strhash("RED"): if (use_on) ac.setBg(COLOR::RED); else ac.setFg(COLOR::RED); break; case strhash("GRE"): if (use_on) ac.setBg(COLOR::GREEN); else ac.setFg(COLOR::GREEN); break; case strhash("YEL"): // if (use_on) throw error! ac.setFg(COLOR::YELLOW); ac.Attr(ATTR::BOLD); break; case strhash("BRO"): if (use_on) ac.setBg(COLOR::BROWN); else ac.setFg(COLOR::BROWN); break; case strhash("CYA"): if (use_on) ac.setBg(COLOR::CYAN); else ac.setFg(COLOR::CYAN); break; case strhash("MAG"): if (use_on) ac.setBg(COLOR::MAGENTA); else ac.setFg(COLOR::MAGENTA); break; case strhash("BLA"): if (use_on) ac.setBg(COLOR::BLACK); else ac.setFg(COLOR::BLACK); break; case strhash("WHI"): if (use_on) ac.setBg(COLOR::WHITE); else ac.setFg(COLOR::WHITE); break; } // skip to the space character while ((*cp != ' ') && (*cp != 0)) ++cp; // skip past the space character while (*cp == ' ') ++cp; } return ac; }; extern ANSIColor reset; #endif