#include "door.h" #include namespace door { /** * Construct a new ANSIColor::ANSIColor object * with sensible defaults (White on Black). * */ ANSIColor::ANSIColor() : fg(COLOR::WHITE), bg(COLOR::BLACK), reset(0), bold(0), blink(0), inverse(0) {} /** * Construct a new ANSIColor::ANSIColor object * with attribute set. * * @param a ATTR */ ANSIColor::ANSIColor(ATTR a) : ANSIColor() { Attr(a); } /** * Construct a new ANSIColor::ANSIColor object * with a foreground color. * * @param f COLOR */ ANSIColor::ANSIColor(COLOR f) : ANSIColor() { fg = f; } /** * Construct a new ANSIColor::ANSIColor object * with a foreground color and attribute. * * @param f COLOR * @param a ATTR */ ANSIColor::ANSIColor(COLOR f, ATTR a) : ANSIColor() { fg = f; Attr(a); } /** * Construct a new ANSIColor::ANSIColor object * with a foreground color and attributes. * * @param f COLOR * @param a1 ATTR * @param a2 ATTR */ ANSIColor::ANSIColor(COLOR f, ATTR a1, ATTR a2) : ANSIColor() { fg = f; Attr(a1); Attr(a2); } /** * Construct a new ANSIColor::ANSIColor object * with a foreground and background color. * * @param f COLOR * @param b COLOR */ ANSIColor::ANSIColor(COLOR f, COLOR b) : ANSIColor() { fg = f; bg = b; } /** * Construct a new ANSIColor::ANSIColor object * with a foreground color, background color, * and attribute. * * @param f COLOR * @param b COLOR * @param a ATTR */ ANSIColor::ANSIColor(COLOR f, COLOR b, ATTR a) : ANSIColor() { fg = f; bg = b; Attr(a); } /** * Construct a new ANSIColor::ANSIColor object * with foreground, background color and attributes. * * @param f COLOR * @param b COLOR * @param a1 ATTR * @param a2 ATTR */ ANSIColor::ANSIColor(COLOR f, COLOR b, ATTR a1, ATTR a2) : ANSIColor() { fg = f; bg = b; Attr(a1); Attr(a2); } /** * Set attribute. We return the object so * calls can be chained. * * @param a ATTR * @return ANSIColor& */ ANSIColor &ANSIColor::Attr(ATTR a) { switch (a) { case ATTR::RESET: reset = 1; break; case ATTR::BOLD: bold = 1; break; case ATTR::BLINK: blink = 1; break; case ATTR::INVERSE: inverse = 1; break; } return *this; } /** * Equal operator. * * This compares colors and attributes, but ignores reset. * * @param c const ANSIColor & * @return bool */ bool ANSIColor::operator==(const ANSIColor &c) const { return ((fg == c.fg) and (bg == c.bg) and (bold == c.bold) and (blink == c.blink) and (inverse == c.inverse)); } /** * Not-equal operator. * * This compares colors and attributes, but ignores reset. * * @param c const ANSIColor & * @return bool */ bool ANSIColor::operator!=(const ANSIColor &c) const { return !((fg == c.fg) and (bg == c.bg) and (bold == c.bold) and (blink == c.blink) and (inverse == c.inverse)); } void ANSIColor::setFg(COLOR f) { fg = f; } void ANSIColor::setBg(COLOR b) { bg = b; } void ANSIColor::attr(ATTR a) { // first, clear all attributes reset = 0; bold = 0; blink = 0; inverse = 0; Attr(a); } /** * Output the full ANSI codes for attributes and color. * This does not look at the previous values. */ std::string ANSIColor::output(void) const { std::string clr(CSI); // check for special cases if (reset and (fg == COLOR::BLACK) and (bg == COLOR::BLACK)) { clr += "0m"; return clr; } if (reset and (fg == COLOR::WHITE) and (bg == COLOR::BLACK)) { clr += "0m"; return clr; } if (reset) { clr += "0;"; } if (bold) { if (blink) { clr += "5;"; } clr += "1;"; } else { if (!reset) clr += "0;"; if (blink) { clr += "5;"; } } clr += std::to_string(30 + (int)fg) + ";"; clr += std::to_string(40 + (int)bg) + "m"; return clr; } std::string ANSIColor::debug(void) { std::string output; output = "ANSIColor FG"; output += std::to_string((int)fg); output += ", BG"; output += std::to_string((int)bg); output += ", B"; output += std::to_string(bold); output += ", R"; output += std::to_string(reset); return output; } /** * Output only what ANSI attributes and colors have changed. * This uses the previous ANSIColor value to determine what * has changed. * * This sets previous to the current upon completion. */ std::string ANSIColor::output(ANSIColor &previous) const { std::string clr(CSI); // color output optimization // check for special cases if (reset and (fg == COLOR::BLACK) and (bg == COLOR::BLACK)) { clr += "0m"; previous = *this; previous.reset = 0; return clr; } bool temp_reset = false; if ((!blink) and (blink != previous.blink)) { temp_reset = true; } if ((reset) or (temp_reset)) { // current has RESET, so default to always sending colors. if (temp_reset) { clr += "0m"; } // this fixes the extra \x1b that shows up with reset. if (clr.compare(CSI) == 0) clr.clear(); clr += output(); /* std::ofstream logf; logf.open("ansicolor.log", std::ofstream::out | std::ofstream::app); logf << "clr = [" << clr << "]" << std::endl; logf.close(); */ previous = *this; previous.reset = 0; return clr; } if (*this == previous) { clr.clear(); return clr; } // resume "optimization" if (bold != previous.bold) { // not the same, so handle this. if (bold) { if (blink) { clr += "5;"; } clr += "1;"; if (fg != previous.fg) clr += std::to_string((int)fg + 30) + ";"; if (bg != previous.bg) clr += std::to_string((int)bg + 40) + ";"; } else { clr += "0;"; if (blink) { clr += "5;"; } // RESET to turn OFF BOLD, clears previous if (fg != COLOR::WHITE) clr += std::to_string((int)fg + 30) + ";"; if (bg != COLOR::BLACK) clr += std::to_string((int)bg + 40) + ";"; } } else { // not bold. if (blink) { clr += "5;"; } if (fg != previous.fg) clr += std::to_string((int)fg + 30) + ";"; if (bg != previous.bg) clr += std::to_string((int)bg + 40) + ";"; }; // Remove ';' if last character std::string::iterator si = clr.end() - 1; if (*si == ';') { clr.erase(si); } if (clr.compare(CSI) == 0) clr.clear(); else clr += "m"; // final step, set previous to current and return the string; previous = *this; return clr; }; /** * This converts ANSI \ref COLOR and \ref ATTR to ANSI codes * understood by the \ref Door output class. */ std::ostream &operator<<(std::ostream &os, const ANSIColor &c) { std::string out; Door *d = dynamic_cast(&os); if (d != nullptr) { d->track = false; out = c.output(d->previous); // if (!out.empty()) if (out.compare("\x1b[") == 0) std::abort(); *d << out; d->track = true; } else { // "dumb" version that can't remember anything/ doesn't optimize color // output. std::string out = c.output(); os << out; } return os; } ANSIColor reset(ATTR::RESET); } // namespace door