/* PIP - Platform Independent Primitives Universal output to console class Ivan Pelipenko peri4ko@yandex.ru This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #include "piincludes_p.h" #include "picout.h" #include "pibytearray.h" #include "pistack.h" #include "piobject.h" #include "pistring_std.h" #ifdef HAS_LOCALE # include # include #endif #ifdef WINDOWS # include # include # include # define COMMON_LVB_UNDERSCORE 0x8000 #endif //! \addtogroup Core //! \{ //! \~\class PICout picout.h //! \~\brief //! \~english Universal output to console class //! \~russian Универсальный вывод в консоль //! //! \~english \section PICout_sec0 Synopsis //! \~russian \section PICout_sec0 Краткий обзор //! \~english //! This class provide many stream operators for output with some features. //! Output to %PICout is thread-sequential, i.e. doesn`t mixed from parallel //! threads. //! //! \~russian //! Данный класс предоставляет множество операторов для вывода в консоль. //! Вывод в %PICout потоково-последовательный, т.е. не смешивается из параллельных //! потоков. //! //! \~english \section PICout_sec1 Features //! \~russian \section PICout_sec1 Особенности //! \~english //! * support text formatting (color, style) //! * insertion spaces between entries //! * insertion new line at the end of output //! * strings are quoted //! * custom output operator can be easily written //! * can outpur to console, internal buffer or both //! //! \~russian //! * поддержка форматирования (цвет, стиль) //! * вставка пробелов между выводами //! * вставка новой строки после последнего вывода //! * строки обрамляются кавычками //! * легко создавать сови операторы вывода //! * может выводить в консоль, внутренний буфер или в оба места //! //! \~english \section PICout_ex0 Usage //! \~russian \section PICout_ex0 Использование //! \~\snippet picout.cpp 0 //! //! \~english \section PICout_ex1 Writing your own output operator //! \~russian \section PICout_ex1 Создание своего оператора вывода //! \~\snippet picout.cpp own //! //! \} //! \addtogroup Core //! \{ //! \~\class PICout::Notifier picout.h //! \~\brief //! \~english Class for emit notifications of PICout //! \~russian Класс для посылки событий от PICout //! //! \~english \section PICoutNotifier_sec0 Synopsis //! \~russian \section PICoutNotifier_sec0 Краткий обзор //! \~english //! This class used as PICout events emitter. When //! PICout constructs with external PIString* buffer //! and some ID, last copy of this PICout on delete //! emit event "finished()" on object Notifier::object(). //! Sample: //! //! \~russian //! Этот класс используется как источник событий PICout. //! Когда PICout сконструирован с внешним буфером PIString* //! и каким-то ID, последняя копия этого PICout при уничтожении //! посылает событие "finished()" у объекта Notifier::object(). //! Пример: //! //! \~\snippet picout.cpp notifier //! //! \} class NotifierObject: public PIObject { PIOBJECT(NotifierObject) public: NotifierObject() {} EVENT2(finished, int, id, PIString*, buffer) }; PICout::Notifier::Notifier() { o = new NotifierObject(); } PICout::Notifier * PICout::Notifier::instance() { static PICout::Notifier * ret = new PICout::Notifier(); return ret; } PIObject * PICout::Notifier::object() { return instance()->o; } using namespace PICoutManipulators; PIMutex & PICout::__mutex__() {static PIMutex * ret = new PIMutex(); return *ret;} PIString & PICout::__string__() {static PIString * ret = new PIString(); return *ret;} PICout::OutputDevices PICout::devs = PICout::StdOut; PRIVATE_DEFINITION_START(PICout) PIStack cos_; #ifdef WINDOWS static void * hOut; static WORD dattr; static DWORD smode; #endif #ifdef HAS_LOCALE std::wstring_convert, char16_t> utf8conv; #endif PRIVATE_DEFINITION_END(PICout) #ifdef WINDOWS void * PICout::__Private__::hOut = 0; WORD PICout::__Private__::dattr = 0; DWORD PICout::__Private__::smode = 0; #endif PICout::PICout(int controls): fo_(true), cc_(false), fc_(false), act_(true), cnb_(10), co_(controls) { init(); } PICout::PICout(bool active): fo_(true), cc_(false), fc_(false), act_(active), cnb_(10), co_(PICoutManipulators::DefaultControls) { buffer_ = nullptr; if (act_) init(); } PICout::PICout(PIString * buffer, int id, PIFlags controls): fo_(true), cc_(false), fc_(false), act_(true), cnb_(10), co_(controls) { init(); buffer_ = buffer; id_ = id; } PICout::PICout(const PICout & other): fo_(other.fo_), cc_(true), fc_(false), act_(other.act_), cnb_(other.cnb_), attr_(other.attr_), id_(other.id_), buffer_(other.buffer_), co_(other.co_) { } PICout::~PICout() { if (!act_) return; if (fc_) applyFormat(PICoutManipulators::Default); if (cc_) return; newLine(); if ((co_ & NoLock) != NoLock) PICout::__mutex__().unlock(); if (buffer_) ((NotifierObject*)Notifier::object())->finished(id_, buffer_); } PICout PICout::operator <<(const PICoutAction v) { if (!act_) return *this; #ifdef WINDOWS CONSOLE_SCREEN_BUFFER_INFO sbi; COORD coord; CONSOLE_CURSOR_INFO curinfo; #endif switch (v) { case PICoutManipulators::Flush: if (!buffer_ && isOutputDeviceActive(StdOut)) std::cout << std::flush; break; case PICoutManipulators::Backspace: if (isOutputDeviceActive(StdOut)) { #ifdef WINDOWS GetConsoleScreenBufferInfo(__Private__::hOut, &sbi); coord = sbi.dwCursorPosition; coord.X = piMax(0, int(coord.X) - 1); SetConsoleCursorPosition(__Private__::hOut, coord); printf(" "); SetConsoleCursorPosition(__Private__::hOut, coord); #else printf("\e[1D \e[1D"); #endif } break; case PICoutManipulators::ShowCursor: if (isOutputDeviceActive(StdOut)) { #ifdef WINDOWS GetConsoleCursorInfo(__Private__::hOut, &curinfo); curinfo.bVisible = true; SetConsoleCursorInfo(__Private__::hOut, &curinfo); #else printf("\e[?25h"); #endif } break; case PICoutManipulators::HideCursor: if (isOutputDeviceActive(StdOut)) { #ifdef WINDOWS GetConsoleCursorInfo(__Private__::hOut, &curinfo); curinfo.bVisible = false; SetConsoleCursorInfo(__Private__::hOut, &curinfo); #else printf("\e[?25l"); #endif } break; case PICoutManipulators::ClearLine: if (isOutputDeviceActive(StdOut)) { #ifdef WINDOWS GetConsoleScreenBufferInfo(__Private__::hOut, &sbi); coord = sbi.dwCursorPosition; int dx = coord.X; coord.X = 0; SetConsoleCursorPosition(__Private__::hOut, coord); if (dx > 0) { char * line = new char[dx + 1]; memset(line, ' ', dx); line[dx] = 0; printf("%s", line); delete[] line; } SetConsoleCursorPosition(__Private__::hOut, coord); #else printf("\e[0G\e[K"); #endif } break; case PICoutManipulators::ClearScreen: if (isOutputDeviceActive(StdOut)) { #ifdef WINDOWS /// TODO : wondows ClearScreen !!! #else printf("\e[H\e[J"); #endif } break; case PICoutManipulators::SaveContol: saveControl(); break; case PICoutManipulators::RestoreControl: restoreControl(); break; default: break; }; return *this; } PICout PICout::operator <<(const PICoutManipulators::PICoutFormat v) { switch (v) { case PICoutManipulators::Bin: cnb_ = 2; break; case PICoutManipulators::Oct: cnb_ = 8; break; case PICoutManipulators::Dec: cnb_ = 10; break; case PICoutManipulators::Hex: cnb_ = 16; break; default: applyFormat(v); }; return *this; } PICout PICout::operator <<(const PIFlags & v) { if (v[PICoutManipulators::Bin]) cnb_ = 2; if (v[PICoutManipulators::Oct]) cnb_ = 8; if (v[PICoutManipulators::Dec]) cnb_ = 10; if (v[PICoutManipulators::Hex]) cnb_ = 16; if (v[PICoutManipulators::Bold]) applyFormat(PICoutManipulators::Bold); if (v[PICoutManipulators::Faint]) applyFormat(PICoutManipulators::Faint); if (v[PICoutManipulators::Italic]) applyFormat(PICoutManipulators::Italic); if (v[PICoutManipulators::Underline]) applyFormat(PICoutManipulators::Underline); if (v[PICoutManipulators::Blink]) applyFormat(PICoutManipulators::Blink); if (v[PICoutManipulators::Black]) applyFormat(PICoutManipulators::Black); if (v[PICoutManipulators::Red]) applyFormat(PICoutManipulators::Red); if (v[PICoutManipulators::Green]) applyFormat(PICoutManipulators::Green); if (v[PICoutManipulators::Blue]) applyFormat(PICoutManipulators::Blue); if (v[PICoutManipulators::Yellow]) applyFormat(PICoutManipulators::Yellow); if (v[PICoutManipulators::Magenta]) applyFormat(PICoutManipulators::Magenta); if (v[PICoutManipulators::Cyan]) applyFormat(PICoutManipulators::Cyan); if (v[PICoutManipulators::White]) applyFormat(PICoutManipulators::White); if (v[PICoutManipulators::BackBlack]) applyFormat(PICoutManipulators::BackBlack); if (v[PICoutManipulators::BackRed]) applyFormat(PICoutManipulators::BackRed); if (v[PICoutManipulators::BackGreen]) applyFormat(PICoutManipulators::BackGreen); if (v[PICoutManipulators::BackBlue]) applyFormat(PICoutManipulators::BackBlue); if (v[PICoutManipulators::BackYellow]) applyFormat(PICoutManipulators::BackYellow); if (v[PICoutManipulators::BackMagenta]) applyFormat(PICoutManipulators::BackMagenta); if (v[PICoutManipulators::BackCyan]) applyFormat(PICoutManipulators::BackCyan); if (v[PICoutManipulators::BackWhite]) applyFormat(PICoutManipulators::BackWhite); if (v[PICoutManipulators::Default]) applyFormat(PICoutManipulators::Default); return *this; } #define PICOUTTOTARGET(v) { \ if (buffer_) {\ (*buffer_) << (v);\ } else {\ if (PICout::isOutputDeviceActive(PICout::StdOut)) std::cout << (v);\ if (PICout::isOutputDeviceActive(PICout::Buffer)) PICout::__string__() << (v);\ }\ } #define PINUMERICCOUT(v) { \ if (!act_) return *this; \ space(); \ if (cnb_ == 10) PICOUTTOTARGET(v) \ else writePIString(PIString::fromNumber(v, cnb_)); \ return *this; \ } PICout PICout::operator <<(const char * v) {if (!act_ || !v) return *this; if (v[0] == '\0') return *this; space(); quote(); PICOUTTOTARGET(v) quote(); return *this;} PICout PICout::operator <<(const bool v) {if (!act_) return *this; space(); if (v) PICOUTTOTARGET("true") else PICOUTTOTARGET("false") return *this;} PICout PICout::operator <<(const char v) {if (!act_) return *this; space(); PICOUTTOTARGET(v) return *this;} PICout PICout::operator <<(const uchar v) {PINUMERICCOUT(ushort(v))} PICout PICout::operator <<(const short int v) {PINUMERICCOUT(v)} PICout PICout::operator <<(const ushort v) {PINUMERICCOUT(v)} PICout PICout::operator <<(const int v) {PINUMERICCOUT(v)} PICout PICout::operator <<(const uint v) {PINUMERICCOUT(v)} PICout PICout::operator <<(const long v) {PINUMERICCOUT(v)} PICout PICout::operator <<(const ulong v) {PINUMERICCOUT(v)} PICout PICout::operator <<(const llong v) {PINUMERICCOUT(v)} PICout PICout::operator <<(const ullong v) {PINUMERICCOUT(v)} PICout PICout::operator <<(const float v) {if (!act_) return *this; space(); PICOUTTOTARGET(v) return *this;} PICout PICout::operator <<(const double v) {if (!act_) return *this; space(); PICOUTTOTARGET(v) return *this;} PICout PICout::operator <<(const void * v) {if (!act_) return *this; space(); PICOUTTOTARGET("0x") writePIString(PIString::fromNumber(ullong(v), 16)); return *this;} PICout PICout::operator <<(const PIObject * v) { if (!act_) return *this; space(); if (v == 0) PICOUTTOTARGET("PIObject*(0x0)") else { PICOUTTOTARGET(v->className()) PICOUTTOTARGET("*(0x") writePIString(PIString::fromNumber(ullong(v), 16)); PICOUTTOTARGET(", \"") writePIString(v->name()); PICOUTTOTARGET("\")") } return *this; } PICout PICout::operator <<(const PICoutSpecialChar v) { if (!act_) return *this; switch (v) { case Null: if (buffer_) { (*buffer_) << PIChar(); } else { if (isOutputDeviceActive(StdOut)) std::cout << char(0); if (isOutputDeviceActive(Buffer)) PICout::__string__() << PIChar(); } break; case NewLine: if (buffer_) { (*buffer_) << "\n"; } else { if (isOutputDeviceActive(StdOut)) std::cout << '\n'; if (isOutputDeviceActive(Buffer)) PICout::__string__() << "\n"; } fo_ = true; break; case Tab: if (buffer_) { (*buffer_) << "\t"; } else { if (isOutputDeviceActive(StdOut)) std::cout << '\t'; if (isOutputDeviceActive(Buffer)) PICout::__string__() << "\t"; } break; case Esc: #ifdef CC_VC if (buffer_) { (*buffer_) << PIChar(27); } else { if (isOutputDeviceActive(StdOut)) std::cout << char(27); if (isOutputDeviceActive(Buffer)) PICout::__string__() << PIChar(27); } #else if (buffer_) { (*buffer_) << "\e"; } else { if (isOutputDeviceActive(StdOut)) std::cout << '\e'; if (isOutputDeviceActive(Buffer)) PICout::__string__() << "\e"; } #endif break; case Quote: if (buffer_) { (*buffer_) << "\""; } else { if (isOutputDeviceActive(StdOut)) std::cout << '"'; if (isOutputDeviceActive(Buffer)) PICout::__string__() << "\""; } break; }; return *this; } PICout & PICout::saveControl() { if (!act_) return *this; PRIVATE->cos_.push(co_); return *this; } PICout & PICout::restoreControl() { if (!act_) return *this; if (!PRIVATE->cos_.isEmpty()) { co_ = PRIVATE->cos_.top(); PRIVATE->cos_.pop(); } return *this; } #undef PICOUTTOTARGET #undef PINUMERICCOUT //! \details //! \~english //! If it is not a first output and control \a AddSpaces is set space character is put //! \~russian //! Добавляет пробел если это не первый вывод и установлен флаг \a AddSpaces //! \~\sa \a quote(), \a newLine() PICout & PICout::space() { if (!act_) return *this; if (!fo_ && co_[AddSpaces]) { if (buffer_) { (*buffer_) << " "; } else { if (isOutputDeviceActive(StdOut)) std::cout << ' '; if (isOutputDeviceActive(Buffer)) PICout::__string__() << " "; } } fo_ = false; return *this; } //! \details //! \~english //! If control \a AddQuotes is set quote character is put //! \~russian //! Добавляет кавычки если установлен флаг \a AddQuotes //! \~\sa \a space(), \a newLine() PICout & PICout::quote() { if (!act_) return *this; if (co_[AddQuotes]) { if (buffer_) { (*buffer_) << "\""; } else { if (isOutputDeviceActive(StdOut)) std::cout << '"'; if (isOutputDeviceActive(Buffer)) PICout::__string__() << "\""; } } fo_ = false; return *this; } //! \details //! \~english //! If control \a AddNewLine is set new line character is put //! \~russian //! Добавляет новую строку если установлен флаг \a AddNewLine //! \~\sa \a space(), \a quote() PICout & PICout::newLine() { if (!act_) return *this; if (co_[AddNewLine]) { if (buffer_) { (*buffer_) << "\n"; } else { if (isOutputDeviceActive(StdOut)) std::cout << std::endl; if (isOutputDeviceActive(Buffer)) PICout::__string__() << "\n"; } } fo_ = false; return *this; } PICout & PICout::write(const char * str, int len) { if (!act_) return *this; if (buffer_) { buffer_->append(PIString(str, len)); } else { if (PICout::isOutputDeviceActive(PICout::StdOut)) std::cout.write(str, len); if (PICout::isOutputDeviceActive(PICout::Buffer)) PICout::__string__().append(PIString(str, len)); } return *this; } PICout & PICout::writePIString(const PIString & s) { if (!act_) return *this; if (buffer_) { buffer_->append(s); } else { if (PICout::isOutputDeviceActive(PICout::StdOut)) { #ifdef HAS_LOCALE std::cout << PRIVATE->utf8conv.to_bytes((char16_t*)&(const_cast(s).front()), (char16_t*)&(const_cast(s).front()) + s.size()); #else for (PIChar c: s) std::wcout.put(c.toWChar()); #endif } if (PICout::isOutputDeviceActive(PICout::Buffer)) PICout::__string__().append(s); } return *this; } void PICout::init() { #ifdef WINDOWS if (__Private__::hOut == 0) { __Private__::hOut = GetStdHandle(STD_OUTPUT_HANDLE); CONSOLE_SCREEN_BUFFER_INFO sbi; GetConsoleScreenBufferInfo(__Private__::hOut, &sbi); __Private__::dattr = sbi.wAttributes; } attr_ = __Private__::dattr; #endif buffer_ = nullptr; id_ = 0; if ((co_ & NoLock) != NoLock) PICout::__mutex__().lock(); } void PICout::applyFormat(PICoutFormat f) { if (!act_) return; if (buffer_ || !isOutputDeviceActive(StdOut)) return; fc_ = true; #ifdef WINDOWS static int mask_fore = ~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); static int mask_back = ~(BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE); switch (f) { case Bin: case Oct: case Dec: case Hex: break; case PICoutManipulators::Bold: attr_ |= FOREGROUND_INTENSITY; break; case PICoutManipulators::Underline: attr_ |= COMMON_LVB_UNDERSCORE; break; case PICoutManipulators::Black: attr_ = (attr_ & mask_fore); break; case PICoutManipulators::Red: attr_ = (attr_ & mask_fore) | FOREGROUND_RED; break; case PICoutManipulators::Green: attr_ = (attr_ & mask_fore) | FOREGROUND_GREEN; break; case PICoutManipulators::Blue: attr_ = (attr_ & mask_fore) | FOREGROUND_BLUE; break; case PICoutManipulators::Yellow: attr_ = (attr_ & mask_fore) | FOREGROUND_RED | FOREGROUND_GREEN; break; case PICoutManipulators::Magenta: attr_ = (attr_ & mask_fore) | FOREGROUND_RED | FOREGROUND_BLUE; break; case PICoutManipulators::Cyan: attr_ = (attr_ & mask_fore) | FOREGROUND_GREEN | FOREGROUND_BLUE; break; case PICoutManipulators::White: attr_ = (attr_ & mask_fore) | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; break; case PICoutManipulators::BackBlack: attr_ = (attr_ & mask_back); break; case PICoutManipulators::BackRed: attr_ = (attr_ & mask_back) | BACKGROUND_RED; break; case PICoutManipulators::BackGreen: attr_ = (attr_ & mask_back) | BACKGROUND_GREEN; break; case PICoutManipulators::BackBlue: attr_ = (attr_ & mask_back) | BACKGROUND_BLUE; break; case PICoutManipulators::BackYellow: attr_ = (attr_ & mask_back) | BACKGROUND_RED | BACKGROUND_GREEN; break; case PICoutManipulators::BackMagenta: attr_ = (attr_ & mask_back) | BACKGROUND_RED | BACKGROUND_BLUE; break; case PICoutManipulators::BackCyan: attr_ = (attr_ & mask_back) | BACKGROUND_GREEN | BACKGROUND_BLUE; break; case PICoutManipulators::BackWhite: attr_ = (attr_ & mask_back) | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE; break; case PICoutManipulators::Default: attr_ = __Private__::dattr; break; default: break; } SetConsoleTextAttribute(__Private__::hOut, attr_); #else switch (f) { case Bin: case Oct: case Dec: case Hex: break; case PICoutManipulators::Bold: printf("\e[1m"); break; case PICoutManipulators::Faint: printf("\e[2m"); break; case PICoutManipulators::Italic: printf("\e[3m"); break; case PICoutManipulators::Underline: printf("\e[4m"); break; case PICoutManipulators::Blink: printf("\e[5m"); break; case PICoutManipulators::Black: printf("\e[30m"); break; case PICoutManipulators::Red: printf("\e[31m"); break; case PICoutManipulators::Green: printf("\e[32m"); break; case PICoutManipulators::Blue: printf("\e[34m"); break; case PICoutManipulators::Yellow: printf("\e[33m"); break; case PICoutManipulators::Magenta: printf("\e[35m"); break; case PICoutManipulators::Cyan: printf("\e[36m"); break; case PICoutManipulators::White: printf("\e[37m"); break; case PICoutManipulators::BackBlack: printf("\e[40m"); break; case PICoutManipulators::BackRed: printf("\e[41m"); break; case PICoutManipulators::BackGreen: printf("\e[42m"); break; case PICoutManipulators::BackBlue: printf("\e[44m"); break; case PICoutManipulators::BackYellow: printf("\e[43m"); break; case PICoutManipulators::BackMagenta: printf("\e[45m"); break; case PICoutManipulators::BackCyan: printf("\e[46m"); break; case PICoutManipulators::BackWhite: printf("\e[47m"); break; case PICoutManipulators::Default: printf("\e[0m"); break; default: break; } #endif } bool PICout::setBufferActive(bool on, bool clear) { PIMutexLocker ml(PICout::__mutex__()); bool ret = isBufferActive(); if (clear) PICout::__string__().clear(); setOutputDevice(Buffer, on); return ret; } bool PICout::isBufferActive() { return isOutputDeviceActive(Buffer); } PIString PICout::buffer(bool clear) { PIMutexLocker ml(PICout::__mutex__()); PIString ret = PICout::__string__(); if (clear) PICout::__string__().clear(); return ret; } void PICout::clearBuffer() { PIMutexLocker ml(PICout::__mutex__()); PICout::__string__().clear(); } bool PICout::setOutputDevice(PICout::OutputDevice d, bool on) { bool ret = devs[d]; devs.setFlag(d, on); return ret; } void PICout::setOutputDevices(PICout::OutputDevices d) { devs = d; } bool PICout::isOutputDeviceActive(PICout::OutputDevice d) { return devs[d]; }