#include "Console.h" #include #ifndef WIN32 # include # include #endif using namespace Framework; ConsoleCommand::ConsoleCommand(Text name) : ReferenceCounter(), name(name) {} ConsoleCommand::~ConsoleCommand() {} const Text& Framework::ConsoleCommand::getName() const { return name; } Framework::StickyConsoleContent::StickyConsoleContent() : ReferenceCounter(), length(0), content(0), color(0), backgroundColor(0), zConsoleHandler(0) {} Framework::StickyConsoleContent::~StickyConsoleContent() { delete[] content; delete[] color; delete[] backgroundColor; } int Framework::StickyConsoleContent::getLength() const { return length; } void Framework::StickyConsoleContent::setContent( int length, const char* content) { delete[] this->content; this->content = new char[length]; memcpy(this->content, content, length); this->length = length; delete[] color; delete[] backgroundColor; color = 0; backgroundColor = 0; } void Framework::StickyConsoleContent::setContent( int length, const char* content, Color color) { setContent(length, content); this->color = new Color[length]; for (int i = 0; i < length; i++) { this->color[i] = color; } } void Framework::StickyConsoleContent::setContent( int length, const char* content, Color color, Color backgroundColor) { setContent(length, content, color); this->backgroundColor = new Color[length]; for (int i = 0; i < length; i++) { this->backgroundColor[i] = backgroundColor; } } void Framework::StickyConsoleContent::setContent( int length, const char* content, Color* color) { setContent(length, content); this->color = new Color[length]; for (int i = 0; i < length; i++) { this->color[i] = color[i]; } } void Framework::StickyConsoleContent::setContent( int length, const char* content, Color* color, Color* backgroundColor) { setContent(length, content, color); this->backgroundColor = new Color[length]; for (int i = 0; i < length; i++) { this->backgroundColor[i] = backgroundColor[i]; } } void Framework::StickyConsoleContent::repaceContent( int start, int length, int newLength, const char* newContent) { int resultLength = this->length + newLength - length; char* resultContent = new char[resultLength]; memcpy(resultContent, content, start); memcpy(resultContent + start, newContent, newLength); memcpy(resultContent + start + newLength, content + start + length, this->length - start - length); delete[] content; content = resultContent; if (color) { Color* resultColor = new Color[resultLength]; memcpy(resultColor, color, start); for (int i = 0; i < newLength; i++) { resultColor[start + i] = color[start]; } memcpy(resultColor + start + newLength, color + start + length, sizeof(Color) * (this->length - start - length)); delete[] color; color = resultColor; } if (backgroundColor) { Color* resultBgColor = new Color[resultLength]; memcpy(resultBgColor, backgroundColor, start); for (int i = 0; i < newLength; i++) { resultBgColor[start + i] = backgroundColor[start]; } memcpy(resultBgColor + start + newLength, backgroundColor + start + length, sizeof(Color) * (this->length - start - length)); delete[] backgroundColor; backgroundColor = resultBgColor; } this->length = resultLength; } void Framework::StickyConsoleContent::repaceContent( int start, int length, int newLength, const char* newContent, Color color) { repaceContent(start, length, newLength, newContent); if (!this->color) { this->color = new Color[this->length]; for (int i = 0; i < this->length; i++) { this->color[i] = (Color)-1; } } for (int i = start; i < start + newLength; i++) { this->color[i] = color; } } void Framework::StickyConsoleContent::repaceContent(int start, int length, int newLength, const char* newContent, Color color, Color backgroundColor) { repaceContent(start, length, newLength, newContent, color); if (!this->backgroundColor) { this->backgroundColor = new Color[this->length]; for (int i = 0; i < this->length; i++) { this->backgroundColor[i] = (Color)-1; } } for (int i = start; i < start + newLength; i++) { this->backgroundColor[i] = backgroundColor; } } void Framework::StickyConsoleContent::repaceContent( int start, int length, int newLength, const char* newContent, Color* color) { repaceContent(start, length, newLength, newContent); if (!this->color) { this->color = new Color[this->length]; for (int i = 0; i < this->length; i++) { this->color[i] = (Color)-1; } } memcpy(this->color + start, color, sizeof(Color) * newLength); } void Framework::StickyConsoleContent::repaceContent(int start, int length, int newLength, const char* newContent, Color* color, Color* backgroundColor) { repaceContent(start, length, newLength, newContent, color); if (!this->backgroundColor) { this->backgroundColor = new Color[this->length]; for (int i = 0; i < this->length; i++) { this->backgroundColor[i] = (Color)-1; } } memcpy(this->backgroundColor + start, backgroundColor, sizeof(Color) * newLength); } void Framework::StickyConsoleContent::triggerUpdate() { if (zConsoleHandler != 0) { zConsoleHandler->print(); } } bool Framework::StickyConsoleContent::isInput() { return false; } void Framework::StickyConsoleContent::setConsoleHandlerZ( ConsoleHandler* zConsoleHandler) { this->zConsoleHandler = zConsoleHandler; } void Framework::StickyConsoleContent::setCursorToBeginning() {} int Framework::StickyConsoleContent::print() const { int lineCount = 0; int maxWidth = zConsoleHandler->getWidth(); std::cout << "\r\33[0K"; // erase current line int lineLength = 0; int lastColor = -1; int lastBgColor = -1; for (int i = 0; i < length; i++) { if (color && (int)color[i] != lastColor) { if ((int)color[i] == -1) { std::cout << "\033[0m"; if (backgroundColor && (int)backgroundColor[i] != -1) { if (backgroundColor[i] < Color::LIGHT_BLACK) { std::cout << Text("\033[1;") + Text(40 + (int)backgroundColor[i]) + "m"; } else { std::cout << Text("\033[1;") + Text(100 + (int)backgroundColor[i] - 8) + "m"; } } } else if (color[i] < Color::LIGHT_BLACK) { std::cout << Text("\033[1;") + Text(30 + (int)color[i]) + "m"; } else { std::cout << Text("\033[1;") + Text(90 + (int)color[i] - 8) + "m"; } } if (backgroundColor && (int)backgroundColor[i] != lastBgColor) { if ((int)backgroundColor[i] == -1) { std::cout << "\033[0m"; if (color && (int)color[i] != -1) { if (color[i] < Color::LIGHT_BLACK) { std::cout << Text("\033[1;") + Text(30 + (int)color[i]) + "m"; } else { std::cout << Text("\033[1;") + Text(90 + (int)color[i] - 8) + "m"; } } } else if (backgroundColor[i] < Color::LIGHT_BLACK) { std::cout << Text("\033[1;") + Text(40 + (int)backgroundColor[i]) + "m"; } else { std::cout << Text("\033[1;") + Text(100 + (int)backgroundColor[i] - 8) + "m"; } } std::cout << content[i]; lastColor = color ? (int)color[i] : -1; lastBgColor = backgroundColor ? (int)backgroundColor[i] : -1; if ((content[i] == '\n' || lineLength == maxWidth) && i < length - 1) { if (lineLength == maxWidth && content[i] != '\n') { std::cout << "\n"; } lineLength = 0; lineCount++; } else { lineLength++; } } if (lastColor != -1 || lastBgColor != -1) std::cout << "\033[0m"; if (lineCount == 0 && lineLength > 0) { lineCount++; } return lineCount; } void Framework::StickyConsoleContent::restoreCursorPos() {} ConsoleHandler* Framework::StickyConsoleContent::zConsoleHandlerRef() const { return zConsoleHandler; } Framework::ConsoleProgressBar::ConsoleProgressBar() : StickyConsoleContent(), progress(0), maxProgress(1), maxWidth(-1) {} void Framework::ConsoleProgressBar::setMaxWidth(int maxWidth) { this->maxWidth = maxWidth; } void Framework::ConsoleProgressBar::setProgress(int progress) { if (progress < 0) progress = 0; this->progress = progress; } void Framework::ConsoleProgressBar::setMaxProgress(int maxProgress) { if (maxProgress < 0) maxProgress = 0; this->maxProgress = maxProgress; } int Framework::ConsoleProgressBar::getProgress() const { return progress; } int Framework::ConsoleProgressBar::print() const { int maxWidth = zConsoleHandlerRef()->getWidth(); int width = this->maxWidth == -1 ? maxWidth : this->maxWidth; if (width > maxWidth) { width = maxWidth; } if (width < 7) { return 0; } std::cout << "\r\33[0K"; // erase current line int progress = this->progress > maxProgress ? maxProgress : this->progress; int progressChars = width - 7; std::cout << "[\033[1;47m"; int progressWidth = (int)(((double)progress / maxProgress) * progressChars); if (progressWidth > progressChars) { progressWidth = progressChars; } for (int i = 0; i < progressWidth; i++) { std::cout << " "; } std::cout << "\033[0m"; for (int i = 0; i < progressChars - progressWidth; i++) { std::cout << " "; } std::cout << "] "; Text str((int)(((double)progress / maxProgress) * 100)); for (int i = 0; i < 3 - str.getLength(); i++) { std::cout << " "; } std::cout << str.getText() << "%"; return 1; } Framework::InputLine::InputLine() : StickyConsoleContent(), Thread(), input(""), cursorPos(0), suggestions(0) {} Framework::InputLine::~InputLine() { suggestions->release(); } void Framework::InputLine::addPossibleCommand(ConsoleCommand* command) { commands.add(command); } bool Framework::InputLine::isInput() { return true; } void Framework::InputLine::setCursorToBeginning() { cs.lock(); std::cout << "\r"; } int Framework::InputLine::print() const { std::cout << "\33[0K" // clear current line << input.getText(); if (suggestions) { std::cout << "\n"; return 1 + dynamic_cast(suggestions)->print(); } return 1; } void Framework::InputLine::restoreCursorPos() { if (cursorPos > 0) { std::cout << Text("\33[") + Text(cursorPos) + "C"; } cs.unlock(); } void Framework::InputLine::thread() { #ifdef WIN32 INPUT_RECORD inputRecord; DWORD eventsRead; HANDLE handle = GetStdHandle(STD_INPUT_HANDLE); while (true) { if (ReadConsoleInput(handle, &inputRecord, 1, &eventsRead)) { cs.lock(); if (eventsRead > 0 && inputRecord.EventType == KEY_EVENT && inputRecord.Event.KeyEvent.bKeyDown) { if (inputRecord.Event.KeyEvent.wVirtualKeyCode == VK_LEFT) { cursorPos--; if (cursorPos < 0) { cursorPos = 0; } else { std::cout << "\33[1D" << std::flush; } } else if (inputRecord.Event.KeyEvent.wVirtualKeyCode == VK_HOME) { // Pos1 key moves cursor to beginning of line std::cout << "\r" << std::flush; cursorPos = 0; } else if (inputRecord.Event.KeyEvent.wVirtualKeyCode == VK_RIGHT) { cursorPos++; if (cursorPos > input.getLength()) { cursorPos = input.getLength(); } else { std::cout << "\33[1C" << std::flush; } } else if (inputRecord.Event.KeyEvent.wVirtualKeyCode == VK_END) { // Pos1 key moves cursor to beginning of line cursorPos = input.getLength(); std::cout << Text("\r\33[") + Text(cursorPos) + "C" << std::flush; } else if (inputRecord.Event.KeyEvent.wVirtualKeyCode == VK_TAB) { applyAutocompletion(); } else if (inputRecord.Event.KeyEvent.wVirtualKeyCode == VK_RETURN) { std::cout << "\r\33[0K" << std::flush; // eraze line Text cmd = input; cs.unlock(); if (executeCommand(cmd)) { cs.lock(); input = ""; cursorPos = 0; cs.unlock(); triggerUpdate(); } continue; // skip the unlock } else if (inputRecord.Event.KeyEvent.wVirtualKeyCode == VK_BACK) { if (cursorPos > 0) { input.remove(cursorPos - 1, cursorPos); cursorPos--; std::cout << "\r\33[0K" // eraze line << input.getText() // output current input // move cursor to current position << Text("\r\33[") + Text(cursorPos) + "C" << std::flush; } } else if (inputRecord.Event.KeyEvent.uChar.AsciiChar) { input.insert( cursorPos, inputRecord.Event.KeyEvent.uChar.AsciiChar); cursorPos++; std::cout << "\r\33[0K" // eraze line << input.getText() // output current input // move cursor to current position << Text("\r\33[") + Text(cursorPos) + "C" << std::flush; } } cs.unlock(); } } #else char c; while (read(STDIN_FILENO, &c, 1) == 1) { if (c == 27) { // Check for the escape key (27 is the ASCII code for escape) char seq[2]; if (read(STDIN_FILENO, &seq, 2) == 2) { cs.lock(); if (seq[0] == '[') { if (seq[1] == 'D') { // left arrow key cursorPos--; if (cursorPos < 0) { cursorPos = 0; } else { std::cout << "\33[1D" << std::flush; } } else if (seq[1] == 'H') { // Pos1 key moves cursor to beginning of line std::cout << "\r" << std::flush; cursorPos = 0; } else if (seq[1] == 'C') { // right arrow key cursorPos++; if (cursorPos > input.getLength()) { cursorPos = input.getLength(); } else { std::cout << "\33[1C" << std::flush; } } else if (seq[1] == 'F') { // End key moves cursor to end of line cursorPos = input.getLength(); std::cout << Text("\r\33[") + Text(cursorPos) + "C" << std::flush; } } cs.unlock(); } } else if (c == 8) { // backspace if (cursorPos > 0) { cs.lock(); input.remove(cursorPos - 1, cursorPos); cursorPos--; std::cout << "\r\33[0K" // eraze line << input.getText() // output current input // move cursor to current position << Text("\r\33[") + Text(cursorPos) + "C" << std::flush; cs.unlock(); } } else if (c == 9) { // tab cs.lock(); applyAutocompletion(); cs.unlock(); } else if (c == 13) { // enter cs.lock(); std::cout << "\r\33[0K" << std::flush; // eraze line Text cmd = input; cs.unlock(); if (executeCommand(cmd)) { cs.lock(); input = ""; cursorPos = 0; cs.unlock(); } } else { cs.lock(); input.insert(cursorPos, c); cursorPos++; std::cout << "\r\33[0K" // eraze line << input.getText() // output current input // move cursor to current position << Text("\r\33[") + Text(cursorPos) + "C" << std::flush; cs.unlock(); } } #endif } void Framework::InputLine::applyAutocompletion() { RCArray params; bool lastArgFinished = 0; Text* cmd = input.getTeilText(0, cursorPos); parseCommand(*cmd, params, lastArgFinished); cmd->release(); bool paramsStarted = params.getEintragAnzahl() > 1 || lastArgFinished; Text* name; if (params.getEintragAnzahl() > 0) { name = params.get(0); params.remove(0); } else { name = new Text(""); } RCArray suggestions; for (ConsoleCommand* command : commands) { if ((!paramsStarted && command->getName().hatAt(0, *name)) || (paramsStarted && command->getName().istGleich(*name))) { if (paramsStarted) { command->addAutocompletePossibilities( params, !lastArgFinished, suggestions); break; } else { suggestions.add(new Text(command->getName())); } } } if (this->suggestions) { this->suggestions->clear(); } else { this->suggestions = new ConsoleListView(); this->suggestions->setConsoleHandlerZ(zConsoleHandlerRef()); } for (Text* suggestion : suggestions) { this->suggestions->addItem(*suggestion); } if (this->suggestions->getItems().getEintragAnzahl() > 0) { bool commonChar = true; for (int i = lastArgFinished ? 0 : (params.getEintragAnzahl() > 0 ? params.z(params.getEintragAnzahl() - 1)->getLength() : name->getLength()); true; i++) { if (i >= this->suggestions->getItems().z(0)->getLength()) { commonChar = false; break; } for (int j = 1; j < this->suggestions->getItems().getEintragAnzahl(); j++) { if (i >= this->suggestions->getItems().z(j)->getLength()) { commonChar = false; break; } if (this->suggestions->getItems().z(j)->getText()[i] != this->suggestions->getItems().z(0)->getText()[i]) { commonChar = false; break; } } if (!commonChar) { break; } input.insert( cursorPos, this->suggestions->getItems().z(0)->getText()[i]); cursorPos++; } } name->release(); if (this->suggestions->getItems().getEintragAnzahl() <= 1) { this->suggestions->release(); this->suggestions = 0; } triggerUpdate(); } bool Framework::InputLine::executeCommand(Text command) { RCArray params; bool lastArgFinished = 0; Text* cmd = input.getTeilText(0, cursorPos); if (!parseCommand(*cmd, params, lastArgFinished)) { cmd->release(); return false; } cmd->release(); Text* name; if (params.getEintragAnzahl() > 0) { name = params.get(0); params.remove(0); } else { name = new Text(""); } RCArray suggestions; for (ConsoleCommand* command : commands) { if (command->getName().istGleich(*name)) { name->release(); return command->execute(params); } } name->release(); return false; } bool Framework::InputLine::parseCommand( Text command, RCArray& split, bool& lastFinished) { const char* cmd = command.getText(); Text str; bool insideString = false; bool insideString2 = false; bool lastEscape = false; lastFinished = false; while (*cmd) { if (*cmd == ' ' && !insideString && !insideString2 && !lastEscape) { if (str.getLength() > 0) { split.add(new Text(str)); str = ""; lastFinished = true; } } else if (*cmd == '"' && !insideString2 && !lastEscape) { insideString = !insideString; lastFinished = !insideString; } else if (*cmd == '\'' && !insideString && !lastEscape) { insideString2 = !insideString2; lastFinished = !insideString2; } else if (*cmd == '\\' && !lastEscape) { lastEscape = true; lastFinished = false; } else { lastEscape = false; str.append(*cmd); lastFinished = false; } cmd++; } if (str.getLength() > 0) { split.add(new Text(str)); } return !insideString && !insideString2 && !lastEscape; } Framework::ConsoleListView::ConsoleListView() : StickyConsoleContent(), maxColumns(-1), maxVisibleLines(5), lineOffset(0) {} int Framework::ConsoleListView::getUsedColumns() const { if (!zConsoleHandlerRef()) return 0; int maxWidth = zConsoleHandlerRef()->getWidth(); int columnSize = 0; for (Text* item : items) { if (item->getLength() > columnSize) { columnSize = item->getLength(); } } columnSize += 4; if (maxWidth < columnSize) { return 0; } int columns = maxWidth / columnSize; if (maxColumns > 0 && columns > maxColumns) { columns = maxColumns; } if (columns > items.getEintragAnzahl()) { columns = items.getEintragAnzahl(); } return columns; } int Framework::ConsoleListView::getNeededLines() const { int columns = getUsedColumns(); if (!columns) { return 0; } if (maxColumns > 0 && columns > maxColumns) { columns = maxColumns; } int lines = items.getEintragAnzahl() / columns; if (items.getEintragAnzahl() % columns != 0) { lines++; } return lines; } void Framework::ConsoleListView::setMaxVisibleLines(int maxVisibleLines) { this->maxVisibleLines = maxVisibleLines; } void Framework::ConsoleListView::setLineOffset(int lineOffset) { this->lineOffset = lineOffset; int maxLines = getNeededLines(); if (this->lineOffset > maxLines - maxVisibleLines) { this->lineOffset = maxLines - maxVisibleLines; } if (this->lineOffset < 0) { this->lineOffset = 0; } } void Framework::ConsoleListView::setMaxColumns(int maxColumns) { this->maxColumns = maxColumns; } void Framework::ConsoleListView::addItem(Text item) { item.ersetzen("\n", " "); int index = 0; for (Text* curreent : items) { int i = 0; while (i < curreent->getLength() && i < item.getLength()) { if (curreent->getText()[i] != item.getText()[i]) { break; } i++; } if (i == curreent->getLength() && i == item.getLength()) { return; // item already exists } if (i < curreent->getLength() && i == item.getLength()) { items.add(new Text(item), index); return; } if (i < curreent->getLength() && i < item.getLength() && curreent->getText()[i] > item.getText()[i]) { items.add(new Text(item), index); return; } index++; } items.add(new Text(item)); } void Framework::ConsoleListView::clear() { items.leeren(); } const RCArray& Framework::ConsoleListView::getItems() const { return items; } int Framework::ConsoleListView::print() const { int lines = lineOffset; int maxLines = getNeededLines(); if (lines > maxLines - maxVisibleLines) { lines = maxLines - maxVisibleLines; } if (lines < 0) { lines = 0; } int columns = getUsedColumns(); if (!columns) { return 0; } int columnSize = zConsoleHandlerRef()->getWidth() / columns; int printetLines = 0; int currentColumn = 0; std::cout << "\33[0K"; // clear current line for (int i = columns * lines; i < items.getEintragAnzahl(); i++) { if (i >= columns * (lines + maxVisibleLines)) { break; } if (currentColumn >= columns) { std::cout << "\n\r\33[0K"; printetLines++; currentColumn++; } if (!printetLines) { printetLines++; } std::cout << items.z(i)->getText(); for (int j = items.z(i)->getLength(); j < columnSize; j++) { std::cout << " "; } currentColumn++; } return printetLines; } Framework::ConsoleHandler::ConsoleHandler() : ReferenceCounter(), lines(0), lineCounts(0), contentCount(0) { #ifdef WIN32 hConsole = GetStdHandle(STD_INPUT_HANDLE); SetConsoleMode(hConsole, ENABLE_PROCESSED_INPUT); // set raw mode hConsole = GetStdHandle(STD_OUTPUT_HANDLE); #else struct termios t; tcgetattr(STDIN_FILENO, &t); t.c_lflag &= ~(ICANON | ECHO); // set raw mode tcsetattr(STDIN_FILENO, TCSANOW, &t); #endif } Framework::ConsoleHandler::~ConsoleHandler() { for (int i = 0; i < contentCount; i++) { lines[i]->release(); } delete[] lines; delete[] lineCounts; } void Framework::ConsoleHandler::addContent( StickyConsoleContent* content, ConsoleContentPosition pos) { cs.lock(); if (content->isInput()) { for (int i = 0; i < contentCount; i++) { if (lines[i]->isInput()) { content->release(); cs.unlock(); throw "There can only be one input line"; } } } if (content->zConsoleHandler) { cs.unlock(); throw "A console content can only be added to one console handler " "at " "the same time."; } content->zConsoleHandler = this; contentCount++; int* lineCounts = new int[contentCount]; StickyConsoleContent** lines = new StickyConsoleContent*[contentCount]; if (pos == ConsoleContentPosition::Top) { lines[0] = content; lineCounts[0] = 0; for (int i = 1; i < contentCount; i++) { lines[i] = this->lines[i - 1]; lineCounts[i] = this->lineCounts[i - 1]; } } else { lines[contentCount - 1] = content; lineCounts[contentCount - 1] = content->print(); for (int i = 0; i < contentCount - 1; i++) { lines[i] = this->lines[i]; lineCounts[i] = this->lineCounts[i]; } } delete[] this->lines; delete[] this->lineCounts; this->lines = lines; this->lineCounts = lineCounts; InputLine* input = dynamic_cast(content); if (input) { input->start(); } cs.unlock(); } void Framework::ConsoleHandler::removeContent(StickyConsoleContent* zContent) { cs.lock(); int index = -1; for (int i = 0; i < contentCount; i++) { if (lines[i] == zContent) { index = i; break; } } if (index == -1) { cs.unlock(); return; } zContent->zConsoleHandler = 0; contentCount--; int* lineCounts = new int[contentCount]; StickyConsoleContent** lines = new StickyConsoleContent*[contentCount]; for (int i = 0; i < index; i++) { lines[i] = this->lines[i]; lineCounts[i] = this->lineCounts[i]; } for (int i = index; i < contentCount; i++) { lines[i] = this->lines[i + 1]; lineCounts[i] = this->lineCounts[i + 1]; } delete[] this->lines; delete[] this->lineCounts; this->lines = lines; this->lineCounts = lineCounts; if (zContent->isInput()) { InputLine* input = dynamic_cast(zContent); if (input) { input->start(); } } zContent->release(); cs.unlock(); } int Framework::ConsoleHandler::getWidth() const { #ifdef WIN32 CONSOLE_SCREEN_BUFFER_INFO csbi; GetConsoleScreenBufferInfo(hConsole, &csbi); return csbi.dwSize.X; #else struct winsize ws; if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1) { return 0; } return ws.ws_col; #endif } int Framework::ConsoleHandler::getHeight() const { #ifdef WIN32 CONSOLE_SCREEN_BUFFER_INFO csbi; GetConsoleScreenBufferInfo(hConsole, &csbi); return csbi.dwSize.Y; #else struct winsize ws; if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1) { return 0; } return ws.ws_row; #endif } void Framework::ConsoleHandler::clear() { cs.lock(); for (int i = 0; i < contentCount; i++) { lines[i]->release(); } delete[] lines; delete[] lineCounts; lines = 0; lineCounts = 0; contentCount = 0; cs.unlock(); } void Framework::ConsoleHandler::print() { cs.lock(); int totalLines = 0; bool adittionalLine = 0; for (int i = 0; i < contentCount; i++) { if (lines[i]->isInput()) { lines[i]->setCursorToBeginning(); break; } totalLines += lineCounts[i]; } if (totalLines > 0) { std::cout << "\33[" << totalLines << "A"; } totalLines = 0; for (int i = 0; i < contentCount; i++) { lineCounts[i] = lines[i]->print(); if (lineCounts[i] > 0) { adittionalLine = 0; if (i < contentCount - 1) { std::cout << "\n"; adittionalLine = 1; } } totalLines += lineCounts[i]; } if (adittionalLine) { totalLines++; } std::cout << "\33[" << (totalLines - 1) << "A\r"; for (int i = 0; i < contentCount; i++) { if (lines[i]->isInput()) { lines[i]->restoreCursorPos(); break; } if (i < contentCount - 1 && lineCounts[i]) { std::cout << "\33[" << lineCounts[i] << "B"; } } std::cout << std::flush; cs.unlock(); } void Framework::ConsoleHandler::print(Text str) { std::cout << "\n"; cs.lock(); int totalLines = 0; bool adittionalLine = 0; for (int i = 0; i < contentCount; i++) { if (lines[i]->isInput()) { lines[i]->setCursorToBeginning(); break; } totalLines += lineCounts[i]; } std::cout << "\33[" << totalLines + 1 << "A"; std::cout << "\33[0K" // clear current line << str.getText(); if (str.getText()[str.getLength() - 1] != '\n') { std::cout << "\n"; } totalLines = 0; for (int i = 0; i < contentCount; i++) { lineCounts[i] = lines[i]->print(); if (lineCounts[i] > 0) { adittionalLine = 0; if (i < contentCount - 1) { std::cout << "\n"; adittionalLine = 1; } } totalLines += lineCounts[i]; } if (adittionalLine) { totalLines++; } std::cout << "\33[" << (totalLines - 1) << "A\r"; for (int i = 0; i < contentCount; i++) { if (lines[i]->isInput()) { lines[i]->restoreCursorPos(); break; } if (i < contentCount - 1 && lineCounts[i]) { std::cout << "\33[" << lineCounts[i] << "B"; } } std::cout << std::flush; cs.unlock(); }