diff --git a/Console/ConsoleTest.cc b/Console/ConsoleTest.cc index 587490fddb..b32da0ea83 100644 --- a/Console/ConsoleTest.cc +++ b/Console/ConsoleTest.cc @@ -9,7 +9,7 @@ namespace retro int main() { retro::InitConsole(); - std::string out = "Hello, world.\nEnter \"exit\" to quit.\n"; + std::string out = "Hello, \033[1mexternal world of \033[0m\033[3mtrue beauty and \033[4mgreatness\033[0m.\nEnter \"exit\" to quit.\n"; retro::Console::currentInstance->write(out.data(), out.size()); std::string in; diff --git a/Console/readme.txt b/Console/readme.txt new file mode 100644 index 0000000000..6abd8b4d30 --- /dev/null +++ b/Console/readme.txt @@ -0,0 +1,17 @@ + IConsole + +This is a slightly improved version of Retro68's Console library, offering a +support for a subset of ANSI control sequences. + +Here is a list of the supported sequences and their meaning: + +ESC[0m Reset all text style +ESC[1m Bold font (*) +ESC[3m Italic font +ESC[4m Underline font + + +NOTES: +(*) Obtained with bold + condense style together, so that the character grid +remains regular (this way, the width of bold characters remains the same as +regular ones). diff --git a/Console/retro/Console.cc b/Console/retro/Console.cc index 41db6cd57e..6024a8bcc0 100644 --- a/Console/retro/Console.cc +++ b/Console/retro/Console.cc @@ -1,5 +1,5 @@ /* - Copyright 2012 Wolfgang Thaller. + Copyright 2012-2020 Wolfgang Thaller, Davide Bucci This file is part of Retro68. @@ -32,11 +32,73 @@ using namespace retro; Console *Console::currentInstance = NULL; +Attributes::Attributes(void) +{ + reset(); +} +void Attributes::reset(void) +{ + cBold=false; + cUnderline=false; + cItalic=false; +} + +bool Attributes::isBold(void) const +{ + return cBold; +} + + +bool Attributes::isUnderline(void) const +{ + return cUnderline; +} + +bool Attributes::isItalic(void) const +{ + return cItalic; +} + +void Attributes::setBold(const bool v) +{ + cBold=v; +} + +void Attributes::setItalic(const bool v) +{ + cItalic=v; +} + +void Attributes::setUnderline(const bool v) +{ + cUnderline=v; +} + +inline bool operator==(const Attributes& lhs, const Attributes& rhs) +{ + return lhs.isBold()==rhs.isBold() && lhs.isUnderline()==rhs.isUnderline() && lhs.isItalic()==rhs.isItalic(); +} + +inline bool operator!=(const Attributes& lhs, const Attributes& rhs) +{ + return !(lhs == rhs); +} + +inline bool operator==(const AttributedChar& lhs, const AttributedChar& rhs) +{ + return lhs.c==rhs.c && lhs.attrs==rhs.attrs; +} + +inline bool operator!=(const AttributedChar& lhs, const AttributedChar& rhs) +{ + return !(lhs == rhs); +} + namespace { class FontSetup { - short saveFont, saveSize; + short saveFont, saveSize, saveFace; public: FontSetup() { @@ -48,15 +110,18 @@ namespace #else saveFont = qd.thePort->txFont; saveSize = qd.thePort->txSize; + saveFace = qd.thePort->txFace; #endif TextFont(kFontIDMonaco); TextSize(9); + TextFace(normal); } ~FontSetup() { TextFont(saveFont); TextSize(saveSize); + TextFace(saveFace); } }; } @@ -80,23 +145,29 @@ void Console::Init(GrafPtr port, Rect r) { consolePort = port; bounds = r; - + PortSetter setport(consolePort); FontSetup fontSetup; InsetRect(&bounds, 2,2); - + cellSizeY = 12; cellSizeX = CharWidth('M'); - + rows = (bounds.bottom - bounds.top) / cellSizeY; cols = (bounds.right - bounds.left) / cellSizeX; - chars = std::vector(rows*cols, ' '); + chars = std::vector(rows*cols, AttributedChar(' ',currentAttr)); onscreen = chars; cursorX = cursorY = 0; + isProcessingEscSequence=false; +} + +void Console::SetAttributes(Attributes aa) +{ + TextFace(aa.isBold()?bold+condense:0 + aa.isUnderline()?underline:0 + aa.isItalic()?italic:0); } Rect Console::CellRect(short x, short y) @@ -120,14 +191,13 @@ void Console::DrawCell(short x, short y, bool erase) if(erase) EraseRect(&r); MoveTo(r.left, r.bottom - 2); - DrawChar(chars[y * cols + x]); + DrawChar(chars[y * cols + x].c); } void Console::DrawCells(short x1, short x2, short y, bool erase) { Rect r = { (short) (bounds.top + y * cellSizeY), (short) (bounds.left + x1 * cellSizeX), - (short) (bounds.top + (y+1) * cellSizeY), (short) (bounds.left + x2 * cellSizeX) }; - + (short) (bounds.top + (y+1) * cellSizeY), (short) (bounds.left + x2 * cellSizeX) }; if(cursorDrawn) { if(y == cursorY && x1 <= cursorX && x2 > cursorX) @@ -136,11 +206,21 @@ void Console::DrawCells(short x1, short x2, short y, bool erase) cursorDrawn = false; } } - + if(erase) EraseRect(&r); MoveTo(r.left, r.bottom - 2); - DrawText(&chars[y * cols + x1], 0, x2 - x1); + + Attributes a=chars[y * cols + x1].attrs; + SetAttributes(a); + for(int i=x1; i 0 ? dirtyRect.bottom - 1 : 0; } +void Console::ProcessEscSequence(char c) +{ + switch(sequenceStep) + { + case 0: + if(c=='[') + ++sequenceStep; + else + isProcessingEscSequence=false; + break; + case 1: + ++sequenceStep; + switch(c) + { + case '0': // Normal character + currentAttr.reset(); + break; + case '1': // Bold + currentAttr.setBold(true); + break; + case '3': // Italic + currentAttr.setItalic(true); + break; + case '4': // Underline + currentAttr.setUnderline(true); + break; + default: + isProcessingEscSequence=false; + } + break; + case 2: + if(c=='m') + isProcessingEscSequence=false; + else if(c==';') + sequenceStep=1; + else + isProcessingEscSequence=false; + break; + default: + sequenceStep=0; + } +} void Console::PutCharNoUpdate(char c) { + if(isProcessingEscSequence) + { + ProcessEscSequence(c); + return; + } InvalidateCursor(); switch(c) { + case '\033': // Begin of an ANSI escape sequence + isProcessingEscSequence=true; + sequenceStep=0; + break; case '\r': cursorX = 0; break; @@ -201,7 +332,9 @@ void Console::PutCharNoUpdate(char c) ScrollUp(); break; default: - chars[cursorY * cols + cursorX] = c; + chars[cursorY * cols + cursorX].c = c; + chars[cursorY * cols + cursorX].attrs = currentAttr; + if(dirtyRect.right == 0) { dirtyRect.right = (dirtyRect.left = cursorX) + 1; @@ -232,12 +365,12 @@ void Console::Update() bool needclear = false; for(short col = dirtyRect.left; col < dirtyRect.right; ++col) { - char old = onscreen[row * cols + col]; + AttributedChar old = onscreen[row * cols + col]; if(chars[row * cols + col] != old) { if(start == -1) start = col; - if(old != ' ') + if(old.c != ' ') needclear = true; onscreen[row * cols + col] = chars[row * cols + col]; } @@ -253,7 +386,7 @@ void Console::Update() DrawCells(start, dirtyRect.right, row, needclear); } dirtyRect = Rect(); - + if(cursorVisible != cursorDrawn) { Rect r = CellRect(cursorX, cursorY); @@ -281,7 +414,7 @@ void Console::write(const char *p, int n) { if(!rows) return; - + for(int i = 0; i < n; i++) Console::currentInstance->PutCharNoUpdate(*p++); Update(); @@ -295,9 +428,9 @@ std::string Console::ReadLine() std::string buffer; char c; - + do - { + { c = WaitNextChar(); if(!c) { @@ -307,7 +440,7 @@ std::string Console::ReadLine() if(c == '\r') c = '\n'; - + if(c == '\b') { if(buffer.size()) @@ -323,7 +456,7 @@ std::string Console::ReadLine() continue; } - + putch(c); buffer.append(1,c); } while(c != '\n'); @@ -335,7 +468,6 @@ void Console::InvalidateCursor() if(cursorDrawn) { PortSetter setport(consolePort); - DrawCell(cursorX, cursorY, true); cursorDrawn = false; } @@ -367,16 +499,16 @@ void Console::Reshape(Rect newBounds) if(cursorY >= newRows) { upshift = cursorY - (newRows - 1); - + InvalidateCursor(); cursorY = newRows - 1; } - std::vector newChars(newRows*newCols, ' '); + std::vector newChars(newRows*newCols, AttributedChar(' ', currentAttr)); for(short row = 0; row < newRows && row + upshift < rows; row++) { - char *src = &chars[(row+upshift) * cols]; - char *dst = &newChars[row * newCols]; + AttributedChar *src = &chars[(row+upshift) * cols]; + AttributedChar *dst = &newChars[row * newCols]; std::copy(src, src + std::min(cols, newCols), dst); } chars.swap(newChars); @@ -389,7 +521,7 @@ void Console::Reshape(Rect newBounds) } onscreen.swap(newChars);*/ onscreen = newChars; - + rows = newRows; cols = newCols; diff --git a/Console/retro/Console.h b/Console/retro/Console.h index 6d626125a0..cc5df88403 100644 --- a/Console/retro/Console.h +++ b/Console/retro/Console.h @@ -1,5 +1,5 @@ /* - Copyright 2012 Wolfgang Thaller. + Copyright 2012-2020 Wolfgang Thaller, Davide Bucci This file is part of Retro68. @@ -29,6 +29,39 @@ namespace retro { + class Attributes + { + public: + + bool isBold(void) const; + bool isUnderline(void) const; + bool isItalic(void) const; + + void setBold(const bool v); + void setUnderline(const bool v); + void setItalic(const bool v); + + Attributes(void); + void reset(void); + + private: + + bool cBold; + bool cUnderline; + bool cItalic; + }; + + class AttributedChar + { + public: + char c; + Attributes attrs; + AttributedChar(char cc, Attributes aa) {c=cc; attrs=aa;} + }; + +// inline bool operator==(const Attributes& lhs, const Attributes& rhs); +// inline bool operator!=(const Attributes& lhs, const Attributes& rhs); + class Console { public: @@ -49,15 +82,18 @@ namespace retro short GetRows() const { return rows; } short GetCols() const { return cols; } - + void Idle(); bool IsEOF() const { return eof; } private: GrafPtr consolePort = nullptr; Rect bounds; + Attributes currentAttr; - std::vector chars, onscreen; + std::vector chars, onscreen; + bool isProcessingEscSequence; + int sequenceStep; short cellSizeX; short cellSizeY; @@ -67,7 +103,7 @@ namespace retro short cursorX, cursorY; Rect dirtyRect = {}; - + long blinkTicks = 0; bool cursorDrawn = false; bool cursorVisible = true; @@ -76,18 +112,21 @@ namespace retro void PutCharNoUpdate(char c); void Update(); + short CalcStartX(short x, short y); Rect CellRect(short x, short y); void DrawCell(short x, short y, bool erase = true); void DrawCells(short x1, short x2, short y, bool erase = true); void ScrollUp(short n = 1); - + void ProcessEscSequence(char c); + void SetAttributes(Attributes aa); + void InvalidateCursor(); virtual char WaitNextChar(); - + protected: void Init(GrafPtr port, Rect r); - + }; diff --git a/Console/retro/ConsoleWindow.cc b/Console/retro/ConsoleWindow.cc index 89fe514e25..5740ede97f 100644 --- a/Console/retro/ConsoleWindow.cc +++ b/Console/retro/ConsoleWindow.cc @@ -1,5 +1,5 @@ /* - Copyright 2012 Wolfgang Thaller. + Copyright 2012-2020 Wolfgang Thaller, Davide Bucci This file is part of Retro68. @@ -35,8 +35,8 @@ namespace ConsoleWindow::ConsoleWindow(Rect r, ConstStr255Param title) { GrafPtr port; - - win = NewWindow(NULL, &r, "\pRetro68 Console", true, 0, (WindowPtr)-1, false, 0); + //Retro68 Improved Console + win = NewWindow(NULL, &r, "\pRetro68 Console", true, 0, (WindowPtr)-1, true, 0); #if !TARGET_API_MAC_CARBON port = win; @@ -73,7 +73,7 @@ char ConsoleWindow::WaitNextChar() #else Rect *boundsPtr = &qd.screenBits.bounds; #endif - + do { #if TARGET_API_MAC_CARBON @@ -86,7 +86,7 @@ char ConsoleWindow::WaitNextChar() SystemTask(); Idle(); } - + switch(event.what) { case updateEvt: @@ -109,7 +109,6 @@ char ConsoleWindow::WaitNextChar() } break; case mouseDown: - switch(FindWindow(event.where, &eventWin)) { case inDrag: @@ -122,10 +121,16 @@ char ConsoleWindow::WaitNextChar() Reshape(Rect {0, 0, (short) (growResult >> 16), (short) (growResult & 0xFFFF) }); } break; + case inGoAway: + { + if (TrackGoAway(eventWin,event.where)) + exit(0); + } + break; } break; } } while(event.what != keyDown && event.what != autoKey); - + return event.message & charCodeMask; } diff --git a/Console/retro/ConsoleWindow.h b/Console/retro/ConsoleWindow.h index e0737ff0ff..e3d71c33bd 100644 --- a/Console/retro/ConsoleWindow.h +++ b/Console/retro/ConsoleWindow.h @@ -1,5 +1,5 @@ /* - Copyright 2012 Wolfgang Thaller. + Copyright 2012-2020 Wolfgang Thaller, Davide Bucci This file is part of Retro68. diff --git a/Console/retro/InitConsole.cc b/Console/retro/InitConsole.cc index b50b8e30de..7824815b07 100644 --- a/Console/retro/InitConsole.cc +++ b/Console/retro/InitConsole.cc @@ -1,17 +1,17 @@ /* - Copyright 2014 Wolfgang Thaller. + Copyright 2014 Wolfgang Thaller. - This file is part of Retro68. + This file is part of Retro68. - Retro68 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. + Retro68 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. - Retro68 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. + Retro68 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. Under Section 7 of GPL version 3, you are granted additional permissions described in the GCC Runtime Library Exception, version