538 lines
16 KiB
C++
538 lines
16 KiB
C++
/*
|
|
PIP - Platform Independent Primitives
|
|
Universal output to console class
|
|
Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU 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 General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "piincludes_p.h"
|
|
#include "picout.h"
|
|
#include "piconsole.h"
|
|
#include "pibytearray.h"
|
|
#include "pistack.h"
|
|
#include "pistring_std.h"
|
|
#ifdef WINDOWS
|
|
# include <windows.h>
|
|
# include <wincon.h>
|
|
# define COMMON_LVB_UNDERSCORE 0x8000
|
|
#endif
|
|
|
|
/*! \class PICout
|
|
* \brief Class for formatted output similar std::cout
|
|
*
|
|
* \section PICout_sec0 Synopsis
|
|
* 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.
|
|
*
|
|
* \section PICout_sec1 Features
|
|
* - insertion spaces between entries
|
|
* - insertion new line at the end of output
|
|
* - strings are quoted
|
|
* - custom output operator can be easily written
|
|
*
|
|
* \section PICout_ex0 Usage
|
|
* \snippet picout.cpp 0
|
|
*
|
|
* \section PICout_ex1 Writing your own output operator
|
|
* \snippet picout.cpp own
|
|
*/
|
|
|
|
|
|
/*! \class PICout::Notifier
|
|
* \brief Class for emit notifications of PICout
|
|
*
|
|
* \section PICout_sec0 Synopsis
|
|
* 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:
|
|
* \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;
|
|
return &ret;
|
|
}
|
|
|
|
|
|
PIObject * PICout::Notifier::object() {
|
|
return instance()->o;
|
|
}
|
|
|
|
|
|
|
|
|
|
using namespace PICoutManipulators;
|
|
|
|
PIMutex & PICout::__mutex__() {static PIMutex ret; return ret;}
|
|
PIString & PICout::__string__() {static PIString ret; return ret;}
|
|
|
|
PICout::OutputDevices PICout::devs = PICout::StdOut;
|
|
|
|
PRIVATE_DEFINITION_START(PICout)
|
|
PIStack<PICoutControls> cos_;
|
|
#ifdef WINDOWS
|
|
static void * hOut;
|
|
static WORD dattr;
|
|
static DWORD smode;
|
|
#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(PIFlags<PICoutControl> controls): fo_(true), cc_(false), fc_(false), cnb_(10), co_(controls) {
|
|
init();
|
|
}
|
|
|
|
|
|
PICout::PICout(PIString * buffer, int id, PIFlags<PICoutManipulators::PICoutControl> controls): fo_(true), cc_(false),
|
|
fc_(false), cnb_(10), co_(controls) {
|
|
init();
|
|
buffer_ = buffer;
|
|
id_ = id;
|
|
}
|
|
|
|
|
|
PICout::PICout(const PICout & other): fo_(other.fo_), cc_(true), fc_(false), cnb_(other.cnb_), attr_(other.attr_),
|
|
id_(other.id_), buffer_(other.buffer_), co_(other.co_) {
|
|
}
|
|
|
|
|
|
PICout::~PICout() {
|
|
if (fc_) applyFormat(PICoutManipulators::Default);
|
|
if (cc_) return;
|
|
newLine();
|
|
PICout::__mutex__().unlock();
|
|
if (buffer_)
|
|
((NotifierObject*)Notifier::object())->finished(id_, buffer_);
|
|
}
|
|
|
|
|
|
PICout PICout::operator <<(const PICoutAction v) {
|
|
#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<int>(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 !!!
|
|
/*GetConsoleCursorInfo(__Private__::hOut, &curinfo);
|
|
curinfo.bVisible = false;
|
|
SetConsoleCursorInfo(__Private__::hOut, &curinfo);
|
|
|
|
SetConsoleCursorPosition(__Private__::hOut, ulcoord);
|
|
FillConsoleOutputAttribute(__Private__::hOut, __Private__::dattr, width * (height + 1), ulcoord, &written);
|
|
FillConsoleOutputCharacter(__Private__::hOut, ' ', width * (height + 1), ulcoord, &written);*/
|
|
#else
|
|
printf("\e[H\e[J");
|
|
#endif
|
|
}
|
|
break;
|
|
case PICoutManipulators::SaveContol: saveControl(); break;
|
|
case PICoutManipulators::RestoreControl: restoreControl(); break;
|
|
default: break;
|
|
};
|
|
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 PICOUTTOTARGETS(v) { \
|
|
if (buffer_) {\
|
|
(*buffer_) << (v);\
|
|
} else {\
|
|
if (PICout::isOutputDeviceActive(PICout::StdOut)) std::cout << (v).dataConsole();\
|
|
if (PICout::isOutputDeviceActive(PICout::Buffer)) PICout::__string__() << (v);\
|
|
}\
|
|
}
|
|
//#define PICOUTTOTARGETS(v) {if (PICout::isOutputDeviceActive(PICout::Buffer)) PICout::__string__() << (v); else printf("%s", (v).dataConsole());}
|
|
#define PINUMERICCOUT if (cnb_ == 10) PICOUTTOTARGET(v) else PICOUTTOTARGETS(PIString::fromNumber(v, cnb_))
|
|
|
|
|
|
PICout PICout::operator <<(const char * v) {if (v[0] == '\0') return *this; space(); quote(); PICOUTTOTARGET(v) quote(); return *this;}
|
|
|
|
//PICout PICout::operator <<(const std::string & v) {space(); quote(); if (PICout::isOutputDeviceActive(PICout::Buffer)) PICout::__string__() << StdString2PIString(v); else std::cout << (v); quote(); return *this;}
|
|
|
|
PICout PICout::operator <<(const bool v) {space(); if (v) PICOUTTOTARGET("true") else PICOUTTOTARGET("false") return *this;}
|
|
|
|
PICout PICout::operator <<(const char v) {space(); PICOUTTOTARGET(v) return *this;}
|
|
|
|
PICout PICout::operator <<(const uchar v) {space(); if (cnb_ == 10) PICOUTTOTARGET(ushort(v)) else PICOUTTOTARGETS(PIString::fromNumber(v, cnb_)) return *this;}
|
|
|
|
PICout PICout::operator <<(const short int v) {space(); PINUMERICCOUT return *this;}
|
|
|
|
PICout PICout::operator <<(const ushort v) {space(); PINUMERICCOUT return *this;}
|
|
|
|
PICout PICout::operator <<(const int v) {space(); PINUMERICCOUT return *this;}
|
|
|
|
PICout PICout::operator <<(const uint v) {space(); PINUMERICCOUT return *this;}
|
|
|
|
PICout PICout::operator <<(const long v) {space(); PINUMERICCOUT return *this;}
|
|
|
|
PICout PICout::operator <<(const ulong v) {space(); PINUMERICCOUT return *this;}
|
|
|
|
PICout PICout::operator <<(const llong v) {space(); PINUMERICCOUT return *this;}
|
|
|
|
PICout PICout::operator <<(const ullong v) {space(); PINUMERICCOUT return *this;}
|
|
|
|
PICout PICout::operator <<(const float v) {space(); PICOUTTOTARGET(v) return *this;}
|
|
|
|
PICout PICout::operator <<(const double v) {space(); PICOUTTOTARGET(v) return *this;}
|
|
|
|
PICout PICout::operator <<(const void * v) {space(); PICOUTTOTARGET("0x") PICOUTTOTARGETS(PIString::fromNumber(ullong(v), 16)) return *this;}
|
|
|
|
PICout PICout::operator <<(const PIObject * v) {
|
|
space();
|
|
if (v == 0) PICOUTTOTARGET("PIObject*(0x0)")
|
|
else {
|
|
PICOUTTOTARGET(v->className())
|
|
PICOUTTOTARGET("*(0x")
|
|
PICOUTTOTARGETS(PIString::fromNumber(ullong(v), 16))
|
|
PICOUTTOTARGET(", \"")
|
|
PICOUTTOTARGET(v->name())
|
|
PICOUTTOTARGET("\")")
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
PICout PICout::operator <<(const PICoutSpecialChar v) {
|
|
switch (v) {
|
|
case Null:
|
|
if (buffer_) {
|
|
(*buffer_) << PIChar(0);
|
|
} else {
|
|
if (isOutputDeviceActive(StdOut)) std::cout << char(0);
|
|
if (isOutputDeviceActive(Buffer)) PICout::__string__() << PIChar(0);
|
|
}
|
|
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() {
|
|
PRIVATE->cos_.push(co_);
|
|
return *this;
|
|
}
|
|
|
|
|
|
PICout & PICout::restoreControl() {
|
|
if (!PRIVATE->cos_.isEmpty()) {
|
|
co_ = PRIVATE->cos_.top();
|
|
PRIVATE->cos_.pop();
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
|
|
#undef PICOUTTOTARGET
|
|
#undef PINUMERICCOUT
|
|
|
|
PICout & PICout::space() {
|
|
if (!fo_ && co_[AddSpaces]) {
|
|
if (buffer_) {
|
|
(*buffer_) << " ";
|
|
} else {
|
|
if (isOutputDeviceActive(StdOut)) std::cout << ' ';
|
|
if (isOutputDeviceActive(Buffer)) PICout::__string__() << " ";
|
|
}
|
|
}
|
|
fo_ = false;
|
|
return *this;
|
|
}
|
|
|
|
PICout & PICout::quote() {
|
|
if (co_[AddQuotes]) {
|
|
if (buffer_) {
|
|
(*buffer_) << "\"";
|
|
} else {
|
|
if (isOutputDeviceActive(StdOut)) std::cout << '"';
|
|
if (isOutputDeviceActive(Buffer)) PICout::__string__() << "\"";
|
|
}
|
|
}
|
|
fo_ = false;
|
|
return *this;
|
|
}
|
|
|
|
PICout & PICout::newLine() {
|
|
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;
|
|
}
|
|
|
|
|
|
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_ = 0;
|
|
id_ = 0;
|
|
PICout::__mutex__().lock();
|
|
}
|
|
|
|
|
|
void PICout::applyFormat(PICoutFormat f) {
|
|
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];
|
|
}
|